AI 모델 서빙 서버를 반복해서 만들다 보면 매번 같은 설정을 처음부터 하게 된다. Poetry 패키지 관리, Docker 구성, pre-commit 코드 품질, 버전 관리까지 한 번에 잡아둔 FastAPI 보일러플레이트를 만들었다.

전체 구조

src/
  main.py       # FastAPI 앱, 미들웨어, 라우트
  interface.py  # 요청/응답 Pydantic 모델
  util.py       # CamelCase 응답 유틸
Dockerfile.pip      # pip 기반 Docker 이미지
Dockerfile.poetry   # poetry 기반 Docker 이미지
docker-compose.yml
pyproject.toml      # Poetry + taskipy 설정
.pre-commit-config.yaml
.versionrc          # standard-version 버전 규칙

FastAPI 앱

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from src.interface import DemoCamelResponse

app = FastAPI(
    title=os.getenv("API_TITLE"),
    version=os.getenv("API_VERSION"),
)

# 정적 파일 서빙 (모델 결과물 등 임시 파일)
app.mount("/static", StaticFiles(directory="tmp"), name="static")

app.add_middleware(
    CORSMiddleware,
    allow_origins=os.getenv("ALLOW_ORIGINS").split(","),
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/predict", response_model=DemoCamelResponse)
def predict():
    pass  # AI 모델 추론 로직 삽입

타이틀, 버전, CORS 허용 도메인 모두 환경변수로 관리한다.

CamelCase 응답 모델

Python은 snake_case, JSON은 camelCase 관례가 다르다. pydantic-humpscamelize로 자동 변환한다.

# util.py
from humps import camelize
from pydantic import BaseModel

class CamelModel(BaseModel):
    class Config:
        alias_generator = camelize
        allow_population_by_field_name = True
# interface.py
class DemoSnakeResponse(BaseModel):
    id: int
    is_success: bool  # JSON: is_success (그대로)

class DemoCamelResponse(CamelModel):
    id: int
    is_success: bool  # JSON: isSuccess (camel 변환)

CamelModel을 상속하면 응답 JSON이 자동으로 camelCase로 나온다. 프론트엔드와 API 필드명 규칙을 맞출 때 유용하다.

Poetry로 패키지 관리

pip 대신 Poetry를 쓰면 의존성 버전 충돌 관리가 훨씬 편하다. poetry.lock으로 팀 전체가 동일한 패키지 버전을 쓸 수 있다.

# 의존성 설치
poetry install

# 개발 서버 실행 (pyproject.toml의 taskipy 설정 사용)
poetry shell
task devel  # uvicorn src.main:app --port 8080 --reload

pyproject.tomltaskipy로 자주 쓰는 명령을 단축키처럼 등록했다.

[tool.taskipy.tasks]
devel = "uvicorn src.main:app --port 8080 --reload"
test = "pytest tests/"
lint = "flake8 src/"

Docker 두 가지 — pip vs poetry

pip 방식과 poetry 방식 두 가지 Dockerfile을 둬서 환경에 맞게 선택한다.

# Dockerfile.pip — 심플한 pip 방식
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8080"]
# docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.pip  # 또는 Dockerfile.poetry
    ports:
      - "8080:8080"
    env_file:
      - .env
    volumes:
      - ./models:/app/models  # 모델 파일 마운트
      - ./tmp:/app/tmp        # 임시 결과물

모델 파일은 이미지에 포함하지 않고 볼륨으로 마운트한다. 이미지 크기를 줄이고, 모델 교체 시 재빌드 없이 파일만 바꾸면 된다.

pre-commit 코드 품질

커밋 전 자동으로 코드 스타일을 검사한다.

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black
    rev: 23.1.0
    hooks:
      - id: black

  - repo: https://github.com/pycqa/isort
    rev: 5.12.0
    hooks:
      - id: isort

  # flake8은 선택적으로 활성화
  # - repo: https://github.com/pycqa/flake8
  #   rev: 6.0.0
  #   hooks:
  #     - id: flake8
pip install pre-commit
pre-commit install  # git hook 등록

이후 git commit 시 자동으로 black 포매팅과 isort가 실행된다.

semantic versioning — CHANGELOG 자동화

버전 관리는 standard-version(Node.js 패키지)으로 처리한다. 커밋 메시지 컨벤션(feat:, fix:, BREAKING CHANGE:)에 따라 버전을 자동으로 올리고 CHANGELOG를 생성한다.

# 버전 올리기
yarn standard-version --release-as 1.1.0

실행하면:

  1. package.json 버전 업데이트
  2. CHANGELOG.md에 변경 내역 자동 추가
  3. git commit + tag 생성

.versionrc에 커밋 타입별 CHANGELOG 섹션을 정의해둔다.

쓰면서 느낀 점

AI 서버는 모델 파일 관리와 추론 환경 재현이 핵심이다. Docker 볼륨으로 모델을 분리하면 모델 버전 교체가 간편하고, 이미지를 가볍게 유지할 수 있다.

CamelModel은 처음 쓸 때 alias 설정이 헷갈릴 수 있는데, allow_population_by_field_name = True를 같이 설정해야 Python 코드에서 snake_case로 접근할 수 있다.