Database에 시간관련 데이터를 Datetime 타입으로 저장하는게 항상 옳을까?
// 이 글은 How to Handle Database Timezones 게시글을 번역한 내용입니다.
로컬 환경에서 단순히 토이 프로젝트 식으로 개발한다던지, 개발하는 서비스가 내가 위치한 지역의 Timezone 밖으로 벗어날 일이 없다면 크게 문제는 없을 것이다.
하지만, 서비스의 규모가 있고 실제 제공하는 제품이라면 문제가 된다. 국제화를 고려해서 수많은 이용자들의 Timezone이 제각각 다르다면 이에 대한 데이터를 보장할 수 있을까?
예를 들어, 내가 5월 10일 오전 10시를 insert 했을 경우 마드리드에서, 싱가포르에서, LA에서의 시간이 똑같지는 않다. 해당 지역에 위치한 사용자에게 편리하게 날짜/시간을 제공해줄 수 없을 것이다.
발생할 문제들에 대해 좀 더 구체적으로 정리하자면 다음과 같다.
몇 가지 고려 사항과 이상적인 해결책을 살펴보도록 하자.
첫 번째로 고려할 수 있는 사항은, 데이터를 삽입하는 해당 사용자의 시간대를 저장하는 방법이다.
이것은 동일한 column(시간대 정보를 저장하는 데이터 유형이 있으므로) 또는 별도의 column 내에 있을 수 있다.
위의 예에서 데이터를 삽입하는 사용자는 UTC +10인 오스트레일리아에 기반을 두고 있다. 데이터는 다음과 같다.
2021-03-25 10:00:00 +10
시간이 끝날 때 UTC 오프셋인 +10을 표시한다. 또는 시간대 이름을 저장할 수도 있다.
2021-03-25 10:00:00 +10 Australian Eastern Standard Time
그러나 여기에는 몇 가지 문제가 있다.
또 다른 옵션은 서버의 시간대에 시간을 저장하는 것이다.
호주에 있고 팀원이 인도에 있고 서버가 미국 동부 해안에 있는 경우 모든 날짜와 시간을 미국 동부 시간대에 간단히 저장할 수 있다.
데이터는 다음과 같을 수 있다.
2021-03-25 10:00:00 -5 US Eastern Standard Time
서버가 있는 시간대가 변경될 수 있으므로 오프셋과 시간대 이름을 저장해야한다. 시간대가 일광 절약 시간으로 변경될 수 있다. 즉, 데이터베이스의 시간이 일관성이 없게 된다(일부는 표준 시간으로, 다른 일부는 일광 절약 시간으로).
또는 데이터베이스 column에서 표준시를 고수하면 데이터를 삽입할 때 데이터를 변환해야 한다.
데이터가 일치하지 않을 수 있다.
거의 모든 경우에 적용되는 널리 권장되는 해결 방안이 있다. 날짜를 UTC 시간대로 저장하고 사용자의 시간대로 표시한다. 이에 대한 몇 가지 예외 케이스 또한 존재한다. 이 권장 해결 방안에 대한 규칙은 다음과 같다.
날짜와 시간을 저장하기 위해 널리 권장되는 솔루션은 날짜와 시간을 UTC로 저장하는 것이다.
즉, 데이터베이스에 datetime
값을 삽입하거나 업데이트하는 사용자가 있을 때마다 UTC로 변환하고 UTC 값을 데이터베이스 column에 저장한다.
귀하의 데이터는 일관됩니다. 이 column(및 모든 datetime
column)의 모든 데이터는 동일한 시간대에 저장되므로 훨씬 쉽게 작업할 수 있다.
그런 다음 모든 변환 또는 계산을 쉽게 수행할 수 있다.
날짜와 시간을 저장하기 위해 각 데이터베이스에서 사용할 수 있는 많은 데이터 타입이 있다. 이 타입들을 적절하게 사용하며 DATE
만 사용하는 것은 피해야 한다.
VARCHAR
와 같은 텍스트 열은 쉽게 유효성을 검사할 수 없으므로 사용하면 안 된다.
날짜 및 시간대를 저장하기 위해 어떤 데이터 유형을 사용해야 할까?
IMESTAMP WITH TIME ZONE
데이터 타입을 사용한다.DATETIMEOFFSET
데이터 타입을 사용한다.TIMESTAMP
데이터 타입을 사용한다.TIMESTAMP WITH TIME ZONE
데이터 타입을 사용한다.날짜와 시간만 저장하는 데이터 타입에 데이터를 저장하고 모두 UTC로 가정할 수 있다. 그러나 시간대가 함께 저장되어 있으면 계산을 수행하기가 더 쉬울 수 있으므로 위의 데이터 타입에 시간대 구성 요소가 포함된다.
세 번째 규칙은 사용자 시간대의 날짜와 시간을 표시하는 것이다. 이는 사용자가 일이 발생한 시간을 더 쉽게 이해할 수 있도록 하기 위한 것이다.
데이터가 UTC로 저장되어 있으면 사용자의 시간대로 변환하여 표시할 수 있다. 이렇게 하면 시간대가 다른 사용자에게 다른 결과가 표시된다.
사용자의 시간대가 무엇인지 어떻게 알 수 있을까? 두 가지 방법이 있다.
사용자의 시간대를 결정하는 한 가지 방법은 사용자가 시간대를 선택하여 자신의 프로필에 저장하게 하는 것이다.
포럼에 가입한 적이 있는 경우 프로필에서 답변한 질문 중 하나는 시간대이다. 포럼 게시물에 표시할 시간을 계산하는 데 사용할 수 있다.
이 접근 방식은 애플리케이션에서 사용할 수 있다. 사용자에게 시간대를 선택하고 시스템의 프로필에 저장하도록 요청한다. 날짜 및 시간을 표시하는 계산은 이 시간대와 UTC 시간대에 저장된 데이터를 사용할 수 있다.
이 방법을 사용하는 경우 시간대 오프셋(예: -5, +9)을 저장하면 안 된다. 예를 들어 일광 절약 시간제와 같이 오프셋이 변경될 수 있기 때문이다. 시간대 이름을 저장하는 것이 좋다.
Timezone 이름은 "America/Los Angeles"와 같은 문자열인 IANA 시간대 식별자 ("Olsen 이름"이라고도 함)를 사용한다. 시간대를 고유하게 식별하기 위해 이 문자열을 저장할 수 있다. 이 값에 대한 고유한 정수 ID는 없지만 다행스럽게도 많은 데이터베이스가 이러한 IANA 이름으로 작동할 수 있다.
이 필드는 얼마나 길어야 할까?
2021년 9월 현재 가장 긴 문자열은 32자이다. "America/Argentina/ComodRivadavia"
비교하자면 MySQL 에는 mysql.time_zone_name
에 64자로 된 "name"이라는 column이 있다. 따라서 향후 새 이름을 수용하기 위해 이것을 VARCHAR(64)
column에 저장할 수 있다.
조회 테이블에 가능한 시간대를 저장하고 각각에 고유한 ID를 부여하고 이를 사용자 테이블과 연관시킬 수 있다.
사용자에게 시간대를 선택하도록 요청하지 않으려는 경우 다른 옵션은 웹 애플리케이션에서 시간대를 계산하도록 하는 것이다.
이것은 일부 언어로 수행될 수 있으며 매우 정확할 수 있다.
사용 중인 프로그래밍 언어에서 이 작업을 수행하는 방법을 찾아볼 수 있다.
향후 이벤트에 대한 날짜/시간을 저장하는 경우는 예외 사항이다.
날짜를 미래 날짜에 대한 UTC 값으로 저장하면 일광 절약 시간제 및 시간대 규칙이 언제 변경되는지 알 수 없기 때문에 나중에 문제가 발생할 수 있다.
이것은 STORING UTC IS NOT A SILVER BULLET에 설명되어 있다.
이 문제의 예는 다음과 같다.
2025-04-19 01:00 PM -5
2025-04-19 08:00 AM +0
이로 인해 향후 날짜 및 시간에 문제가 발생한다. 이 예에서는 2025년 4월 19일이 되면 오후 1시로 생성된 일정이 오전 8시로 저장되지만 사용자의 시간대로 변환하면 오후 2시로 표시된다.(원래 시간 대신 6시간이 추가됨)
원래 요청자는 오후 1시로 설정했지만 지금은 잘못된 오후 2시로 표시된다. 시간대가 변경되면 원래 시간을 변경해야 하는가?
위 블로그 게시글에 따르면 자신의 기사에서 몇 가지 솔루션을 제안한다.
많은 미래 날짜를 저장할 계획이라면 위 블로그 게시물에서 자세한 내용을 확인해보자.
로그 이벤트 또는 레코드 생성 시간과 같은 과거 날짜만 저장하는 경우 문제가 되지 않을 수 있다.
이 규칙에는 또 다른 예외가 있으며 이는 최근 과거에 발생한 이벤트에 대한 것이다.
이는 새 표준 시간대 규칙이 적용되는 시점과 이를 처리하기 위해 애플리케이션이 업데이트되는 시점 사이에 지연이 있을 때 발생한다.
예를 들면 다음과 같다.
따라서 배포 프로세스와 얼마나 빨리 변경할 수 있는지에 따라 시간대가 변경될 때 데이터가 부정확해지는 기간이 있을 수 있다.
시간대는 고려해야 할 사항이 많기 때문에 데이터베이스에서 작업하기 까다로울 수 있다. 일반적으로 UTC 시간대에 날짜와 시간을 저장하고 표시할 사용자 시간대로 변환하는 것이 선호되는 옵션이다. 미래의 날짜는 이에 대한 예외일 수 있지만 이에 대해서도 작동하는 솔루션이 있다.