배포후 에러없이 강제종료되는 문제
개발 과정에서 애플리케이션에 명백한 오류가 발생함에도 불구하고 (), 서버 로그에 어떠한 에러 메시지도 출력되지 않는 현상이 관찰되었다. 프론트엔드에서는 500 Internal Server Error를 수신하며 서비스 이상이 감지되나, 백엔드 서버 콘솔이나 로그 파일에는 정상적인 INFO 또는 DEBUG 레벨의 로그만 기록될 뿐, 예외 상황에 대한 어떠한 정보도 확인할 수 없었다. 이로 인해 문제 발생 지점을 특정하기 어렵고, 디버깅에 막대한 시간이 소요되며, 시스템의 안정성을 위협하는 '조용한 실패' 상태에 직면하게 되었다. 은 Node.js 환경에서 널리 사용되는 유연한 로깅 라이브러리이나, 특정 설정이나 사용 방식에 따라 에러를 "삼키는" 듯한 동작을 보일 수 있다. 주요 원인은 다음과 같다. 1. * 옵션의 오해:* - 로거 인스턴스를 생성할 때 옵션을 설정할 수 있다. 이 옵션은 Node.js의 발생 시 애플리케이션 프로세스의 강제 종료를 방지하고 로깅만 수행하도록 하는 기능을 한다. - 그러나 이 옵션을 로 설정했을 때, 만약 로거 자체의 트랜스포트(콘솔, 파일 등) 구성에 문제가 있거나 로깅 과정에서 또 다른 에러가 발생하여 실제 로깅에 실패한다면, 프로세스는 종료되지 않으면서 에러 정보도 기록되지 않는 상황이 발생할 수 있다. 이는 치명적인 에러의 발생 사실 자체를 인지하지 못하게 만든다. 2. * 및 핸들러의 부재 또는 잘못된 설정:* - Node.js 환경에서 비동기 코드( 등)의 에러() 및 전역에서 처리되지 않은 동기 예외()는 애플리케이션의 안정성에 직접적인 영향을 미친다. - 은 이러한 전역 예외를 처리하기 위한 기능을 제공하나, 로거 설정 시 및 옵션을 명시적으로 트랜스포트에 적용하지 않거나, Node.js의 객체에 대한 별도의 이벤트 핸들러(예: )를 구성하지 않으면, 해당 에러들이 로깅 시스템에 도달하지 못하고 버려질 수 있다. - 특히 의 경우, Node.js v15 이전 버전에서는 기본적으로 프로세스를 종료하지 않았으며, v15 이후에는 과 유사하게 프로세스를 종료시키는 방향으로 변경되었다. 그러나 로거에서 이를 명시적으로 처리하지 않으면 단순히 프로세스만 종료될 뿐 에러 로그를 남기지 못하는 문제가 발생할 수 있다. 3. 로깅 레벨 설정 불일치: - 각 트랜스포트(예: , )는 독립적인 로깅 레벨()을 가질 수 있다. 만약 특정 트랜스포트의 이 로 설정되어 있다면, 나 레벨의 로그는 해당 트랜스포트를 통해 출력되지 않는다. 따라서 레벨의 로그가 필요한 경우, 트랜스포트의 을 또는 그보다 낮은 수준(예: )으로 설정해야 한다. 우리의 로거가 에러를 다시 제대로 기록하도록 만들기 위해 코드를 어떻게 수정했는지, 이전 코드와 개선된 코드를 비교하며 살펴보겠다. --- [이전 코드] 내가 처음 사용했던 로거 설정은 다음과 같았다. 이전 코드의 문제점: 1. * 누락:* 과 트랜스포트 모두 옵션이 명시적으로 설정되어 있지 않았다. 이는 비동기 코드에서 발생하는 거부(Unhandled Rejection) 에러가 로거에 의해 포착되지 않고 조용히 사라질 수 있는 결정적인 원인이었다. 2. * 부재:* 에러 스택 트레이스를 로그에 포함시키기 위한 포맷이 체인에 없었다. 이로 인해 에러 객체가 제대로 파싱되지 않고 함수로 전달될 때 스택 정보가 유실될 가능성이 높았다. 3. * 포맷의 한계:* 함수 내부에서 만 직접 사용하고 있어, 포맷이 적용되지 않은 상태에서는 에러 객체로부터 스택 트레이스 등 상세 정보를 추출하기 어려웠다. --- [개선된 코드] 위에서 언급된 문제점들을 해결하여, 로거가 모든 에러를 안정적으로 포착하고 상세한 정보를 함께 기록하도록 코드를 개선하였다. 개선된 코드의 주요 변경 사항 및 그 효과: 1. * 추가:* - 트랜스포트와 (dailyOptions) 모두에 옵션을 명시적으로 추가하여, 비동기 에서 발생하는 처리되지 않은 거부(Unhandled Rejection) 에러까지 로깅 시스템이 포착하고 기록하도록 변경했다. 이전에는 이 에러들이 로거를 통과하지 못하고 '삼켜질' 수 있었다. 2. * 적용:* - 체인에 포맷을 추가했다. 이 포맷은 에러 객체가 로그 레코드에 전달될 때, 에러의 스택 트레이스를 속성에 포함시켜 준다. 이로써 함수에서 상세한 스택 트레이스 정보를 쉽게 접근하고 로그에 출력할 수 있게 되었다. - 트랜스포트의 개발() 및 프로덕션() 포맷에도 각각 적용하여, 콘솔 출력에서도 상세한 에러 정보가 나타나도록 했다. 3. * 포맷 개선:* - 함수 내부에서 속성을 확인하여, 스택 트레이스가 존재할 경우 이를 메시지에 추가하도록 수정했다. 이는 에러 발생 시 개발자가 문제의 원인을 파악하는 데 결정적인 단서가 된다. 이전에는 단순히 만 출력하여 스택 트레이스 정보를 놓칠 수 있었다. 4. * 조정:* - 파일 로그의 을 로 변경하여, 파일에는 치명적인 에러 로그만 집중적으로 기록되도록 조정했다. (필요에 따라 등으로 변경 가능) 5. * 설정에 대한 상세 설명 추가:* - 설정이 가지는 양면성에 대해 주석으로 상세 설명을 추가하여, 이 옵션의 사용에 있어 개발자가 충분히 인지하고 신중하게 결정할 것을 강조했다. 로깅 시스템은 애플리케이션의 동작 상태를 파악하고 문제 발생 시 신속하게 원인을 진단하는 개발자의 핵심 도구이다. 로거가 에러를 '삼키는' 현상은 잘못된 설정이나 의 예외 처리 메커니즘에 대한 오해에서 비롯되는 경우가 많다. 안정적인 로깅 시스템을 구축하기 위해서는 의 각 트랜스포트에서 예외 처리(, )를 명시적으로 활성화하고, 전역 예외(uncaughtException, unhandledRejection)를 포착하여 상세한 스택 트레이스까지 기록하는 포맷을 적용해야 한다. 또한, 옵션의 의미를 정확히 이해하고 애플리케이션의 안정성 요구 사항에 맞춰 신중하게 설정한다. 로깅 시스템에 대한 면밀한 구성은 '조용한 실패'를 방지하고, 서비스의 신뢰성을 확보하며, 개발 과정의 효율성을 극대화하는 데 필수적인 요소이다. 아래는 숨겨진 에러가 모습을 드러낸 장면이다.
2025년 11월 19일