TypeScript 미드레벨 시스템 설계 면접
새 면접Q. RESTful API를 설계할 때 리소스 중심 설계와 RPC 스타일 설계의 차이를 설명하고, 주문 취소 기능을 구현할 때 PATCH /orders/{id} (status: cancelled) vs POST /orders/{id}/cancel 중 어떤 방식을 선택할지 판단 기준을 설명해주세요.
RESTful API는 리소스의 상태를 HTTP 메서드로 조작하는 방식이고, RPC 스타일은 동작(action)을 URL에 명시하는 방식입니다. 주문 취소는 단순 상태 변경이 아니라 재고 복구, 결제 취소, 알림 발송 등 복잡한 비즈니스 로직을 포함하므로 POST /orders/{id}/cancel이 더 적합합니다. 단순 필드 업데이트는 PATCH를 사용하고, 복잡한 비즈니스 트랜잭션은 명시적인 액션 엔드포인트를 사용하는 것이 의도를 명확히 전달합니다. TypeScript에서는 각 엔드포인트의 요청/응답 타입을 명확히 정의하여 API 계약을 강제할 수 있습니다.
- • RESTful은 리소스 중심, RPC는 동작 중심 설계
- • 복잡한 비즈니스 로직은 명시적 액션 엔드포인트 사용
- • TypeScript 타입으로 API 계약 명확화
- • 의도의 명확성과 유지보수성 고려
GraphQL을 도입한다면 주문 취소 기능을 어떻게 설계할 것이며, REST API 대비 어떤 장단점이 있을까요?
Q. 전자상거래 시스템에서 상품의 가격 이력을 관리해야 할 때, 현재 가격을 products 테이블에 컬럼으로 두고 이력을 별도 테이블로 관리하는 방식과 모든 가격을 이력 테이블에만 저장하고 최신 레코드를 조회하는 방식의 트레이드오프를 설명하고, TypeScript ORM(TypeORM, Prisma)에서 각 방식을 구현할 때 고려사항을 설명해주세요.
현재 가격을 products 테이블에 중복 저장하는 방식은 조회 성능이 우수하지만 데이터 정합성 관리가 필요하고, 이력 테이블만 사용하는 방식은 정합성은 보장되지만 매번 최신 레코드를 찾는 조인이 필요합니다. 실무에서는 읽기 성능이 중요하므로 현재 가격을 중복 저장하고 트랜잭션으로 정합성을 보장하는 방식을 선호합니다. TypeScript ORM에서는 @AfterInsert, @AfterUpdate 같은 엔티티 리스너나 트랜잭션 데코레이터를 활용해 가격 변경 시 이력 자동 생성과 현재 가격 업데이트를 원자적으로 처리합니다. Prisma에서는 $transaction API로 여러 쿼리를 묶어 처리할 수 있습니다.
- • 조회 성능 vs 데이터 정합성 트레이드오프
- • 중복 저장 시 트랜잭션으로 일관성 보장
- • ORM 엔티티 리스너 활용
- • 읽기 패턴에 따른 최적화 전략
가격 변경이 매우 빈번하고 이력 조회도 자주 발생하는 경우, 이벤트 소싱(Event Sourcing) 패턴을 적용하는 것이 적합할까요?
Q. Redis를 사용한 캐싱 전략 중 Cache-Aside와 Write-Through 패턴의 차이를 설명하고, 소셜 미디어의 피드 시스템에서 사용자별 타임라인을 캐싱할 때 어떤 패턴이 적합한지 캐시 무효화(invalidation) 전략과 함께 설명해주세요. TypeScript에서 데코레이터를 활용한 캐싱 추상화 방법도 포함해주세요.
Cache-Aside는 애플리케이션이 캐시를 직접 관리하며 캐시 미스 시 DB 조회 후 캐시에 저장하는 방식이고, Write-Through는 쓰기 시 캐시와 DB를 동시에 업데이트하는 방식입니다. 피드 시스템은 읽기가 압도적으로 많고 실시간성이 중요하므로 Cache-Aside가 적합하며, 새 게시물 작성 시 팔로워의 캐시를 즉시 무효화하거나 TTL을 짧게 설정합니다. TypeScript에서는 메서드 데코레이터로 @Cacheable(key, ttl)을 구현하여 Redis 조회/저장 로직을 추상화하고, 제네릭 타입으로 캐시 데이터의 타입 안정성을 보장할 수 있습니다. ioredis 같은 라이브러리와 결합하여 파이프라인, 트랜잭션도 지원 가능합니다.
- • Cache-Aside는 lazy loading, Write-Through는 eager loading
- • 읽기 중심 시스템에서 Cache-Aside 선호
- • 캐시 무효화 전략: TTL, 이벤트 기반 무효화
- • TypeScript 데코레이터로 캐싱 로직 추상화
캐시 스탬피드(Cache Stampede) 문제가 발생할 수 있는 시나리오와 이를 방지하기 위한 Lock 메커니즘을 설명해주세요.
Q. 마이크로서비스 아키텍처에서 분산 트랜잭션을 처리하기 위한 Saga 패턴의 Orchestration 방식과 Choreography 방식의 차이를 설명하고, TypeScript 기반 Node.js 환경에서 각 방식을 구현할 때 메시지 브로커(RabbitMQ, Kafka) 선택 기준과 보상 트랜잭션(Compensating Transaction) 구현 전략을 설명해주세요.
Orchestration은 중앙 조정자가 각 서비스를 순차적으로 호출하며 트랜잭션을 관리하는 방식이고, Choreography는 각 서비스가 이벤트를 발행하고 구독하며 분산 방식으로 트랜잭션을 진행하는 방식입니다. Orchestration은 플로우가 명확하지만 단일 장애점이 생기고, Choreography는 느슨한 결합이지만 디버깅이 어렵습니다. RabbitMQ는 복잡한 라우팅과 재시도가 필요한 Orchestration에 적합하고, Kafka는 이벤트 스트림과 순서 보장이 중요한 Choreography에 적합합니다. TypeScript에서는 각 단계의 상태를 타입으로 정의하고, 실패 시 이전 단계의 보상 트랜잭션을 실행하는 State Machine을 구현하며, 멱등성 키로 중복 처리를 방지합니다.
- • Orchestration은 중앙 집중, Choreography는 분산 방식
- • RabbitMQ vs Kafka 선택 기준
- • 보상 트랜잭션으로 일관성 유지
- • TypeScript 타입 시스템으로 상태 관리
Saga 패턴에서 격리성(Isolation) 문제를 해결하기 위한 Semantic Lock 패턴을 설명해주세요.
Q. JWT 기반 인증 시스템에서 Access Token과 Refresh Token을 분리하는 이유를 설명하고, Refresh Token Rotation 전략의 보안상 이점과 TypeScript Express 미들웨어에서 토큰 갱신 로직을 구현할 때 고려해야 할 Race Condition 문제와 해결 방법을 설명해주세요.
Access Token은 짧은 만료 시간으로 탈취 위험을 최소화하고, Refresh Token은 긴 만료 시간으로 사용자 편의성을 제공하며 서버에서 무효화 가능하도록 분리합니다. Refresh Token Rotation은 토큰 사용 시마다 새 토큰을 발급하고 이전 토큰을 무효화하여 토큰 재사용 공격을 방지합니다. TypeScript 미들웨어에서는 동시에 여러 요청이 토큰 갱신을 시도할 때 Redis의 SETNX나 분산 락을 사용해 한 번만 갱신되도록 보장하고, 갱신 중인 요청은 대기 후 새 토큰을 받도록 구현합니다. 타입 가드로 토큰 페이로드 검증을 강화하고, 환경변수로 비밀키를 관리합니다.
- • Access Token 짧은 TTL, Refresh Token 긴 TTL 분리
- • Rotation으로 재사용 공격 방지
- • Redis 분산 락으로 Race Condition 해결
- • TypeScript 타입 가드로 페이로드 검증
Refresh Token을 HttpOnly 쿠키에 저장하는 방식과 LocalStorage에 저장하는 방식의 보안 차이와 XSS, CSRF 공격 관점에서의 위험성을 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!