[SQLAlchemy][Database] Timezone에 대한 이모저모 - UTC에 대한 이해

쩡뉴·2024년 2월 3일
0

백엔드 개발

목록 보기
2/8

들어가는 글 🙋‍♀️

백엔드 개발을 할 때, 서비스에 관련한 테이블 설계를 하다 보면 화면에 표출할 목적 또는 로깅의 목적으로 데이터의 입력 시간, 수정 시간, 삭제 시간 등을 저장하곤 한다. 이 때, 이러한 시간 데이터는 DateTime 타입의 컬럼에 저장한다.
DateTime 컬럼은 Timezone이라는 개념을 포함하고 있다. 필자는 관성적으로 DateTime의 timezone 값을 True로 설정해왔지만, 실상은 개념적으로 정확히 알지 못했다. 따라서 이번 포스팅에서는 Timezone에 대한 이해를 명확히 하고 실제 서비스에서 어떻게 쓰여야 하는지 정리해보고자 한다.
그럼 가볍게 예시 코드를 확인하고 Timezone에 대한 내용을 확인하러 출발☝️

timezone=True의 의미를 찾아라!

시간 관련 컬럼이 포함된 테이블을 ORM 모델로 정의 시, 특히 SQLAlchemy를 사용한다면, 다음과 같이 코드를 작성할 수 있다.

from sqlalchemy import create_engine, Column, Integer, String, DateTime, MetaData
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func

engine = create_engine('sqlite:///example.db', echo=True)

Base = declarative_base()

class MyTable(Base):
    __tablename__ = 'my_table'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    created_at = Column(DateTime(timezone=True), server_default=func.now())

위의 예시 코드를 보면 created_at라는 컬럼을 정의할 때 DateTime로 정의하였다. 이 때 timezone=True로 설정을 할 수가 있다.(default는 False이다.)

Timezone?🤔

개념 정리부터 하자!

그럼 여기서 timezone(타임존)은 무엇을 의미인가? 위키백과를 보면 다음과 같이 정의되어 있다.

시간대(時間帶,time zone)는 영국의 그리니치 천문대(본초 자오선, 경도 0도)를 기준으로 지역에 따른 시간의 차이,
다시 말해 지구의 자전에 따른 지역 사이에 생기는 낮과 밤의 차이를 인위적으로 조정하기 위해 고안된 시간의 구분선을
일컫는다. 그리니치 천문대를 기준으로 세계의 0시가 결정된다.

모두가 다 아는 사실이지만, 지구는 둥글고, 그 둥근 지구의 특성에 의해 각 나라 내지 지역별로 각기 다른 시간대를 갖게 된다. 이는 같은 시점이라도 나라마다 다르게 시간을 표기하게 된다는 것이다. (이로 인해 시차라는 것이 생긴다.)
이 때 이 시간대를 표기하는 국제 표준이 있는데, 가장 널리 알려져 있는 것은 UTC다. 위키백과 설명에 나온 것처럼 영국에 있는 그리니치 천문대를 기준으로 각 지역을 1시간씩 시차를 두는 기준 시간대를 의미한다.

한국의 시간을 예를 들어보겠다. 동일한 시점에 각 지역의 시간을 표현한다면 다음과 같은 표로 정리할 수 있다.

한국 시간그리니치 천문대
2024-02-01 00:00:002024-01-31 15:00:00
2024-02-01 09:00:002024-02-01 00:00:00

항상 한국은 그리니치 천문대의 시간보다 9시간 빠르다. 따라서 한국의 시간을 국제 표준으로 표현한다면 UTC+9로 표기한다. (정확한 설명은 한국 표준시(KST) 문서 참고)

DB에서는 어떻게 저장이 되는가?

사용한 데이터베이스: PostgreSQL 16.1

들어가는 글의 예시 코드에 해당하는 테이블을 만들어, 임의의 데이터를 넣었다.

insert into my_table ("name") values ('test')

신기하게도 +0900라는 문구가 같이 들어갔다. 이유인 즉슨, 현재 디비의 timezone 설정이 'Asia/Seoul'로, 데이터베이스가 한국 시간대를 따른다는 설정이 되어 있기 때문에 실제 데이터가 한국 시간(UTC+9에 맞춰)으로 들어간 것이다.

-- 현재 database에서 설정된 timezone 확인 방법
show timezone;

다음의 SQLAlchemy 코드로 해당 데이터를 select하면, 실제 UTC 시간으로 가져와진다.

from sqlalchemy import create_engine, inspect, Table, text
from sqlalchemy.orm import sessionmaker

engine = create_engine('postgresql+psycopg2://postgres:1234@localhost:5432/postgres', echo=True)

Session = sessionmaker(bind=engine)
session = Session()

res = session.execute(text("select * from my_table"))

print(res.fetchone().created_at)
> 2024-01-31 15:00:00+00:00  // 데이터는 한국 시간으로 저장되었지만, 실제 UTC 시간으로 print 되었다.

UTC는 왜 쓰이는가?

위에서 말한 것처럼 UTC는 '표준' 그 자체이기 때문이다. 시간대가 서로 다른 지역에서 서비스를 운영한다면, 어떤 데이터가 실제로 화면 단에 보여질 때, 각 지역의 시간대에 맞춰 표현이 되어야 한다. 그 말인 즉슨, 어느 지역에서 어느 시점에 데이터가 저장되었는지를 파악할 수 있어야 한다.
다음의 예시를 통해 왜 표준이 중요한지를 확인할 수 있다.

1. 한국에 사는 유진이(UTC+9 시간대 사용)와 뉴욕에 사는 Smith(UTC-5 시간대 사용)는 펜팔을 하고 있다.
2. 유진이는 한국 시간 2024년 2월 1일 14시에 Smith에게 메일을 보냈다.
  - 이는 UTC 시간 2024년 2월 1일 5시에 해당하는 시점이다. (한국 시각 - 9시간)
  - 뉴욕에서는 이 시점의 시간이 2024년 2월 1일 0시다. (UTC 시간 - 5시간)
3. 따라서 Smith는 뉴욕 시간 2월 1일 0시에 해당 메일을 받았다.
  • 위 내용에서 메일을 보낸 시점에서, 각 지역의 시간
UTC한국 시간뉴욕 시간
2024-02-01 05:00:00+00002024-02-01 14:00:00+09002024-02-01 00:00:00-0500
  • 한국에서 메일 보낸 시간이 DB의 DateTime 데이터로 저장되었다고 했을 때, 2024-02-01 14:00:00 +0900로 저장이 되었을 것이다. 그럼에도 '표준'에 의해 해당 DateTime 데이터가 뉴욕에서는 2024-02-01 00:00:00 -0500로 확인할 수 있는 것이다.
  • 웹에서 이를 구현할 땐,
    • 백엔드에서는 DB에서 가져온 데이터를 UTC 시간대로 변경하여 response를 보내고
    • 프런트엔드에서는 접속한 지역의 시간대로 변환하도록 처리하는 것이 권장되고 있다.

마치며⏰

국내만 타겟으로 하거나, 어느 나라에서든, 폐쇄망에서 운영되는 서비스의 데이터베이스 테이블을 설계를 한다면 timezone을 크게 고려할 이유가 없다. 그러나 서비스가 추후 글로벌화 될 가능성이 있고, 여러 지역(또는 여러 나라)의 고객을 유치할 계획이 있다면 timezone 설정을 하여 DateTime 관리를 할 필요가 있다고 여긴다.

References

profile
파이썬으로 백엔드를 하고 있습니다. Keep debugging life! 📌 archived: https://blog.naver.com/lizziechung

0개의 댓글