Node.js uncaughtException 처리: 처리되지 않은 예외로 프로세스가 종료되는 문제
1. 문제 정의
Node.js를 처음 사용하다 보면 프로그램에서 **처리되지 않은 예외(unhandled exception)**가 발생하는 순간 Node 프로세스 전체가 종료되는 것을 발견하게 됩니다.
| |
일반 웹 서버 컨테이너라면 Worker Thread만 죽고 컨테이너는 계속 요청을 받지만, Node.js에서는 프로세스가 통째로 죽습니다. 이때 자연히 다음과 같은 질문이 생깁니다.
process.on('uncaughtException')이 유일한 방어 수단인가?- 이 핸들러를 쓰는 것이 안전한가?
2. 원인 탐구
Node.js의 동작 모델을 이해하면 프로세스 종료의 이유가 명확해집니다.
- Node.js는 **단일 이벤트 루프(single event loop)**를 가진 프로세스입니다.
- 사전에 예외를 잡아둔 핸들러가 없다면, 예외는 이벤트 루프의 맨 위까지 전파되고 결국 실행 환경이 더 이상 안전하게 실행을 계속할 수 없다고 판단합니다.
- 따라서 Node는 충돌을 방지하기 위해 프로세스를 종료합니다. 원 질문의 스코어가 831점으로 매우 높을 만큼, 이 패턴에 직면한 개발자가 많은 공통 이슈입니다.
3. 근본 원인 분석
핵심 원인은 **‘예외가 전파되어 이벤트 루프를 핸들러 없는 상태로 통과했기 때문’**입니다.
process.on('uncaughtException')는 존재하지만, 공식 문서에서는 이를 만병통치로 쓰지 말 것을 경고합니다. 핸들러는 “마지막 순간에 상태를 정리하고 종료하거나, 안전하게 종료 절차를 밟기 위한” 용도입니다.- 진짜 해결책은 예외를 구조적으로 미리 잡는 것으로, 비동기 콜백의
err첫 번째 인자 규약과 Promise 기반 에러 전파를 사용하는 방식입니다.
Joyent의 공식 가이드(StackOverflow 채택 답변 요약)는 “safely throwing errors"라는 개념을 제시합니다.
4. 코드 해결책
안전하게 ‘던지기’ — 동기 코드
동기 함수에서 오류가 나면, throw 대신 오류를 반환하는 방식입니다.
| |
콜백 기반 코드 — err-first 규약
| |
이벤트 이미터 / Promise로 내보내기
에러를 emit('error')로 내보내거나 Promise reject로 처리해도, 안전하게 전파되고 uncaughtException까지 새지 않습니다.
5. 향후 예방 조치
process.on('uncaughtException')는 절대 ‘모든 예외 무시’로 쓰지 마십시오. 안전한 사용법은 로그를 남기고 프로세스를 깨끗이 종료하는 것입니다.- 모든 IO는 try/catch 또는
.catch()를 통해 실패 경로를 명시하십시오. - worker처럼 격리된 프로세스/Worker Thread에서 취약한 작업을 수행하면, 실패해도 메인이 종료되지 않게 만들 수 있습니다.
- 예외를 함수의 실패 반환으로 변환하는 “safely throwing” 패턴을 코드베이스 규약으로 정하십시오.