Next.js
로그 레벨을 운영에서 DEBUG로 둔 실수와 재발 방지 기준
운영 서버의 로그가 갑자기 폭증했던 원인을 배포 설정에서 찾고, 로그 레벨과 민감정보 점검 기준을 다시 세웠습니다.
디스크 사용량 알림으로 시작했다
배포 다음 날 새벽 디스크 사용량이 80%를 넘었다는 알림을 받았다. 애플리케이션 오류가 폭발한 줄 알았지만 장애율은 평소와 같았다. 로그 파일을 확인하니 요청마다 디버그 객체 전체를 찍고 있었고, 그 안에 불필요한 헤더까지 포함돼 있었다.
설정이 코드보다 먼저 바뀌었다
코드에는 logger.debug가 많았지만 운영에서 어느 레벨까지 출력할지는 환경 변수로 결정했다. 배포 설정의 LOG_LEVEL=debug가 임시 분석 뒤 되돌아오지 않은 것이 직접적인 원인이었다. 단순히 로그 파일을 지우는 것으로 끝내지 않고 다음 배포 때 잘못된 값이 들어오면 실패하도록 검증을 추가했다.
const allowed = new Set(['info', 'warn', 'error'])
const level = process.env.LOG_LEVEL ?? 'info'
if (process.env.NODE_ENV === 'production' && !allowed.has(level)) {
throw new Error(`Invalid production LOG_LEVEL: ${level}`)
}
운영에서 일시적으로 상세 로그가 필요할 때는 전체 레벨을 바꾸는 대신 특정 모듈의 샘플링이나 요청 ID 기반 추적을 사용하기로 했다. 로그 양을 줄이는 것과 비밀값을 숨기는 것은 별개의 문제이므로 둘 다 확인했다.
민감정보는 레벨과 무관하다
헤더 객체를 통째로 출력하면 authorization, 쿠키 같은 값이 섞일 가능성이 있다. 지금은 필요한 필드만 구조화해서 기록하고, 토큰·세션·비밀번호 키는 출력 전에 마스킹한다. 로그 검색 화면에서 마스킹된 문자열만 보이는지 테스트 데이터로 확인했다.
journalctl -u my-app --since '10 minutes ago' | grep -Ei 'authorization|cookie|password|token'
운영 점검에서 grep 결과가 0건이어야 한다는 기준을 배포 문서에 넣었다. 실제 셸에서는 파이프라인의 종료 코드 처리 방식도 확인해야 해서 CI에서는 pipefail을 켰다.
다시 같은 실수를 하지 않기 위한 메모
- 운영 기본 레벨은
info로 코드에 명시한다. - 허용되지 않은 운영 설정은 기동 단계에서 거부한다.
- 배포 후 로그 발생량과 디스크 증가량을 함께 본다.
- 민감한 키 이름 검색과 샘플 로그 검사를 자동화한다.
- 임시 디버깅 설정에는 만료 시각과 담당자를 남긴다.
이번 일은 로그를 많이 남기는 것이 관찰 가능성을 높인다는 생각이 항상 맞지는 않다는 점을 보여줬다. 운영 로그는 양보다 목적, 보관 기간, 노출 위험을 함께 정해야 한다.