FastAPI 백엔드 패턴 가이드를 실행합니다.
Scanned 6/6/2026
Install via CLI
openskills install excatt/superclaude-plusplus---
name: "fastapi"
description: "FastAPI 백엔드 패턴 가이드를 실행합니다."
user-invocable: true
---
# FastAPI Skill
FastAPI 백엔드 패턴 가이드를 실행합니다.
## 프로젝트 구조
```
app/
├── main.py
├── config.py
├── dependencies.py
├── api/
│ ├── __init__.py
│ ├── deps.py
│ └── v1/
│ ├── __init__.py
│ ├── router.py
│ └── endpoints/
│ ├── users.py
│ └── auth.py
├── core/
│ ├── config.py
│ ├── security.py
│ └── exceptions.py
├── models/
│ ├── __init__.py
│ └── user.py
├── schemas/
│ ├── __init__.py
│ └── user.py
├── services/
│ └── user_service.py
├── repositories/
│ └── user_repository.py
└── db/
├── session.py
└── base.py
```
## 기본 CRUD
### Schemas (Pydantic v2)
```python
# schemas/user.py
from pydantic import BaseModel, EmailStr, ConfigDict
from datetime import datetime
class UserBase(BaseModel):
email: EmailStr
name: str
class UserCreate(UserBase):
password: str
class UserUpdate(BaseModel):
email: EmailStr | None = None
name: str | None = None
class UserResponse(UserBase):
id: int
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class UserListResponse(BaseModel):
data: list[UserResponse]
total: int
```
### Models (SQLAlchemy 2.0)
```python
# models/user.py
from sqlalchemy import String, DateTime
from sqlalchemy.orm import Mapped, mapped_column
from datetime import datetime
from app.db.base import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
name: Mapped[str] = mapped_column(String(255))
hashed_password: Mapped[str] = mapped_column(String(255))
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow
)
```
### Repository
```python
# repositories/user_repository.py
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.user import User
class UserRepository:
def __init__(self, session: AsyncSession):
self.session = session
async def get_by_id(self, user_id: int) -> User | None:
result = await self.session.execute(
select(User).where(User.id == user_id)
)
return result.scalar_one_or_none()
async def get_by_email(self, email: str) -> User | None:
result = await self.session.execute(
select(User).where(User.email == email)
)
return result.scalar_one_or_none()
async def get_all(
self, skip: int = 0, limit: int = 10, search: str | None = None
) -> tuple[list[User], int]:
query = select(User)
count_query = select(func.count()).select_from(User)
if search:
query = query.where(User.name.ilike(f"%{search}%"))
count_query = count_query.where(User.name.ilike(f"%{search}%"))
result = await self.session.execute(
query.offset(skip).limit(limit)
)
count_result = await self.session.execute(count_query)
return list(result.scalars().all()), count_result.scalar()
async def create(self, user: User) -> User:
self.session.add(user)
await self.session.commit()
await self.session.refresh(user)
return user
async def update(self, user: User) -> User:
await self.session.commit()
await self.session.refresh(user)
return user
async def delete(self, user: User) -> None:
await self.session.delete(user)
await self.session.commit()
```
### Service
```python
# services/user_service.py
from fastapi import HTTPException, status
from app.repositories.user_repository import UserRepository
from app.schemas.user import UserCreate, UserUpdate
from app.models.user import User
from app.core.security import get_password_hash
class UserService:
def __init__(self, repository: UserRepository):
self.repository = repository
async def get_user(self, user_id: int) -> User:
user = await self.repository.get_by_id(user_id)
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
return user
async def get_users(
self, skip: int = 0, limit: int = 10, search: str | None = None
) -> tuple[list[User], int]:
return await self.repository.get_all(skip, limit, search)
async def create_user(self, data: UserCreate) -> User:
existing = await self.repository.get_by_email(data.email)
if existing:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Email already registered"
)
user = User(
email=data.email,
name=data.name,
hashed_password=get_password_hash(data.password)
)
return await self.repository.create(user)
async def update_user(self, user_id: int, data: UserUpdate) -> User:
user = await self.get_user(user_id)
update_data = data.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(user, field, value)
return await self.repository.update(user)
async def delete_user(self, user_id: int) -> None:
user = await self.get_user(user_id)
await self.repository.delete(user)
```
### Endpoints
```python
# api/v1/endpoints/users.py
from fastapi import APIRouter, Depends, Query, status
from app.schemas.user import (
UserCreate, UserUpdate, UserResponse, UserListResponse
)
from app.services.user_service import UserService
from app.api.deps import get_user_service, get_current_user
router = APIRouter(prefix="/users", tags=["users"])
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def create_user(
data: UserCreate,
service: UserService = Depends(get_user_service)
):
user = await service.create_user(data)
return user
@router.get("", response_model=UserListResponse)
async def get_users(
skip: int = Query(0, ge=0),
limit: int = Query(10, ge=1, le=100),
search: str | None = None,
service: UserService = Depends(get_user_service)
):
users, total = await service.get_users(skip, limit, search)
return UserListResponse(data=users, total=total)
@router.get("/me", response_model=UserResponse)
async def get_current_user_info(
current_user = Depends(get_current_user)
):
return current_user
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(
user_id: int,
service: UserService = Depends(get_user_service)
):
return await service.get_user(user_id)
@router.put("/{user_id}", response_model=UserResponse)
async def update_user(
user_id: int,
data: UserUpdate,
service: UserService = Depends(get_user_service)
):
return await service.update_user(user_id, data)
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(
user_id: int,
service: UserService = Depends(get_user_service)
):
await service.delete_user(user_id)
```
## 인증 (JWT)
```python
# core/security.py
from datetime import datetime, timedelta
from jose import jwt, JWTError
from passlib.context import CryptContext
from app.core.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def create_access_token(subject: str | int) -> str:
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode = {"exp": expire, "sub": str(subject)}
return jwt.encode(to_encode, settings.SECRET_KEY, algorithm="HS256")
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
# api/deps.py
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError
from app.core.config import settings
from app.services.user_service import UserService
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
async def get_current_user(
token: str = Depends(oauth2_scheme),
service: UserService = Depends(get_user_service)
):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
user_id: str = payload.get("sub")
if user_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = await service.get_user(int(user_id))
return user
```
## DB 설정 (Async)
```python
# db/session.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from app.core.config import settings
engine = create_async_engine(settings.DATABASE_URL, echo=True)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async def get_db():
async with async_session() as session:
try:
yield session
finally:
await session.close()
```
## 예외 처리
```python
# core/exceptions.py
from fastapi import Request, status
from fastapi.responses import JSONResponse
class AppException(Exception):
def __init__(self, status_code: int, detail: str):
self.status_code = status_code
self.detail = detail
async def app_exception_handler(request: Request, exc: AppException):
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail}
)
# main.py에서 등록
app.add_exception_handler(AppException, app_exception_handler)
```
## Main 설정
```python
# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from app.api.v1.router import api_router
from app.db.session import engine
from app.db.base import Base
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
# Shutdown
await engine.dispose()
app = FastAPI(
title="My API",
version="1.0.0",
lifespan=lifespan
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(api_router, prefix="/api/v1")
@app.get("/health")
async def health_check():
return {"status": "healthy"}
```
## 체크리스트
- [ ] Pydantic v2 사용
- [ ] SQLAlchemy 2.0 async
- [ ] Repository 패턴
- [ ] Dependency Injection
- [ ] JWT 인증
- [ ] 에러 핸들링
- [ ] CORS 설정
- [ ] OpenAPI 문서화
## 출력 형식
```
## FastAPI Implementation
### Endpoints
| Method | Path | Description |
|--------|------|-------------|
| POST | /users | Create user |
| GET | /users | List users |
### Schemas
```python
# Pydantic 모델
```
### Services
```python
# 비즈니스 로직
```
```
---
요청에 맞는 FastAPI 구현을 설계하세요.
No comments yet. Be the first to comment!