본문 바로가기
Backend2026년 7월 9일4분 읽기

타임존 지옥 — UTC로 저장하고 변환해서 표시하기

YS
김영삼
조회 537
타임존 지옥 — UTC로 저장하고 변환해서 표시하기

핵심 요약

날짜/시간 버그의 90%는 "언제 어느 타임존으로 다루는지"가 뒤섞여 생긴다. 정석은 하나다 — 저장·연산은 UTC로, 표시할 때만 사용자 타임존으로 변환. 서버·DB·API는 UTC로 통일하고, 지역 시간은 오직 화면에서만 만든다.

1. 원칙

계층다루는 방식
DB 저장UTC (timestamptz 권장)
서버 연산UTC
API 전송ISO 8601 (…Z)
화면 표시사용자 타임존으로 변환

2. 저장과 전송

// 항상 UTC ISO 문자열로 주고받기
new Date().toISOString()   // "2026-07-05T04:00:00.000Z"

// PostgreSQL: timestamptz는 UTC로 정규화 저장
// timestamp(타임존 없음)는 피할 것 — 해석이 모호

3. 표시할 때 변환

// 사용자 로캘·타임존으로 포맷
new Intl.DateTimeFormat('ko-KR', {
  dateStyle: 'medium', timeStyle: 'short',
  timeZone: 'Asia/Seoul',
}).format(new Date(isoString))
// → 2026. 7. 5. 오후 1:00

4. 흔한 함정

  • new Date("2026-07-05")UTC 자정으로 해석 — 한국에선 하루 밀려 보일 수 있다
  • 서버 로컬 타임존에 의존하지 말 것(TZ=UTC로 고정)
  • DST(서머타임)가 있는 지역은 오프셋이 계절마다 바뀐다 — 고정 "+9" 하드코딩 금지, 지역명(Asia/Seoul)을 쓴다
  • "오늘"의 경계도 타임존마다 다르다 — 일별 집계는 어느 타임존 기준인지 명시

5. 실무 팁

  • 날짜만 필요한 값(생일 등)은 시간·타임존 없는 DATE로 저장
  • 복잡한 타임존 계산은 date-fns-tz·Luxon·Temporal 같은 라이브러리에 맡긴다
  • 서버·DB·컨테이너 타임존을 모두 UTC로 통일하면 사고가 급감한다

자주 묻는 질문

왜 KST로 바로 저장하면 안 되나요?

사용자가 여러 지역에 있거나 서버가 이전되면 기준이 흔들립니다. UTC로 저장하면 절대 기준점이 하나로 고정돼 어느 타임존으로든 정확히 변환할 수 있습니다. 지역 시간은 표시 순간에만 만드는 게 안전합니다.

"+9:00"으로 오프셋을 저장하면 되지 않나요?

서머타임이 있는 지역은 오프셋이 계절마다 바뀌어 고정 숫자로는 틀립니다. 한국은 DST가 없지만, 이식성과 미래 대비를 위해 오프셋 숫자 대신 Asia/Seoul 같은 IANA 지역명을 쓰는 게 정석입니다.

날짜만 필요한데도 UTC 시간까지 저장해야 하나요?

생일·기념일처럼 시간 개념이 없는 값은 타임존 없는 DATE 타입으로 저장하는 게 맞습니다. 여기에 시간을 붙이면 타임존 변환 과정에서 하루가 밀리는 버그가 생깁니다. 값의 성격에 맞는 타입을 고르세요.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록