라우터 구현과 에러 처리 - Part 2

2025. 1. 31. 12:13·파이썬/Fast API
반응형

라우터 구조화

대규모 애플리케이션에서는 라우터를 체계적으로 구성하는 것이 중요합니다. 버전별, 기능별로 라우터를 분리하여 관리합니다.

# app/api/v1/router.py
from fastapi import APIRouter
from app.api.v1.endpoints import user, item

api_router = APIRouter()

api_router.include_router(
    user.router,
    prefix="/users",
    tags=["users"]
)

api_router.include_router(
    item.router,
    prefix="/items",
    tags=["items"]
)

# app/api/v1/endpoints/user.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from app.api import deps
from app.services import user as user_service
from app.schemas.user import UserCreate, UserInDB

router = APIRouter()

@router.post("/", response_model=UserInDB)
async def create_user(
    *,
    db: AsyncSession = Depends(deps.get_db),
    user_in: UserCreate,
) -> UserInDB:
    user = await user_service.get_user_by_email(db, user_in.email)
    if user:
        raise HTTPException(
            status_code=400,
            detail="Email already registered"
        )
    return await user_service.create_user(db, user_in)

예외 처리와 커스텀 예외

일관된 에러 응답을 위한 예외 처리 시스템을 구현합니다.

# app/core/exceptions.py
from fastapi import HTTPException, status
from typing import Any

class AppException(HTTPException):
    def __init__(
        self,
        status_code: int,
        detail: Any = None,
        headers: dict = None
    ) -> None:
        super().__init__(status_code=status_code, detail=detail, headers=headers)

class NotFoundException(AppException):
    def __init__(self, detail: str = "Resource not found") -> None:
        super().__init__(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=detail
        )

class BadRequestException(AppException):
    def __init__(self, detail: str = "Bad request") -> None:
        super().__init__(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=detail
        )

# app/api/v1/endpoints/user.py
@router.get("/{user_id}", response_model=UserInDB)
async def get_user(
    user_id: int,
    db: AsyncSession = Depends(deps.get_db)
) -> UserInDB:
    user = await user_service.get_user(db, user_id)
    if not user:
        raise NotFoundException(f"User {user_id} not found")
    return user

미들웨어 구현

애플리케이션 전반에 걸쳐 적용되는 미들웨어를 구현합니다.

# app/core/middleware.py
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
import time
from app.core.logger import logger

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        start_time = time.time()
        response = await call_next(request)
        process_time = time.time() - start_time

        logger.info(
            f"Path: {request.url.path} "
            f"Method: {request.method} "
            f"Processing Time: {process_time:.3f}s"
        )

        return response

# app/main.py
from fastapi import FastAPI
from app.core.middleware import LoggingMiddleware
from app.api.v1.router import api_router
from app.core.config import settings

app = FastAPI(title=settings.PROJECT_NAME)
app.add_middleware(LoggingMiddleware)
app.include_router(api_router, prefix=settings.API_V1_STR)

페이지네이션 구현

재사용 가능한 페이지네이션 컴포넌트를 구현합니다.

# app/core/pagination.py
from typing import Generic, TypeVar, Sequence
from pydantic import BaseModel
from fastapi import Query

T = TypeVar("T")

class PageParams:
    def __init__(
        self,
        page: int = Query(1, ge=1, description="Page number"),
        size: int = Query(10, ge=1, le=100, description="Items per page")
    ):
        self.page = page
        self.size = size
        self.offset = (page - 1) * size

class Page(BaseModel, Generic[T]):
    items: Sequence[T]
    total: int
    page: int
    size: int
    pages: int

    @classmethod
    def create(cls, items: Sequence[T], total: int, params: PageParams) -> "Page[T]":
        pages = -(-total // params.size)  # 올림 나눗셈
        return cls(
            items=items,
            total=total,
            page=params.page,
            size=params.size,
            pages=pages
        )

# 사용 예시
@router.get("/", response_model=Page[UserInDB])
async def get_users(
    pagination: PageParams = Depends(),
    db: AsyncSession = Depends(deps.get_db)
) -> Page[UserInDB]:
    users, total = await user_service.get_users_paginated(
        db,
        skip=pagination.offset,
        limit=pagination.size
    )
    return Page.create(users, total, pagination)

캐싱 구현

성능 최적화를 위한 캐싱 시스템을 구현합니다.

# app/core/cache.py
from functools import wraps
from redis import asyncio as aioredis
import json
from app.core.config import settings

redis = aioredis.from_url(settings.REDIS_URL)

def cache(expire: int = 60):
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            # 캐시 키 생성
            key = f"{func.__name__}:{str(args)}:{str(kwargs)}"

            # 캐시된 데이터 확인
            cached = await redis.get(key)
            if cached:
                return json.loads(cached)

            # 새 데이터 가져오기
            result = await func(*args, **kwargs)

            # 캐시 저장
            await redis.set(
                key,
                json.dumps(result),
                ex=expire
            )

            return result
        return wrapper
    return decorator

# 사용 예시
@router.get("/{user_id}", response_model=UserInDB)
@cache(expire=300)  # 5분 캐시
async def get_user(
    user_id: int,
    db: AsyncSession = Depends(deps.get_db)
) -> UserInDB:
    user = await user_service.get_user(db, user_id)
    if not user:
        raise NotFoundException(f"User {user_id} not found")
    return user

이상으로 Part 2에서는 FastAPI의 라우터 구현, 예외 처리, 미들웨어, 페이지네이션, 캐싱 등 실전적인 내용을 다뤄보았습니다. Part 3에서는 테스트 작성, 배포 전략, 성능 최적화 등에 대해 다루도록 하겠습니다.

저작자표시 비영리 변경금지 (새창열림)

'파이썬 > Fast API' 카테고리의 다른 글

Router Tags 활용  (0) 2025.02.01
테스트, 배포, 성능 최적화 - Part 3  (0) 2025.01.31
프로젝트 구조와 기본 설정 - Part 1  (0) 2025.01.31
[FastAPI] add_api_route 파라미터  (0) 2025.01.30
[FastAPI] add_api_route로 동적 라우팅 구현  (1) 2025.01.28
'파이썬/Fast API' 카테고리의 다른 글
  • Router Tags 활용
  • 테스트, 배포, 성능 최적화 - Part 3
  • 프로젝트 구조와 기본 설정 - Part 1
  • [FastAPI] add_api_route 파라미터
코샵
코샵
나의 코딩 일기장
    반응형
  • 코샵
    끄적끄적 코딩 공방
    코샵
    • 분류 전체보기 (725)
      • 스마트팜 (0)
      • 상품 추천 (223)
      • MongoDB (4)
      • 하드웨어 (17)
      • 일기장 (4)
      • 파이썬 (130)
        • Basic (41)
        • OpenCV (8)
        • Pandas (15)
        • PyQT (3)
        • SBC(Single Board Computer) (1)
        • 크롤링 (14)
        • Fast API (29)
        • Package (6)
      • Unity (138)
        • Tip (41)
        • Project (1)
        • Design Pattern (8)
        • Firebase (6)
        • Asset (2)
      • Linux (4)
      • C# (97)
        • Algorithm (11)
        • Window (7)
      • TypeScript (51)
        • CSS (10)
      • Git (11)
      • SQL (5)
      • Flutter (10)
        • Tip (1)
      • System (1)
      • BaekJoon (6)
      • Portfolio (2)
      • MacOS (1)
      • 유틸리티 (1)
      • 서비스 (6)
      • 자동화 (3)
      • Hobby (10)
        • 물생활 (10)
        • 식집사 (0)
  • 인기 글

  • 태그

    카페24리뷰이관
    unity
    programmerlife
    cv2
    programming101
    리스트
    라떼우유
    상품 리뷰 크롤링
    유니티
    devlife
    스크립트 실행 순서
    리뷰관리
    codingtips
    list
    스마트스토어리뷰
    믈레코비타멸균우유
    스크립트 실행
    카페24리뷰
    appdevelopment
    쇼핑몰리뷰
    파이썬
    셀레니움
    Python
    ipcamera
    긴유통기한우유
    codingcommunity
    learntocode
    C#
    rtsp
    리뷰이관
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
코샵
라우터 구현과 에러 처리 - Part 2
상단으로

티스토리툴바