테스트, 배포, 성능 최적화 - Part 3

2025. 1. 31. 14:15·파이썬/Fast API
반응형

테스트 구현

테스트는 애플리케이션의 안정성을 보장하는 핵심입니다. pytest를 활용하여 체계적인 테스트를 구현해보겠습니다.

# tests/conftest.py
import pytest
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker

from app.main import app
from app.api import deps
from app.db.base import Base

TEST_DATABASE_URL = "postgresql+asyncpg://test:test@localhost/test_db"

@pytest.fixture(scope="session")
async def test_engine():
    engine = create_async_engine(TEST_DATABASE_URL)
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    yield engine
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)

@pytest.fixture
async def db_session(test_engine):
    async_session = sessionmaker(
        test_engine, class_=AsyncSession, expire_on_commit=False
    )
    async with async_session() as session:
        yield session

@pytest.fixture
async def client(db_session):
    async def override_get_db():
        yield db_session

    app.dependency_overrides[deps.get_db] = override_get_db
    async with AsyncClient(app=app, base_url="http://test") as client:
        yield client

# tests/api/test_users.py
import pytest
from app.schemas.user import UserCreate

async def test_create_user(client: AsyncClient):
    user_data = {
        "email": "test@example.com",
        "password": "testpassword"
    }
    response = await client.post("/api/v1/users/", json=user_data)
    assert response.status_code == 200
    data = response.json()
    assert data["email"] == user_data["email"]
    assert "id" in data

성능 최적화

대규모 애플리케이션에서 성능은 매우 중요합니다. 주요 최적화 전략들을 살펴보겠습니다.

# app/core/optimizations.py
from functools import wraps
from sqlalchemy import select
from sqlalchemy.orm import selectinload

def optimize_query(model, *related_models):
    def decorator(func):
        @wraps(func)
        async def wrapper(db: AsyncSession, *args, **kwargs):
            query = select(model)
            for related in related_models:
                query = query.options(selectinload(related))
            result = await db.execute(query)
            return result.scalars().all()
        return wrapper
    return decorator

# app/services/user.py
@optimize_query(User, User.items)
async def get_users_with_items(db: AsyncSession):
    pass  # 쿼리는 데코레이터에서 처리됨

# Background Tasks 활용
from fastapi import BackgroundTasks

@router.post("/users/")
async def create_user(
    user_in: UserCreate,
    background_tasks: BackgroundTasks,
    db: AsyncSession = Depends(deps.get_db)
):
    user = await user_service.create_user(db, user_in)
    background_tasks.add_task(send_welcome_email, user.email)
    return user

보안 설정

애플리케이션 보안을 강화하기 위한 설정들입니다.

# app/core/security.py
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(
        to_encode,
        settings.SECRET_KEY,
        algorithm=settings.ALGORITHM
    )
    return encoded_jwt

# 미들웨어로 Rate Limiting 구현
from fastapi import Request
import time
from app.core.cache import redis

class RateLimitMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        client_ip = request.client.host
        key = f"rate_limit:{client_ip}"

        # 1분당 최대 60회 요청 제한
        requests = await redis.incr(key)
        if requests == 1:
            await redis.expire(key, 60)

        if requests > 60:
            raise HTTPException(
                status_code=429,
                detail="Too many requests"
            )

        return await call_next(request)

배포 설정

Docker를 활용한 배포 설정입니다.

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    env_file:
      - .env
    depends_on:
      - db
      - redis

  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=fastapi
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password

  redis:
    image: redis:6
    ports:
      - "6379:6379"

volumes:
  postgres_data:

모니터링 설정

애플리케이션 모니터링을 위한 설정입니다.

# app/core/monitoring.py
from prometheus_client import Counter, Histogram
import time

REQUEST_COUNT = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
)

REQUEST_LATENCY = Histogram(
    'http_request_duration_seconds',
    'HTTP request latency',
    ['method', 'endpoint']
)

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

        REQUEST_COUNT.labels(
            method=request.method,
            endpoint=request.url.path,
            status=response.status_code
        ).inc()

        REQUEST_LATENCY.labels(
            method=request.method,
            endpoint=request.url.path
        ).observe(duration)

        return response

이상으로 FastAPI 시리즈를 마무리하겠습니다. 이 시리즈에서는 프로젝트 구조부터 테스트, 배포, 성능 최적화까지 FastAPI를 사용한 대규모 애플리케이션 개발에 필요한 다양한 측면을 다뤄보았습니다. 각 부분은 프로젝트의 요구사항에 맞게 조정하여 사용하시면 됩니다.

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

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

FastAPI-Cache로 구현하는 효율적인 API 캐싱  (0) 2025.02.02
Router Tags 활용  (0) 2025.02.01
라우터 구현과 에러 처리 - Part 2  (0) 2025.01.31
프로젝트 구조와 기본 설정 - Part 1  (0) 2025.01.31
[FastAPI] add_api_route 파라미터  (0) 2025.01.30
'파이썬/Fast API' 카테고리의 다른 글
  • FastAPI-Cache로 구현하는 효율적인 API 캐싱
  • Router Tags 활용
  • 라우터 구현과 에러 처리 - Part 2
  • 프로젝트 구조와 기본 설정 - Part 1
코샵
코샵
나의 코딩 일기장
    반응형
  • 코샵
    끄적끄적 코딩 공방
    코샵
    • 분류 전체보기 (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)
  • 인기 글

  • 태그

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

  • hELLO· Designed By정상우.v4.10.3
코샵
테스트, 배포, 성능 최적화 - Part 3
상단으로

티스토리툴바