웹 애플리케이션에서 CRUD(Create, Read, Update, Delete) 기능은 백엔드 개발의 핵심입니다.
이번 글에서는 FastAPI와 SQLModel을 활용해 자산(Asset) 모델을 예시로 CRUD API를 만들고, PostgreSQL 데이터베이스와 연동하며, Swagger UI에서 API를 테스트하는 방법까지 단계별로 안내합니다.
🧩 참고
이번 글에서는 FastAPI를 활용한 CRUD API 구현에 초점을 맞추었기 때문에, 데이터베이스 테이블 모델링에 대한 내용은 다루지 않았습니다.
모델 설계와 관계 설정 등은 추후 별도의 포스팅에서 자세히 소개할 예정입니다.
📌 목차
📂 프로젝트 구조
portfolio-app/backend 폴더 기준으로 주요 구조는 다음과 같습니다.
portfolio-app/backend/
├─ main.py
├─ models.py
├─ db.py
├─ routes/
│ └─ asset.py
└─ schemas/
└─ asset.py
- routes/asset.py: Asset CRUD API 라우터
- models.py: Asset, Portfolio 모델 정의
- schemas/asset.py: Pydantic 요청/응답 모델 정의
- db.py: SQLModel 세션 관리
⚙️ FastAPI CRUD API 설계
이번 글에서 구현할 CRUD API는 다음과 같습니다.
| 기능 | HTTP Method | URL | 설명 |
|---|---|---|---|
| Create | POST | /assets/ | 새로운 Asset 생성 |
| Read All | GET | /assets/ | 모든 Asset 조회 |
| Update | PUT | /assets/{asset_id} | 특정 Asset 수정 |
| Delete | DELETE | /assets/{asset_id} | 특정 Asset 삭제 |
핵심 포인트:
- 평단가(
average_price) 기본값 처리 - 트랜잭션 안전성 확보 (
try/except)
🔍 Asset 라우터 코드 분석
routes/asset.py를 중심으로 CRUD 기능을 분석합니다.
1️⃣ Create (자산 생성)
@router.post("/", response_model=AssetRead)
def create_asset(asset_create: AssetCreate, session: Session = Depends(get_session)):
asset = Asset.model_validate(asset_create)
if not asset.portfolio_id:
raise HTTPException(status_code=400, detail="portfolio_id is required")
portfolio = session.get(Portfolio, asset.portfolio_id)
if not portfolio:
raise HTTPException(status_code=404, detail="Portfolio not found")
if asset.average_price is None:
asset.average_price = 0.0
try:
session.add(asset)
session.commit()
session.refresh(asset)
except:
session.rollback()
raise
return asset
model_validate로 입력 데이터 검증- 존재하지 않는 Portfolio일 경우 404 반환
- 평균 단가(average_price) 누락 시 기본값 0.0 적용
2️⃣ Read (모든 자산 조회)
@router.get("/", response_model=List[AssetRead])
def read_assets(session: Session = Depends(get_session)):
return session.exec(select(Asset)).all()
- SQLModel
select사용 - List[AssetRead] 형태로 반환
3️⃣ Update (자산 수정)
@router.put("/{asset_id}", response_model=AssetRead)
def update_asset(asset_id: int, updated_asset: AssetUpdate, session: Session = Depends(get_session)):
asset = session.get(Asset, asset_id)
if not asset:
raise HTTPException(status_code=404, detail="Asset not found")
for field, value in updated_asset.model_dump(exclude_unset=True).items():
setattr(asset, field, value)
try:
session.add(asset)
session.commit()
session.refresh(asset)
except:
session.rollback()
raise
return asset
exclude_unset=True로 부분 업데이트 지원- 없는 Asset일 경우 404 처리
4️⃣ Delete (자산 삭제)
@router.delete("/{asset_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_asset(asset_id: int, session: Session = Depends(get_session)):
asset = session.get(Asset, asset_id)
if not asset:
raise HTTPException(status_code=404, detail="Asset not found")
try:
session.delete(asset)
session.commit()
except:
session.rollback()
raise
- 삭제 성공 시 HTTP 204 반환
- 존재하지 않는 자산 삭제 시 404 처리
🚀 FastAPI 서버 실행 및 Swagger UI 테스트
FastAPI 실행 명령어:
uvicorn main:app --reload
- Swagger UI: http://localhost:8000/docs
- CRUD API를 직접 테스트하고 JSON 요청/응답 확인 가능

🧪 실제 API 테스트 절차
✅ (1) 자산 생성 테스트
- Swagger UI에서 POST /assets/ 클릭
- “Try it out” 버튼 클릭
- 아래 예시 JSON을 입력합니다:
{
"name": "예금",
"category": "현금성자산",
"amount": 5000000,
"average_price": 0,
"portfolio_id": 14
}
- “Execute” 클릭 → 200 응답과 함께 생성된 자산 정보가 반환됩니다.
📸


✅ (2) 전체 자산 조회
- GET /assets/ 선택
- “Try it out” → “Execute” 클릭
- 모든 자산 목록이 JSON 배열 형태로 반환됩니다.
📸

✅ (3) 자산 수정 테스트
- PUT /assets/{asset_id} 클릭
- URL 파라미터에 수정할
asset_id입력 - 아래와 같이 수정 데이터를 입력:
{
"amount": 7000000
}
- “Execute” 클릭 → 수정된 자산 정보가 응답으로 표시됩니다.
📸


✅ (4) 자산 삭제 테스트
- DELETE /assets/{asset_id} 클릭
- 삭제할 ID를 입력 후 “Execute” 클릭
- 성공 시 204 No Content 응답이 반환됩니다.
📸


🎯 Swagger UI 활용 팁
- 각 API의 요청 및 응답 스키마를 Swagger에서 직접 확인할 수 있습니다.
- FastAPI는 Pydantic 모델 기반으로 자동 문서화되므로, 스키마가 변경되면 문서도 자동 갱신됩니다.
- 팀 개발 시 Swagger URL만 공유해도, 모든 팀원이 API를 쉽게 테스트할 수 있습니다.
🔧 정리
이번 글에서는 자산(Asset) 데이터를 예시로 FastAPI CRUD 구현과 테스트를 살펴보았습니다.
핵심 요약
- FastAPI + SQLModel + PostgreSQL 기반 CRUD
- 필수 입력값 검증 및 기본값 처리
- 트랜잭션 안전성 확보
- Swagger UI로 API 테스트 가능