내 API가 매년 1월 1일에 고장 난 이유 - 코드 리뷰를 통과한 시간대 버그
요약
UTC 시간대 설정 오류로 인해 발생한 API 데이터 버그 사례와 해결 방법을 다룹니다. 프로덕션과 스테이징 환경의 시간대 불일치가 초래한 문제와 이를 방지하기 위한 실무적인 대응책을 제시합니다.
핵심 포인트
- 시스템 시간대(TZ)를 신뢰하지 말고 항상 UTC로 명시적 설정 필요
- 시간대 정보가 없는 날짜 문자열 파싱 시 주의
- CI/CD 환경 및 Docker 컨테이너에 TZ=UTC 설정 적용
- 린터(Linter)를 통한 시간대 관련 코드 패턴 검증
제 API는 정확히 UTC 기준 1월 1일 자정(00:00)에 작동을 멈췄습니다. 사용자들의 자정이 아니라, _UTC 자정_이었습니다. 이는 도쿄의 사용자들이 현지 시간으로 아침 9시부터 깨진 데이터를 경험하고 있었다는 것을 의미합니다.
그리고 최악인 것은요? 모든 테스트가 통과했습니다. 스테이징 환경도 문제없이 작동했습니다. 오직 프로덕션에서만 문제가 발생했는데, 그 이유는 프로덕션의 시간대가 스테이징과 달랐기 때문입니다.
버그의 원인
코드는 다음과 같았습니다:
function getDailyReport(date) {
const start = new Date(date).toISOString().split('T')[0];
const end = new Date(start + 'T23:59:59Z');
...
괜찮아 보이죠? toISOString()은 UTC를 반환합니다. 우리는 날짜로 필터링하고 있습니다. 뭐가 잘못될 수 있겠어요?
잘못된 부분은 이겁니다: date가 단순히 `
-
CI에 시간대 검증 추가 — 이제 테스트 스위트가
process.env.TZ === 'UTC'인지 명시적으로 확인합니다. 누군가 CI의 시간대를 변경하면 테스트가 실패합니다. -
모든 Dockerfile에
TZ=UTC설정 — 모든 컨테이너, 모든 환경에서 동일한 시간대입니다. 놀랄 일이 없습니다. -
배포 스크립트에 시간대 확인 추가 — 배포가 진행되기 전에
date +%Z는UTC를 반환해야 합니다. -
린터 규칙 작성 — 문자열에 시간대 정보가 포함되지 않은 모든
new Date(string)을 플래그 지정합니다.
교훈
시간대 버그는 충돌을 일으키지 않기 때문에 교활합니다. 그들은 올바르게 보이는 잘못된 데이터를 생성합니다. 사용자들은 에러 페이지를 받지 못하고, 조용히 틀린 숫자를 받고 그것들을 신뢰하게 됩니다.
제가 이제 따르는 세 가지 규칙:
- 시스템 시간대를 절대 신뢰하지 마세요. 항상
TZ=UTC를 명시적으로 설정하세요. - 시간대 없이 날짜를 파싱하지 마세요. `
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기