반응형
MVP에서는 DB를 빨리 붙여야 한다. 근데 또 나중에 바꿀 수도 있다.
그래서 내가 택한 조합은 이거.
- SQLModel: Pydantic + SQLAlchemy 감성 합쳐진 ORM
- SQLite: 설치 없이 파일로 끝, MVP 속도 최강
1) SQLite 파일 경로를 “정책”으로 만든다
내 설정은 기본으로 홈 디렉토리 아래에 DB를 만든다.
~/.doubles/server/doubles.db
이걸 코드로 만들면 대충 이런 느낌.
def get_default_database_path() -> Path:
return Path.home().joinpath(".doubles", "server", "doubles.db")
def build_sqlite_url(path: Path) -> str:
return f"sqlite:///{path.resolve()}"
장점이 뭐냐면:
- 로컬에서 어디서 실행하든 DB 위치가 예측 가능
- 도커에서는
DOUBLES_DATABASE_URL로 완전히 갈아끼울 수 있음
2) SQLite는 디렉토리가 없으면 터진다 → 미리 만들어주기
이거 은근히 자주 맞는 함정이다.
그래서 엔진 만들기 전에 DB 디렉토리를 만들어준다.
def _prepare_sqlite_directory(database_url: str) -> None:
if not database_url.startswith("sqlite:///"):
return
database_path = Path(database_url.removeprefix("sqlite:///"))
database_path.parent.mkdir(parents=True, exist_ok=True)
3) 엔진/Settings 캐시를 같이 쓰면 깔끔하다
Settings는 캐시(lru_cache), 엔진도 캐시(lru_cache)로 맞춰두면 “프로세스 생명주기” 관점에서 자연스럽다.
@lru_cache
def _get_engine_for_url(database_url: str) -> Engine:
_prepare_sqlite_directory(database_url)
return create_engine(database_url, connect_args={"check_same_thread": False})
SQLite에서 check_same_thread=False는 FastAPI 같은 웹앱에서 자주 필요한 세팅이라 같이 기억해두면 좋다.
4) 모델 설계: JSON 컬럼이 MVP에서는 꽤 쓸모 있다
Diary 모델에는 이런 데이터가 들어간다.
- 동선(place_visits): 리스트/딕셔너리
- 사진 EXIF(photos_exif): 리스트/딕셔너리
- AI 결과(photo_descriptions): 리스트/딕셔너리
MVP에서는 RDB 정규화보다 “일단 쌓고 기능 만들기”가 급할 때가 많다.
그래서 JSON 컬럼이 꽤 편했다.
place_visits: list[dict[str, Any]] = Field(
default_factory=list,
sa_column=Column(JSON, nullable=False, default=[]),
)
단, 이건 나중에 확장하면 구조 바꿀 수도 있다. (MVP니까… ㅎㅎ)
실습: DB가 자동으로 만들어지는지 확인
서버를 띄우고 /api/v1/diaries를 한 번 호출해보자.
curl -sS "http://127.0.0.1:8000/api/v1/diaries"
여기서 중요한 건 “응답이 뭐냐”보다, 앱 시작 시점에 테이블이 생성되어 오류 없이 돌아가느냐다.
마무리
SQLModel + SQLite 조합은 “빠르게 시작하고, 나중에 갈아끼우기”에 좋다.
다음 편에서는 multipart/form-data로 Form + File을 같이 받으면서 검증을 어떻게 했는지 본다. (여기부터 진짜 실전 느낌)
참고
반응형