Node.js 시니어 프레임워크 심화 면접

Node.js 시니어 (7년+) 프레임워크 5문항 조회수 237 · 2026-06-08 (월) 11:11:04
1 Node.js Core Modules
Hard

Q. Node.js의 Stream 모듈에서 Backpressure(배압) 메커니즘의 동작 원리를 설명하고, Readable Stream과 Writable Stream 간 데이터 처리 속도 차이로 인한 메모리 문제를 어떻게 해결하는지 설명해주세요. 특히 pipe() 메서드가 내부적으로 backpressure를 어떻게 처리하며, 커스텀 Transform Stream을 구현할 때 주의해야 할 사항은 무엇인가요?

A. 모범답안

Backpressure는 데이터 소비자가 생산자보다 느릴 때 발생하는 메모리 과부하를 방지하는 메커니즘입니다. Writable Stream의 write() 메서드가 false를 반환하면 내부 버퍼가 highWaterMark에 도달했다는 신호이며, 이때 Readable Stream은 일시적으로 데이터 읽기를 중단해야 합니다. pipe() 메서드는 이를 자동으로 처리하여 'drain' 이벤트를 기다렸다가 resume()을 호출합니다. Transform Stream 구현 시에는 _transform() 메서드에서 callback을 적절히 호출하여 처리 완료를 알리고, push()의 반환값을 확인하여 더 이상 데이터를 읽지 않도록 해야 합니다. 대용량 파일 처리나 네트워크 스트리밍에서 이를 무시하면 메모리 누수가 발생할 수 있습니다.

핵심 포인트
  • • write() 메서드의 반환값(boolean)이 backpressure 신호
  • • pipe()는 'drain' 이벤트와 pause()/resume()을 통해 자동 제어
  • • Transform Stream에서는 callback 호출과 push() 반환값 확인 필수
  • • highWaterMark 설정으로 버퍼 크기 조절 가능
Follow-up 질문

프로덕션 환경에서 대용량 로그 파일을 실시간으로 처리하는 Stream Pipeline을 구축할 때, 여러 Transform Stream을 체인으로 연결하면서도 전체 시스템의 메모리 사용량을 일정하게 유지하려면 어떤 모니터링 지표를 확인하고 어떻게 튜닝하시겠습니까?

2 Node.js Worker Threads
Hard

Q. Node.js의 Worker Threads와 Child Process의 차이점을 메모리 공유, 통신 방식, 사용 사례 관점에서 비교 설명해주세요. 특히 SharedArrayBuffer와 Atomics API를 사용한 멀티스레드 프로그래밍의 장점과 위험성, 그리고 Worker Thread Pool을 구현할 때 적정 워커 수를 결정하는 전략을 설명해주세요.

A. 모범답안

Worker Threads는 같은 프로세스 내에서 메모리를 공유할 수 있어 SharedArrayBuffer를 통해 효율적인 데이터 전달이 가능하지만, Child Process는 완전히 독립된 메모리 공간을 가지며 IPC를 통해 통신합니다. Worker Threads는 CPU 집약적 작업(암호화, 이미지 처리)에 적합하고, Child Process는 독립적인 Node.js 인스턴스 실행이나 격리가 필요한 경우에 사용합니다. SharedArrayBuffer와 Atomics는 락 없는 동시성 제어를 가능하게 하지만, race condition과 데이터 불일치 위험이 있어 신중한 설계가 필요합니다. Worker Pool 크기는 일반적으로 CPU 코어 수를 기준으로 하되, 작업의 I/O 대기 시간 비율과 컨텍스트 스위칭 오버헤드를 고려하여 벤치마크를 통해 최적값을 찾아야 합니다.

핵심 포인트
  • • Worker Threads는 메모리 공유 가능, Child Process는 완전 격리
  • • SharedArrayBuffer로 zero-copy 데이터 전달, Atomics로 동기화
  • • Worker Threads는 CPU 작업, Child Process는 격리가 필요한 작업에 적합
  • • Worker Pool 크기는 CPU 코어 수와 작업 특성을 고려하여 결정
Follow-up 질문

실시간 비디오 인코딩 서비스에서 Worker Threads를 활용한다면, 작업 큐 관리 전략과 워커 간 부하 분산, 그리고 특정 워커의 크래시 발생 시 복구 메커니즘을 어떻게 설계하시겠습니까?

3 Node.js Module System
Medium

Q. Node.js의 CommonJS(require)와 ES Modules(import/export)의 차이점을 로딩 시점, 동기/비동기 처리, 순환 참조 해결 방식 관점에서 설명해주세요. 또한 대규모 Node.js 프로젝트에서 두 모듈 시스템을 혼용할 때 발생할 수 있는 문제점과 해결 방안을 설명해주세요.

A. 모범답안

CommonJS는 동기적으로 모듈을 로드하며 런타임에 require()가 실행되는 시점에 모듈을 평가합니다. 반면 ES Modules는 정적 분석이 가능하며 비동기적으로 로드되고, import 문은 호이스팅되어 파일 최상단에서 평가됩니다. 순환 참조 시 CommonJS는 불완전한 exports 객체를 반환할 수 있지만, ES Modules는 바인딩을 통해 더 안전하게 처리합니다. 혼용 시에는 .mjs/.cjs 확장자 명시, package.json의 type 필드 설정, import()를 통한 동적 로딩이 필요하며, Named Export와 Default Export 변환 시 주의가 필요합니다. ES Modules에서 CommonJS를 import할 때는 가능하지만 반대는 불가능하므로, 점진적 마이그레이션 전략이 중요합니다.

핵심 포인트
  • • CommonJS는 동기/런타임 로딩, ES Modules는 비동기/정적 분석 가능
  • • ES Modules는 순환 참조를 더 안전하게 처리
  • • 혼용 시 package.json type 필드와 확장자로 구분
  • • ES Modules에서 CommonJS import는 가능, 역방향은 불가
Follow-up 질문

레거시 CommonJS 기반의 대규모 모노레포 프로젝트를 ES Modules로 마이그레이션한다면, 어떤 단계로 진행하시겠으며 마이그레이션 중 서비스 중단 없이 안전하게 전환하는 방법은 무엇인가요?

4 Node.js Performance
Hard

Q. Node.js의 V8 엔진에서 가비지 컬렉션(GC)이 애플리케이션 성능에 미치는 영향을 설명하고, Scavenge(Minor GC)와 Mark-Sweep-Compact(Major GC)의 동작 방식을 비교해주세요. 또한 --max-old-space-size 플래그를 조정할 때 고려해야 할 사항과, GC로 인한 Stop-The-World 현상을 최소화하기 위한 코딩 패턴을 제시해주세요.

A. 모범답안

V8의 GC는 Young Generation과 Old Generation으로 메모리를 분리하여 관리합니다. Scavenge는 Young Generation에서 수행되며 Cheney 알고리즘을 사용해 살아있는 객체만 복사하는 방식으로 빠르지만(1-10ms), Mark-Sweep-Compact는 Old Generation 전체를 대상으로 하여 느리고(100ms 이상) Stop-The-World 시간이 깁니다. --max-old-space-size를 늘리면 GC 빈도는 줄지만 한 번의 GC 시간이 길어지므로, 애플리케이션의 메모리 사용 패턴과 레이턴시 요구사항을 고려해야 합니다. 객체 풀 재사용, 대용량 객체의 스트림 처리, 클로저 남용 방지, 전역 변수 최소화 등이 GC 부담을 줄이는 패턴입니다.

핵심 포인트
  • • Scavenge는 빠르고 빈번, Mark-Sweep-Compact는 느리고 드물게 발생
  • • 메모리 증가는 GC 빈도를 줄이지만 한 번의 GC 시간을 증가시킴
  • • 객체 재사용과 스트림 처리로 메모리 할당 최소화
  • • 프로덕션에서 --trace-gc로 GC 패턴 모니터링 필요
Follow-up 질문

실시간 게임 서버처럼 레이턴시가 극도로 중요한 Node.js 애플리케이션에서 GC로 인한 지연을 1ms 이하로 유지해야 한다면, 어떤 아키텍처 패턴과 메모리 관리 전략을 사용하시겠습니까?

5 Node.js Native Addons
Hard

Q. Node.js에서 C++ Native Addon을 개발할 때 N-API(Node-API)를 사용하는 이유와 장점을 설명하고, JavaScript와 C++ 간 데이터 교환 시 발생하는 성능 오버헤드를 최소화하는 방법을 설명해주세요. 또한 Native Addon에서 비동기 작업을 처리할 때 libuv의 스레드 풀을 활용하는 방법과, Event Loop를 블로킹하지 않기 위한 설계 원칙을 제시해주세요.

A. 모범답안

N-API는 ABI(Application Binary Interface) 안정성을 제공하여 Node.js 버전이 바뀌어도 네이티브 모듈 재컴파일 없이 사용할 수 있습니다. 데이터 교환 오버헤드를 줄이기 위해서는 Buffer나 TypedArray를 사용하여 메모리를 직접 공유하고, 빈번한 호출 시 배치 처리로 경계 넘나들기를 최소화해야 합니다. 비동기 작업은 napi_create_async_work를 통해 libuv 스레드 풀에서 실행하고, 완료 시 콜백을 Event Loop에 큐잉하여 메인 스레드를 블로킹하지 않습니다. CPU 집약적 작업은 반드시 워커 스레드로 분리하고, JavaScript 객체 접근은 최소화하며, 에러 처리와 메모리 해제를 철저히 해야 합니다.

핵심 포인트
  • • N-API는 ABI 안정성으로 Node.js 버전 독립성 제공
  • • Buffer/TypedArray로 zero-copy 데이터 공유
  • • napi_create_async_work로 libuv 스레드 풀 활용
  • • JavaScript 객체 접근 최소화와 배치 처리로 오버헤드 감소
Follow-up 질문

머신러닝 추론 엔진을 Node.js Native Addon으로 구현할 때, 대용량 텐서 데이터를 효율적으로 전달하고 GPU 가속을 활용하면서도 Node.js 애플리케이션의 안정성을 보장하려면 어떤 아키텍처를 설계하시겠습니까?

댓글 0

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

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