개발
home
🕒

Spring Boot에서 DB + System + Spirng Boot Timezone 처리 방법

Created
2022/10/11
Tags
SpringBoot
TimeZone
Docker
2022-10-11 @이영훈
lastUpdatedAt: 2022-10-16
Spring Boot에서 DB에서 불러온 데이터의 타임존을 어떻게 처리하는 지 테스트 해 보았습니다.
Hibernate의 공식문서를 보면 타임존을 어떻게 처리하는 지 설명이 되어 있습니다.
When the time zone is not specified, the JDBC driver is going to use the underlying JVM default time zone, which might not be suitable if the application is used from all across the globe. For this reason, it is very common to use a single reference time zone (e.g. UTC) whenever saving/loading data from the database. 타임존이 지정되어 있지 않으면, JDBC 드라이버는 JVM의 기본 타임존을 사용하는데, 이는 글로벌 서비스에서 사용된다면 적합하지 않을 수 있습니다. 그래서, 데이터베이스에 데이터를 적재하거나 가져올 때, UTC와 같은 하나의 타임존을 사용하는 것이 일반적입니다.
두 가지 방법으로 모든 서비스에서 동일한 타임존을 사용하도록 설정하는 방법이 있습니다.

첫번째, JVM의 타임존을 설정하는 방법

JVM에서 타임존을 설정하는 방법입니다.
명시적으로 설정하는 방법은 java를 실행할 때 타임존을 명시하는 것입니다.
java -Duser.timzone=UTC ...
Bash
복사
스프링부트에서 프로그래밍으로 하는 방법은 다음과 같습니다.
@Configuration class GlobalConfig { @PostConstruct fun setTimeZone() { TimeZone.setDefault(TimeZone.getTimeZone("UTC")) } }
Kotlin
복사

️ 두번째, hibernate.jdbc.time_zone 설정하는 방법 (권장)

첫번째 방법은 JVM의 타임존을 설정하기 때문에 DB에서 데이터를 불러오고 저장하는 것보다 더 큰 어플리케이션 레벨에서 처리합니다. 그래서 UI에 시간을 랜더링하는 부분이 있다면 해당 타임존도 함께 변경되는 문제가 있습니다.
Hibernate 5.2.3 버전부터 JBCD 레벨에서 타임존을 설정할 수 있습니다.
application.yml 파일에 hibernate.jdbc.time_zone 설정을 추가합니다.
spring: jpa: properties: hibernate: jdbc: time_zone: UTC
YAML
복사

첫번째와 두번째 방법을 혼용했을 때 실행결과

hibernate.jdbc.time_zone 을 UTC로 설정한 뒤에 데이터베이스에 어떻게 저장이 되는지, 어떻게 호출이 되는 지 확인해보겠습니다.
다음과 같이 테스트를 위해서 Person Entity를 만듭니다.
@Entity class Person( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long? = null, val name: String, val createdAt: OffsetDateTime = OffsetDateTime.now(), ) { companion object { fun construct(name: String) = Person(name = name) } }
Kotlin
복사
그리고 다음과 같이 person 객체를 저장하고 시스템의 현재 시간과 person의 createdAt을 불러오는 테스트 코드를 작성하였습니다.
@SpringBootTest internal class PersonTest { @Autowired private lateinit var personRepository: PersonRepository @Test fun createPerson() { // given var person = Person.construct("leedo") // when person = personRepository.save(person) // then println("person.createdAt = ${person.createdAt}") println("OffsetDateTime.now() = ${OffsetDateTime.now()}") } }
Kotlin
복사
테스트 #1
추가로 시스템의 현재 시간을 UTC로 표시하기 위해서 스프링부트의 타임존을 UTC로 설정하였습니다
@Configuration class GlobalConfig { @PostConstruct fun setTimeZone() { TimeZone.setDefault(TimeZone.getTimeZone("UTC")) } }
Kotlin
복사
한국시간으로 2022-10-16 17:20에 코드를 실행했습니다
person.createdAt = 2022-10-16T08:20:13.832229Z OffsetDateTime.now() = 2022-10-16T08:20:13.879878Z
Kotlin
복사
결과
데이터베이스에 UTC 타임존으로 저장되었습니다.
호출된 createdAt도 UTC 타임존으로 표시되는 것을 확인할 수 있습니다.
시스템의 현재시간의 타임존도 UTC로 표시되었습니다.
테스트 #2
이번에는 시스템의 타임존을 KST로 변경하고 테스트코드를 실행하였습니다
@Configuration class GlobalConfig { @PostConstruct fun setTimeZone() { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")) } }
Kotlin
복사
한국시간으로 2022-10-16 17:30에 코드를 실행했습니다
person.createdAt = 2022-10-16T17:30:34.650527+09:00 OffsetDateTime.now() = 2022-10-16T17:30:34.700086+09:00
Kotlin
복사
결과
데이터베이스에 UTC 타임존으로 저장되었습니다.
호출된 createdAt 필드는 KST 타임존으로 표시되었습니다.
시스템의 현재시간의 타임존은 KST로 표시되었습니다.

정리

위의 결과로 보면, 첫번째와 두번째 방법을 섞어서 사용했을 때
데이터베이스에 저장되는 것은 application.yml(hibernate.jdbc.time_zone)에 정의된 방식으로 처리되고 (내부적으로 데이터베이스와 연결될 때 명시된 타임존을 세션 타임존을 설정하는 것으로 추측)
데이터베이스에 접근해서 객체로 가지고 온 뒤에 콘솔에 표시하거나 할 때는 스프링에서 정의된 타임존을 따라갑니다.

Reference