배경
멤버십 서비스를 론칭하기 위해 개발 중이다.
멤버십 서비스를 구독하면 한 달 단위의 구독 날짜가 생기는데, 이때 유저에서 보여주는 날짜가 하루 전으로 표시되는 문제가 있었다.
api에서 응답하는 날짜의 형식은 ISO 8601으로 내려주고 있다.
ex) "2025-10-29T 00:00:00+09:00"

문제 상황
날짜 데이터의 사용하는 곳은 여러 군데가 있었는데, 흐름은 다음과 같다.
- 백엔드 API → 서버 컴포넌트 (Next.js 서버)
- 백엔드 API → 서버 컴포넌트 → 클라이언트 컴포넌트
두 경우 모두 apis.ts에서 받은 응답 데이터를 컴포넌트에서 바로 렌더링 할 수 있도록 변환하는 models.ts 레이어를 거친다.
models.ts는 Next.js 서버 환경에서 실행되며, 내부에서 다음과 같이 날짜 데이터를 가공한다:
new Date("2025-10-29T00:00:00+09:00").toString()
Date.prototype.toString()은 현재 코드가 실행되는 환경의 타임존을 기준으로 문자열을 반환한다.
문제는 Next.js 서버가 Docker 컨테이너 내에서 실행되고 있었고, 컨테이너의 default 타임존은 UTC+0으로 설정된다는 점이었다.
이로 인해 "2025-10-29T 00:00:00+09:00"이 서버에서는 2025-10-28 15:00:00 UTC로 변환되어 렌더링 시 2025-10-28로 표시되는 문제가 발생했다.
해결
서비스는 한국(KST, UTC+9)에서만 사용되며, 기존 레거시 시스템도 Asia/Seoul로 고정되어 있었기 때문에
Dockerfile에 TZ=Asia/Seoul 환경변수를 추가하여 모든 서버 환경의 타임존을 고정함으로써 문제를 해결했다.
# Dockerfile
ENV TZ=Asia/Seoul
생각
서비스는 한국에서만 운영되고 한국인만 사용할 수 있어서, 그리고 결정적으로 레거시의 TZ가 Asia/Seoul로 고정되어 있기 때문에 나도 Dockerfile에서 TZ를 Asia/Seoul로 고정해 버렸다.
그렇게 하면 날짜가 하루 어긋나는 문제는 해결되지만, 약간의 찝찝함이 있다. (혹시나 우리 서비스가 글로벌 서비스로 확장한다면 마이그레이션 비용은..?)
근본적으로는 유저가 접속한 시간대에 맞춰서 Date를 가공해서 보여주는 게 맞는 것 같다.
그렇게 하려면 Date.toString()이 브라우저(클라이언트)에서 호출되어야 하는데,
지금 구조에서는 models.ts가 서버에서 실행되기 때문에 항상 서버 시간 기준으로 가공된다.
models.ts를 따로 두는 이유는 컴포넌트가 raw API 데이터를 직접 다루지 않도록 하고,
렌더링에 필요한 형태로 변환된 데이터를 props로 넘겨주는 일종의 어댑터 패턴 때문이다.
결국 서버에서 가공된 데이터가 넘어오기 때문에, 클라이언트에서 렌더링 하더라도 이미 서버 시간 기준으로 변환된 상태다.
유저의 실제 시간대를 반영하려면 toString()을 클라이언트 쪽에서 실행해야 한다.
예를 들어 날짜를 표시하는 부분만 클라이언트 컴포넌트로 분리하는 방법이 있을 것 같다. 그러나 이 방법은 어댑터 패턴에 예외를 만들기 때문에 달갑지 않은 문제가 있다.
또는 서비스에 “내 시간대 설정” 같은 기능을 만들어서,
그 설정값을 DB에 저장해 두고 서버에서 해당 시간대 기준으로 날짜를 가공해 주는 방식도 가능할 것 같다.
'개발끄적' 카테고리의 다른 글
| LLM-agent 사용을 위한 기초 개념 (0) | 2026.02.20 |
|---|---|
| mutationOptions (0) | 2025.10.21 |