Home 애플리케이션의 타임존은 어떻게 관리하는 것이 좋을까?
Post
Cancel

애플리케이션의 타임존은 어떻게 관리하는 것이 좋을까?

1. 글을 작성하게 된 계기


서버/애플리케이션과 데이터베이스의 Timezone 설정 관리 에 대해 고민하면서 생각을 정리하기 위해 글을 작성하게 되었습니다.





2 시간대 관리 정책


먼저 서버/애플리케이션과 데이터베이스에서 Timezone을 관리하는 정책 케이스를 간단히 살펴보겠습니다.

  1. Server: UTC / Database: UTC
  2. Server: UTC / Database: Local
  3. Server: Local / Database: UTC
  4. Server: Local / Database: Local





2-1. Server: UTC / Database: UTC

서버/애플리케이션, 그리고 데이터베이스 모두 UTC 타임존을 사용 하는 방식입니다. 이는 시스템에 전 세계 어디에서나 동일한 시간 기준 을 적용할 수 있습니다.

1
2
3
4
5
                      ----------------        ----------------
                      |  Application |        |      DB      |
                      |--------------|  --->  |--------------|
                      |     (UTC)    |        |     (UTC)    |
                      ----------------        ----------------





이 방식의 장/단점은 다음과 같습니다. 서버/애플리케이션, 데이터베이스 모두 UTC 시간대를 사용하기 때문에 서버/사용자 위치 와 관계없이 동일한 기준을 적용할 수 있습니다. 하지만 클라이언트 또는 서버 측에서 현지 시간대 로 데이터를 변환하는 추가 작업이 필요합니다.

  1. 장점
    • 일관된 시간 처리.
    • 사용자 위치, 시간대에 무관한 처리.
  2. 단점
    • 사용자의 로컬 시간대로 변환 필요(UI에서 처리).





2-2. Server: UTC / Database: Local

서버/애플리케이션은 UTC 타임존 을 사용하고, 데이터베이스는 로컬 시간대 로 데이터를 저장하는 방식입니다. 서버/애플리케이션 데이터베이스 간 시간대가 일치하지 않기 때문에, 애플리케이션 또는 클라이언트에 시간 변환 로직을 추가해야 합니다.

1
2
3
4
5
                      ----------------        ----------------
                      |  Application |        |      DB      |
                      |--------------|  --->  |--------------|
                      |     (UTC)    |        |     (로컬)    |
                      ----------------        ----------------





이 방식의 장/단점은 다음과 같습니다. 애플리케이션에서는 일관된 방식으로 데이터를 처리할 수 있으나 서버와 데이터베이스 간 시간대가 다르기 때문에 변환 작업이 필요합니다.

  1. 장점
    • 서버/애플리케이션에서 일관된 방식으로 데이터 처리.
  2. 단점
    • 애플리케이션과 DB 간 시간대 불일치로 변환 필요.
    • 시간대 관련 버그 발생 가능성 증가.





2-3. Server: Local / Database: UTC

서버/애플리케이션은 로컬 시간대 를, 데이터베이스는 UTC 타임존 을 사용하는 방식입니다. 데이터베이스에서 일관된 시간대로 데이터를 처리하기 때문에 각 서버/애플리케이션마다 로컬 시간과 UTC 간의 추가 변환 작업이 필요합니다.

1
2
3
4
5
                      ----------------        ----------------
                      |  Application |        |      DB      |
                      |--------------|  --->  |--------------|
                      |     (로컬)    |        |     (UTC)    |
                      ----------------        ----------------





이 방식의 장/단점은 다음과 같습니다. 데이터베이스에서는 UTC를 기준으로 시간을 관리하기 때문에 데이터의 일관성을 유지할 수 있습니다. 하지만 서버와 데이터베이스 간 시간대가 다르기 때문에 시간 변환 작업이 필요하며, 이로 인해 코드 복잡성이 증가할 수 있습니다.

  1. 장점
    • 사용자에게 로컬 시간대로 시간 제공.
    • DB는 UTC로 글로벌 일관성 유지 가능.
  2. 단점
    • 애플리케이션에서 로컬과 UTC 간 변환이 지속적으로 필요.





2-4. Server: Local / Database: Local

서버/애플리케이션과 데이터베이스 모두 로컬 시간대를 사용 하는 방식입니다. 잘 사용하지 않는 방식으로, 어느 곳을 기준으로 시간을 맞출지 확실히 정해야 합니다.

1
2
3
4
5
                      ----------------        ----------------
                      |  Application |        |      DB      |
                      |--------------|  --->  |--------------|
                      |     (로컬)    |        |     (로컬)    |
                      ----------------        ----------------





이 방식의 장/단점은 다음과 같습니다. 서버/애플리케이션과 데이터베이스 모두 로컬 시간대를 사용하므로 정확한 시간대를 알기 위해서는 변환이 필요 합니다. 이때, 어느 곳을 기준으로 시간을 변환할 지 명확한 기준이 필요합니다. 또한 글로벌 서비스의 경우, 각기 다른 시간대에서 접속하는 사용자들을 처리할 때 혼란이 생길 수 있습니다.

  1. 장점
    • 애플리케이션과 DB 모두 로컬 시간으로 일치하여 직관적.
    • 로컬 환경에서 시간 변환 필요 없음.
  2. 단점
    • 글로벌 서비스 시 다양한 시간대 처리 복잡.
    • 서버 위치에 따라 시간대가 달라질 수 있어 데이터 일관성 문제 발생.







3. 어떤 방법이 가장 괜찮을까?


개인적으로 서버와 애플리케이션 모두 UTC로 관리하는 방법이 괜찮다고 생각합니다. 서버/애플리케이션과 데이터베이스 모두 동일 시간대를 기준으로 데이터를 처리한 후, UI에 보여줄 때만 변환하면 일관된 방식으로 데이터를 처리 할 수 있으며, 시간대에 따른 혼란을 방지 할 수 있기 때문입니다.

1
2
3
4
5
            ----------------       ----------------        ----------------
            |    Client    |       |  Application |        |      DB      |
            |--------------|  ---> |--------------|  --->  |--------------|
            |   (Custom)   |       |     (UTC)    |        |     (UTC)    |
            ----------------       ----------------        ----------------





이를 통해 글로벌 서비스를 제공할 때도, 다양한 시간대의 사용자를 동일한 방식으로 처리할 수 있습니다. 뿐만 아니라 로그가 기록될 때도 동일한 기준으로 로그가 기록되면, 시간대와 관계없이 동일한 기준으로 데이터를 분석할 수 있습니다. 다른 다양한 장점이 있지만 이런 이유로 서버/애플리케이션, 데이터베이스가 모두 UTC로 데이터를 관리하는 게 가장 좋은 방식 같습니다. 프로젝트에도 이렇게 적용했고요.

  1. 글로벌 서비스에서 일관된 시간대 적용
  2. 로그 일관성







4. 마이그레이션


만약 데이터가 이미 저장된 경우 라면 상황이 조금 복잡해집니다. 타임존을 알고 있다면 비교적 간단히 해결하지만, 타임존 정보가 없다면 사용자 위치를 기준으로 시간대를 추정해 데이터를 변환해야 하기 때문입니다. 또한 데이터가 많을 경우, DST와 같은 기타 고려 사항들이 존재하는데, 이에 대해서도 간단히 살펴보겠습니다.

  1. 타임존 정보가 있는 경우
  2. 타임존 정보가 없는 경우
  3. 데이터가 많을 경우
  4. 기타 주의 사항





4-1. 타임존 정보가 있는 경우

타임존 정보가 있는 경우는 비교적 간단하게 처리할 수 있습니다. 물론 DST와 같은 기타 고려 사항이 존재하지만, 우선은 가장 기본적인 상황만 고려하겠습니다. 예를 들어, 사용자(users), 국가(countries), 게시글(posts) 테이블이 존재한다고 가정할 때, Asia/Seoul로 된 데이터를 UTC로 변경해 보겠습니다.

1
2
3
4
5
6
CREATE TABLE users
(
   user_id    INT PRIMARY KEY,
   name       VARCHAR(50),
   country_id INT
);
1
2
3
4
5
6
CREATE TABLE countries
(
   country_id   INT PRIMARY KEY,
   country_name VARCHAR(50),
   timezone     VARCHAR(50)
);
1
2
3
4
5
6
7
CREATE TABLE posts
(
   post_id    INT PRIMARY KEY,
   user_id    INT,
   created_at DATETIME,
   FOREIGN KEY (user_id) REFERENCES users (user_id)
);
1
2
3
4
5
6
7
8
9
10
11
INSERT INTO users (user_id, name, country_id)
VALUES (1, 'Alice', 101),
       (2, 'Bob', 102);

INSERT INTO countries (country_id, country_name, timezone)
VALUES (101, 'South Korea', 'Asia/Seoul'),
       (102, 'United States', 'America/New_York');

INSERT INTO posts (post_id, user_id, created_at)
VALUES (1001, 1, '2023-09-15 14:00:00'), -- 한국 시간
       (1002, 2, '2023-09-15 10:00:00'); -- 미국 동부 시간






이 경우 타임존 정보를 알고 있기 때문에 조인을 사용하면 비교적 간단히 데이터를 변경할 수 있습니다.

1
2
3
4
UPDATE posts p
JOIN users u ON p.user_id = u.user_id
JOIN countries c ON u.country_id = c.country_id
SET p.created_at = CONVERT_TZ(p.created_at, c.timezone, 'UTC');







4-2. 타임존 정보가 없는 경우

타임존 정보가 없는 경우, 사용자의 위치브라우저 정보 를 기반으로 타임존을 추정해야 하며, 예측에 의존하기 때문에 데이터 정합성에 문제가 생길 수 있습니다. 예를 들어, 자바 스크립트에서 아래와 같은 함수로 사용자 정보를 전송하면, 다른 지역에서 접속 하거나 VPN/프록시 서버 를 통해 접속하는 경우, 실제 위치와 다른 정보가 올 수 있습니다.

1
2
3
function getUserTimeZone() {
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
}






또한 사용자 기기(Device) 가 잘못된 시간대로 설정되어 있을 경우, 반환 시간대 정보가 잘못될 수 있습니다. 예를 들어, 사용자가 한국에 있지만, 사용 중인 노트북은 UTC 시간대로 설정되어 있다면 데이터 불일치 문제가 발생하겠죠?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ timedatectl
               Local time: Wed 2024-09-18 04:29:27 KST
           Universal time: Tue 2024-09-17 19:29:27 UTC
                 RTC time: Tue 2024-09-17 19:29:26
                Time zone: Asia/Seoul (KST, +0900)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no
          
 # UTC로 변경         
$ sudo timedatectl set-timezone UTC
$ timedatectl
               Local time: Tue 2024-09-17 19:29:44 UTC
           Universal time: Tue 2024-09-17 19:29:44 UTC
                 RTC time: Tue 2024-09-17 19:29:44
                Time zone: UTC (UTC, +0000)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no







따라서 타임존을 정확히 알 수 없는 경우, 이전 데이터의 경우 잘못된 시간을 보여줄 수도 있다는 공지를 띄우고 정책 을 통해 해결하거나, 정확한 사용자 타임존을 얻은 후, 마이그레이션을 진행할 수밖에 없습니다.

  1. 정책을 통한 문제 해결
  2. 정확한 정보를 습득 후 마이그레이션 진행







4-3. 데이터가 많은 경우

만약 데이터가 많을 때 한 번에 모든 데이터를 변경하면, 트랜잭션이 길어져 성능 저하, 데이터베이스 락, 롤백 발생, 데이터 불일치 등의 문제가 발생할 수 있습니다. 이를 방지하기 위해서는 커서 페이징 을 이용해 작은 단위로 나누고 인덱스를 통해 처리해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SET @limit = 100; 
SET @idx_cursor = 0;

WHILE @offset IS NOT NULL DO
  UPDATE posts p
  JOIN users u ON p.user_id = u.user_id
  JOIN countries c ON u.country_id = c.country_id
  SET p.created_at = CONVERT_TZ(p.created_at, c.timezone, 'UTC')
  WHERE p.post_id IN (
      SELECT post_id
      FROM posts
      ORDER BY post_id
      LIMIT @limit OFFSET @idx_cursor
  );

  SET @idx_cursor = @idx_cursor + @limit;

  IF (SELECT COUNT(*) FROM posts LIMIT @idx_cursor, @limit) = 0 THEN
    SET @idx_cursor = NULL;
  END IF;
END WHILE;






4-4. 기타 주의 사항

이 외에도 데이터를 마이그레이션 할 때, DST(Daylight Saving Time), 윤초(Leap Seconds), 분산 시스템(Distributed System) 등 다양한 상황이 있기 때문에 이를 고려해야 합니다.

  1. DST(Daylight Saving Time)
  2. 윤초(Leap Seconds)
  3. 분산 시스템(Distributed System)





4-4-1. DST

DST는 일광 절약 시간제라고 하며, 봄/여름 동안 낮 시간을 더 활용하기 위해 시계를 1시간 앞당기는 제도 입니다. 주로 봄에 시작해 가을에 끝나며, 가을이 되면 다시 표준 시간으로 1시간 되돌립니다. 즉, 일정 기간, 2:00 AM이 되면 3:00 AM으로 건너뛰는 것입니다. 이는 봄과 여름철 동안 낮 시간을 더 효율적으로 사용 하기 위해 도입되었습니다. 해가 더 일찍 뜨고 늦게 지는 계절에 맞춰 활동 시간을 앞당겨 에너지를 절약하고, 일조 시간을 최대한 활용할 수 있기 때문입니다.

Daylight saving time (DST), also referred to as daylight saving(s), daylight savings time, daylight time, or summer time, is the practice of advancing clocks to make better use of the longer daylight available during summer so that darkness falls at a later clock time.





DST를 적용하면 2:00 AM에서 3:00 AM으로 건너뛰기 때문에 특정 시간대 이벤트예약된 작업 이 실행되지 않을 수 있으며, 로그 기록에 빈 시간이 발생할 수 있습니다. 또한 DST가 끝나면, 2:00 AM이 됐을 때, 1:00 AM으로 시간을 돌리는데, 이때 동일한 시간이 2번 반복 돼 로그나 트랜잭션에서 동일 타임스탬프가 두 번 기록되는 문제가 발생합니다. 따라서 DST와 같은 특수한 상황을 반드시 고려하도록 합니다.

1
2
3
4
5
-- DST 적용 전: 1:59 AM
SELECT CONVERT_TZ('2024-03-10 01:59:00', 'America/New_York', 'UTC');

-- DST 적용 후: 3:00 AM
SELECT CONVERT_TZ('2024-03-10 03:00:00', 'America/New_York', 'UTC');






4-4-2. 윤초

윤초는 지구의 자전 속도 변화로 인해 표준 시간(UTC)과 지구 자전 시간(UT1) 간의 차이를 조정하도록 1초를 추가하는 제도입니다. 윤초는 주로 6월 30일 또는 12월 31일에 추가되며, 이에 따라 해당 날짜에 23:59:59 다음 23:59:60이 추가됩니다. 이를 적용하지 않으면 시간이 서서히 어긋나기 시작하며, 위성, GPS 등 정확한 시간을 요구하는 시스템에서 문제가 발생할 수 있습니다.

A leap year is a calendar year that contains an additional day compared to a common year. The 366th day is added to keep the calendar year synchronised with the astronomical year or seasonal year.





이를 해결하는 방법은 다양한데, MySQL에서 이를 허용하지 않습니다. 즉, MySQL에는 59:60 초가 존재하지 않으며, 따라서 윤초가 발생할 때, NOW를 사용하면 2초 또는 3초간 연속된 값을 반환할 수 있습니다. 이를 다루기 위해서는 UNIX_TIMESTAMP를 사용할 것을 권장하고 있고요. 다른 시스템에서는 또 다른 방식들로 이 문제를 해결하고 있는데, 궁금하면 직접 찾아보세요. 👋

Leap second values are returned with a time part that ends with :59:59. This means that a function such as NOW() can return the same value for two or three consecutive seconds during the leap second. It remains true that literal temporal values having a time part that ends with :59:60 or :59:61 are considered invalid.






4-4-3. 분산 시스템

분산 시스템에서는 데이터 일관성 유지이벤트 순서 보장, 분산 트랜잭션 및 동기화 추적 등의 이유로 시간대를 정확히 맞추는 것이 중요합니다. 이 부분은 경험해 보지 않았고, 별도의 학습/포스팅이 필요할 것 같아, 연관 링크만 첨부하겠습니다.







5. 정리


타임존을 어떻게 관리할 수 있는지 살펴보았는데, 다양한 타임존 관리 방식이 존재하는 것을 알고 자신의 상황에 맞게 적용하는 것이 중요합니다. 타임존은 한 번 이슈가 발생하면 정말 해결하기 힘들거든요.


This post is licensed under CC BY 4.0 by the author.

서버 리소스 변경으로 인한 롤링 배포 실패 해결기

비동기 환경에서 MDC의 문맥 복사 어떤 원리로 동작할까?