DB Timezone 다루기

라모스·2023년 5월 11일
0
post-thumbnail

Database에 시간관련 데이터를 Datetime 타입으로 저장하는게 항상 옳을까?

// 이 글은 How to Handle Database Timezones 게시글을 번역한 내용입니다.

Issue

로컬 환경에서 단순히 토이 프로젝트 식으로 개발한다던지, 개발하는 서비스가 내가 위치한 지역의 Timezone 밖으로 벗어날 일이 없다면 크게 문제는 없을 것이다.

하지만, 서비스의 규모가 있고 실제 제공하는 제품이라면 문제가 된다. 국제화를 고려해서 수많은 이용자들의 Timezone이 제각각 다르다면 이에 대한 데이터를 보장할 수 있을까?

예를 들어, 내가 5월 10일 오전 10시를 insert 했을 경우 마드리드에서, 싱가포르에서, LA에서의 시간이 똑같지는 않다. 해당 지역에 위치한 사용자에게 편리하게 날짜/시간을 제공해줄 수 없을 것이다.

발생할 문제들에 대해 좀 더 구체적으로 정리하자면 다음과 같다.

  • DB에 저장된 날짜 및 시간 값은 참조하는 시간대에 대해 명확하지 않을 수 있다.
  • 여러 시간대에 있는 여러 사용자로 인해 날짜와 시간이 명확하지 않을 수 있다.
  • Daylight 절약 시간은 시간을 저장하고 읽을 때 문제를 일으킬 수 있다.

시간대 작업 관련 용어 정리

  • UTC : Coordinated Universal Time의 약자로, 세계 시계가 가는 표준시다.
  • 시간대 : 특정 시간을 따르는 세계의 영역. 각각 "미국 동부 표준시" 또는 줄여서 EST와 같은 이름이 있다.
  • 시간대 오프셋 : 시간대의 시간이 UTC 시간보다 빠르거나 뒤처지는 시간입니다. -5 또는 +11과 같은 숫자 및 기호로 표시됩니다.
  • Daylight Saving Time : 시간대에 일시적으로 적용되는 표준 시간에 대한 시간 오프셋이다. (summer time)
  • GMT : 그리니치 표준시(Greenwich Mean Time)의 약자로 UTC와 유사하지만 약간의 차이가 있는 시간이다.

몇 가지 고려 사항과 이상적인 해결책을 살펴보도록 하자.

고려 사항

1. 데이터를 삽입한 사용자의 시간대 저장

첫 번째로 고려할 수 있는 사항은, 데이터를 삽입하는 해당 사용자의 시간대를 저장하는 방법이다.

이것은 동일한 column(시간대 정보를 저장하는 데이터 유형이 있으므로) 또는 별도의 column 내에 있을 수 있다.

위의 예에서 데이터를 삽입하는 사용자는 UTC +10인 오스트레일리아에 기반을 두고 있다. 데이터는 다음과 같다.

2021-03-25 10:00:00 +10

시간이 끝날 때 UTC 오프셋인 +10을 표시한다. 또는 시간대 이름을 저장할 수도 있다.

2021-03-25 10:00:00 +10 Australian Eastern Standard Time

그러나 여기에는 몇 가지 문제가 있다.

  • 이 사용자가 미래의 시간을 읽을 때 일광 절약 시간으로 변환하거나 일광 절약 시간에서 변환해야 할 수 있다.
  • 다른 사용자가 이 시간을 읽어야 할 때 현지 시간대로 변환해야 한다.
  • 이 column의 데이터는 모두 다른 시간대에 있으므로 정렬, 필터링 및 다른 작업을 수행하기 어렵다.

2. 서버 시간 저장

또 다른 옵션은 서버의 시간대에 시간을 저장하는 것이다.

호주에 있고 팀원이 인도에 있고 서버가 미국 동부 해안에 있는 경우 모든 날짜와 시간을 미국 동부 시간대에 간단히 저장할 수 있다.

데이터는 다음과 같을 수 있다.

2021-03-25 10:00:00 -5 US Eastern Standard Time

서버가 있는 시간대가 변경될 수 있으므로 오프셋과 시간대 이름을 저장해야한다. 시간대가 일광 절약 시간으로 변경될 수 있다. 즉, 데이터베이스의 시간이 일관성이 없게 된다(일부는 표준 시간으로, 다른 일부는 일광 절약 시간으로).

또는 데이터베이스 column에서 표준시를 고수하면 데이터를 삽입할 때 데이터를 변환해야 한다.

데이터가 일치하지 않을 수 있다.

권장되는 해결 방안

거의 모든 경우에 적용되는 널리 권장되는 해결 방안이 있다. 날짜를 UTC 시간대로 저장하고 사용자의 시간대로 표시한다. 이에 대한 몇 가지 예외 케이스 또한 존재한다. 이 권장 해결 방안에 대한 규칙은 다음과 같다.

Rule 1. 날짜를 UTC로 저장

날짜와 시간을 저장하기 위해 널리 권장되는 솔루션은 날짜와 시간을 UTC로 저장하는 것이다.

즉, 데이터베이스에 datetime 값을 삽입하거나 업데이트하는 사용자가 있을 때마다 UTC로 변환하고 UTC 값을 데이터베이스 column에 저장한다.

귀하의 데이터는 일관됩니다. 이 column(및 모든 datetime column)의 모든 데이터는 동일한 시간대에 저장되므로 훨씬 쉽게 작업할 수 있다.

그런 다음 모든 변환 또는 계산을 쉽게 수행할 수 있다.

Rule 2. 적절한 데이터 유형 사용

날짜와 시간을 저장하기 위해 각 데이터베이스에서 사용할 수 있는 많은 데이터 타입이 있다. 이 타입들을 적절하게 사용하며 DATE만 사용하는 것은 피해야 한다.

VARCHAR와 같은 텍스트 열은 쉽게 유효성을 검사할 수 없으므로 사용하면 안 된다.

날짜 및 시간대를 저장하기 위해 어떤 데이터 유형을 사용해야 할까?

  • Oracle : UTC 시간대를 저장할 수 있으므로 TIMESTAMP WITH TIME ZONE 데이터 타입을 사용한다.
  • SQL Server : UTC 시간대를 저장할 수 있는 DATETIMEOFFSET 데이터 타입을 사용한다.
  • MySQL : 시간대 구성 요소를 포함할 수 있는 TIMESTAMP 데이터 타입을 사용한다.
  • PostgreSQL : UTC 시간대를 저장할 수 있으므로 TIMESTAMP WITH TIME ZONE 데이터 타입을 사용한다.

날짜와 시간만 저장하는 데이터 타입에 데이터를 저장하고 모두 UTC로 가정할 수 있다. 그러나 시간대가 함께 저장되어 있으면 계산을 수행하기가 더 쉬울 수 있으므로 위의 데이터 타입에 시간대 구성 요소가 포함된다.

Rule 3. 사용자에게 해당 사용자의 시간대를 표시

세 번째 규칙은 사용자 시간대의 날짜와 시간을 표시하는 것이다. 이는 사용자가 일이 발생한 시간을 더 쉽게 이해할 수 있도록 하기 위한 것이다.

데이터가 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에 설명되어 있다.

이 문제의 예는 다음과 같다.

  1. 20201년 12월에 사용자가 2025년 4월 19일 미국 동부 표준시(UTC -5) 기준 오후 1시에 이벤트를 예약합니다. 2025-04-19 01:00 PM -5
  2. 이것은 UTC로 변환된 다음 데이터베이스에 저장된다: 2025-04-19 08:00 AM +0
  3. 2023년 어느 시점에 미국 동부는 시간대 규칙을 변경하여 UTC 오프셋을 -5에서 -6으로 변경한다.

이로 인해 향후 날짜 및 시간에 문제가 발생한다. 이 예에서는 2025년 4월 19일이 되면 오후 1시로 생성된 일정이 오전 8시로 저장되지만 사용자의 시간대로 변환하면 오후 2시로 표시된다.(원래 시간 대신 6시간이 추가됨)

원래 요청자는 오후 1시로 설정했지만 지금은 잘못된 오후 2시로 표시된다. 시간대가 변경되면 원래 시간을 변경해야 하는가?

위 블로그 게시글에 따르면 자신의 기사에서 몇 가지 솔루션을 제안한다.

  • UTC로 변환하고 영원히 사용(이 예에 표시된 대로 문제가 발생할 수 있음)
  • UTC로 변환하고 규칙이 변경되면 다시 변환한다(이는 시간대 및 규칙에 대한 더 많은 메타데이터를 저장해야 함을 의미함).
  • 현지 시간을 보존하고 UTC를 파생 시간으로 사용(위 블로그 저자가 선호하는 옵션)

많은 미래 날짜를 저장할 계획이라면 위 블로그 게시물에서 자세한 내용을 확인해보자.

로그 이벤트 또는 레코드 생성 시간과 같은 과거 날짜만 저장하는 경우 문제가 되지 않을 수 있다.

최근 지난 날짜

이 규칙에는 또 다른 예외가 있으며 이는 최근 과거에 발생한 이벤트에 대한 것이다.

이는 새 표준 시간대 규칙이 적용되는 시점과 이를 처리하기 위해 애플리케이션이 업데이트되는 시점 사이에 지연이 있을 때 발생한다.

예를 들면 다음과 같다.

  1. 2021년 7월 1일에 미국 중부 시간대가 오프셋이 -6인 시간대와 -6:30인 시간대의 두 시간대로 분할될 것이라고 발표했다.
  2. 이 규정은 2021년 9월 1일부터 시행된다.
  3. 우리의 팀은 변경 사항을 알게 되었지만 2021년 12월 1일까지 애플리케이션의 시간대 계산 논리에 대한 업데이트를 배포할 수 없다.
  4. 이는 2021년 9월 1일부터 2021년 12월 1일 사이의 기간 동안 우리의 애플리케이션이 잘못된 계산 논리를 사용하고 있음을 의미한다. 이 영향을 받는 시간대의 날짜와 시간이 약간 잘못되었다. 배포가 발생하면 영향을 받는 데이터를 업데이트하는 스크립트가 포함될 수 있다.

따라서 배포 프로세스와 얼마나 빨리 변경할 수 있는지에 따라 시간대가 변경될 때 데이터가 부정확해지는 기간이 있을 수 있다.

결론

시간대는 고려해야 할 사항이 많기 때문에 데이터베이스에서 작업하기 까다로울 수 있다. 일반적으로 UTC 시간대에 날짜와 시간을 저장하고 표시할 사용자 시간대로 변환하는 것이 선호되는 옵션이다. 미래의 날짜는 이에 대한 예외일 수 있지만 이에 대해서도 작동하는 솔루션이 있다.

References

profile
Step by step goes a long way.

0개의 댓글