상황


글로벌 서비스 준비중인데, 모든 시간을 KST 로 작업하고 있었다는..


그래서 모두 UTC 기준으로 변경을 하고 싶어서 프로그래머들끼리 가볍게 이야기를 나눠봤다.


여기서 전제조건!


1. 퍼블리셔는 OS 시간을 변경해줄 수 없음

2. 기존 Code 의 변경은 최소화 (로직을 변경하면서 버그를 만들 수 있는 상황이 아님)



우선, 기준이 되는 UTC 를 사용하려면, DB 에서 쓰고있던 Procedure base logic 들도

CURRENT_TIMESTAMP() -> UTC_TIMESTAMP() 로 변경 (1) 이 필요했고,

Java Code 에서 Calendar, Date, Timestamp 를 쓰는 코드를 모두 점검 (2) 해야 했다.


(1) 은 replace 가 용이한 상황이어서 난이도 하하하

(2) 는 일일이 봐야해서 난이도가 높다고 생각했지만 생각보다 코드가 많지 않아서 중하하


라고 판단함




수정 감행


시간이 그리 많지 않아서 신속하게 결정하고, 아래와 같이 진행했다.



1. TimeZone 기반으로 Calendar 를 하나의 함수를 통해 얻어오도록 수정


기존에는 Calendar.getInstance() 를 여기저기서 호출했는데,

특정 날짜관련 클래스를 하나 만들고 거기서 getNow() 를 통해 Calendar 를 얻어가도록 수정



2. DB 의 모든 CURRENT_TIMESTAMP() 를 UTC_TIMESTAMP() 로 수정


기존에는 CURRENT_TIMESTAMP() 를 사용하고 있어서

MySQL 의 서버 시간 설정에 의한 시간값을 얻어가고 있었는데

UTC_TIMESTAMP() 를 이용함으로써, 서버 시간설정에 영향을 받지 않도록 수정



그 후, 원하는대로 값이 저장되는지 확인해봤다.


그런데, 좀 이상했다..


분명, WAS Code 에서 2015-07-01 00:00:00 을 얻었는데,

DB 에 2015-07-01 09:00:00 이 저장됨


그리고 WAS Code 에서 그 값을 읽어서 클라이언트로 전달하면서

클라이언트는 그 시간이 UTC 라고 믿고 기기가 KST 를 표시해야 하니, +09:00 를 추가로 함 -_-


서버가 클라로 줘야하는 시간이 UTC 로 약속되어있기 때문에,

서버에서 DB 의 값을 읽을 때 UTC 로 변환해야할 필요가 있는것 처럼 느껴졌었는데,


Calendar 를 얻어가는 함수 내부에서 TimeZone 을 UTC 로 설정하고 있기 때문에

코드상의 문제는 아닌것 같았고, 좀더 debugging 해서 결국 문제점을 찾았다.




핵심 문제


핵심 문제는 Timestamp 를 사용해서 DB 쪽으로 인자를 전달하는 과정에서

Timestamp 값이 Calendar의 TimeZone 에 의해 변경되지 않고

JVM의 TimeZone 에 의해 결정된 시간으로 저장되는 것을 몰랐던 것이 문제였다.


깊이 몰랐던 것이 문제여서, 이번 기회에 정리를 해두려고 한다.



Calendar 의 속성


Calendar 는 setTimeZone 에 의해 설정된대로 충실히 동작한다.


예를 들어, 2015-07-01 00:00:00 KST 인 값도 TimeZone 만 변경하면

get(Calendar.HOUR_OF_DAY) 를 했을때 설정된 TimeZone 에 의한 값을 return 한다.


set(Calendar.HOUR_OF_DAY, hour) 도, 내부적으로 설정된 TimeZone 에 의한 시간으로 set 한다.


그래서 현재 Calendar 가 무슨 TimeZone 을 갖는지 확인하고,

비교하는 데이터가 동일한 TimeZone 의 값인지 조심해야 한다.



Date 의 속성


Date 는 TimeZone 과 Independent 하다.


Calendar.getInstance().getTime() 의 Date 는, 내부적으로 fastTime 을 갖고 있고,

이 값이 TimeZone 에 의해 해석되는 형식이다.


여담인데, CallableStatement 의 인자를 setDate 로 넘기면, 날짜만 넘긴다.. -_-



MySQL 의 속성


MySQL 에도 TimeZone 이 엄연히 존재하지만,

JDBC 의 setTimestamp 를 통해 전달된 인자를 변경 없이 충실히 저장해주기 때문에

MySQL 내부에서 UTC_TIMESTAMP() 를 사용하면, 사실상 TimeZone Independent 한 작업을 할 수 있을 것 같다.




결론


여하한 이유로 OS 의 시간을 변경할 수 없다면,

JVM 에서 UTC 를 사용하고, MySQL 에서 UTC_TIMESTAMP() 를 사용하는게 좋은 것 같다.


만약, JVM 에서 UTC 를 설정하기조차 할 수 없었다면,

너무 슬펐을 것 같다. -_-


마지막으로, Tomcat6 에서 TimeZone 설정 변경법




/etc/tomcat6/tomcat6.conf 


... 


# Cannot resolve user database reference - naming-factory-dbcp

# the real problem is a cnfe that is avoided by configuring 

# the -Djavax.sql.DataSource.Factory. This fixes the rpm install. 

JAVA_OPTS="${JAVA_OPTS} -Djavax.sql.DataSource.Factory=org.apache.commons.dbcp.BasicDataSourceFactory -Duser.timezone=GMT” 


... 



+ Recent posts