o2o-castad-backend/docs/plan/timezone_plan.md

6.2 KiB

타임존 설정 작업 계획

개요

프로젝트 전체에 서울 타임존(Asia/Seoul, UTC+9)을 일관되게 적용합니다.


현재 상태

완료된 설정

구성 요소 상태 비고
MySQL 데이터베이스 완료 DB 생성 시 Asia/Seoul로 설정됨
config.py 완료 TIMEZONE = ZoneInfo("Asia/Seoul") 설정됨
SQLAlchemy 모델 정상 server_default=func.now() → DB 타임존(서울) 따름

문제점

  • Python 코드에서 datetime.now() (naive) 또는 datetime.now(timezone.utc) (UTC) 혼용
  • 타임존이 적용된 현재 시간을 얻는 공통 유틸리티 없음

수정 대상 파일

파일 라인 현재 코드 문제
app/user/services/jwt.py 26, 51, 109 datetime.now() naive datetime
app/user/services/auth.py 170, 225, 485, 510 datetime.now() naive datetime
app/user/api/routers/v1/auth.py 437 datetime.now(timezone.utc) UTC 사용 (서울 아님)
app/social/services.py 408, 448, 509, 562, 578 datetime.now() naive datetime
app/social/worker/upload_task.py 73 datetime.now() naive datetime
app/utils/logger.py 89, 119 datetime.today() naive datetime

작업 단계

1단계: 타임존 유틸리티 생성 (신규 파일)

파일: app/utils/timezone.py

"""
타임존 유틸리티

프로젝트 전역에서 일관된 서울 타임존(Asia/Seoul) 시간을 사용하기 위한 유틸리티입니다.
모든 datetime.now() 호출은 이 모듈의 함수로 대체해야 합니다.
"""
from datetime import datetime

from config import TIMEZONE


def now() -> datetime:
    """
    서울 타임존(Asia/Seoul) 기준 현재 시간을 반환합니다.

    Returns:
        datetime: 서울 타임존이 적용된 현재 시간 (aware datetime)

    Example:
        >>> from app.utils.timezone import now
        >>> current_time = now()  # 2024-01-15 15:30:00+09:00
    """
    return datetime.now(TIMEZONE)


def today_str(fmt: str = "%Y-%m-%d") -> str:
    """
    서울 타임존 기준 오늘 날짜를 문자열로 반환합니다.

    Args:
        fmt: 날짜 포맷 (기본값: YYYY-MM-DD)

    Returns:
        str: 포맷된 날짜 문자열

    Example:
        >>> from app.utils.timezone import today_str
        >>> today_str()  # "2024-01-15"
        >>> today_str("%Y/%m/%d")  # "2024/01/15"
    """
    return datetime.now(TIMEZONE).strftime(fmt)

2단계: 기존 코드 수정

모든 datetime.now(), datetime.today(), datetime.now(timezone.utc)를 타임존 유틸리티 함수로 교체합니다.

2.1 app/user/services/jwt.py

# Before
from datetime import datetime, timedelta

expire = datetime.now() + timedelta(...)

# After
from datetime import timedelta
from app.utils.timezone import now

expire = now() + timedelta(...)

2.2 app/user/services/auth.py

# Before
from datetime import datetime

user.last_login_at = datetime.now()
if db_token.expires_at < datetime.now():
revoked_at=datetime.now()

# After
from app.utils.timezone import now

user.last_login_at = now()
if db_token.expires_at < now():
revoked_at=now()

2.3 app/user/api/routers/v1/auth.py

# Before
from datetime import datetime, timezone

user.last_login_at = datetime.now(timezone.utc)

# After
from app.utils.timezone import now

user.last_login_at = now()

2.4 app/social/services.py

# Before
from datetime import datetime, timedelta

buffer_time = datetime.now() + timedelta(minutes=10)
account.token_expires_at = datetime.now() + timedelta(...)
account.connected_at = datetime.now()

# After
from datetime import timedelta
from app.utils.timezone import now

buffer_time = now() + timedelta(minutes=10)
account.token_expires_at = now() + timedelta(...)
account.connected_at = now()

2.5 app/social/worker/upload_task.py

# Before
from datetime import datetime

upload.uploaded_at = datetime.now()

# After
from app.utils.timezone import now

upload.uploaded_at = now()

2.6 app/utils/logger.py

# Before
from datetime import datetime

today = datetime.today().strftime("%Y-%m-%d")

# After
from app.utils.timezone import today_str

today = today_str()

참고: logger.py에서는 날짜만 사용하는 것이 맞습니다. 로그 파일명이 {날짜}_app.log, {날짜}_error.log 형식이므로 일별 로그 파일 관리에 적합합니다. 시간까지 포함하면 매 시간/분마다 새 파일이 생성되어 로그 관리가 어려워집니다.


작업 체크리스트

순서 작업 파일 상태
1 타임존 유틸리티 생성 app/utils/timezone.py 완료
2 JWT 서비스 수정 app/user/services/jwt.py 완료
3 Auth 서비스 수정 app/user/services/auth.py 완료
4 Auth 라우터 수정 app/user/api/routers/v1/auth.py 완료
5 Social 서비스 수정 app/social/services.py 완료
6 Upload Task 수정 app/social/worker/upload_task.py 완료
7 Logger 유틸리티 수정 app/utils/logger.py 완료

예상 작업 범위

  • 신규 파일: 1개 (app/utils/timezone.py)
  • 수정 파일: 6개
  • 수정 위치: 약 15곳

참고 사항

naive datetime vs aware datetime

  • naive datetime: 타임존 정보가 없는 datetime (예: datetime.now())
  • aware datetime: 타임존 정보가 있는 datetime (예: datetime.now(TIMEZONE))

왜 서울 타임존을 사용하는가?

  1. 데이터베이스가 Asia/Seoul로 설정됨
  2. 서비스 대상 지역이 한국
  3. Python 코드와 DB 간 시간 일관성 확보

주의사항

  • 기존 DB에 저장된 시간 데이터는 이미 서울 시간이므로 마이그레이션 불필요
  • JWT 토큰 만료 시간 비교 시 타임존 일관성 필수
  • 로그 파일명에 사용되는 날짜도 서울 기준으로 통일