Node.js 미드레벨 기술면접
새 면접Q. Node.js 애플리케이션에서 Cache-Aside 패턴과 Write-Through 패턴의 차이점을 설명하고, 각 패턴의 장단점과 적합한 사용 사례를 설명해주세요. 또한 캐시 일관성 문제를 해결하기 위한 전략도 포함해주세요.
Cache-Aside는 애플리케이션이 캐시를 먼저 확인하고 없으면 DB에서 조회 후 캐시에 저장하는 패턴으로, 읽기 위주 작업에 적합하며 캐시 장애 시에도 서비스가 가능합니다. Write-Through는 데이터 쓰기 시 캐시와 DB에 동시에 저장하는 패턴으로, 데이터 일관성이 높지만 쓰기 지연이 발생합니다. Cache-Aside는 구현이 간단하지만 초기 조회 시 지연이 있고, Write-Through는 항상 최신 데이터를 보장하지만 불필요한 캐시 저장이 발생할 수 있습니다. 캐시 일관성을 위해 TTL 설정, 이벤트 기반 캐시 무효화, 버전 관리 등의 전략을 사용할 수 있습니다.
- • Cache-Aside는 Lazy Loading 방식으로 읽기 최적화
- • Write-Through는 쓰기 시점에 캐시 갱신으로 일관성 보장
- • TTL과 이벤트 기반 무효화로 캐시 일관성 관리
- • 사용 패턴에 따른 적절한 캐싱 전략 선택
Write-Behind(Write-Back) 패턴은 언제 사용하며, 이 패턴의 위험성과 이를 완화하는 방법은 무엇인가요?
Q. Node.js 애플리케이션에서 외부 API 호출을 포함한 서비스 로직을 테스트할 때 Mock, Stub, Spy의 차이점을 설명하고, 각각을 사용하는 적절한 상황과 Jest 또는 Sinon을 활용한 구현 전략을 설명해주세요.
Mock은 객체의 전체 동작을 가짜로 대체하고 호출 여부와 파라미터를 검증하는 데 사용하며, 주로 의존성 전체를 대체할 때 적합합니다. Stub은 특정 함수의 반환값만 미리 정의하여 테스트 시나리오를 제어하는 데 사용하며, 외부 API 응답을 시뮬레이션할 때 유용합니다. Spy는 실제 함수를 그대로 실행하면서 호출 정보만 기록하여, 함수가 올바르게 호출되었는지 검증할 때 사용합니다. Jest에서는 jest.fn()으로 Mock을, jest.spyOn()으로 Spy를 구현하며, Stub은 mockReturnValue()나 mockResolvedValue()로 구현할 수 있습니다.
- • Mock은 전체 객체 대체 및 상호작용 검증
- • Stub은 반환값 제어로 테스트 시나리오 구성
- • Spy는 실제 동작 유지하며 호출 정보 추적
- • 테스트 목적에 따라 적절한 테스트 더블 선택
통합 테스트에서 실제 데이터베이스 대신 인메모리 DB를 사용하는 것과 Mock을 사용하는 것의 장단점을 비교해주세요.
Q. Node.js에서 Unhandled Promise Rejection과 Uncaught Exception의 차이점을 설명하고, 각각을 처리하는 방법과 process.on('unhandledRejection')과 process.on('uncaughtException') 이벤트 핸들러를 사용할 때의 주의사항 및 프로덕션 환경에서의 모니터링 전략을 설명해주세요.
Unhandled Promise Rejection은 Promise가 reject되었지만 catch 핸들러가 없을 때 발생하며, Uncaught Exception은 동기 코드에서 try-catch로 처리되지 않은 에러가 발생할 때 나타납니다. process.on('unhandledRejection')으로 처리되지 않은 Promise 에러를 잡을 수 있지만, 근본적으로는 모든 Promise에 적절한 에러 핸들링을 추가해야 합니다. process.on('uncaughtException')은 애플리케이션이 불안정한 상태가 되므로 로그를 남기고 graceful shutdown을 수행해야 하며, 절대 계속 실행해서는 안 됩니다. 프로덕션에서는 Sentry나 DataDog 같은 APM 도구로 이러한 에러를 모니터링하고, PM2나 Kubernetes의 자동 재시작 기능을 활용해야 합니다.
- • Unhandled Rejection은 비동기, Uncaught Exception은 동기 에러
- • uncaughtException 발생 시 프로세스 재시작 필수
- • 모든 비동기 코드에 명시적 에러 핸들링 구현
- • APM 도구와 프로세스 매니저로 에러 모니터링 및 복구
Domain 모듈이 deprecated된 이유와 이를 대체하는 현대적인 에러 처리 방법은 무엇인가요?
Q. Node.js 애플리케이션에서 N+1 쿼리 문제가 무엇인지 설명하고, Sequelize나 TypeORM 같은 ORM을 사용할 때 이 문제가 발생하는 구체적인 상황과 DataLoader 패턴을 활용한 해결 방법, 그리고 eager loading과 lazy loading의 트레이드오프를 설명해주세요.
N+1 쿼리 문제는 부모 엔티티 N개를 조회한 후 각각의 연관 엔티티를 개별적으로 조회하여 총 N+1번의 쿼리가 발생하는 성능 문제입니다. ORM에서 관계 데이터를 lazy loading으로 접근할 때 흔히 발생하며, 예를 들어 사용자 목록을 조회한 후 각 사용자의 게시글을 순회하며 접근하면 발생합니다. DataLoader는 동일한 이벤트 루프 내의 요청을 배치로 모아 한 번에 처리하여 이 문제를 해결하며, 특히 GraphQL에서 유용합니다. Eager loading(include, join 사용)은 초기 쿼리에서 모든 데이터를 가져와 N+1을 방지하지만 불필요한 데이터까지 로드할 수 있고, lazy loading은 필요할 때만 조회하지만 N+1 위험이 있어 사용 패턴에 따라 선택해야 합니다.
- • N+1은 반복문 내 개별 쿼리 실행으로 발생
- • DataLoader로 배치 처리 및 캐싱 구현
- • Eager loading으로 JOIN 쿼리 한 번에 처리
- • 필요한 데이터만 조회하는 전략적 쿼리 설계
GraphQL Resolver에서 N+1 문제를 진단하는 방법과 실제 프로덕션에서 쿼리 성능을 모니터링하는 도구는 무엇이 있나요?
Q. Node.js에서 CQRS(Command Query Responsibility Segregation) 패턴의 개념을 설명하고, 이 패턴이 해결하는 문제와 Event Sourcing과의 관계, 그리고 실제 구현 시 고려해야 할 점과 적합한 사용 사례를 설명해주세요.
CQRS는 데이터 변경(Command)과 조회(Query)의 책임을 분리하는 패턴으로, 각각 다른 모델과 데이터베이스를 사용할 수 있습니다. 복잡한 도메인에서 읽기와 쓰기 요구사항이 다를 때 유용하며, 읽기 모델을 비정규화하여 조회 성능을 최적화하고 쓰기 모델은 비즈니스 로직에 집중할 수 있습니다. Event Sourcing과 함께 사용하면 모든 상태 변경을 이벤트로 저장하고 이를 통해 읽기 모델을 구성할 수 있어 시너지가 있습니다. 구현 시 데이터 동기화 지연(Eventual Consistency), 복잡도 증가, 인프라 비용 증가를 고려해야 하며, 높은 읽기/쓰기 비율 차이가 있거나 복잡한 조회 요구사항이 있는 시스템에 적합합니다.
- • Command와 Query 모델 분리로 각각 최적화
- • 읽기 모델 비정규화로 조회 성능 향상
- • Event Sourcing으로 이벤트 기반 상태 관리
- • 복잡도와 일관성 트레이드오프 고려 필요
CQRS를 적용한 시스템에서 Command 실행 후 즉시 Query로 조회했을 때 데이터가 반영되지 않는 문제를 사용자 경험 측면에서 어떻게 처리할 수 있나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!