Q1) schemas.py == dto?
파이썬 자체에 대한 질문이라기 보다는 nest.js 와의 비교 일것 같은데, schemas.py 에서 정의한 class 들은 nest.js 의 dto 같은 거라고 봐야 할까요?
A1)
정확합니다! 💯 schemas.py
의 클래스들은 NestJS의 DTO와 거의 동일한 역할을 합니다!
NestJS DTO vs FastAPI Pydantic Schemas
NestJS DTO 예시
// create-book.dto.ts
import { IsString, IsOptional, IsInt, Min, Max, Matches } from 'class-validator';
export class CreateBookDto {
@IsString()
@Length(1, 200)
title: string;
@IsString()
@Length(1, 100)
author: string;
@IsOptional()
@IsString()
description?: string;
@IsOptional()
@Matches(/^\d{13}$/)
isbn?: string;
@IsOptional()
@IsInt()
@Min(1000)
@Max(2024)
publishedYear?: number;
}
// book-response.dto.ts
export class BookResponseDto {
id: number;
title: string;
author: string;
description?: string;
isbn?: string;
publishedYear?: number;
createdAt: Date;
updatedAt?: Date;
}
FastAPI Pydantic Schemas (완전히 동일한 역할!)
# schemas.py
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime
class BookCreate(BaseModel): # CreateBookDto와 동일!
title: str = Field(..., min_length=1, max_length=200)
author: str = Field(..., min_length=1, max_length=100)
description: Optional[str] = None
isbn: Optional[str] = Field(None, regex=r'^\d{13}$')
published_year: Optional[int] = Field(None, ge=1000, le=2024)
class BookResponse(BaseModel): # BookResponseDto와 동일!
id: int
title: str
author: str
description: Optional[str]
isbn: Optional[str]
published_year: Optional[int]
created_at: datetime
updated_at: Optional[datetime]
class Config:
from_attributes = True # ORM 모델과 호환
역할과 기능 비교
1. 요청 데이터 검증 (Request DTO)
NestJS:
@Post('/books')
async createBook(@Body() createBookDto: CreateBookDto) {
// createBookDto는 이미 검증된 데이터
return this.bookService.create(createBookDto);
}
FastAPI:
@app.post("/books")
def create_book(book: BookCreate, db: Session = Depends(get_db)):
# book은 이미 검증된 데이터
return crud.create_book(db=db, book=book)
2. 응답 데이터 직렬화 (Response DTO)
NestJS:
@Get('/books/:id')
async getBook(@Param('id') id: number): Promise<BookResponseDto> {
const book = await this.bookService.findOne(id);
return plainToClass(BookResponseDto, book); // 직렬화
}
FastAPI:
@app.get("/books/{book_id}", response_model=BookResponse)
def get_book(book_id: int, db: Session = Depends(get_db)):
book = crud.get_book(db, book_id=book_id)
return book # 자동으로 BookResponse 형태로 직렬화!
3. 자동 문서화
NestJS (Swagger):
@ApiProperty({ description: '책 제목', minLength: 1, maxLength: 200 })
title: string;
FastAPI (자동 Swagger):
title: str = Field(..., min_length=1, max_length=200)
# 자동으로 OpenAPI 문서에 포함됨!
주요 차이점들
1. 검증 방식
NestJS - 데코레이터 방식:
@IsString()
@Length(1, 200)
title: string;
FastAPI - Field 함수 방식:
title: str = Field(..., min_length=1, max_length=200)
2. 상속과 재사용
NestJS:
// base-book.dto.ts
export class BaseBookDto {
title: string;
author: string;
}
// create-book.dto.ts
export class CreateBookDto extends BaseBookDto {
description?: string;
}
// update-book.dto.ts
export class UpdateBookDto extends PartialType(BaseBookDto) {
// 모든 필드가 optional
}
FastAPI:
# schemas.py
class BookBase(BaseModel):
title: str
author: str
class BookCreate(BookBase):
description: Optional[str] = None
class BookUpdate(BookBase):
title: Optional[str] = None # 모든 필드 optional
author: Optional[str] = None
3. ORM 통합
NestJS:
// 별도의 매핑 필요
const bookEntity = new Book();
Object.assign(bookEntity, createBookDto);
FastAPI:
# 직접 호환!
class BookResponse(BaseModel):
class Config:
from_attributes = True # SQLAlchemy 모델과 자동 호환
# 사용
db_book = Book(**book_create.dict()) # DTO → ORM
return BookResponse.from_orm(db_book) # ORM → DTO
실제 사용 패턴 비교
NestJS Controller
@Controller('books')
export class BookController {
@Post()
async create(@Body() createBookDto: CreateBookDto): Promise<BookResponseDto> {
const book = await this.bookService.create(createBookDto);
return plainToClass(BookResponseDto, book);
}
@Put(':id')
async update(
@Param('id') id: number,
@Body() updateBookDto: UpdateBookDto
): Promise<BookResponseDto> {
const book = await this.bookService.update(id, updateBookDto);
return plainToClass(BookResponseDto, book);
}
}
FastAPI Router
@app.post("/books", response_model=BookResponse)
def create_book(book: BookCreate, db: Session = Depends(get_db)):
return crud.create_book(db=db, book=book)
@app.put("/books/{book_id}", response_model=BookResponse)
def update_book(book_id: int, book: BookUpdate, db: Session = Depends(get_db)):
return crud.update_book(db, book_id=book_id, book=book)
핵심 정리
측면 | NestJS DTO | FastAPI Pydantic Schema |
---|---|---|
역할 | 요청/응답 데이터 검증 및 직렬화 | 요청/응답 데이터 검증 및 직렬화 |
검증 | class-validator 데코레이터 | Pydantic Field 함수 |
타입 안전성 | TypeScript 타입 시스템 | Python 타입 힌트 |
문서화 | Swagger 데코레이터 필요 | 자동 OpenAPI 생성 |
ORM 통합 | 수동 매핑 | 자동 호환 (from_attributes) |
상속 | extends, PartialType | BaseModel 상속 |
결론: FastAPI의 Pydantic schemas는 NestJS의 DTO와 거의 동일한 역할을 하면서도, 더 간단하고 자동화된 기능들을 제공합니다! 🚀
NestJS 경험이 있으시다면 schemas.py를 DTO 폴더라고 생각하시면 됩니다!
Q2) pydantic, sqlalchemy ?
좋습니다! 그러면 pydantic 라이브러리(라이브러리가 맞나요??) 와 sqlalchemy 라이브러리에 대해 궁금해요!
A2) 각 라이브러리 설명
네, 맞습니다! 둘 다 라이브러리입니다! 🐍
이 두 라이브러리는 FastAPI 개발의 핵심이 되는 중요한 도구들이에요. 자세히 알아보겠습니다!
1. Pydantic 라이브러리
개념과 역할
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime
# Pydantic = "Python Data Validation" 라이브러리
# 데이터 검증, 직렬화, 타입 변환을 자동으로 처리
핵심 기능들
1) 자동 타입 변환
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
email: str
# 문자열로 들어와도 자동 변환!
user_data = {
"name": "김개발",
"age": "25", # 문자열 → 자동으로 int로 변환
"email": "dev@example.com"
}
user = User(**user_data)
print(user.age) # 25 (int)
print(type(user.age)) # <class 'int'>
2) 데이터 검증
from pydantic import BaseModel, Field, ValidationError
class BookCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
author: str = Field(..., min_length=1, max_length=100)
isbn: Optional[str] = Field(None, regex=r'^\d{13}$')
published_year: Optional[int] = Field(None, ge=1000, le=2024)
# ✅ 올바른 데이터
book = BookCreate(
title="파이썬 입문",
author="김개발자",
isbn="1234567890123",
published_year=2024
)
# ❌ 잘못된 데이터 - 자동으로 에러 발생
try:
invalid_book = BookCreate(
title="", # 빈 문자열 - min_length 위반
author="김개발자",
isbn="123", # 13자리가 아님 - regex 위반
published_year=3000 # 2024 초과 - le 위반
)
except ValidationError as e:
print(e.json()) # 상세한 에러 정보
3) JSON 직렬화/역직렬화
# Python 객체 → JSON
book_json = book.json()
print(book_json)
# '{"title":"파이썬 입문","author":"김개발자","isbn":"1234567890123","published_year":2024}'
# JSON → Python 객체
book_from_json = BookCreate.parse_raw(book_json)
4) 딕셔너리 변환
# Python 객체 → 딕셔너리
book_dict = book.dict()
print(book_dict)
# {'title': '파이썬 입문', 'author': '김개발자', 'isbn': '1234567890123', 'published_year': 2024}
# 딕셔너리 → Python 객체
book_from_dict = BookCreate(**book_dict)
NestJS class-validator와 비교
// NestJS
class CreateBookDto {
@IsString()
@Length(1, 200)
title: string;
}
// Pydantic
class BookCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
2. SQLAlchemy 라이브러리
개념과 역할
# SQLAlchemy = "SQL Alchemy" (SQL 연금술)
# Python에서 SQL 데이터베이스를 객체지향적으로 다루는 ORM
핵심 컴포넌트들
1) Core - 저수준 SQL 처리
from sqlalchemy import create_engine, text
engine = create_engine("postgresql://user:password@localhost/db")
# 직접 SQL 실행
with engine.connect() as conn:
result = conn.execute(text("SELECT * FROM books"))
for row in result:
print(row)
2) ORM - 고수준 객체 매핑
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func
Base = declarative_base()
class Book(Base): # Python 클래스 = 데이터베이스 테이블
__tablename__ = "books"
id = Column(Integer, primary_key=True, index=True) # SQL: id INTEGER PRIMARY KEY
title = Column(String(200), nullable=False) # SQL: title VARCHAR(200) NOT NULL
author = Column(String(100), nullable=False) # SQL: author VARCHAR(100) NOT NULL
created_at = Column(DateTime, server_default=func.now()) # SQL: created_at TIMESTAMP DEFAULT NOW()
# 실제 테이블 생성
Base.metadata.create_all(bind=engine)
3) Session - 데이터베이스 작업 관리
from sqlalchemy.orm import sessionmaker
SessionLocal = sessionmaker(bind=engine)
def database_operations():
db = SessionLocal()
try:
# CREATE - 데이터 생성
new_book = Book(title="FastAPI 입문", author="김개발자")
db.add(new_book)
db.commit()
# READ - 데이터 조회
books = db.query(Book).filter(Book.author == "김개발자").all()
# UPDATE - 데이터 수정
book = db.query(Book).filter(Book.id == 1).first()
book.title = "FastAPI 마스터"
db.commit()
# DELETE - 데이터 삭제
db.query(Book).filter(Book.id == 1).delete()
db.commit()
finally:
db.close()
TypeORM과 비교
// TypeORM (NestJS)
@Entity()
export class Book {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 200 })
title: string;
@CreateDateColumn()
createdAt: Date;
}
// SQLAlchemy
class Book(Base):
__tablename__ = "books"
id = Column(Integer, primary_key=True)
title = Column(String(200))
created_at = Column(DateTime, server_default=func.now())
3. Pydantic + SQLAlchemy 연동
데이터 흐름
# 1. 요청 데이터 → Pydantic으로 검증
@app.post("/books", response_model=BookResponse)
def create_book(book: BookCreate, db: Session = Depends(get_db)):
# 2. Pydantic → SQLAlchemy ORM
db_book = Book(**book.dict()) # Pydantic 데이터를 ORM 모델로 변환
db.add(db_book)
db.commit()
db.refresh(db_book)
# 3. SQLAlchemy ORM → Pydantic (자동 직렬화)
return db_book # BookResponse로 자동 변환되어 반환
from_attributes 설정
class BookResponse(BaseModel):
id: int
title: str
author: str
created_at: datetime
class Config:
from_attributes = True # SQLAlchemy 모델과 호환 가능
# 사용
db_book = Book(id=1, title="책", author="저자", created_at=datetime.now())
response = BookResponse.from_orm(db_book) # ORM → Pydantic 자동 변환
4. 라이브러리 생태계
설치
# Pydantic
uv add pydantic
# SQLAlchemy
uv add sqlalchemy
# 데이터베이스 드라이버들
uv add "psycopg[binary]" # PostgreSQL
uv add pymysql # MySQL
uv add sqlite3 # SQLite (기본 포함)
대안 라이브러리들
Pydantic 대안
# marshmallow (예전에 많이 사용)
from marshmallow import Schema, fields
class BookSchema(Schema):
title = fields.Str(required=True, validate=validate.Length(min=1, max=200))
author = fields.Str(required=True)
# attrs + cattrs
import attrs
@attrs.define
class Book:
title: str
author: str
SQLAlchemy 대안
# Tortoise ORM (비동기)
from tortoise.models import Model
from tortoise import fields
class Book(Model):
id = fields.IntField(pk=True)
title = fields.CharField(max_length=200)
# Django ORM
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
5. 정리
Pydantic
- 역할: 데이터 검증, 직렬화, 타입 변환
- 특징: FastAPI와 완벽 호환, 자동 문서화
- 비교: NestJS의 class-validator + class-transformer 역할
SQLAlchemy
- 역할: ORM, 데이터베이스 추상화
- 특징: Python에서 가장 성숙한 ORM, 유연함
- 비교: NestJS의 TypeORM과 유사
함께 사용하는 이유
- 데이터 검증 (Pydantic) + 데이터 저장 (SQLAlchemy)
- API 계층과 DB 계층의 분리
- 타입 안전성과 자동 문서화
# 완벽한 조합!
API 요청 → Pydantic 검증 → SQLAlchemy 저장 → SQLAlchemy 조회 → Pydantic 직렬화 → API 응답
이 두 라이브러리가 FastAPI 생태계의 핵심이며, NestJS + TypeORM + class-validator 조합과 매우 유사한 역할을 합니다! 🚀
'TIL' 카테고리의 다른 글
[250719 TIL] Tanstack SSR시 왜 클라이언트 매번 새로 생성? (0) | 2025.07.19 |
---|---|
[250713 TIL] 파이썬 Comprehension (1) | 2025.07.13 |
[250712 TIL] 파이썬 모듈 시스템 궁금증 (0) | 2025.07.12 |
[250712 TIL] fastapi 제너레이터 궁금증 (0) | 2025.07.12 |
[250712 TIL] FastAPI 기초(uv) (2) | 2025.07.12 |