FastAPI Hot Reload - Sqlmodel Metadata Reload Issue
FastAPI에서 sys.modules
조작이 잘 유지된 상태라고 가정하더라도, SQLModel
의 metadata
초기화 문제가 리로드 시 발생할 가능성이 있습니다.
이는 SQLModel
(SQLAlchemy 기반)이 FastAPI 애플리케이션의 재시작과 함께 metadata
객체를 초기화하기 때문입니다.
문제 분석
문제의 원인: SQLModel의 metadata
초기화
SQLModel
은 기본적으로 sqlmodel.sqlalchemy.MetaData
객체를 사용해 테이블 스키마를 관리합니다. Uvicorn의 --reload
가 작동하면 Python 프로세스가 재시작되고, 이에 따라 다음 문제가 발생할 수 있습니다:
- 테이블 등록 정보 손실: 리로드로 인해
SQLModel.metadata
가 초기화되면서 기존에 등록된 테이블 정보가 사라집니다. - 재등록 문제: 애플리케이션이 다시 시작되면서 동일한 테이블이 재등록되거나, 기존 테이블과 충돌이 발생할 수 있습니다.
- 세션 관리 문제: 기존
Session
또는engine
객체가 유효하지 않게 되어, 데이터베이스 작업이 실패할 가능성이 있습니다.
문제 확인 방법
다음 사항을 점검하여 리로드 문제인지 확인하세요:
metadata
초기화 확인
리로드 시SQLModel.metadata
객체가 초기화되는지 확인합니다.from sqlmodel import SQLModel print(f"Tables in metadata: {SQLModel.metadata.tables.keys()}")
리로드 후 테이블 목록이 빈 값으로 출력된다면
metadata
가 초기화된 것입니다.리로드 시 에러 확인
리로드 후 로그에서 다음과 같은 에러를 확인합니다:sqlalchemy.exc.NoSuchTableError
sqlalchemy.exc.InvalidRequestError: Table already exists
테이블 중복 정의 확인
리로드 이후 테이블이 중복으로 정의되거나, 데이터베이스에 충돌이 발생하는지 확인합니다.
문제 해결 방안
리로드 시 SQLModel.metadata
가 초기화되는 문제를 방지하거나 복구하기 위해 다음 방법을 사용할 수 있습니다.
1. metadata
재등록 로직 추가
리로드 시 SQLModel.metadata
가 초기화되었다면, 이를 다시 설정해주는 로직을 추가합니다. FastAPI 애플리케이션 초기화 시, metadata
를 재등록하도록 설정합니다.
from sqlmodel import SQLModel, create_engine
# 엔진 생성
DATABASE_URL = "sqlite:///example.db"
engine = create_engine(DATABASE_URL)
# 테이블 재등록
def register_metadata():
# 모든 모델에서 테이블을 재등록
SQLModel.metadata.create_all(bind=engine)
print("[INFO] Metadata registered!")
# FastAPI 앱 초기화
from fastapi import FastAPI
app = FastAPI()
@app.on_event("startup")
def on_startup():
register_metadata()
@app.get("/")
def read_root():
return {"message": "Metadata reloaded successfully!"}
2. metadata
충돌 방지
SQLAlchemy가 동일한 테이블을 두 번 정의하지 않도록 방지하려면, 테이블 정의 전에 metadata
객체를 초기화하는 조건을 추가합니다.
from sqlmodel import SQLModel
# 초기화 방지
if not SQLModel.metadata.tables:
SQLModel.metadata.clear()
이 코드로 테이블 중복 정의를 방지할 수 있습니다.
3. sys.modules
활용하여 상태 유지
리로드 시에도 SQLModel.metadata
를 유지하기 위해 sys.modules
를 활용해 테이블 상태를 저장할 수 있습니다.
import sys
from sqlmodel import SQLModel
# 기존 metadata 유지
if "app_metadata" in sys.modules:
SQLModel.metadata = sys.modules["app_metadata"]
else:
sys.modules["app_metadata"] = SQLModel.metadata
정리
리로드로 인해 SQLModel.metadata
가 초기화되는 문제는 다음과 같이 해결할 수 있습니다:
metadata
재등록: FastAPI의startup
이벤트에서SQLModel.metadata.create_all()
을 호출.- 테이블 중복 방지: 리로드 전에
metadata
를 초기화하거나,sys.modules
를 활용하여 상태를 유지.
이 접근 방식으로 리로드 시 발생하는 SQLModel
의 문제를 안정적으로 해결할 수 있습니다.
'IT Best Practise > FastAPI' 카테고리의 다른 글
FastAPI를 이용한 멀티테넌트 동적 모듈 로딩 시스템 구현 (1) | 2024.11.19 |
---|