개발현황

크레플의 R&D를 소개합니다.

R&D

개발현황

Python + FastAPI로 설계한 시간대 처리법

  • 관리자
  • 2025.02.03

기술문서 #1 - Python + FastAPI에서 올바른 시간대 핸들링 방법

기술문서 #1 - Python + FastAPI에서 올바른 시간대 핸들링 방법

안녕하세요. 크레플에서 AI 기반 점검시스템인 EQ-0의 개발을 담당하고 있는 김대현입니다. 오늘은 첫 내용으로 EQ-0를 전세계 어디서든 사용할수 있도록 하기 위해 지역별 시간대 기능을 만들면서 배운 내용을 공유해보겠습니다.

1. 시간대

Timezone이라고도 불리는 시간대는 매우 복잡합니다. 지금은 UTC라고 부르지만 과거에는 GMT라고도 불렀죠.

GMT는 Greenwich Mean Time이라고 해서 영국 그리니치 천문대의 시간을 기준으로 하는 시간대입니다.

하지만 GMT는 지구 자전의 영향을 받아 매년 조금씩 시간이 변하게 되는데, 이를 보정하기 위해 원자시계를 도입하면서 GMT를 좀더 국제적인 기준으로 사용가능하도록 보완한것이 UTC입니다.

그래서 UTC는 한글로 풀어서 말하면 세계 협정시라고 하지요. UTC는 GMT에 기반하고 있기에, 지구 자전에 의해 달라지는 시간대를 경도 기준으로 나눈다는 기본적인 컨셉은 동일합니다.

하지만, 위 지도에서 보는것처럼 시간대의 구분은 경도에 기준하여 일직선으로 나눠지지 않습니다.

한 국가가 여러 시간대에 걸쳐 존재하거나, 국가의 정책에 의거하여 각 지역은 지리적 위치만으로 시간대를 선택하지 않게 됩니다.

이러한 배경지식에 대해 python 공식문서(https://docs.python.org/ko/3.13/library/datetime.html)에서는 아래와 같이 언급하고 있습니다.


Date and time objects may be categorized as “aware” or “naive” depending on whether or not they include time zone information.

시간대와 일광 절약 시간 정보와 같은 적용 가능한 알고리즘과 정치적 시간 조정에 대한 충분한 지식을 통해, 
어웨어 객체는 다른 어웨어 객체와의 상대적인 위치를 파악할 수 있습니다. 어웨어 객체는 자의적으로 해석할 여지 없는 특정 시간을 나타냅니다. [1]

이 문서를 참조하다보면 “어웨어(aware) 객체”라는 단어가 나옵니다. 대체 어웨어란 무엇을 의미하는 것일까요.

2. 파이썬의 Aware와 Naive 객체.

파이썬 공식문서를 좀더 참조해 보겠습니다. 어웨어 객체는 자의적으로 해석할 여지 없는 특정 시간을 나타낸다고 합니다.


어웨어와 나이브 객체

Date and time objects may be categorized as “aware” or “naive” depending on whether or not they include time zone information.

시간대와 일광 절약 시간 정보와 같은 적용 가능한 알고리즘과 정치적 시간 조정에 대한 충분한 지식을 통해, 어웨어 객체는 다른 어웨어 객체와의 상대적인 위치를 파악할 수 있습니다. 
어웨어 객체는 자의적으로 해석할 여지 없는 특정 시간을 나타냅니다. [1]

A naive object does not contain enough information to unambiguously locate itself relative to other date/time objects. 
Whether a naive object represents Coordinated Universal Time (UTC), local time, or time in some other time zone is purely up to the program,
just like it is up to the program whether a particular number represents metres, miles, or mass.
Naive objects are easy to understand and to work with, at the cost of ignoring some aspects of reality.

어웨어 객체가 필요한 응용 프로그램을 위해, datetime 과 time 객체에는 추상 tzinfo 클래스의 서브 클래스 인스턴스로 설정할 수 있는 선택적 시간대 정보 어트리뷰트인 tzinfo가 있습니다. 
이러한 tzinfo 객체는 UTC 시간으로부터의 오프셋, 시간대 이름 및 일광 절약 시간이 적용되는지에 대한 정보를 보관합니다.

Only one concrete tzinfo class, the timezone class, is supplied by the datetime module. 
The timezone class can represent simple time zones with fixed offsets from UTC, such as UTC itself or North American EST and EDT time zones.
 Supporting time zones at deeper levels of detail is up to the application. 
The rules for time adjustment across the world are more political than rational, change frequently, and there is no standard suitable for every application aside from UTC.

좀더 정확히 말하자면, 어웨어 객체는 자의적으로 해석할 여지가 없는것이 아니라 해석할 방법이 없습니다. 지리정보가 없기 때문이죠.

어웨어 객체가 말하는 시간은 글자그대로 시간일뿐, 이것이 어느국가, 어느 시간대의 시간인지 알수가 없습니다.

이런 시간대 없는 정보를 그대로 쓰는것은 큰 문제가 될수 있습니다.

예를 하나 들어보죠.

일일점검 결과리포트를 EQ-0가 매일 밤 12시 30분에 생성한다 해보겠습니다. 생성 시점상 결과리포트를 만드는 데이터는 전날 0시부터 23시 59분 59초 사이에 누적된 데이터를 이용해야 할 것입니다.

문제는, 서버의 시간이 밤 12시 30분이라 하더라도 이는 해당 서버의 시간대이지, 각 지역의 시간이 아니라는 것이죠.

지역에 따라 새벽 1시 30분일수도 있고, 아직 전날 23시 30분일수 있습니다. 한국만 하더라도 한국 시간 0시는 UTC로는 전날 15시입니다.

일반적으로 서버는 UTC로 작동하니, 한국에 있는 고객들이 만든 데이터를 이용해 점검 리포트를 생성한다고 하면 UTC로는 전전날 15시부터 전날 14시 59분 59초사이의 데이터를 취합해야 하겠죠.

3. 시간대 정보의 관리

위에서 언급한것처럼 시간대 정보는 경도기준으로 나눠지지 않으니, 지리적 정보에 근거해 단순히 몇시간을 더하거나 빼는 옵셋만 가지고는 특정 지역의 시간을 정확히 알아낼수 없습니다.

다행히도, 이런 정보를 제공해주는 라이브러리가 있습니다.


https://pypi.org/project/pytz/

이 패키지를 이용하면 아래와 같은 코드로 시간대 정보를 변환할수 있습니다.

위 코드는 아래와 같은 결과를 출력합니다.

주의해야 할 점은, 지역 시간대 변환은 어웨어 객체로는 불가능하다는 것입니다.

생각해보면 시간대 변환은 한 지역의 시간을 다른 지역의 시간으로 변환하는것이니 시간대 정보가 없는 어웨어 객체의 경우에는 기준점이 없는것과 마찬가지겠죠.

다만, pytz의 경우 어웨어 객체를 받게되면 해당 객체의 시간대 정보만 채워줍니다. 시간값은 변환하지 않죠. 마치 datetime객체에 replace()연산을 한것과 동일합니다.

4. 그렇다면 서버에서는 시간대 정보를 어떻게 핸들링 해야 하는가?

위에서 살짝 언급한대로, 서버는 UTC 기준으로 작동합니다.

이에 맞춰 서버는 RestAPI를 제공할때 몇가지 사항을 고려해야 합니다.

  • 프론트엔드에서 보낸 시간 정보는 먼저 UTC로 변환한 후 처리합니다.
  • 프론트엔드의 요청에 대한 응답도 UTC로 합니다.
  • 일부 예외->결과를 날짜 문자열로 묶어서 줘야하는 경우 해당 국가의 날짜 값으로 묶어야 하므로 이때는 별도의 시간대 변환을 해줍니다.
  • 프론트엔드는 local time으로 돌아가도 되지만 서버와의 통신은 UTC가 기준입니다.
  • UTC로 못준다 하더라도 ISO 8601 포맷은 반드시 맞춰야 합니다
  • 백엔드에서 시간값을 ISO-8601로 파싱하지 못할때는 bad request를 응답합니다.

여기까지 간략하게 시간대에 대한 개념과 서버에서 이를 다룰때 어떻게 다루면 좋을지에 대한 고민과 나름의 결론을 공유해 보았습니다.

혹시 비슷한 고민을 다뤄보신 분이 있다면 이 글에 답글을 남겨주셔도 좋고, 더 나아가 크레플과 함께 할 기회가 있으면 좋겠네요.

마치겠습니다.

다음에 또 뵙죠.