Node.js 미드레벨 기술면접

Node.js 미드레벨 (3~7년) 코딩 · 알고리즘 5문항 조회수 102 · 2026-05-24 (일) 03:11:01
1 성능 최적화
Medium

Q. Node.js 애플리케이션에서 대용량 JSON 데이터를 파싱할 때 발생할 수 있는 성능 문제와 이를 해결하기 위한 Streaming JSON Parser의 동작 원리를 설명하고, 일반 JSON.parse()와의 차이점을 메모리 관리 측면에서 비교해주세요.

A. 모범답안

일반 JSON.parse()는 전체 JSON 문자열을 메모리에 로드한 후 파싱하므로 대용량 데이터 처리 시 메모리 부족과 Event Loop 블로킹이 발생합니다. Streaming JSON Parser는 데이터를 청크 단위로 읽으면서 점진적으로 파싱하여 메모리 사용량을 일정하게 유지하고 다른 요청 처리를 방해하지 않습니다. JSONStream, stream-json 같은 라이브러리를 활용하면 Transform Stream을 통해 파싱과 데이터 처리를 파이프라인으로 연결할 수 있습니다. 특히 수백 MB 이상의 JSON 배열을 처리할 때는 스트리밍 방식이 필수적이며, 메모리 사용량을 10분의 1 이하로 줄일 수 있습니다.

핵심 포인트
  • • JSON.parse()는 전체 데이터를 메모리에 로드하여 대용량 처리 시 메모리 부족 및 블로킹 발생
  • • Streaming Parser는 청크 단위 파싱으로 일정한 메모리 사용량 유지
  • • Transform Stream을 활용한 파이프라인 구성으로 효율적인 데이터 처리 가능
  • • JSONStream, stream-json 등의 라이브러리 활용
Follow-up 질문

Streaming JSON Parser를 사용할 때 에러 처리는 어떻게 다르게 해야 하며, 부분적으로 파싱된 데이터를 롤백해야 하는 상황에서는 어떤 전략을 사용해야 할까요?

2 트러블슈팅
Hard

Q. Node.js 애플리케이션에서 특정 API 엔드포인트가 간헐적으로 응답하지 않고 타임아웃되는 문제가 발생했습니다. 데이터베이스 연결 풀이 고갈되는 것이 원인일 때, 이를 진단하는 방법과 연결 풀 고갈이 발생하는 주요 원인들, 그리고 해결 방법을 설명해주세요.

A. 모범답안

연결 풀 고갈은 주로 연결을 제대로 반환하지 않거나, 장시간 실행되는 쿼리가 연결을 점유하거나, 풀 크기가 부적절할 때 발생합니다. 진단 방법으로는 DB 라이브러리의 풀 상태 모니터링(pool.totalCount, pool.idleCount, pool.waitingCount), APM 도구를 통한 쿼리 실행 시간 추적, 애플리케이션 로그에서 connection timeout 에러 패턴 분석이 있습니다. 해결 방법은 try-finally 블록으로 연결 확실히 반환, 쿼리 타임아웃 설정, 트랜잭션 타임아웃 설정, 적절한 풀 크기 조정(일반적으로 CPU 코어 수의 2-3배), 슬로우 쿼리 최적화입니다. 또한 connectionTimeoutMillis와 idleTimeoutMillis를 적절히 설정하여 좀비 커넥션을 방지해야 합니다.

핵심 포인트
  • • 연결 미반환, 슬로우 쿼리, 부적절한 풀 크기가 주요 원인
  • • 풀 상태 메트릭 모니터링과 APM 도구로 진단
  • • try-finally로 연결 확실히 반환, 쿼리/트랜잭션 타임아웃 설정
  • • 풀 크기는 CPU 코어 수의 2-3배가 적절
  • • connectionTimeoutMillis와 idleTimeoutMillis 설정으로 좀비 커넥션 방지
Follow-up 질문

Sequelize나 TypeORM 같은 ORM을 사용할 때 연결 풀 관리에서 주의해야 할 점과, 마이크로서비스 환경에서 여러 서비스가 하나의 DB를 공유할 때 연결 풀을 어떻게 설계해야 할까요?

3 설계 패턴
Medium

Q. Node.js에서 Repository 패턴을 구현할 때 TypeScript를 활용한 설계 방법과 이 패턴이 제공하는 이점을 설명하고, 실제 프로젝트에서 Repository 계층과 Service 계층을 어떻게 분리해야 하는지 책임 분리 관점에서 설명해주세요.

A. 모범답안

Repository 패턴은 데이터 접근 로직을 캡슐화하여 비즈니스 로직과 분리하는 패턴입니다. TypeScript에서는 Generic과 Interface를 활용하여 IRepository<T> 인터페이스를 정의하고, 각 엔티티별로 구현체를 만듭니다. Repository는 순수하게 데이터 CRUD와 쿼리 로직만 담당하며, Service 계층은 비즈니스 로직, 트랜잭션 관리, 여러 Repository 조합을 담당합니다. 이점으로는 데이터 소스 변경 시 Repository만 수정하면 되는 유연성, 테스트 시 Mock Repository로 쉽게 대체 가능, 데이터 접근 로직의 재사용성 증가가 있습니다. 예를 들어 UserService는 여러 Repository를 주입받아 사용자 등록 시 UserRepository와 NotificationRepository를 함께 사용하는 비즈니스 플로우를 구성합니다.

핵심 포인트
  • • 데이터 접근 로직 캡슐화로 비즈니스 로직과 분리
  • • TypeScript Generic과 Interface로 IRepository<T> 인터페이스 정의
  • • Repository는 CRUD와 쿼리만, Service는 비즈니스 로직과 트랜잭션 관리
  • • 데이터 소스 변경 유연성, 테스트 용이성, 재사용성 증가
  • • 의존성 주입으로 Service에서 여러 Repository 조합 사용
Follow-up 질문

Repository 패턴에서 복잡한 조인 쿼리나 집계 쿼리는 어디에 위치시켜야 하며, QueryBuilder를 Repository에 노출시키는 것과 특화된 메서드를 만드는 것 중 어떤 접근이 더 나은지 설명해주세요.

4 코딩 문제
Medium

Q. Node.js에서 여러 개의 비동기 API 호출을 병렬로 실행하되, 동시 실행 개수를 제한하는 concurrency control 로직을 구현해야 합니다. Promise.all()과 달리 동시 실행 수를 제한하는 방법과 p-limit 같은 라이브러리의 동작 원리를 설명하고, 직접 구현한다면 어떤 방식으로 설계할지 설명해주세요.

A. 모범답안

Promise.all()은 모든 Promise를 동시에 실행하여 시스템 리소스를 과도하게 사용할 수 있습니다. Concurrency control은 세마포어 패턴을 활용하여 동시 실행 수를 제한합니다. p-limit은 내부적으로 실행 중인 Promise 개수를 추적하고, 제한에 도달하면 대기 큐에 보관했다가 완료 시 다음 작업을 실행하는 방식입니다. 직접 구현 시에는 activeCount와 queue 배열을 관리하며, limit 함수는 새 Promise를 받아 activeCount가 허용치 이하면 즉시 실행하고, 초과하면 큐에 추가합니다. 각 Promise 완료 시 activeCount를 감소시키고 큐에서 다음 작업을 꺼내 실행하는 재귀적 구조로 구현합니다. 이는 외부 API 호출, 파일 처리, DB 배치 작업에서 필수적입니다.

핵심 포인트
  • • Promise.all()은 모든 작업을 동시 실행하여 리소스 과다 사용 가능
  • • 세마포어 패턴으로 activeCount와 queue 관리
  • • 제한 도달 시 큐에 보관, 완료 시 다음 작업 실행
  • • p-limit은 이러한 패턴을 라이브러리로 구현
  • • 외부 API 호출, 파일 처리, DB 배치 작업에서 필수적
Follow-up 질문

동시 실행 제한과 함께 재시도 로직을 구현해야 한다면 어떻게 설계해야 하며, 일부 작업이 실패했을 때 전체 작업을 중단할지 계속 진행할지 선택할 수 있는 옵션은 어떻게 구현할 수 있을까요?

5 코드 리뷰
Hard

Q. Node.js에서 JWT 기반 인증을 구현할 때 흔히 발생하는 보안 취약점과 안티패턴을 설명하고, Access Token과 Refresh Token을 사용하는 이유와 각각의 적절한 저장 위치 및 만료 시간 설정 전략을 설명해주세요. 또한 Token Refresh 로직을 구현할 때 주의해야 할 점을 설명해주세요.

A. 모범답안

주요 보안 취약점으로는 JWT를 localStorage에 저장하여 XSS 공격에 노출되는 것, 약한 시크릿 키 사용, 만료 시간을 너무 길게 설정, 토큰 블랙리스트 미구현 등이 있습니다. Access Token은 짧은 만료 시간(15분~1시간)으로 메모리나 httpOnly secure cookie에 저장하고, Refresh Token은 긴 만료 시간(7~30일)으로 httpOnly secure cookie에 저장하거나 DB에 해시하여 보관합니다. 이렇게 분리하면 Access Token 탈취 시 피해를 최소화하고, Refresh Token으로 재발급받을 수 있습니다. Token Refresh 시에는 Refresh Token의 유효성을 DB에서 검증하고, Refresh Token Rotation을 적용하여 재발급 시 새로운 Refresh Token을 발급하며 기존 것은 무효화해야 합니다. 또한 동시 요청으로 인한 race condition을 방지하기 위해 적절한 동기화 처리가 필요합니다.

핵심 포인트
  • • localStorage 저장 시 XSS 취약, httpOnly secure cookie 사용 권장
  • • Access Token은 짧게(15분~1시간), Refresh Token은 길게(7~30일) 설정
  • • Refresh Token은 DB에 해시하여 저장하고 검증
  • • Refresh Token Rotation으로 재발급 시 기존 토큰 무효화
  • • 동시 요청으로 인한 race condition 방지 처리 필요
  • • 로그아웃 시 Refresh Token을 블랙리스트에 추가
Follow-up 질문

마이크로서비스 환경에서 여러 서비스가 JWT를 검증해야 할 때 공개 키 배포 전략과, JWT Payload에 어떤 정보까지 포함시키는 것이 적절한지, 그리고 권한 정보가 변경되었을 때 이미 발급된 토큰은 어떻게 처리해야 할까요?

댓글 0

로그인 후 댓글을 작성할 수 있습니다.

아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!