[CS] SSR에 대해 설명해주세요

[ TIL ]2026. 4. 17. 10:06

2026.04.17

SSR(Server Side Rendering)이란 서버에서 완성된 HTML을 만들어서 내려주는 방식을 의미합니다. 브라우저는 받아온 HTML을 바로 그려서 초기 화면이 빠르게 보입니다.

 

장점

- SEO에 강함 (크롤러가 콘텐츠를 바로 읽음)

- 초기 로딩 속도 빠름 -> 첫인상 좋음

 

단점

- 요청마다 HTML 생성 -> 서버 비용 증가 가능

- 전통 SSR은 페이지 전환이 부자연스러움

 

이와 반대되는 개념으로는 CSR(Client Side Rendering)이 있습니다. 빈 HTML + JS 번들을 받아서 브라우저가 직접 렌더링한다는 특징을 가지고 있습니다.

 

장점

- SPA처럼 부드러운 페이지 전환

- 한 번 로드 이후 인터넥션 빠름

 

단점

- JS 다운로드 및 실행으로 인한 초기 로딩 성능 저하 이슈

- SEO 불리

 

추가로 Next.js에서는 SSR과 CSR을 모두 사용하는데요, 초기에는 SSR로 빠르게 HTML을 보여주고 이후 hydration을 통해 JS를 붙여서 동적으로 전환합니다. 이로 인한 단점으로는 hydration 전까지 입력을 받지 못하는 기간이 존재한다는 점과, 서버/클라이언트 로직 분리가 필요해 구현 복잡도가 높다는 특징이 있습니다.

[CS] useEffect와 useLayoutEffect의 차이점에 대해 설명해주세요

[ TIL ]2026. 4. 15. 11:35

2026.04.15

리액트에서 사이드 이펙트를 처리할 때 가장 많이 사용하는 훅이 바로 useEffect와 useLayoutEffect입니다.

둘 다 렌더링 이후 실행된다는 공통점이 있지만 실행 타이밍과 사용 목적에서 중요한 차이가 있습니다.

 

useEffect - 렌더링 이후 실행되는 작업

useEffect는 컴포넌트가 렌더링된 이후, 브라우저가 화면을 모두 그린 다음 실행됩니다.

즉, 사용자에게 이미 UI가 보인 상태에서 비동기적으로 실행됩니다.

 

이러한 특징 덕분에 useEffect는 화면 렌더링을 방해하지 않으며, 비교적 가벼운 작업이나 UI에 직접적인 영향을 주지 않는 작업을 처리하는 데 적합합니다.

 

대표적으로는 다음과 같은 경우에 사용됩니다.

- API 요청을 통한 데이터 fetching

- 이벤트 리스너 등록 및 해제

- 콘솔 로그 또는 외부 라이브러리 호출

useEffect(() => {
	fetchData().then(data => setData(data));
}, []);

위와 같은 코드는 렌더링이 완료된 이후 데이터를 가져와 상태를 업데이트하며, 사용자 입장에서는 자연스럽게 화면이 갱신되는 흐름을 경험하게 됩니다.

 

useLayoutEffect - 화면이 그려지기 전에 실행되는 작업

반면 useLayoutEffect는 렌더링이 완료된 직후 실행되지만, 브라우저가 화면을 그리기 전에 동기적으로 실행됩니다.

이 말은 곧, useLayoutEffect 내부의 작업이 모두 끝나기 전까지는 화면이 사용자에게 보이지 않는다는 것을 의미합니다.

 

이러한 특성은 특히 레이아웃 계산이나 DOM 조작이 필요한 경우에 강력하게 작용합니다. 예를 들어 특정 요소의 크기를 측정한 뒤 그 값을 기반으로 레이아웃을 조정해야 하는 상황을 생각해볼 수 있습니다.

useLayoutEffect(() => {
	const height = ref.current.offsetHeight;
    setHeight(height);
}, []);

이 경우 useEffect를 사용하면 이미 한 번 화면이 그려진 뒤 값이 변경되면서 깜빡임이 발생할 수 있지만, useLayoutEffect를 사용하면 최종 레이아웃이 계산된 상태로 한 번에 렌더링됩니다.

 

언제 어떤 것을 사용해야 할까?

일반적인 경우엔 useEffect를 사용하는 것이 좋다고 생각합니다. 비동기적으로 실행되기에 렌더링 성능에 부담을 주지 않기 떄문이며,

아래와 같은 몇몇 상황에서는 useLayoutEffect 사용을 고려해볼 수 있을 것 같습니다.

- 애니메이션의 초기 위치를 정확하게 맞춰야 하는 경우

- UI가 순간적으로 깨지는 현상을 방지해야 하는 경우

- DOM의 크기나 위치를 측정해야 하는 경우

 

useLayoutEffect 사용 시 주의사항

useLayoutEffect는 동기적으로 실행되기 때문에, 내부에서 무거운 작업이 수행될 경우 렌더링 자체를 지연시킬 수 있습니다.

즉, 잘못 사용하면 오히려 UX를 해칠 수 있습니다.

그러므로 가능하면 useEffect를 기본으로 사용하고, 정말로 화면에 영향을 주는 경우에만 useLayoutEffect를 사용하는 것이 좋아보입니다.

 

정리!

useEffect와 useLayoutEffect의 차이는 동기, 비동기의 차이보다 브라우저가 화면을 그리는 시점의 관계에 있습니다.

useEffect -> 화면이 그려진 이후 실행 (성능 친화적)

useLayoutEffect -> 화면이 그려지기 전에 실행 (레이아웃 제어에 적합)

따라서 기본적으로는 useEffect를 사용하는 것이 바람직하며,
레이아웃 계산이나 UI 깜빡임을 방지해야 하는 경우에만 useLayoutEffect를 선택적으로 사용하는 것이 중요합니다.

[알고리즘] 이분탐색 & 그리디

[ 알고리즘 ]2026. 4. 14. 11:19

2026.04.14

이분 탐색 (Binary Search)

이분 탐색은 정렬된 배열에서 원하는 값을 빠르게 찾는 알고리즘이다. 핵심 개념으로는 탐색 범위를 절반씩 줄인다는 점이다.

- 시간 복잡도: O(log N)

 

동작 방식:

1. 중간값(mid)를 정한다

2. 찾는 값(target)과 비교한다

3. 범위를 반씩 줄이며 이 과정을 반복한다

 

예시 코드:

def binary_search(arr, target):
	left = 0
    right = len(arr) - 1
    
    while left <= right:
    	mid = (left + right) // 2
        
        if arr[mid] == target:
        	return mid
        elif arr[mid] < target:
        	left = mid + 1
        else:
        	right = mid - 1
        
        return -1

 

자주 나오는 유형으로는 

1. 단순 탐색

2. Upper Bound, Lower Bound (특정 값 이상/이하의 첫 위치 찾기)

3. Parametric Search (조건을 만족하는 최소/최대값 찾기)

 

그리디 알고리즘 (Greedy)

그리디는 현재를 기준으로 최선의 선택을 하는 알고리즘이다.

시간 복잡도는 O(N) 혹은 O(N logN)을 가진다.

 

동작 방식:

1. 기준 정의 (ex. 가장 적은 값, 가장 큰 값)

2. 현재 최선 선택

3. 위 과정 반복

 

대표 문제로는 동전문제 등이 있다.

coins = [500, 100, 50, 10]
n = 1260
count = 0

for coin in coins:
	count += n // coin
    n %= coin

print(count)

 

자주 나오는 유형으로는

1. 거스름 돈

2. 회의실 배정

3. 최소 비용 / 최대 이익 등이 있다.

[CS] 리액트에서 index를 key값으로 사용하면 안되는 이유에 대해서 설명해주세요

[ TIL ]2026. 4. 13. 11:14

2026.04.13

 

리액트에서 index를 key로 사용하는 것이 권장되지 않는 이유는 리스트의 순서가 변경될 경우 리액트의 Reconciliation 과정에서 요소를 잘못 식별할 수 있기 때문입니다.

 

리액트는 key를 기준으로 이전 Virtual DOM과 비교하여 어떤 요소가 변경, 추가, 삭제되었는지를 판단합니다. 하지만 index를 key로 사용하면 요소의 순서가 바뀌는 순간 key도 함께 변경되어, 리액트가 기존 요소를 다른 요소로 잘못 인식하게 됩니다.

 

이로 인해 DOM이 불필요하게 재생성되거나, input과 같은 컴포넌트의 상태가 다른 요소로 이동하는 등의 문제가 발생할 수 있습니다.

 

따라서 key는 순서 변경과 무관하게 유지되는 고유한 값을 사용하는 것이 중요하며, 일반적으로는 서버에서 제공하는 고유 ID를 사용하는 것이 가장 좋습니다. 만약 없다면 여러 필드를 조합하거나, UUID를 데이터 생성 시점에 한번만 생성하여 사용하는 방법이 있습니다.

 

단, 리스트가 절대 변경되지 않는 정적인 경우에는 index를 사용해도 큰 문제가 되지 않습니다.

[Gemini 3] Google Gemini 3 해커톤: AI 악성 댓글 분석기 프론트 개발기

[ 해커톤 ]2026. 4. 10. 16:22

2026.04.10

지난 2월 28일, 세빛 둥둥섬에서 열림 Google Gemini 3 Seoul Hackathon에 참여했습니다! 운이 좋게도 200명 선발에 들게되어 먼 여정을 떠났다 왔는데요, Social Good을 주제로, 유튜브 계정에 달리는 스팸, 욕설, 성적인 악성 댓글들을 AI로 분석해 리포트를 제공해주는 웹 서비스를 개발했습니다. 저희는 4명의 인원으로 진행했으며, 당일에 어쩌다보니,, 같이 앉아있는 분들께 간택되어 팀을 찾을 수 있었습니다. 다행히 스택이 겹치지는 않아 프론트엔드 전반을 설계하고 구현하게 되었습니다!

기술 스택 선정

사실 이번에 ai를 전문으로 다루는 분들과는 처음 협업을 진행했던 거라 많이 두려웠는데(+3분 다 현업자) 다행히 스프링쪽 백엔드와 소통하는 것과 크게 다른 점은 없어서 임무를 잘 완수 할 수 있었던 것 같습니다. 프론트는 Next.js, 타스, 테일윈드, React Context, hooks를 사용했습니다. 차트 쪽이 핵심이 되어야해서 초기에는 시각화를 강화하자는 의견도 있어 recharts를 기반으로해서 3d 시각화를 진행하려고 했으나 이는 무산되어 svg 기반 커스텀 차트로 진행하기로 했습니다. 추가로 백엔드는 FastAPI와 LangGraph를 사용했습니다!

기술적 도전

7시간동안 진행하는 데이톤이다보니 기획 단계에선 시간이 남을거라 생각했지만 결국 또 부족하더라구요...(프엔 능력부족)

중간중간 백엔드 현업자분들과 실시간으로 소통하면서 기술적인 부분에 대해서도 얘기를 많이 나눈 점이 귀한 경험이였던 것 같습니다.

이렇게 태그에 관해서도 계속 얘기했어요
ui초안도 슥슥그리고

 

첫번째 문제 - 태그 관련 문제

이런 식으로 프롬프트/rule설정을 진행했는데 아무래도 ai모델에 따라 분석이 많이 튀는 부분이 있더라구요. 심지어 영문 영상일 경우/한글 영상인 경우 쓰는 비속어나 슬랭들이 각각 다르기에 언어별로 설정해야한다는 문제도 있었습니다.. 일단 한글영상들만 집중해서 해보기로 정하고 시작했습니다.

 

두번째 문제 - 맥락 파악 못함 이슈

영상에서 잠깐 나온 네일샵에 대한 부정적 코멘트도 전부 영상 주인의 악플로 감지하고 리뷰하는 등, 이러한 문제가 발생되었으나 시간 부족 이슈로 일단 다른 영상으로 데모 촬영을 진행해 해결했습니다. 이 부분을 해결하는게 조금 어렵지 않을까 싶어요..

좋았던 점

욕설 탐지기가 꽤나 잘 나와서 기뻤던 것 같다. 한국어에 있는 특유의 비꼬는 말투나 초성 욕설 등을 잡아내고, 대시보드에 시각화까지 시키는 걸 단시간에 완료했다는 점에서 뿌듯했었다.

그리고 세빛둥둥섬! 반포는 한번 예전에 와봤던 기억이 있는데 둥둥섬쪽은 아예 처음이여서 장소 자체가 좋았던 것 같다. 

 

밥도 호텔식이였어서 너무 맛있었고,, 무료 해커톤인데 제미나이 20달러 토큰 + 맛있는 밥 나쁘지 않은거 같기도..

아쉬웠던 점

초기 ui를 피그마로 직접 그리고, mcp서버로 커서한테 넘겨서 디자인 짜게 하고 그 이후의 디자인 디벨롭은 커서를 통해서 진행했는데 그냥 초기단계부터 prd만 뽑아서 디자인도 넘겨서 작업하는게 훨씬 빠르고.. 토큰도 덜 들었지 않을까.. 라는 생각이 있다.

 

그리고 그래도 학생 때 수업도 듣고, 생성형 ai로 작업 해보고 랭체인도 써보고 했어서 소통이 원활하지 않을까? 라는 막연한 자신감이 있었는데 막상 실제 현업자 분들과 대화하니 모르는 단어 투성이였다. 랭그래프도 대충 자연어 처리 프레임워크? 라는 내용만 알았고 사용법이나 이런 걸 처음봐서 백엔드쪽 코드는 작업하면서 거의 무지했던 것 같다.. 중간중간 팀장님이 클로드로 스트럭처 그려서 설명해주시고.. 그래서 다행이였다.

 

웨딩홀..? 그런 데에서 진행했었는데 한정된 자리에 비해 인원이 너무 몰려 자리가 없던 문제가 있었었다. 심지어 assign도 400명이 넘는 인원을 assign해두고 그 중 250명만 선착순 입장이라길래 갈지말지 고민을 꽤나 많이 했었다..(집에서 1시간 50분) 장소 공지 자체도 대회 3일 전에 디스코드로 진행했어서 진행이 조금 매끄럽지 않았던 점이 아쉬웠던 것 같다.

 

프로젝트 결과물은 아래 깃허브에서 확인하실 수 있습니다!

https://github.com/euisuk-chung/gemini-hak-creator-hub 

[알고리즘] 재귀 & DFS 문제 정리

[ 알고리즘 ]2026. 4. 7. 19:01

2026.04.07

1. 재귀 (Recursion)

재귀란 함수 안에서 자기 자신을 다시 호출하는 방식을 말한다.

def func():
	func()

이런 식으로 사용되며, 무한 루프의 특성을 가지기에 반드시 종료조건 (base condition)이 필요하다.

 

재귀는 문제를 더 작게 쪼갠다는 구조를 가지며 아래 코드블럭처럼 정리가 가능하다.

def recursion(상태):
	if 종료조건:
    	return 값
       
    return recursion(더 작은 문제)

 

예제: 팰린드롬 판별

def is_palindrome(s, l, r):
	if l >= r:
    	return True
    if s[l] != s[r]:
    	return False
    return is_palindrome(s, l+1, r-1)

개인적으로는 종료 조건을 잘못 설정하거나 호출 순서를 이해 못하고 코드를 구성하는 일 때문에 접근이 어려웠던 것 같다.

2. DFS (Depth-First Search)

DFS란 깊이 우선 탐색으로 한 방향으로 끝까지 갔다가 돌아오는 방식을 말한다. 기본적으로 아래의 구조를 가진다.

def dfs(idx):
	if 종료조건:
    	return
        
    for 선택지:
    	dfs(다음 상태)

아래는 예제 문제이다.

# 타겟 넘버
def solution(numbers, target):
	answer = 0
    
    def dfs(idx, total):
    	nonlocal answer
        
        if idx == len(numbers):
        	if total == target:
            	answer += 1
            return
            
        dfs(idx+1, total + numbers[idx]
        dfs(idx+1, total - numbers[idx]
    
    dfs(0, 0)
    return answer

개인적으로 생각하기에 재귀로직 + 트리 구조라고 생각한다.

각 단계가 노드가 되고, 선택지는 브랜치, 마지막 끝이 리프 노드라고 생각하고 구조를 짰던 것 같다.

[CS] CommonJS와 ES Module의 차이점에 대해서 설명해주세요

[ TIL ]2026. 4. 7. 10:15

2026.04.07

CommonJS와 ES Module(ESM)은 자바스크립트에서 모듈을 관리하고 불러오는 두 가지 주요 방식입니다.

 

먼저 CommonJS는 주로 Node.js 환경에서 사용되며, 모듈을 동기적으로 불러옵니다. 즉 require로 모듈을 가져올 때 해당 파일의 실행이 완료될 때까지 다음 코드가 진행되지 않습니다. 모듈을 내보낼 때는 module.exports를 사용합니다.

// CommonJS
const moduleA = require('./moduleA');
module.exports = { foo };

반면 ES Module은 ECMAScript 2015(ES6)부터 도입된 공식 표준 모듈 시스템입니다. import와 export 문법을 사용하며, 정적 구조를 가지기 때문에 빌드 단계에서 의존성 분석이 가능합니다.

// ESM
import { foo } from './moduleA.js';
export default foo;

ESM은 정적 분석이 가능해 트리 쉐이킹과 같은 최적화에 유리하며, 필요할 경우 import()를 통해 비동기적으로 모듈을 로드할 수도 있습니다.

const moduleA = await import('./moduleA.js');

정리하면 CommonJS는 런타임 기반의 동기 로딩 방식이고, ESM은 정적 분석이 가능한 구조를 가진 모듈 시스템이라는 차이가 있습니다.

 

최근 Node.js에서도 ESM 사용은 증가하는 추세입니다. Node.js는 v12부터 ESM을 지원하며, package.json의 "type": "module" 설정을 통해 사용할 수 있습니다. 브라우저와 서버 간의 모듈 호환성을 고려할 때, 풀스택 환경에서는 ESM이 점점 더 표준으로 자리잡고 있는 추세입니다.

[CS] 자바스크립트 배열에 대해서 설명해주세요

[ TIL ]2026. 4. 2. 17:11

2026.04.02

배열이란?

자바스크립트의 배열은 순서가 있는 값들의 집합이며, 다양한 타입의 데이터를 하나의 구조에 저장할 수 있다.

const arr = [1, 'apple', true, {key: 'value'}];

- 제로 인덱스 기반 (0부터 시작)

- 서로 다른 타입 저장 가능

 

배열은 사실 객체다.

자바스크립트 배열은 특수한 객체이다.

 
typeof arr; //object

- 인덱스 → key

- 값 → value

배열 자체도 내부적으로는 

{
    0: 1,
    1: 'apple',
    ...
}

이러한 형태로 동작한다.

 

동적 크기

배열은 크기가 고정되지 않고 자동으로 확장된다.

const arr = [1, 2, 3];

arr.push(4);
console.log(arr); // [1, 2, 3, 4]

 

Sparse Array (희소 배열)

중간 인덱스를 건너뛰면 비어있는 배열이 생성된다

const arr = [1, 2, 3];
arr[5] = 6;

console.log(arr);
// [1, 2, 3, empty * 2, 6]

console.log(arr.length); //6

비어있는 값은 undefined가 아니라 empty slot으로 저장되며, 성능 저하 이슈를 일으킨다.

 

Length 속성의 특징

length는 단순 조회가 아니라 배열을 직접 변경하는 속성이다

const arr = [1, 2, 3, 4];
arr.length = 2;
console.log(arr); // [1, 2]

length로 배열을 줄일 시에 데이터가 잘리며, 늘릴 시에는 empty가 생성된다.

 

정리

자바스크립트 배열은 객체 기반의 동적 리스트로, sparse 구조를 허용하며 엔진 최적화에 따라 성능이 달라지는 자료구조다.

[CS] 리액트의 render phase와 commit phase에 대해서 설명해주세요

[ TIL ]2026. 3. 31. 11:57

2026.03.31

리액트의 렌더링 과정은 render phase와 commit phase로 나뉩니다.

 

1. Render Phase

- 변경된 state/props를 기반으로 어떤 UI가 바뀔지 계산하는 단계

- 실제 DOM은 건드리지 않고 Virtual DOM(가상 돔)에서 diff 계산

- 순수 계산 과정이기에 중단 및 재시작이 가능함 (Concurrent 특징)

- 비동기적으로 처리될 수 있음 

 

2. Commit Phase

- Render Phase에서 계산된 결과를 실제 DOM에 반영

- UI가 실제로 업데이트 됨

- 이후 useEffect 같은 사이드 이펙트 실행

[알고리즘] 파이썬 DFS & BFS 정리

[ 알고리즘 ]2026. 3. 31. 10:03

2026.03.31

DFS (Depth-First-Search)

DFS는 깊이 우선 탐색으로, 그래프에서 한 방향으로 최대한 깊게 들어간 뒤 다시 돌아오는 방식의 탐색 알고리즘이다.

그래프는 노드(node)와 간선(edge)로 구성되며, 노드는 정점(vertex)이라고도 한다.

 

그래프 탐색이란 하나의 노드를 시작으로 다수의 노드를 방문하는 것을 말한다. 또한 두 노드가 간선으로 연결되어 있다면, 두 노드는 인접하다(Adjacent)라고 표현한다.

 

그래프는 보통 두가지 방식으로 표현한다.

 

1. 인접 행렬 (Adjacent Matrix)

2차원 배열로 노드 간 연결 관계를 표현

INF = 999999999

graph = [
    [0, 7, 5],
    [7, 0, INF],
    [5, INF, 0]
]

print(graph)
# [[0,7,5],[7,0,999999999],[5,999999999,0]]

- 연결 X → INF (아주 큰 값)

- 장점: 구현 간단, 직관적

- 단점: 메모리 많이 먹음 (O(N^2))

 

2. 인접 리스트 (Adjacent List)

각 노드마다 연결된 노드를 리스트로 저장

g = [[] for _ in range(3)]

g[0].append((1,7))
g[0].append((2,5))

g[1].append((0,7))
g[2].append((0,5))

print(g)
# [[(1,7),(2,5)],[(0,7)],[(0,5)]]

- 장점: 메모리 효율 좋음

- 단점: 연결 확인은 느림

 

DFS 동작 방식

DFS는 스택 기반으로 동작함(재귀도 스택)

  1. 시작 노드를 스택에 넣고 방문 처리
  2. 스택 top 노드 기준
    1. 방문 안 한 인접 노드 → 스택 push + 방문 처리
    2. 없을 시 → pop
  3. 2번의 과정을 더 이상 수행할 수 없을 때까지 반복
  • 끝까지 체크하고 막히면 돌아오는게 메인 포인트

 

BFS는 너비 우선 탐색으로, 쉽게 말해 가까운 노드부터 탐색하는 알고리즘이다.
DFS는 최대한 멀리 있는 노드를 우선으로 탐색하는 방식으로 동작한다. BFS는 그 반대이다.
BFS에서는 선입선출 방식인 큐 자료구조를 이용하는 것이 정석이다. 인접한 노드를 반복적으로 큐에 넣도록 알고리즘을 작성하면 자연스럽게 먼저 들어온 것이 먼저나가게 되어, 가까운 노드부터 탐색을 진행하게 된다.

 

BFS는 큐 자료구조를 이용

  1. 시작 노드를 큐에 삽입하고 방문 처리를 한다.
  2. 큐에서 노드를 꺼낸다
  3. 인접 노드 중 방문 안 한 노드를 전부 큐에 삽입한다
  4. 3번의 과정을 더 이상 수행할 수 없을 때까지 반복

 

최종 비교

구분 DFS BFS
방식 깊이 우선 너비 우선
자료구조 스택
특징 끝까지 탐색 가까운 것부터
사용 예 백트래킹, 경로 탐색 최단 거리

 

[CS] 리액트의 Strict Mode에 대해 설명해주세요

[ TIL ]2026. 3. 30. 09:44

2026.03.30

프론트엔드 개발을 하다 보면 예상치 못한 버그나 사이드 이펙트 때문에 디버깅이 꼬이는 경우가 많습니다.

이때 유용하게 사용하는 것이 바로 리액트의 Strict Mode입니다.

 

Strict Mode란 개발 단계에서 잠재적인 문제를 사전에 감지하기 위한 도구입니다.

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>
);

 

 

Strict Mode의 기능으로는 먼저 오래된 라이프사이클 메서드와 비권장 API의 사용을 감지합니다. 구 버전 리액트에서 자주 사용되던 componentWillMount(), componentWillUpdate()와 같은 메서드들을 체크하고 최신 API 사용을 유도해서 코드 안정성과 유지보수성을 높여줍니다.

 

또한 컴포넌트와 일부 훅들을 일부러 두 번 실행하며, 대표적으로 함수 컴포넌트, useEffect, useState등을 초기화 시켜서 두번 동작시킵니다. 같은 입력으로 같은 결과를 출력하기 위함이며, 두 번 실행했을 때 발생하는 API 중복 호출 문제, 이벤트 중복 등록 문제와 같은 사이드 이펙트를 확인합니다.

 

이처럼 개발 단계에서 버그를 터뜨림으로서 비동기 처리 실수, 사이드 이펙트 누락, 상태 관리 오류, 메모리 누수 등의 문제를 해결합니다. 물론 코드 자체도 개발 환경에서만 두 번 실행되며, 실제 프로덕션에서는 영향을 주지 않습니다.

 

개발 단계에서 불편하더라도 컴포넌트의 성능을 상승시킬 수 있다는 점이 메리트로 다가오는 것 같습니다.

[알고리즘] 스택 파고들기

[ 알고리즘 ]2026. 3. 28. 00:15

이번 주는 본격적으로 스택(Stack) 개념을 파면서, 단순 구현에서 → “흐름을 따라가는 문제”로 넘어간 한 주였다.
처음엔 쉬웠는데, 중간에 한 번 제대로 막히면서 오히려 더 많이 배웠던 느낌..


🧩 이번 주 문제 구성

총 7문제를 풀었고 흐름은 이랬다:

배열 → 스택 기초 → 스택 응용 → 괄호

점점 난이도 올라가는 구조라 학습 흐름 자체는 꽤 좋았다.


💡 핵심 배운 흐름

1. 배열 & 기초 문제

  • 같은 숫자는 싫어
  • 배열 뒤집기
  • 거꾸로 출력

여기서는 그냥 파이썬 문법 + 기본 처리를 진행했다. 슬라이싱은 좋은거구나.. 분명 2학년 때 배웠던 내용들인데 기억이 안나서 다시 메서드를 찾아보고 했다.


2. 스택 기본 (10828)

여기까지는 솔직히 재밌었다.

  • push / pop / size / empty / top 구현
  • 리스트로 스택 구현 가능

ㅠ그냥 리스트인줄 알았어요.


3. 스택 수열 (1874)

여기서 사고가 한 번 깨졌다. 맨 처음에 i값으로 비교하면서 돌게끔 처리하려고 했는데 current를 다음에 push 할 숫자로 사용 할 생각을 못했었다. 이 개념이 없어서 계속 막혔다. 단순 비교문제가 아니란 걸 늦게 깨달아버렸다ㅠ


4. 제로 (10773)

이건 다시 쉬워졌었다. 0이면 pop하고 아니면 push되도록 구현했다.


5. 괄호 (9012)

여기서 또 막힘...

내가 했던 실수

  • stack을 매번 새로 만듦
  • 인덱스로 접근하려고 함

근데 정답 사고는

문자를 하나씩 순회하면서 상태 관리

핵심:

( → push
) → pop

그리고 pop이 안된다면 바로 실패


복잡도

  • 시간복잡도: O(n)
  • 공간복잡도: O(n)

회고

스택 수열이랑 괄호 문제에서 “아 내가 지금 잘못 생각하고 있구나”를 느낀 게 제일 컸다. 늘 느끼지만 코테는 독해싸움+암기싸움인 것 같다.

 

코드리뷰!

https://github.com/umc-codingtest-2team/codingtest-study/pull/1

[CS] 인터넷 창에 www.google.com를 입력하면 무슨 일이 일어나는지 설명해주세요.

[ TIL ]2026. 3. 27. 13:46

2026.03.27

첫번째로 DNS 조회가 진행됩니다. 사용자가 www.google.com 을 입력하면 브라우저는 먼저 이 도메인 이름을 IP 주소로 변환해야 합니다. 이 과정을 DNS 조회라고 합니다. 브라우저는 브라우저 → OS → DNS Resolver 순으로 캐시를 확인하고 www.google.com에 해당하는 IP 주소를 얻습니다.

 

두번째로 TCP 연결 수립입니다. IP 주소가 확인되면, 브라우저는 서버와 TCP 연결을 수립합니다. TCP는 데이터를 신뢰성 있게 전달하기 위한 프로토콜입니다. 이 과정에서 브라우저는 서버와 3-wat handshake를 수행합니다. 즉, 브라우저가 SYN 패킷을 보내고, 서버가 SYN-ACK 패킷을 보내며, 다시 브라우저가 ACK 패킷을 보내는 과정입니다.

 

세번째로 HTTP 요청입니다. TCP 연결이 수립되면, 브라우저는 HTTP 또는 HTTPS 요청을 보냅니다. 이 요청은 "GET / HTTP /1.1" 같은 형식으로, 웹 페이지를 요청하는 메시지입니다. 만약 HTTPS를 사용할 경우, 이 단계 이전에 SSL/TLS 핸드셰이크도 수행됩니다. 이 과정에서는 브라우저와 서버가 암호화된 연결을 설정하기 위해 보안 인증서를 교환하고, 암호화 키를 협상합니다.

 

네번째로 서버의 응답을 받습니다. 서버는 요청을 받고, 해당 리소스(HTML, CSS, JavaScript, 이미지등)를 브라우저에게 응답으로 보냅니다. 이 응답은 HTTP 응답 코드(예: 200 OK)와 함께 전달됩니다.

 

마지막으로 받은 리소스를 바탕으로 브라우저 렌더링 파이프라인을 진행합니다. DOM과 CSSOM을 생성하고, 렌더 트리를 구성한 뒤, 레이아웃과 페인트 단계를 통해 웹 페이지가 화면에 표시됩니다.

 

최종 정리

URL 입력 → DNS로 IP 조회 → TCP/TLS 연결 → HTTP 요청/응답 → 브라우저가 DOM·CSSOM 구성 후 렌더링

[CS] 리액트의 props와 state에 대해서 설명해주세요

[ TIL ]2026. 3. 25. 10:58

2026.03.25

props는 부모 컴포넌트가 자식 컴포넌트에 전달하는 데이터입니다. props는 읽기 전용(immutable)으로,

자식 컴포넌트는 props 수정이 불가능합니다.

function ChildComponent(props) {
  props.name = "New Name"; // 불변성 위반 (리렌더링 보장 X)
  return <div>{props.name}</div>;
}

리액트는 참조 비교(shallow comparison) 를 기반으로 변경을 감지하기 때문에 props를 직접 수정하면 변경이 감지되지 않아 리렌더링이 정상적으로 동작하지 않을 수 있습니다.

 

반면 state는 컴포넌트 내부에서 관리되는 값으로 동적으로 변경될 수 있으며, state가 변경되면 컴포넌트는 다시 렌더링됩니다.

 

props가 자식 컴포넌트에서 변하지 않는 이유는 무엇인가요?

props가 불변인 이유는 리액트의 단방향 데이터 흐름(one-way data flow)과 관련이 있습니다.

데이터가 부모 -> 자식 방향으로 흐르도록 제한함으로써 상태 변화를 예측 가능하고 일관성 있게 만들 수 있어 애플리케이션 상태 관리가 간단해집니다.

 

props는 읽기 전용이기 때문에, 부모 컴포넌트에서 전달된 값이 자식 컴포넌트 내에서 임의로 변경되지 않습니다. 이로 인해, 특정 상태가 어디서 어떻게 변했는지를 예측할 수 있어 버그 발생 가능성을 줄이고 디버깅을 쉽게 합니다.

 

만약 props가 변경될 수 있다면, 자식 컴포넌트는 독립적으로 동작하지 않게 되고, 재사용이 어려워질 수 있습니다. props가 불변으로 유지됨으로써 컴포넌트는 외부 입력에 의존할 뿐 내부적으로 변경하지 않아 재사용성이 높아지고, 코드의 캡슐화가 강화됩니다.

 

만약 자식 컴포넌트에서 부모 컴포넌트로 받은 props를 변경해야 한다면 어떻게 해야 할까요?

props를 직접 수정하는 대신, 부모 컴포넌트에서 state로 값을 관리하고 변경 함수를 자식에게 전달해야 합니다.

function Parent() {
  const [name, setName] = useState("Doe");

  return <Child name={name} setName={setName} />;
}

이렇게 하면 데이터는 여전히 단방향으로 흐르고, 상태는 부모 컴포넌트가 관리하므로 일관성을 유지할 수 있습니다. 이러한 기법을 상태 끌어올리기(lifting state up)라고 합니다.

 

참고 아티클 :

https://www.maeil-mail.kr/question/16

[알고리즘] 파이썬 스택, 큐, 해시맵 정리

[ 알고리즘 ]2026. 3. 22. 00:11

2026.03.21

1. 스택(Stack)

스택이란 LIFO(Last In First Out) 구조의 자료구조이다. 즉 나중에 들어온 데이터가 먼저 나간다.

대표적인 예시로는 접시 쌓기, 브라우저 뒤로가기 등이 있다.

 

파이썬에서는 보통 리스트로 구현한다.

stack = []

stack.append(1)
stack.append(2)
stack.append(3)

print(stack.pop())  # 3

2. 큐(Queue)

큐란 FIFO(First In First Out) 구조이다. 즉 먼저 들어온 데이터가 먼저 나간다.

대표적인 예시로는 줄 서기, 프린트 대기열 등이 있다.

 

파이썬에서는 보통 collections.deque를 사용하는 것이 일반적이다.

from collections import deque

queue = deque()

queue.append(1)
queue.append(2)
queue.append(3)

print(queue.popleft())  # 1

 

유명한 BFS 탐색 문제들도 보통 큐를 사용해서 해결한다.

3. 해시맵(Hash Map)

해시맵이란 key-value 형태로 데이터를 저장하는 자료구조이다.

파이썬에서는 dict가 해시맵이다.

hash_map = {}

hash_map["apple"] = 3
hash_map["banana"] = 2

print(hash_map["apple"])

 

해시맵의 가장 큰 특징(장점)은 탐색 속도가 매우 빠르다는 점이다. (평균적으로 O(1))

그래서 코테에서 빈도 계산이나 빠른 탐색이 필요할 때 사용된다.

빈도 수 계산하는 문제에서 많이 사용된다.

ex)

# 가장 많이 등장한 숫자 찾기
nums = [1,1,2,2,2,3]

count = {}

for n in nums:
    count[n] = count.get(n, 0) + 1

print(count)

4. 문제 접근 방식

코테를 처음 입문했을 때 제일 막막했던 점이다. 문제를 보고 어떻게 풀어야 할 지,,, 정말 고민이 많았는데 개인적으로 자료구조를 외우는 것보다 패턴 자체를 외우는 식으로 해결했던 것 같다(+문제 푸는 수 늘리기)

 

예를 들어서 빠른 탐색 / 빈도 계산 문제 같은 경우엔 해시맵을 쓴다, 순서대로 처리하는 문제면 큐를 쓴다. 이런 식으로 문제 패턴을 외웠던 것 같다. 외우다보니 풀이 속도가 조금씩 빨라지는 것 같았고, 어떤 자료구조를 쓰면 문제를 더 쉽게 풀 수 있는지가 중요한 것 같다.