Search

또 다시 만난 9시간 오프셋 문제

개요

KST 기준 하루(00~24시) 채팅 수를 집계하려고 했는데,
9시간 전까지의 데이터만 조회되는 문제가 발생.

원인 파악

쿼리 결과에서 시간대별 데이터가 KST 하루로 안 맞고, UTC 기준 하루처럼 보임.
예를 들어 2025-08-15 기준 데이터를 보면, 0~8시 데이터가 빠지거나 섞여 있음.

뭐가 문젤까?

1.
브라우저에서 파라미터를 잘못 보내는지?
2.
*서버(day.js)**에서 KSTUTC 변환이 틀린지?
3.
RDS TimeZone 설정 문제인지?
4.
TypeORM이 날짜 파라미터 변환 과정에서 문제를 만드는지?
5.
DB 쿼리에서 타임존이 꼬이는지?

검증 과정

DB 직접 조회

DBeaver로 WHERE createdAt BETWEEN '2025-08-14T15:00:00Z' AND '2025-08-15T15:00:00Z' 실행
AT TIME ZONE 'Asia/Seoul' 변환까지 포함해보니,
→ UTC/KST 모두 기대한 데이터 범위가 정확히 나옴
DB 저장 데이터 자체에는 문제 없음 확인.

서버 로그 확인

API 요청 시 전달되는 startDate, endDate 파라미터 출력
day.js로 KST 하루 → UTC 변환 결과 확인
서버에서 만드는 파라미터도 정상.

RDS TimeZone 확인

SHOW TIME ZONE;Asia/Seoul
이미 세션 타임존은 KST
RDS 설정 자체 문제 아님.

TypeORM 동작 확인

TypeORM의 createQueryBuilderWHERE createdAt >= :start AND createdAt < :end 정상 작동 확인
값 전달에는 문제 없음.
→ 여기까지 오면 데이터 저장, 전달, 필터 조건 모두 정상.
남은 건 SELECT 절에서 뭔가 꼬였다는 의심.

문제 지점 발견 – EXTRACT

SELECT EXTRACT(HOUR FROM chat.createdAt) ...
SQL
복사
timestamptz 컬럼에서 EXTRACT(HOUR FROM ...) 하면,
세션 타임존(KST) 기준으로 변환 후 hour를 뽑음.
필터는 UTC 기준이었는데, 집계는 KST로 → 시간대가 9시간 밀린 값이 나옴.
이게 “9시간 전까지만 나오는” 원인이었음.

해결

EXTRACT(HOUR FROM (chat.createdAt AT TIME ZONE 'Asia/Seoul'))
SQL
복사
날짜 범위는 반열림 < :next로 설정해 경계 문제 제거.

결론?

원인: EXTRACT가 세션 타임존을 사용하기 때문에, 필터(UTC)와 집계(KST)가 불일치
해결: AT TIME ZONE으로 명시적으로 타임존을 고정 → 원하는 시간대 기준으로 정확히 집계 가능

참조