FastAPI: 현대적인 고성능 Python 웹 프레임워크

자동 생성된 문서와 타입 안정성을 갖춘 초고속 API를 구축하세요.

Page content

FastAPI은 API를 구축하는 데 사용되는 가장 흥미로운 Python 웹 프레임워크 중 하나로, 현대적인 Python 기능과 뛰어난 성능 및 개발자 경험을 결합하고 있습니다.

마이크로서비스, 서버리스 함수, 또는 복잡한 웹 애플리케이션을 구축하든간에 FastAPI는 프로덕션용 API를 위해 필요한 도구를 제공합니다.

python and fastapi castle

FastAPI란 무엇인가요?

FastAPI는 Python 3.8+ 기반으로 표준 Python 타입 힌트를 사용하여 구축된 현대적이고 고성능 웹 프레임워크입니다. Sebastián Ramírez가 개발한 이 프레임워크는 사용하기 쉬우며, 코드 작성이 빠르고, 처음부터 프로덕션용으로 설계되었습니다.

이 프레임워크는 두 가지 강력한 라이브러리에 기반하고 있습니다:

  • Starlette - 웹 라우팅 및 비동기 기능 제공
  • Pydantic - Python 타입 힌트를 사용한 데이터 검증

이러한 조합은 Node.js와 Go의 성능과 비슷하면서도 Python의 간결성과 가독성을 유지합니다. FastAPI는 GitHub에서 수천 개의 스타를 얻었으며, 전 세계의 주요 회사들에서 채택하고 있습니다.

FastAPI를 구분하는 주요 기능

1. 자동 API 문서 생성

FastAPI의 가장 사랑받는 기능 중 하나는 자동 생성되는 상호작용형 API 문서입니다. OpenAPI 표준(구 Swagger) 기반으로 FastAPI는 두 가지 문서 인터페이스를 생성합니다:

  • Swagger UI - /docs에서 직접 브라우저에서 API 엔드포인트를 테스트할 수 있는 상호작용형 문서
  • ReDoc - /redoc에서 깔끔한 3패널 디자인의 대체 문서

이 문서는 코드의 타입 힌트와 문서 주석에서 자동으로 생성되며, 추가 설정이 필요하지 않습니다. 이는 문서가 항상 코드와 동기화되어 있음을 의미합니다.

2. 타입 힌트 및 검증

FastAPI는 문서화뿐만 아니라 런타임 검증 및 직렬화에도 Python의 타입 힌트를 활용합니다. int로 엔드포인트 파라미터를 정의하면 FastAPI가 자동으로:

  • 요청에 정수를 포함하는지 검증
  • 문자열을 정수로 변환
  • 검증 실패 시 명확한 오류 메시지 반환
  • API 문서에서 파라미터 타입을 문서화
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    username: str
    email: str
    age: int

@app.post("/users/")
async def create_user(user: User):
    return {"message": f"User {user.username} created successfully"}

이 접근 방식은 버그를 조기에 포착하고, 보일러플레이트 코드를 줄이며, API를 자동으로 문서화합니다.

3. 비동기 지원

FastAPI는 WSGI가 아닌 ASGI(Asynchronous Server Gateway Interface)를 기반으로 구축되어, 네이티브 async/await 지원을 제공합니다. 이는 많은 I/O 호출(데이터베이스 쿼리, API 요청, 파일 작업)을 수행하는 고성능 애플리케이션에 필수적입니다.

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    # 비동기 작업은 다른 요청을 차단하지 않습니다
    data = await fetch_from_database(item_id)
    return data

비동기 지원은 FastAPI가 수천 개의 동시 연결을 효율적으로 처리할 수 있게 해주며, 현대적인 클라우드 애플리케이션 및 마이크로서비스 아키텍처에 이상적입니다.

4. 성능

FastAPI는 사용 가능한 가장 빠른 Python 프레임워크 중 하나로, Node.js와 Go 프레임워크와 비슷한 성능을 제공합니다. 독립적인 벤치마크는 FastAPI가 Flask와 Django와 같은 전통적인 프레임워크보다 특히 I/O 중심 작업에서 큰 차이를 보입니다.

이러한 속도는 다음 요인에서 비롯됩니다:

  • Pydantic의 C 레벨 검증
  • Starlette의 효율적인 비동기 구현
  • 프레임워크 자체의 최소한의 오버헤드

FastAPI 시작하기

FastAPI를 설치하는 것은 간단합니다. Python 프로젝트 작업 시 Python 패키지 관리자를 사용하면 의존성 관리가 훨씬 쉬워집니다. FastAPI와 Uvicorn과 같은 ASGI 서버가 필요합니다:

pip install fastapi uvicorn[standard]

다음은 최소한의 FastAPI 애플리케이션입니다:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

다음 명령어로 실행합니다:

uvicorn main:app --reload

http://localhost:8000/docs에 접속하여 상호작용형 API 문서를 확인해 보세요.

프로덕션용 API 구축

요청 및 응답 모델

명확한 데이터 모델 정의는 유지 가능한 API를 위한 필수 요소입니다. Python의 청소 아키텍처를 위한 설계 패턴을 따르면 잘 구성된 애플리케이션을 만들 수 있습니다:

from pydantic import BaseModel, EmailStr, Field
from typing import Optional
from datetime import datetime

class UserCreate(BaseModel):
    username: str = Field(..., min_length=3, max_length=50)
    email: EmailStr
    password: str = Field(..., min_length=8)

class UserResponse(BaseModel):
    id: int
    username: str
    email: str
    created_at: datetime
    
    class Config:
        orm_mode = True  # Pydantic이 ORM 객체와 함께 작동하도록 허용

@app.post("/users/", response_model=UserResponse)
async def create_user(user: UserCreate):
    # 여기에 비즈니스 로직 작성
    new_user = save_user_to_database(user)
    return new_user

response_model 매개변수는 FastAPI가 지정된 필드만 반환하도록 보장하며, 비밀 정보(예: 비밀번호)를 자동으로 필터링합니다.

의존성 주입

FastAPI의 의존성 주입 시스템은 강력하고 우아합니다. 이를 통해 다음을 수행할 수 있습니다:

  • 엔드포인트 간 코드 공유
  • 인증/인가 강제
  • 데이터베이스 연결 관리
  • 복잡한 의존성 처리
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = verify_token(token)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials"
        )
    return user

@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

오류 처리

FastAPI는 내장된 예외 처리기와 사용자 정의 오류 응답을 허용합니다:

from fastapi import HTTPException

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    if item_id not in items_db:
        raise HTTPException(
            status_code=404,
            detail="Item not found",
            headers={"X-Error": "Custom header"}
        )
    return items_db[item_id]

백그라운드 작업

반환하기 전에 완료할 필요가 없는 작업을 위해:

from fastapi import BackgroundTasks

def send_email_notification(email: str, message: str):
    # 이메일 전송 로직
    pass

@app.post("/send-notification/")
async def send_notification(
    email: str,
    background_tasks: BackgroundTasks
):
    background_tasks.add_task(send_email_notification, email, "Welcome!")
    return {"message": "Notification scheduled"}

FastAPI 애플리케이션 테스트

신뢰할 수 있는 API를 위해 파이썬의 단위 테스트는 필수입니다. FastAPI는 pytest와 호환되며 테스트 클라이언트를 제공합니다:

from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_read_main():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello World"}

def test_read_item():
    response = client.get("/items/42?q=test")
    assert response.status_code == 200
    assert response.json() == {"item_id": 42, "q": "test"}

비동기 엔드포인트 테스트를 위해:

import pytest
from httpx import AsyncClient

@pytest.mark.asyncio
async def test_async_endpoint():
    async with AsyncClient(app=app, base_url="http://test") as ac:
        response = await ac.get("/")
    assert response.status_code == 200

데이터베이스 통합

FastAPI는 다양한 데이터베이스 및 ORM과 잘 작동합니다. 다음은 SQLAlchemy와 함께 사용하는 예입니다:

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from fastapi import Depends

DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(bind=engine)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String, unique=True, index=True)
    email = Column(String, unique=True)

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/users/{user_id}")
async def read_user(user_id: int, db: Session = Depends(get_db)):
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

배포 옵션

도커 배포

FastAPI 애플리케이션은 쉽게 컨테이너화됩니다. 다음은 프로덕션용 Dockerfile입니다:

FROM python:3.11-slim

WORKDIR /app

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

COPY . .

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

AWS Lambda 배포

FastAPI는 서버리스 플랫폼과 잘 작동합니다. 파이썬과 테라폼을 사용한 이중 모드 AWS Lambda를 구축할 때, Mangum이라는 어댑터를 사용하여 AWS Lambda에 FastAPI 애플리케이션을 배포할 수 있습니다:

from mangum import Mangum
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello from Lambda!"}

handler = Mangum(app)

더 복잡한 배포를 위해 AWS SAM과 파이썬 파워도구은 FastAPI 애플리케이션에 대한 훌륭한 도구를 제공합니다.

전통적인 서버 배포

전통적인 서버 또는 쿠버네티스에서 프로덕션 배포를 위해:

# Gunicorn과 Uvicorn 워커와 함께
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000

고급 기능

CORS (Cross-Origin Resource Sharing)

프론트엔드 애플리케이션을 위해 CORS를 활성화합니다:

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://example.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

WebSocket 지원

FastAPI는 실시간 통신을 위한 WebSocket을 지원합니다:

from fastapi import WebSocket

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Message received: {data}")

GraphQL 통합

FastAPI는 Strawberry 또는 다른 GraphQL 라이브러리를 통해 GraphQL과 함께 작동할 수 있습니다:

import strawberry
from strawberry.fastapi import GraphQLRouter

@strawberry.type
class Query:
    @strawberry.field
    def hello(self) -> str:
        return "Hello World"

schema = strawberry.Schema(query=Query)
graphql_app = GraphQLRouter(schema)

app.include_router(graphql_app, prefix="/graphql")

LLM 및 AI와의 작업

FastAPI는 AI 기반 API를 구축하기에 탁월합니다. 구조화된 출력을 사용하여 Ollama를 통해 LLM 제한을 수행할 때, FastAPI의 Pydantic 모델은 구조화된 응답에 대한 완벽한 스키마 정의를 제공합니다:

from pydantic import BaseModel
from typing import List
import httpx

class SentimentResponse(BaseModel):
    sentiment: str
    confidence: float
    keywords: List[str]

@app.post("/analyze-sentiment/", response_model=SentimentResponse)
async def analyze_sentiment(text: str):
    # LLM 서비스 호출
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "http://localhost:11434/api/generate",
            json={
                "model": "qwen2.5",
                "prompt": f"Analyze sentiment: {text}",
                "format": SentimentResponse.schema_json()
            }
        )
    return response.json()

최선의 실천 및 팁

1. 경로 작업 데코레이터를 일관되게 사용

명확한 HTTP 메서드로 엔드포인트를 조직하세요:

@app.get("/items/")      # 항목 목록
@app.post("/items/")     # 항목 생성
@app.get("/items/{id}")  # 특정 항목 가져오기
@app.put("/items/{id}")  # 항목 업데이트
@app.delete("/items/{id}") # 항목 삭제

2. API 버전 관리

처음부터 API 버전 관리를 포함하세요:

from fastapi import APIRouter

api_v1 = APIRouter(prefix="/api/v1")
api_v2 = APIRouter(prefix="/api/v2")

@api_v1.get("/users/")
async def get_users_v1():
    return {"version": "1.0"}

app.include_router(api_v1)
app.include_router(api_v2)

3. 환경 변수 사용

비밀을 하드코딩하지 마세요. 환경 변수를 사용하세요:

from pydantic import BaseSettings

class Settings(BaseSettings):
    database_url: str
    secret_key: str
    api_key: str
    
    class Config:
        env_file = ".env"

settings = Settings()

4. 적절한 로깅 구현

파이썬의 로깅 모듈을 사용하세요:

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.get("/items/")
async def get_items():
    logger.info("Fetching all items")
    return items

5. 성능 모니터링

요청 시간을 위한 미들웨어를 사용하세요:

import time
from fastapi import Request

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response

FastAPI와 다른 프레임워크 비교

FastAPI vs Flask

Flask는 더 최소주의적이며 더 많은 제어권을 제공하지만, FastAPI는 더 많은 사전 제공 기능을 제공합니다:

  • FastAPI는 자동 검증 및 문서화 제공
  • Flask는 비동기 지원을 위해 확장이 필요
  • FastAPI는 I/O 중심 작업에서 훨씬 빠름
  • Flask는 더 큰 생태계와 더 많은 학습 자료 제공

FastAPI vs Django REST Framework

Django REST Framework(DRF)는 더 큰 Django 생태계의 일부입니다:

  • DRF는 전체 ORM과 관리 인터페이스 포함
  • FastAPI는 더 가볍고 빠름
  • DRF는 복잡하고 데이터베이스 중심 애플리케이션에 더 적합
  • FastAPI는 마이크로서비스 및 독립형 API에 탁월

FastAPI vs Node.js/Express

FastAPI는 Python의 간결성과 Node.js와 유사한 성능을 제공합니다:

  • 유사한 비동기 성능 특성
  • Python의 타입 시스템은 JavaScript/TypeScript보다 더 견고
  • Node.js는 더 큰 패키지 생태계 제공
  • FastAPI의 자동 문서화는 우수

실제 사용 사례

마이크로서비스 아키텍처

FastAPI의 가볍고 빠른 시작 시간은 마이크로서비스에 이상적입니다. 각 서비스는 독립적으로 배포되고 확장될 수 있습니다.

머신러닝 API

ML 모델을 제공하는 것은 FastAPI의 일반적인 사용 사례입니다. 프레임워크의 비동기 지원은 여러 예측 요청을 효율적으로 처리합니다.

모바일/웹 앱 백엔드

FastAPI는 자동 CORS 처리 및 WebSocket 지원으로 현대적인 SPAs(단일 페이지 애플리케이션) 및 모바일 앱의 뛰어난 백엔드로 작용합니다.

IoT 데이터 수집

FastAPI는 IoT 장치로부터의 고부하 데이터 수집을 처리하고 센서 데이터를 효율적으로 처리 및 저장할 수 있습니다.

챗봇 및 대화형 AI

FastAPI의 WebSocket 지원 및 비동기 기능은 대화형 인터페이스 및 챗봇 백엔드 구축에 이점을 제공합니다.

일반적인 함정 및 해결책

1. 비동기 함수 내에서 블로킹 작업 수행

문제: 동기 블로킹 코드를 비동기 함수 내에서 실행하는 것:

@app.get("/slow")
async def slow_endpoint():
    time.sleep(10)  # 전체 이벤트 루프를 차단!
    return {"status": "done"}

해결책: 블로킹 작업에 run_in_executor 사용:

from fastapi import BackgroundTasks
import asyncio

@app.get("/fast")
async def fast_endpoint():
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(None, blocking_function)
    return {"result": result}

2. 응답 모델 사용하지 않음

문제: 원시 데이터베이스 객체를 반환하여 내부 구조를 노출.

해결책: 항상 응답 모델 정의:

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    return db_user  # UserResponse를 통해 자동 필터링

3. 부족한 오류 처리

문제: 처리되지 않은 예외가 애플리케이션을 충돌시킴.

해결책: 예외 처리기 사용:

@app.exception_handler(ValueError)
async def value_error_handler(request, exc):
    return JSONResponse(
        status_code=400,
        content={"message": str(exc)}
    )

파이썬 생태계 자원

FastAPI는 더 넓은 파이썬 생태계와 원활하게 작동합니다. 파이썬 기초에 대한 종합적인 개요를 보려면 파이썬 체크리스트를 확인하세요. 이는 FastAPI 애플리케이션에서 사용하는 필수 파이썬 구문 및 패턴을 다룹니다.

결론

FastAPI는 파이썬 웹 프레임워크 설계에서 중요한 발전을 대표합니다. 현대적인 파이썬 기능인 타입 힌트 및 async/await를 채택함으로써, 뛰어난 성능과 개발자 경험을 제공합니다. 간단한 REST API를 만들거나 복잡한 마이크로서비스 아키텍처를 구축하든간에, FastAPI는 프로덕션 애플리케이션에 필요한 도구와 성능을 제공합니다.

프레임워크의 자동 문서화, 타입 안전성, 직관적인 디자인은 초보자와 경험 많은 개발자 모두에게 탁월한 선택입니다. 파이썬 생태계가 계속 진화하면서 FastAPI는 현대적인 API 개발을 위한 프레임워크로 자리 잡고 있습니다.

간단한 API부터 시작하여 문서를 탐색하고, 애플리케이션이 성장하면서 더 고급 기능을 점차 채택하세요. FastAPI 커뮤니티는 활발하며 도움을 주는 것으로 알려져 있으며, 광범위한 문서 및 예제가 제공됩니다.

유용한 링크

외부 자원 및 참고 자료