Pylance 말 맞음. datetime.utcnow()는 timezone 없는(naive) UTC라 요즘은 권장 안 함. SQLAlchemy에서도 UTC timezone 붙여서 가는 이유가? SQLAlchemy에서 나중에 개고생하는 포인트가 몇 개 보인다. 특히 네가 말한 lazy loading / N+1 / 중복 시청로그 / 공통 timestamp 상속
두 개 합치면 네가 처음 만든 버전(건너뛰기 감지 max_time, 완료 여부 is_completed) 기능이 더 강력하고, 두 번째 버전(FK + relationship)이 DB 설계는 더 제대로 되어 있음.
last_watched_time은 사실 current_time이랑 역할이 겹쳐서 제거하는 게 맞고, FK/relationship 유지하면서 어뷰징 방지까지 넣는 게 최종형임.
Pylance 말 맞음. datetime.utcnow()는 timezone 없는(naive) UTC라 요즘은 권장 안 함. SQLAlchemy에서도 UTC timezone 붙여서 가는 게 깔끔함.
SQLAlchemy 2.x/Pylance 기준
더 개선가능한거 있나? 특히나 상속이랑 저번에 뭐 레이지 로딩때문에 개고생한거 지금 미리 방지하려는데 여기서 설정 잘하면 될거같은데
있음. 지금 구조도 괜찮은데 SQLAlchemy에서 나중에 개고생하는 포인트가 몇 개 보인다. 특히 네가 말한 lazy loading / N+1 / 중복 시청로그 / 공통 timestamp 상속은 지금 미리 잡는 게 좋음.
바로 적용 가능한 개선 포인트:
1) 공통 created_at, updated_at 상속
지금 모든 테이블에 반복됨. 나중에 수정하다 한 군데 빼먹기 쉽다.
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, DateTime
from datetime import datetime, timezone
Base = declarative_base()
class TimestampMixin:
created_at = Column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc)
)
updated_at = Column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc)
)
사용:
class User(TimestampMixin, Base):
__tablename__ = "users"
이걸 Course, Video, WatchLog, UserCoursePermission 전부 적용.
2) lazy 로딩 미리 설정 (중요)
네가 전에 터진 게 거의 이거일 가능성 큼.
기본:
watch_logs = relationship("WatchLog")
이러면:
users = db.query(User).all()
for user in users:
print(user.watch_logs)
사용자 수만큼 SQL 또 날림 (N+1 지옥)
미리:
watch_logs = relationship(
"WatchLog",
back_populates="user",
lazy="selectin"
)
추천:
lazy="selectin"← 거의 국룰lazy="joined"← 항상 JOINlazy="raise"← 실수하면 즉시 에러
내 추천:
course_permissions = relationship(
"UserCoursePermission",
back_populates="user",
lazy="selectin"
)
watch_logs = relationship(
"WatchLog",
back_populates="user",
lazy="selectin"
)
전부 동일하게.
3) WatchLog 중복 방지 (매우 중요)
지금은 같은 유저가 같은 영상 기록 무한 생성 가능
예:
user=1
video=3
10개 생성 가능
대신:
from sqlalchemy import UniqueConstraint
class WatchLog(...):
__table_args__ = (
UniqueConstraint(
"user_id",
"video_id",
name="uq_user_video"
),
)
그러면:
유저당 영상 하나의 로그만 유지
업데이트만 하면 됨.
이거 안 넣으면 나중에 이어보기 꼬임.
4) 인덱스 추가
조회 패턴 생각하면:
__table_args__ = (
UniqueConstraint(
"user_id",
"video_id",
name="uq_user_video"
),
Index(
"idx_watch",
"user_id",
"video_id"
)
)
시청기록 조회 빨라짐.
5) cascade 정리
지금 일부만 있음.
추천:
watch_logs = relationship(
"WatchLog",
back_populates="user",
cascade="all, delete-orphan",
lazy="selectin"
)
의미:
유저 삭제 → 시청로그 자동삭제
안 하면 고아 데이터 쌓임.
6) max_watched_time 보호
실수 방지:
현재:
max_watched_time=20
last_watched_time=100
가능
말 안 됨
나중에 서비스 레이어:
watch.max_watched_time = max(
watch.max_watched_time,
incoming_time
)
스킵감지 안정됨.
최종 추천 우선순위
필수:
TimestampMixinlazy="selectin"UniqueConstraint(user_id, video_id)
있으면 좋음:
4. cascade
5. Index
이 5개 넣으면 SQLAlchemy에서 겪는 70~80% 삽질 미리 막는다.
댓글
댓글 쓰기