2단계
2단계 — 폴더 구조 철학
20 분
2단계 — 폴더 구조 철학
main.py 하나로 시작하지만, 그 안에서 어떤 기준 으로 갈라야 할지가 핵심이에요. 답은 도메인 단위.
권장 구조
my-api/
├── pyproject.toml ← uv 관리
├── main.py ← FastAPI app + 라우터 등록
├── routers/ ← HTTP 엔드포인트 (도메인별)
│ ├── users.py
│ ├── posts.py
│ └── auth.py
├── services/ ← 비즈니스 로직
│ ├── user_service.py
│ └── post_service.py
├── db/
│ ├── connection.py ← 풀 연결
│ └── repositories/
├── schemas/ ← Pydantic 모델 (입출력)
├── utils/
│ ├── http_client.py
│ └── time.py
├── schedulers/ ← APScheduler 작업
└── tests/
핵심: 도메인 (users, posts) 이 폴더의 첫 분기, 기능 (router/service/repo) 은 그 다음 분기.
main.py — 라우터 등록만
from fastapi import FastAPI
from routers import users, posts, auth
app = FastAPI(title="My API")
app.include_router(users.router, prefix="/api/users", tags=["users"])
app.include_router(posts.router, prefix="/api/posts", tags=["posts"])
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
엔드포인트 코드가 main.py 에 안 들어가게 — 라우터로 분리.
라우터 한 장
# routers/posts.py
from fastapi import APIRouter, Depends
from services.post_service import PostService
from schemas.post import PostCreate, PostOut
router = APIRouter()
@router.get("", response_model=list[PostOut])
def list_posts(limit: int = 20, service: PostService = Depends()):
return service.find_recent(limit)
@router.post("", response_model=PostOut, status_code=201)
def create_post(payload: PostCreate, service: PostService = Depends()):
return service.create(payload)
Depends(PostService) 가 자동으로 인스턴스를 주입해 줍니다 (Spring 의 @Autowired 비슷).
직접 해 보기
routers/, services/, schemas/ 세 폴더를 만들고 1단계의 /items 라우트를 그쪽으로 옮겨 보세요. main.py 가 깔끔해지면 성공.
더 깊이
다음 단계
3단계에서는 데이터를 진짜로 저장할 PostgreSQL 풀 연결을 배워요.