2026.02.22. 07:23
1. 문제
날짜 값이 자꾸 UTC로 뜨는 문제가 있다. 이걸 불러오는 과정에서 KST(UTC+9)로 보정을 해줘야한다.
2. 수정 과정
created_At 컬럼은 UTC로 생성이 되고 있다.
이걸 불러오는 과정에서 KST(UTC+9)로 보정을 해줘야하는데 일반적으로 프론트엔드 단에서 보정을 해준다.
import { format } from "date-fns";
export const formatDate = (created_at:string) => {
return format(new Date(created_at),"yyyy.MM.dd. HH:mm");
}
날짜 포맷 함수를 기존에 사용하던 프로젝트에서 가져와서 넣었는데 CSR에서는 브라우저가 해당 값을 알아서 KST로 보정을 해줘서 문제가 없었다.
문제는 SSR컴포넌트에선 서버에서 렌더링이 돌아가고 서버는 UTC를 기준으로 돌아가다 보니 보정처리가 안되는 문제가 발생했다.
따라서 변환을 해줘야 한다.
import { formatInTimeZone } from "date-fns-tz";
export const formatDate = (created_at:string) => {
const kst = formatInTimeZone(new Date(created_at), "Asia/Seoul","yyyy.MM.dd. HH:mm");
return kst
}
이런 식으로 변환을 해줬는데....
안된다.
2026-02-21T21:24:02.969156 // 2026.02.21. 21:24
로그를 찍어보니 끝 값에 Z가 붙지 않아 UTC로 인식을 못하고 로컬 타임으로 인식해서였다. 즉 이미 백에서 넘어온 값이 보정된 값이라고 함수가 착각하는거다.
@CreationTimestamp
@Column(name = "created_at")
private LocalDateTime createdAt;
// 수정 일자
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;엔터티는 LocalDateTime으로 되어있었다. 근데 나는 분명히 우분투에 시간 설정을 KST로 해뒀다.
System information as of Sun Feb 22 07:15:55 AM KST 2026추측해볼 때 아마도 도커의 컨테이너가 utc로 설정되어있기 때문이다.
해결 방안을 고민을 해봤는데 억지로 Z를 뒤에 붙여 포맷을 바꾸도록 처리하거나 타임설정을 바꾸는건 문제가 좀 있었다. 기존 값을 전부 업데이트를 해줘야 되기도 하고 컨테이너 별로 일일히 kst를 잡아주는건 유지보수성을 고려할 때 별로 좋은 것 같지 않다.
따라서 백엔드, DB 쪽은 UTC로 통일을 하고 프론트에서만 utc를 kst로 보정해주는 쪽으로 가닥을 잡았다.
제일 좋은 방안은 entity를 전부 LocalDateTime에서 OffsetDateTime으로 수정해주는 것. 이렇게 하면 알아서 Z값을 붙여서 리턴해준다.
@CreationTimestamp
@Column(name = "created_at")
private OffsetDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private OffsetDateTime updatedAt;3. 수정사항
JPA 엔터티와 DTO 필드를 LocalDateTime에서 OffsetDateTime으로 수정
프론트단 날짜 포맷 함수 수정
4. 리팩토링 고려 사항
createdAt 이나 updatedAt, isDeleted 같은 건 공통으로 선언해서 상속처리 하는 것이 좋을까 고민 중이다.