Node.js 시니어 백엔드 개발자 면접 질문: 완벽 가이드

Milad Bonakdar
작성자
시스템 설계, 아키텍처 패턴, 성능 최적화, 확장성, 마이크로서비스 및 리더십을 다루는 30개의 필수 면접 질문으로 고급 Node.js 백엔드 개발을 마스터하세요. 시니어 백엔드 개발자 면접을 위한 완벽한 준비입니다.
소개
이 종합 가이드에는 고급 Node.js 백엔드 개발에 대한 30가지 필수 면접 질문이 포함되어 있습니다. 이 질문들은 이벤트 루프, 스트림, 시스템 설계 및 성능 최적화의 핵심 개념을 다루어 숙련된 백엔드 개발자가 면접을 준비하는 데 도움이 되도록 설계되었습니다. 각 질문에는 자세한 답변, 희소성 평가 및 난이도 등급이 포함되어 있습니다.
숙련된 개발자로서, 여러분은 Node.js의 단일 스레드 특성을 깊이 이해하고 이를 중심으로 확장 가능하고 고성능 시스템을 구축할 수 있을 것으로 기대됩니다.
고급 Node.js 개념 (10가지 질문)
1. Node.js 이벤트 루프를 자세히 설명하세요. 서로 다른 단계는 무엇입니까?
답변: 이벤트 루프는 Node.js가 단일 스레드임에도 불구하고 논블로킹 I/O 작업을 수행할 수 있도록 하는 것입니다. 가능한 경우 시스템 커널에 작업을 오프로드합니다.
- 단계:
- 타이머:
setTimeout()및setInterval()에 의해 예약된 콜백을 실행합니다. - 보류 중인 콜백: 다음 루프 반복으로 연기된 I/O 콜백을 실행합니다.
- 유휴, 준비: 내부 전용입니다.
- 폴: 새로운 I/O 이벤트를 검색합니다. I/O 관련 콜백을 실행합니다. 다른 작업이 없으면 루프가 차단되는 곳입니다.
- 확인:
setImmediate()에 의해 예약된 콜백을 실행합니다. - 닫기 콜백: 닫기 콜백(예:
socket.on('close', ...))을 실행합니다.
- 타이머:
희소성: 매우 흔함 난이도: 어려움
2. process.nextTick()과 setImmediate()의 차이점은 무엇입니까?
답변:
process.nextTick(): 이벤트 루프의 일부가 아닙니다. 현재 작업이 완료된 직후에 실행되지만 이벤트 루프가 계속되기 전에 실행됩니다.setImmediate()보다 우선순위가 높습니다. 과도하게 사용하면 이벤트 루프를 차단할 수 있습니다(고갈).setImmediate(): 이벤트 루프의 확인 단계의 일부입니다. 폴 단계 후에 실행됩니다.
희소성: 흔함 난이도: 중간
3. Node.js는 단일 스레드인 경우 어떻게 동시성을 처리합니까?
답변: Node.js는 이벤트 기반의 논블로킹 I/O 모델을 사용합니다.
- 메인 스레드: JavaScript 코드(V8 엔진)를 실행합니다.
- Libuv: 이벤트 루프와 스레드 풀(기본 4개 스레드)을 제공하는 C 라이브러리입니다.
- 메커니즘: 비동기 작업(예: 파일 I/O 또는 네트워크 요청)이 시작되면 Node.js는 이를 Libuv에 오프로드합니다. Libuv는 스레드 풀(파일 I/O, DNS용) 또는 시스템 커널 비동기 메커니즘(네트워크용)을 사용합니다. 작업이 완료되면 콜백이 메인 스레드에서 실행될 수 있도록 이벤트 루프 큐로 푸시됩니다.
희소성: 흔함 난이도: 중간
4. Node.js의 스트림과 그 유형을 설명하세요.
답변: 스트림은 연속적인 청크 단위로 소스에서 데이터를 읽거나 대상에 데이터를 쓸 수 있게 해주는 객체입니다. 전체 데이터를 메모리에 로드할 필요가 없으므로 메모리 효율적입니다.
- 유형:
- 읽기 가능: 데이터 읽기용 (예:
fs.createReadStream). - 쓰기 가능: 데이터 쓰기용 (예:
fs.createWriteStream). - 이중: 읽기 및 쓰기 가능 (예: TCP 소켓).
- 변환: 출력이 입력을 기반으로 계산되는 이중 스트림 (예:
zlib.createGzip).
- 읽기 가능: 데이터 읽기용 (예:
희소성: 흔함 난이도: 중간
5. 스트림의 백프레셔란 무엇이며 어떻게 처리합니까?
답변: 백프레셔는 읽기 가능 스트림이 쓰기 가능 스트림이 처리할 수 있는 것보다 빠르게 데이터를 푸시할 때 발생합니다. 처리하지 않으면 프로세스가 충돌할 때까지 메모리 사용량이 급증합니다.
- 처리:
.pipe(): 가장 쉬운 방법입니다. 쓰기 가능 스트림의 버퍼가 가득 차면 읽기 가능 스트림을 일시 중지하고 비워지면 다시 시작하여 백프레셔를 자동으로 관리합니다.- 수동: 쓰기 가능 스트림에서
drain이벤트를 수신하고 읽기 가능 스트림을 수동으로 일시 중지/다시 시작합니다.
희소성: 중간 난이도: 어려움
6. cluster 모듈은 어떻게 작동합니까?
답변:
Node.js는 단일 스레드이므로 단일 CPU 코어에서 실행됩니다. cluster 모듈을 사용하면 동일한 서버 포트를 공유하는 자식 프로세스(워커)를 만들 수 있습니다.
- 마스터 프로세스: 워커를 관리합니다.
- 워커 프로세스: 각각 애플리케이션 인스턴스를 실행합니다.
- 이점: 사용 가능한 모든 CPU 코어를 활용하여 처리량을 늘릴 수 있습니다.
희소성: 흔함 난이도: 중간
7. Worker Threads vs Cluster 모듈: 언제 무엇을 사용해야 합니까?
답변:
- Cluster: 별도의 프로세스를 만듭니다. 각각 자체 메모리 공간과 V8 인스턴스가 있습니다. HTTP 서버 (I/O 바운드)를 확장하는 데 가장 적합합니다.
- Worker Threads: 단일 프로세스 내에서 스레드를 만듭니다. 메모리 (
SharedArrayBuffer를 통해)를 공유합니다. 메인 이벤트 루프를 차단하지 않도록 CPU 집약적인 작업 (예: 이미지 처리, 암호화)에 가장 적합합니다.
희소성: 중간 난이도: 어려움
8. 잡히지 않은 예외와 처리되지 않은 프로미스 거부를 어떻게 처리합니까?
답변:
- 잡히지 않은 예외:
process.on('uncaughtException', cb)를 수신합니다. 애플리케이션 상태가 손상되었을 수 있으므로 오류를 기록하고 프로세스 (PM2와 같은 프로세스 관리자 사용)를 다시 시작하는 것이 가장 좋습니다. - 처리되지 않은 거부:
process.on('unhandledRejection', cb)를 수신합니다. - 모범 사례: 항상
try/catch블록과 프로미스에.catch()를 사용합니다.
희소성: 흔함 난이도: 쉬움
9. package-lock.json의 역할은 무엇입니까?
답변: 중간 종속성 업데이트에 관계없이 후속 설치가 동일한 트리를 생성할 수 있도록 생성된 정확한 트리를 설명합니다. 프로젝트가 모든 머신(CI/CD, 다른 개발자)에서 정확히 동일하게 작동하도록 보장합니다.
희소성: 흔함 난이도: 쉬움
10. Express.js의 미들웨어 개념을 설명하세요.
답변:
미들웨어 함수는 애플리케이션의 요청-응답 주기에서 요청 객체(req), 응답 객체(res) 및 다음 미들웨어 함수(next)에 액세스할 수 있는 함수입니다.
- 작업: 코드 실행, req/res 객체 수정, 요청-응답 주기 종료, 다음 미들웨어 호출.
- 순서: 정의된 순서대로 순차적으로 실행됩니다.
희소성: 흔함 난이도: 쉬움
시스템 설계 및 아키텍처 (10가지 질문)
11. 실시간 채팅 애플리케이션을 어떻게 설계하시겠습니까?
답변:
- 프로토콜: 전이중 통신을 위한 WebSocket (
socket.io또는ws사용). - 백엔드: Node.js는 많은 동시 연결을 처리하는 이벤트 기반 특성으로 인해 이상적입니다.
- 확장:
- Redis Pub/Sub: 여러 서버 인스턴스가 있는 경우 서버 A에 연결된 사용자가 서버 B의 사용자에게 메시지를 보내야 합니다. Redis Pub/Sub은 서버 간에 메시지를 브로드캐스트하는 메시지 브로커 역할을 합니다.
- 데이터베이스:
- 메시지: 높은 쓰기 처리량을 위한 NoSQL (MongoDB/Cassandra).
- 사용자: 관계형 (PostgreSQL) 또는 NoSQL.
희소성: 매우 흔함 난이도: 어려움
12. Node.js의 마이크로서비스: 통신 패턴.
답변:
- 동기: HTTP/REST 또는 gRPC. 간단한 요청/응답에 적합합니다.
- 비동기: 메시지 큐 (RabbitMQ, Kafka, SQS). 서비스 분리 및 로드 스파이크 처리에 적합합니다.
- 이벤트 기반: 서비스는 이벤트를 내보내고 다른 서비스는 수신합니다.
희소성: 흔함 난이도: 중간
13. 분산 트랜잭션 (사가 패턴)을 어떻게 처리합니까?
답변: 마이크로서비스에서 트랜잭션은 여러 서비스에 걸쳐 있을 수 있습니다. ACID를 보장하기는 어렵습니다.
- 사가 패턴: 로컬 트랜잭션 시퀀스입니다. 각 로컬 트랜잭션은 데이터베이스를 업데이트하고 이벤트 또는 메시지를 게시하여 사가의 다음 로컬 트랜잭션을 트리거합니다.
- 보상: 로컬 트랜잭션이 실패하면 사가는 이전 로컬 트랜잭션에서 수행한 변경 사항을 취소하는 일련의 보상 트랜잭션을 실행합니다.
희소성: 중간 난이도: 어려움
14. 서킷 브레이커 패턴을 설명하세요.
답변: 애플리케이션이 실패할 가능성이 높은 작업(예: 다운된 마이크로서비스 호출)을 반복적으로 실행하려고 시도하는 것을 방지합니다.
- 상태:
- 닫힘: 요청이 통과합니다.
- 열림: 서비스 호출 없이 요청이 즉시 실패합니다(빠른 실패).
- 반쯤 열림: 제한된 수의 요청을 허용하여 서비스가 복구되었는지 확인합니다.
희소성: 중간 난이도: 중간
15. Node.js API를 어떻게 보호합니까?
답변:
- Helmet: 앱을 보호하기 위해 다양한 HTTP 헤더를 설정합니다.
- 속도 제한: DDoS/무차별 대입을 방지하기 위해
express-rate-limit를 사용합니다. - 입력 유효성 검사:
Joi또는Zod와 같은 라이브러리를 사용합니다. - 인증: JWT 또는 OAuth2.
- CORS: 신뢰할 수 있는 도메인만 허용하도록 적절하게 구성합니다.
- NoSQL 주입: MongoDB 주입으로부터 입력을 소독합니다.
희소성: 흔함 난이도: 중간
16. 서버리스란 무엇이며 Node.js와 어떻게 어울립니까?
답변: 서버리스 (예: AWS Lambda)를 사용하면 서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 있습니다.
- Node.js 적합성: Node.js는 빠른 시작 시간 (콜드 스타트)과 가벼운 특성으로 인해 서버리스에 탁월합니다.
- 사용 사례: API 엔드포인트, 이벤트 처리 (S3 업로드), 예약된 작업.
희소성: 중간 난이도: 중간
17. GraphQL vs REST를 설명하세요. 언제 GraphQL을 사용해야 합니까?
답변:
- REST: 여러 엔드포인트, 데이터의 과도한 페칭 또는 과소 페칭.
- GraphQL: 단일 엔드포인트, 클라이언트가 필요한 것만 요청합니다.
- GraphQL 사용: 복잡한 데이터 요구 사항, 서로 다른 데이터 모양이 필요한 여러 클라이언트 (웹, 모바일)가 있거나 네트워크 왕복을 줄여야 하는 경우.
희소성: 흔함 난이도: 중간
18. Node.js에서 캐싱을 어떻게 구현합니까?
답변:
- 인 메모리:
node-cache(단일 인스턴스에 적합하지만 다시 시작하면 데이터가 손실되고 공유되지 않음). - 분산: Redis (업계 표준).
- 전략: Cache-Aside, Write-Through.
- HTTP 캐싱: ETag, Cache-Control 헤더를 사용합니다.
희소성: 흔함 난이도: 중간
19. 데이터베이스 연결 풀링.
답변: 모든 요청에 대해 새 데이터베이스 연결을 여는 것은 비용이 많이 듭니다.
- 풀링: 재사용할 수 있는 데이터베이스 연결 캐시를 유지 관리합니다.
- Node.js:
pg(PostgreSQL) 또는mongoose와 같은 라이브러리는 풀링을 자동으로 처리합니다. 워크로드 및 DB 제한에 따라 풀 크기를 구성해야 합니다.
희소성: 중간 난이도: 중간
20. Node.js에서 파일 업로드를 어떻게 처리합니까?
답변:
- Multipart/form-data: 파일 업로드의 표준 인코딩입니다.
- 라이브러리:
multer(Express용 미들웨어),formidable,busboy. - 저장: 서버 파일 시스템에 파일을 저장하지 마십시오 (상태 비저장). AWS S3와 같은 클라우드 스토리지에 업로드합니다. 메모리에 로드하지 않도록 파일을 S3로 직접 스트리밍합니다.
희소성: 흔함 난이도: 중간
성능 및 테스트 (10가지 질문)
21. Node.js에서 메모리 누수를 어떻게 디버깅합니까?
답변:
- 증상: 시간이 지남에 따라 메모리 사용량이 증가하고 (RSS) 결국 충돌합니다.
- 도구:
- Node.js 검사기:
--inspect플래그, Chrome DevTools와 연결합니다. - 힙 스냅샷: 스냅샷을 찍어 비교하여 가비지 수집되지 않는 객체를 찾습니다.
process.memoryUsage(): 프로그래밍 방식으로 모니터링합니다.
- Node.js 검사기:
희소성: 흔함 난이도: 어려움
22. Node.js 애플리케이션 프로파일링.
답변: 프로파일링은 CPU 병목 현상을 식별하는 데 도움이 됩니다.
- 내장 프로파일러:
node --prof app.js. 로그 파일을 생성합니다.node --prof-process isolate-0x...log로 처리합니다. - Clinic.js: 성능 문제를 진단하는 도구 모음 (
clinic doctor,clinic flame,clinic bubbleprof).
희소성: 중간 난이도: 어려움
23. "이벤트 루프를 차단하지 마십시오" 규칙을 설명하세요.
답변: 스레드가 하나만 있으므로 장기 실행 동기 작업(예: 과도한 계산, 동기 파일 읽기, 복잡한 정규식)을 실행하면 이벤트 루프가 중지됩니다. 다른 요청은 처리할 수 없습니다.
- 해결 방법: 계산을 분할하거나(setImmediate), 워커 스레드를 사용하거나 마이크로서비스로 오프로드합니다.
희소성: 매우 흔함 난이도: 쉬움
24. Node.js의 단위 테스트 vs 통합 테스트.
답변:
- 단위 테스트: 개별 함수/모듈을 격리하여 테스트합니다. 종속성을 모의합니다. (도구: Jest, Mocha, Chai).
- 통합 테스트: 모듈이 함께 작동하는 방식을 테스트합니다 (예: API 엔드포인트 + 데이터베이스). (도구: Supertest).
희소성: 흔함 난이도: 쉬움
25. TDD (테스트 주도 개발)란 무엇입니까?
답변: 코드를 작성하기 전에 테스트를 작성하는 개발 프로세스입니다.
- 실패하는 테스트를 작성합니다 (Red).
- 테스트를 통과하는 최소한의 코드를 작성합니다 (Green).
- 코드를 리팩터링합니다 (Refactor).
희소성: 중간 난이도: 중간
26. 프로덕션 Node.js 앱에서 로깅을 어떻게 처리합니까?
답변:
console.log를 사용하지 마십시오: 일부 경우에는 터미널/파일에 쓸 때 동기식(차단)이고 구조가 없습니다.- 로거를 사용하십시오: Winston, Bunyan 또는 Pino.
- 구조: 로그 관리 도구 (ELK 스택, Datadog)에서 쉽게 구문 분석할 수 있도록 JSON 형식입니다.
- 수준: 오류, 경고, 정보, 디버그.
희소성: 흔함 난이도: 쉬움
27. 시맨틱 버전 관리 (SemVer)를 설명하세요.
답변:
형식: MAJOR.MINOR.PATCH (예: 1.2.3).
- MAJOR: 호환되지 않는 API 변경 사항.
- MINOR: 이전 버전과 호환되는 기능.
- PATCH: 이전 버전과 호환되는 버그 수정.
^vs~:^1.2.3은<2.0.0으로 업데이트됩니다.~1.2.3은<1.3.0으로 업데이트됩니다.
희소성: 흔함 난이도: 쉬움
28. 환경 변수란 무엇이며 어떻게 관리합니까?
답변:
- 목적: DB URL, API 키와 같은 개발, 스테이징, 프로덕션과 같은 환경에 따라 달라지는 구성입니다.
- 사용법:
process.env.VARIABLE_NAME. - 관리: 로컬 개발을 위한
.env파일 (dotenv패키지 사용). 프로덕션에서는 OS 또는 컨테이너/플랫폼 설정에서 설정합니다.
희소성: 흔함 난이도: 쉬움
29. Node.js 애플리케이션을 어떻게 배포합니까?
답변:
- 프로세스 관리자: PM2 (앱을 계속 실행하고, 다시 시작을 처리하고, 로그를 기록합니다).
- 역방향 프록시: Nginx (SSL, 정적 파일, 로드 밸런싱을 처리합니다) -> Node.js 앱.
- 컨테이너화: Docker (표준).
- 오케스트레이션: Kubernetes.
- CI/CD: GitHub Actions, Jenkins.
희소성: 흔함 난이도: 중간
30. 이벤트 이미터란 무엇입니까?
답변:
events 모듈은 Node.js 이벤트 기반 아키텍처의 핵심입니다.
- 사용법:
- 많은 핵심 모듈이 확장됩니다:
fs.ReadStream,net.Server,http.Server.
희소성: 흔함 난이도: 쉬움



