스크립트 마지막 로그가 가끔 잘려 나간 이유
문제 발생
CLI 스크립트에서 사용법을 출력하고 종료했는데, 파이프로 연결하거나 CI에서 실행하면 마지막 몇 줄이 사라졌습니다.
if (!isValid(options)) {
printUsage(); // 여러 줄 출력
process.exit(1); // 여기서 잘린다
}터미널에서 직접 실행할 때는 멀쩡해서 한동안 재현하지 못했습니다.
원인 분석
process.exit()은 기다리지 않습니다. Node.js 문서의 경고가 정확합니다 — process.exit()을 호출하면 아직 완료되지 않은 비동기 작업이 남아 있어도, process.stdout과 process.stderr에 대한 I/O 작업을 포함해, 가능한 한 빨리 프로세스를 종료하도록 강제합니다.
문서는 이유까지 설명합니다 — Node.js에서 process.stdout에 대한 쓰기는 때때로 비동기이며 이벤트 루프의 여러 틱에 걸쳐 일어날 수 있는데, process.exit()은 그 추가 쓰기가 수행되기 전에 프로세스를 종료시킵니다.
터미널에서만 잘 되던 이유가 여기 있습니다. stdout이 TTY일 때와 파이프·파일일 때 쓰기의 동기/비동기 성격이 다릅니다. 환경이 바뀌면 재현되는 종류의 버그입니다.
문서의 권고는 명확합니다 — 코드는 process.exitCode를 설정하고, 이벤트 루프에 추가 작업을 예약하지 않음으로써 프로세스가 자연스럽게 종료되도록 해야 합니다.
해결 방안
- 종료 코드만 설정하고 자연 종료를 기다립니다.
if (!isValid(options)) {
printUsage();
process.exitCode = 1;
return; // exit() 대신 흐름을 끝낸다
}- 정리 작업이 남아 있으면 그것도 끝나게 둡니다. DB 풀 종료, 파일 flush 같은 작업이 있으면
await후 자연 종료가 맞습니다.
await pool.end();
process.exitCode = 0;-
정말 즉시 죽여야 할 때만 예외로 씁니다. 치명적 오류로 더 이상 진행하면 안 되는 상황이 그 경우입니다 — 그때도 로그를 남겨야 한다면
console.error이후 곧바로 exit하지 말고, 로깅 라이브러리의 flush를 기다립니다. -
프로세스가 안 끝나는 문제와 구분합니다. "exit을 안 하면 프로세스가 안 죽는다"면 원인은 살아 있는 핸들입니다(열린 서버, 타이머, DB 커넥션). exit으로 덮는 대신 그 핸들을 닫습니다. 테스트에서
--test-force-exit가 필요한 상황도 같은 성격입니다. -
파이프로 실행해 확인합니다.
node script.js | cat처럼 stdout을 파이프로 바꿔 돌려보면 이 문제가 드러납니다 — 터미널 실행만으로는 통과합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.