Skip to content

Fix/server timezone - #97

Merged
tember8003 merged 2 commits into
devfrom
fix/server-timezone
Sep 30, 2026
Merged

tember8003 merged 2 commits into
devfrom
fix/server-timezone

Conversation

@tember8003

Copy link
Copy Markdown
Collaborator

Summary

  • 운영 컨테이너의 JVM 기본 시간대가 UTC라서 생성, 제출 시각이 9시간 이르게 보이고, 결석 알림과 과제 미제출 보고가 의도보다 9시간 늦게 발송되던 문제를 고친다.
  • JDBC 변환 시간대(hibernate.jdbc.time_zone)는 Asia/Seoul인데 JVM만 UTC여서 생긴 문제다. 과거에 바꾼 serverTimezone은 저장값에 영향이 없다는 것을 운영과 같은 MySQL 8.0 조건에서 재현해 확인했다.
  • 시간대를 TimeZones 상수로 모으고 JVM 기본값, 스케줄러, 알림 조회 기준을 모두 서울로 고정한다. 기존에 +9시간으로 저장된 plan의 시작일과 마감일은 운영 기동 시 한 번 자동 보정한다.
  • 과제 제출에서 허용하는 블로그 도메인을 늘린다.

Flow

운영에서 앱이 뜨면 다음 순서로 돈다.

  • LmsApplication.main()이 스프링을 띄우기 전에 JVM 기본 시간대를 Asia/Seoul로 고정한다. 이후 LocalDateTime.now(), 생성 시각, 로그 시각이 모두 한국 시각이 된다.
  • 모든 빈이 만들어진 뒤, 웹 서버가 요청을 받기 전에 PlanDateCorrection이 돈다(prod 프로필에서만).
    • data_correction 표식이 있으면 건너뛴다.
    • 표식이 없으면 plan을 plan_backup_20260930으로 백업한 뒤, 한 트랜잭션에서 표식을 남기고 start_date, end_date를 9시간 당긴다.
    • 실패하면 표식과 UPDATE가 함께 롤백되고 앱이 뜨지 않는다. 다음 기동 때 처음부터 다시 시도한다.
  • 스케줄러는 zone = TimeZones.SEOUL로 해석되어 과제 미제출 보고는 매주 월요일 0시, 결석 알림은 매일 오전 9시에 발송된다. 두 알림의 조회 기준 시각도 서울 기준으로 계산한다.

과제 제출 시 본문의 URL 호스트가 허용 도메인이거나 그 하위 도메인이면 통과한다. 이름만 비슷한 도메인(evilgithub.io, github.io.evil.com, github.com@evil.com)은 여전히 거절된다.

Changes

  • config/TimeZones — 서비스 기준 시간대 상수(SEOUL, SEOUL_ZONE)
  • LmsApplication — 스프링 기동 전에 JVM 기본 시간대를 서울로 고정
  • config/PlanDateCorrection — 운영 기동 시 기존 plan 시작일, 마감일을 한 번만 9시간 당기는 긴급 보정(백업, 표식, 트랜잭션)
  • ScheduledTaskService — 두 cron에 zone = TimeZones.SEOUL 지정
  • WeeklyAlertService, AttendanceAlertService — 조회 기준 now()를 서울 기준으로 계산
  • ValidationUtils — 허용 도메인에 github.io, github.com, notion.site, notion.so, oopy.io, medium.com, brunch.co.kr, blogspot.com, gitbook.io 추가

Test

  • LmsApplicationTimeZoneTest — JVM이 UTC여도 서울로 고정되는지, main()이 스프링 기동 전에 고정하는지
  • ScheduledTaskServiceZoneTest — 두 cron이 서울 기준 월요일 0시, 매일 오전 9시로 해석되는지
  • AlertServiceSeoulClockTest — JVM이 UTC여도 두 알림이 서울 기준으로 조회하는지
  • PlanDateCorrectionTest — 9시간 보정, NULL 유지, 백업, 한 번만 실행, prod 외 환경에서 미등록. 다른 계획 행이 있는 DB에서는 건너뛴다
  • ValidationUtilsSubmissionDomainTest — 기존, 추가 도메인 허용과 사칭 도메인 거절
  • 전체 66건 통과(로컬 MySQL 8.0, JVM UTC). 패키징한 jar를 prod 프로필과 JVM UTC로 두 번 띄워 첫 기동에서 웹 서버보다 먼저 보정되고 두 번째 기동에서 건너뛰는 것을 확인했다.

후속

  • 이 PR이 배포된 뒤 이전 이미지로 롤백하면 안 된다. 보정은 한 번만 돌기 때문에 옛 버전이 다시 뜨면 데이터가 다시 어긋난다. 롤백이 필요하면 plan_backup_20260930 기준으로 복구한다.
  • 배포 후 기존 계획의 마감 시각이 입력값대로 보이는지, 새 제출의 생성 시각이 한국 시각인지 확인한다. 백엔드 push와 프론트 CD가 동시에 시작되어 이전 이미지가 배포될 수 있다.
  • 과거 출석 중 한국 시각 0시에서 9시 사이에 활동 글로 만든 행은 날짜가 전날로 저장됐을 수 있으나, 어느 행인지 가려낼 수 없어 보정하지 않는다.
  • 보정이 적용되어 data_correction 표식이 남은 뒤에는 PlanDateCorrection을 지워도 된다.

운영 JVM이 UTC라 생성, 제출 시각이 9시간 이르게 보이고 알림이 의도보다 9시간 늦게 발송되던 문제 수정. 기존 plan 날짜는 운영 기동 시 한 번 자동 보정(백업 plan_backup_20260930), 배포 후 이전 이미지로 롤백 금지
github.io, github.com, notion.site, notion.so, oopy.io, medium.com, brunch.co.kr, blogspot.com, gitbook.io 추가
@jongcoding

Copy link
Copy Markdown
Member

개추드립니다

@jongcoding
jongcoding self-requested a review September 30, 2026 02:34
@tember8003

Copy link
Copy Markdown
Collaborator Author

개추크레용 감사합니다

@tember8003
tember8003 merged commit 338271a into dev Sep 30, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants