MySQL·Docker·Spring 환경에서 발생한 Timezone 차이

김소희·2025년 12월 1일

본 문서는 댓글 작성 시각이 실제 시각과 약 8~9시간 차이가 발생한 문제에 대해
원인 분석부터 해결까지의 전 과정을 구조적으로 정리한 기술 문서입니다.
시간대 문제는 서버·DB·컨테이너 환경의 시간대 불일치에서 비롯된 것으로 분석되었으며,
각 레이어의 시간대를 한국 표준시로 통일하여 문제를 해결하였습니다.


문제 현상

  • 댓글 작성 직후 조회 시, 실제 작성 시간과 다르게 8시간 이상 과거로 표시되는 현상이 지속적으로 발생함.
  • 로그 및 조회된 createdAt 값의 차이를 검증한 결과, 응답 데이터의 시간 자체가 실제 값보다 약 9시간 늦게 저장 또는 직렬화되고 있음이 확인됨.

시간 표시 함수나 화면 출력 로직과 무관하게,
서버에서 내려오는 날짜·시간 값 자체가 이미 UTC 기준으로 기록되고 있었던 문제로 판단되었다.


1차 원인 분석: Spring Boot 설정 문제

초기 application.yml 구조를 확인하던 중,
일부 설정이 잘못된 계층 아래 배치되어 있어 Spring이 이를 인식하지 못하고 있는 상황이 발견되었다.

1. 문제점

예시:

spring:
  application:
    name: backend
    jackson:
      time-zone: Asia/Seoul

위 구조는 다음과 같은 문제를 가지고 있었다.

  • jacksondatasource 관련 설정이 spring.application 아래 잘못 배치되어 있었고,
  • 실제로는 spring.jackson.time-zone 키로 등록되지 않기 때문에
    Jackson 직렬화 시 정상적으로 시간대가 적용되지 않음.

2. 올바른 설정 구조

application.yml은 다음과 같이 수정되었다.

spring:
  application:
    name: backend

  jackson:
    time-zone: Asia/Seoul

  datasource:
    url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3308}/${DB_NAME:codenemsy}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
    username: ${DB_USERNAME:root}
    password: ${DB_PASSWORD:1234}
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      data-source-properties:
        serverTimezone: Asia/Seoul

이 설정을 통해 다음 사항이 보장된다.

  • Jackson이 날짜를 JSON 직렬화 시 Asia/Seoul 기준 적용
  • JDBC 드라이버가 MySQL 서버 시간대를 Asia/Seoul로 해석
  • HikariCP 커넥션 풀에서도 동일한 시간대 기준을 전달

그러나 이 수정만으로도 시간 차이는 지속되었고,
문제는 Spring이나 애플리케이션 레이어가 아니라 DB 또는 Docker 환경에서 비롯된 것으로 보였다.


2차 원인 분석: MySQL 컨테이너 Timezone 문제

MySQL은 다음 쿼리를 통해 현재 시간대 설정을 확인할 수 있다.

SELECT @@global.time_zone, @@session.time_zone;

실제 출력:

+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| SYSTEM             | SYSTEM              |
+--------------------+---------------------+

이는 다음을 의미한다.

  • MySQL이 시간대를 자체적으로 명시하지 않았으며,
  • 컨테이너 OS(리눅스)의 시스템 시간대를 그대로 따르고 있음.

따라서 정확한 문제를 파악하기 위해 MySQL 컨테이너의 OS 시간대를 점검하였다.


Docker 컨테이너 OS 시간대 점검

MySQL 컨테이너 내부로 진입하여 OS의 시간을 확인하였다.

docker exec -it mysql8 bash
date

이때 출력되는 시간이 한국 표준시(KST)가 아닌 경우,
다음과 같은 결론을 얻을 수 있다.

  • 현재 MySQL은 SYSTEM 시간대를 사용 중이므로,
  • OS가 UTC이면 MySQL 역시 사실상 UTC로 동작하게 된다.
  • 그 결과, DB에 저장되는 created_at 값이 실제 한국 시간보다 9시간 느리게 기록된다.

이는 API 응답에서도 동일하게 반영되어
작성한 지 얼마 되지 않은 데이터가 9시간 전으로 표시되는 현상을 발생시킨다.


문제 해결: Docker 컨테이너 OS 시간대 변경

컨테이너 내에서 다음 명령을 실행하여 OS 시간대를 KST로 변경하였다.

ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
echo "Asia/Seoul" > /etc/timezone

이후 MySQL 서버를 재시작하였다.

docker restart mysql8

MySQL 서버 시간대 재설정

MySQL 프로세스는 재시작 후에도 시스템 시간을 따라가지만,
보다 확실한 일관성을 위해 MySQL 내부에서도 time_zone을 명시적으로 설정하였다.

SET GLOBAL time_zone = 'Asia/Seoul';
SET time_zone = 'Asia/Seoul';

이후 다시 확인:

SELECT @@global.time_zone, @@session.time_zone;

이제 이상적으로 다음과 같은 결과를 얻을 수 있다.

+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| Asia/Seoul         | Asia/Seoul          |
+--------------------+---------------------+

이 상태에서는

  • DB 저장 시간
  • Jackson 직렬화 시간
  • JDBC 드라이버 해석 시간

모두 Asia/Seoul 기준으로 일치하게 된다.


정리

시간대 문제는 여러 레이어의 조합으로 발생할 수 있다

  1. Docker 컨테이너 OS 시간대
  2. MySQL 서버의 글로벌 및 세션 시간대
  3. Spring Jackson 직렬화 시간대
  4. JDBC 서버 시간대 설정
  5. HikariCP 데이터소스 프로퍼티 적용 여부

이 중 하나라도 UTC 또는 잘못된 값으로 설정되어 있으면
전체 파이프라인에서 시간 차이가 누적될 수 있다.


YAML 들여쓰기 오류는 설정 무효화를 일으킨다

spring.jackson이나 spring.datasource.hikari가 잘못된 위치에 존재하면
설정이 정상적으로 반영되지 않으며,
결과적으로 예상치 못한 시간대 처리 문제를 초래할 수 있다.


MySQL에서 SYSTEM은 컨테이너 OS를 의미한다

MySQL에서 time_zone이 SYSTEM으로 보인다고 해서 정상이라는 의미가 아니며,
컨테이너 OS의 시간대가 한국 기준인지 반드시 확인해야 한다.

profile
개발자 소희의 노트

0개의 댓글