출시·고도화 중
FastAPI 안내서 · 2/6
FastAPI는 폴더 구조를 강제하지 않습니다. 파일 하나로 시작해도 되지만, 엔드포인트가 늘어나면 기능별로 파일을 나누는 것이 좋습니다. 이 장에서는 공식 문서의 "Bigger Applications" 예제를 바탕으로 흔히 쓰는 구조와 라우터, 설정 파일, 관례를 살펴봅니다.
hello-fastapi/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI() 생성, 라우터 등록
│ ├── dependencies.py # 여러 곳에서 쓰는 의존성
│ ├── config.py # 설정(pydantic-settings)
│ ├── models.py # Pydantic · SQLModel 모델
│ └── routers/
│ ├── __init__.py
│ ├── items.py
│ └── users.py
├── tests/
│ └── test_items.py
├── .env # 로컬 환경 변수(커밋하지 않음)
├── pyproject.toml # 또는 requirements.txt
└── Dockerfileapp 폴더는 하나의 Python 패키지이며, 모든 폴더에 __init__.py를 두어 from app.routers import items 같은 가져오기가 동작하게 합니다. 이 경우 개발 서버는 fastapi dev app/main.py로 실행합니다. fastapi 명령은 파일 경로를 보고 패키지 구조를 파악해 app 객체를 찾아 줍니다.
APIRouter는 작은 FastAPI 앱처럼 동작합니다. 기능마다 라우터를 만들고, 공통 접두 경로와 문서용 태그를 한 번에 지정할 수 있습니다.
# app/routers/items.py
from fastapi import APIRouter, HTTPException
router = APIRouter(prefix="/items", tags=["items"])
fake_items = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}
@router.get("/")
def list_items():
return fake_items
@router.get("/{item_id}")
def read_item(item_id: str):
if item_id not in fake_items:
raise HTTPException(status_code=404, detail="Item not found")
return fake_items[item_id]main.py에서는 앱을 만들고 라우터를 등록하기만 합니다. 이렇게 하면 진입점이 짧게 유지되고, 각 기능의 코드는 자기 파일 안에 모입니다.
# app/main.py
from fastapi import FastAPI
from app.routers import items, users
app = FastAPI(title="Hello FastAPI", version="0.1.0")
app.include_router(items.router)
app.include_router(users.router)
@app.get("/health")
def health():
return {"status": "ok"}include_router에도 prefix, tags, dependencies를 넘길 수 있어서, 같은 라우터를 /api/v1 아래에 붙이거나 관리자 라우터 전체에 인증 의존성을 거는 식으로 활용할 수 있습니다.
여러 라우터가 함께 쓰는 의존성은 dependencies.py에 모읍니다. 아래는 요청 헤더의 토큰을 확인하는 간단한 예입니다.
# app/dependencies.py
from typing import Annotated
from fastapi import Header, HTTPException
async def verify_token(x_token: Annotated[str, Header()]):
if x_token != "secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")라우터를 만들 때 APIRouter(dependencies=[Depends(verify_token)])처럼 넘기면 그 라우터의 모든 경로에 적용됩니다. 매개변수 이름 x_token은 자동으로 x-token 헤더로 바뀝니다.
의존성은 보통 pyproject.toml에 적습니다. uv, Poetry, Hatch 같은 도구가 이 파일을 읽어 가상 환경을 관리합니다. pip만 쓴다면 requirements.txt도 충분합니다.
[project]
name = "hello-fastapi"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
"fastapi[standard]",
"sqlmodel",
"pydantic-settings",
]
[dependency-groups]
dev = ["pytest", "ruff"]데이터베이스 주소나 비밀 키 같은 값은 코드에 쓰지 않고 환경 변수나 .env 파일로 넘깁니다. .env는 .gitignore에 넣고, 필요한 변수 이름만 적은 .env.example을 저장소에 둡니다. 설정을 읽는 방법은 4장에서 다룹니다.
데이터베이스 연결 풀이나 머신러닝 모델처럼 앱이 시작할 때 한 번 준비하고 끝날 때 정리해야 하는 자원은 lifespan으로 다룹니다.
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
print("starting up") # 연결 준비, 모델 불러오기
yield
print("shutting down") # 자원 정리
app = FastAPI(lifespan=lifespan)yield 앞의 코드는 첫 요청을 받기 전에, 뒤의 코드는 서버가 멈출 때 실행됩니다.
| 항목 | 관례 |
|---|---|
| 진입점 | app/main.py의 app 변수 |
| 라우터 | 기능별 파일, router = APIRouter(prefix=..., tags=[...]) |
| 모델 | 입력용 · 출력용 모델을 나눔(ItemCreate, ItemPublic) |
| 테스트 | tests/test_*.py, pytest로 실행 |
| 비밀 값 | 환경 변수 또는 .env, 저장소에 넣지 않음 |
main.py 하나로 시작하고, 커지면 app 패키지와 routers 폴더로 나눕니다.APIRouter로 기능별 경로를 묶고 app.include_router로 등록합니다.dependencies.py, 설정은 환경 변수와 .env로 관리합니다.lifespan으로 처리합니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.