본문으로 건너뛰기
Previous
Next
FastAPI 완벽 가이드 — Python 최고 성능 웹 프레임워크, Django·Flask 대체

FastAPI 완벽 가이드 — Python 최고 성능 웹 프레임워크, Django·Flask 대체

FastAPI 완벽 가이드 — Python 최고 성능 웹 프레임워크, Django·Flask 대체

이 글의 핵심

FastAPI는 2018년 출시 이후 Python 웹 프레임워크의 혁신을 이끈 프로젝트입니다. Python 3.6+ 타입 힌트를 기반으로 자동 검증·직렬화·OpenAPI(Swagger) 문서를 생성하고, async/await + Starlette + uvloop로 Django·Flask보다 2-3배 빠른 성능을 제공합니다. 2026년 현재 GitHub Star 80k+, Netflix·Uber·Microsoft가 프로덕션 도입, Python 백엔드 신규 프로젝트의 사실상 표준입니다.

설치

pip install "fastapi[standard]"   # uvicorn + 기타 의존성 포함
# 또는
pip install fastapi uvicorn[standard]

첫 API

# main.py
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 = None):
    return {"item_id": item_id, "q": q}
uvicorn main:app --reload
# http://127.0.0.1:8000
# http://127.0.0.1:8000/docs  ← Swagger UI

타입 검증

from fastapi import FastAPI, Query
from pydantic import BaseModel, EmailStr

app = FastAPI()

class User(BaseModel):
    name: str
    email: EmailStr
    age: int | None = None

@app.post("/users/")
async def create_user(user: User):
    return user

@app.get("/search/")
async def search(q: str = Query(..., min_length=3, max_length=50)):
    return {"q": q}
  • User 모델: JSON body 자동 파싱·검증
  • Query(): 쿼리 파라미터 제약

경로 파라미터

from enum import Enum

class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    return {"model_name": model_name, "message": f"Model {model_name.value}"}

Enum이면 OpenAPI에 enum 값 자동 표시.

의존성 주입

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)):
    # 토큰 검증 로직
    if token != "secret":
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
    return {"username": "johndoe"}

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

Depends()로 재사용 가능한 의존성 정의. DB 세션·인증·설정 주입에 활용.

데이터베이스 (SQLModel)

pip install sqlmodel psycopg2-binary
from sqlmodel import Field, Session, SQLModel, create_engine, select

class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None

DATABASE_URL = "postgresql://user:pass@localhost/db"
engine = create_engine(DATABASE_URL)

def create_db_and_tables():
    SQLModel.metadata.create_all(engine)

@app.on_event("startup")
def on_startup():
    create_db_and_tables()

@app.post("/heroes/")
def create_hero(hero: Hero):
    with Session(engine) as session:
        session.add(hero)
        session.commit()
        session.refresh(hero)
        return hero

@app.get("/heroes/")
def read_heroes():
    with Session(engine) as session:
        heroes = session.exec(select(Hero)).all()
        return heroes

SQLModel = SQLAlchemy 2.0 + Pydantic. 같은 클래스를 DB 모델 + API 스키마로 사용.

async DB (asyncpg)

pip install databases[asyncpg] asyncpg
from databases import Database

DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/db"
database = Database(DATABASE_URL)

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

@app.get("/users/")
async def read_users():
    query = "SELECT * FROM users"
    rows = await database.fetch_all(query)
    return rows

인증 (JWT)

pip install python-jose[cryptography] passlib[bcrypt]
from datetime import datetime, timedelta, timezone
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm

SECRET_KEY = "secret"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

def verify_password(plain, hashed):
    return pwd_context.verify(plain, hashed)

def get_password_hash(password):
    return pwd_context.hash(password)

def create_access_token(data: dict):
    to_encode = data.copy()
    expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    # 유저 검증 (DB에서 조회)
    user = fake_db.get(form_data.username)
    if not user or not verify_password(form_data.password, user["hashed_password"]):
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    
    access_token = create_access_token({"sub": user["username"]})
    return {"access_token": access_token, "token_type": "bearer"}

async def get_current_user(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise HTTPException(status_code=401)
    except JWTError:
        raise HTTPException(status_code=401)
    return {"username": username}

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

CORS

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

백그라운드 태스크

from fastapi import BackgroundTasks

def send_email(email: str, message: str):
    # 이메일 전송 로직
    print(f"Sending email to {email}: {message}")

@app.post("/send-notification/")
async def send_notification(email: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(send_email, email, "Notification!")
    return {"message": "Notification sent in the background"}

파일 업로드

from fastapi import File, UploadFile

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
    contents = await file.read()
    return {"filename": file.filename, "size": len(contents)}

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: {data}")

테스트

from fastapi.testclient import TestClient

client = TestClient(app)

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

def test_create_user():
    response = client.post("/users/", json={"name": "Alice", "email": "[email protected]"})
    assert response.status_code == 200

Docker

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db/dbname
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: dbname

Alembic (Migration)

pip install alembic
alembic init alembic
# alembic/env.py
from sqlmodel import SQLModel
from myapp.models import Hero  # 모든 모델 import
target_metadata = SQLModel.metadata
alembic revision --autogenerate -m "Create hero table"
alembic upgrade head

프로덕션 배포

# Gunicorn + Uvicorn workers
pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000

또는 Railway·Fly.io·Render에 Dockerfile 배포.

트러블슈팅

RuntimeError: no running event loop

  • async 함수 안에서 asyncio.run() 호출 금지
  • 동기 함수를 async 선언 X (FastAPI가 스레드풀 실행)

Pydantic validation 에러

  • 타입 힌트 정확히 명시 (str | None 등)
  • Field()로 제약사항 추가

DB 연결 끊김

  • connection pool 설정 (create_engine(..., pool_pre_ping=True))
  • on_event("startup")·on_event("shutdown") 정리

CORS 에러

  • allow_origins 정확히 설정 (와일드카드는 credentials=True와 충돌)

체크리스트

  • FastAPI + uvicorn 설치
  • Pydantic 모델 정의
  • /docs OpenAPI 확인
  • SQLModel 또는 SQLAlchemy 연동
  • JWT 인증 구현
  • pytest + TestClient 테스트
  • Alembic migration
  • Docker + docker-compose
  • Gunicorn 프로덕션 배포

마무리

FastAPI는 “타입 힌트만 잘 쓰면 프레임워크가 모든 boilerplate를 해결한다”는 철학의 Python 웹 프레임워크입니다. Django·Flask가 지배하던 Python 백엔드 생태계에 async·타입 안전성·자동 문서를 가져왔고, 2026년 현재 신규 API 프로젝트의 표준이 되었습니다. Pydantic v2 + SQLModel + uv 조합으로 개발 경험이 TypeScript + Prisma 수준에 도달했고, 성능도 Go·Rust에 근접합니다. 지금 Django REST Framework나 Flask로 API를 만들고 있다면, FastAPI로의 전환을 강력히 권장합니다.

관련 글

  • Python 완벽 가이드
  • Pydantic 완벽 가이드
  • Django 완벽 가이드
  • SQLAlchemy 완벽 가이드

자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. FastAPI 완벽 가이드. Pydantic·async·타입 힌트 기반 자동 검증·OpenAPI 문서·의존성 주입. Django·Flask보다 3배 빠른 Python 웹 프레임워크. uvicorn·SQLModel·A… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

FastAPI, Python, Web Framework, API, Async, Pydantic, OpenAPI, Backend 등으로 검색하시면 이 글이 도움이 됩니다.