과제 기록은 9시간 이르게, 마감은 9시간 늦게 저장되던 문제를 JVM과 JDBC의 시간대 불일치에서 찾았다
- 생성 시각 오차 -9시간 → 0
- 마감 저장 오차 +9시간 → 0
문제 상황
동아리원이 과제 기록의 시각이 실제와 다르다고 제보했다. 서버가 만든 시각과 사용자가 입력한 마감 시각을 나누어 DB 저장값과 API 응답을 비교했더니, 제출과 생성 시각은 실제보다 9시간 이르게 보였고 사용자가 입력한 마감 시각은 DB에 9시간 늦게 저장돼 있었다. 결석 알림도 09:00가 아닌 18:00에, 주간 미제출 보고는 월요일 0시가 아닌 월요일 09:00에 나갔다. 2025-09-30에는 과제 제출 엔티티만 ZonedDateTime으로 바꿨다가, 다른 시각 필드와 기준이 달라져 같은 날 되돌린 적이 있었다.
해결 방안
운영과 같은 MySQL 8.0 컨테이너에서 JVM 시간대와 JDBC 설정을 바꿔 가며 DB 원본값과 API 반환값을 비교했다. JVM 기본 시간대는 UTC인데 Hibernate의 JDBC 시간대는 Asia/Seoul이라, 시간대 정보가 없는 LocalDateTime이 서로 다른 기준으로 만들어지고 변환되고 있었다. 한국에서 쓰는 동아리 서비스이므로 앱 시작 시 JVM 기본 시간대를 Asia/Seoul로 고정하고 cron에도 시간대를 적었다. 기존 계획의 시작일과 마감일만 +9시간으로 저장돼 있어서, 이 두 값만 9시간 앞당기는 일회성 보정 코드를 따로 만들었다. 중복 보정을 막으려고 보정 완료 기록과 UPDATE를 같은 트랜잭션에 묶고, 보정 전 백업과 실패 시 기동 중단도 넣었다.
결과
JVM을 UTC로 띄운 실제 앱에서 수정 전에는 생성 시각이 -9시간, 마감값이 +9시간이었고, 수정 뒤에는 생성 시각이 실제 한국 시각과 같고 DB 마감값이 입력값과 같았다. 2026-10-30에는 MySQL 8.0.41, Connector/J 8.0.33에서 JVM 시간대 2가지와 serverTimezone 2가지의 4가지 조합을 다시 재, 저장값이 serverTimezone이 아니라 JVM 시간대에 따라 달라진다는 것을 확인했다. 2026-09-30 운영 배포가 성공했고 배포 이미지에 수정 코드가 들어 있는 것을 확인했다. 기존 데이터 보정이 운영에 실제로 적용됐는지는 아직 확인하지 못했다.

