ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

python

[python] Pydantic ์ •๋ฆฌ

Mr.Ban 2026. 3. 3. 17:19

๐Ÿ›ฃ๏ธ Pydantic ์„ ํƒ ์ด์œ 

์š”์ฆ˜ ๊ฐ™์€ AI ์‹œ๋Œ€์—๋Š” ์Šคํฌ๋ฆฝํŠธ ์–ธ์–ด๋„ ์ •์  ํƒ€์ž…์„ ์ด์šฉํ•˜์—ฌ ๋ณด๋‹ค ๋ช…์‹œ์ ์œผ๋กœ ์ฝ”๋”ฉํ•˜๋ ค๋Š” ํ๋ฆ„์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

๋ช…์„ธ๋ฅผ ๋ณด๋‹ค ๋ช…ํ™•ํžˆ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ผ์ด ์˜ˆ์ „์ด๋ฉด ๋ณ‘๋ชฉ์ธ ์ž‘์—…์ด์˜€๋‹ค๋ฉด,

์š”์ƒˆ๋Š” AI์˜ ๋„์›€์œผ๋กœ ๋ณด๋‹ค ์•ˆ์ •์ ์ด๋ฉฐ ๋” ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜๋‹จ์ด ๋˜์—ˆ๋‹ค.

์ด๋Ÿฐ ํ๋ฆ„์— ๋งž์ถฐ์„œ Javascript ์ง„์˜์—๋Š” Typescript,Python ์ง„์˜์—๋Š” Pydantic ์ธ๊ธฐ๊ฐ€ ๋ถ€์ƒํ•œ๊ฒŒ ์•„๋‹๊นŒ ์‹ถ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์–ด๋–ค ๊ธฐ๋Šฅ์ด ์žˆ๋Š”์ง€ ํ•œ ๋ฒˆ ์ •๋ฆฌํ•ด๋ณด๋„๋ก ํ•˜์ž.

 

โœ… 1. ์ž๋™ ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ

๋‹จ์ˆœํ•œ ํƒ€์ž… ์ฒดํฌ๋ฅผ ๋„˜์–ด, ์ž…๋ ฅ๋œ ๊ฐ’์„ ์„ ์–ธ๋œ ํƒ€์ž…์œผ๋กœ ๊ฐ•์ œ ๋ณ€ํ™˜.

from pydantic import BaseModel

class Item(BaseModel):
    id: int
    name: str
    price: float
    is_available: bool

# ๋ฌธ์ž์—ด "100"๊ณผ "4500.50", "true"๊ฐ€ ๋“ค์–ด์™€๋„ ์ž๋™์œผ๋กœ ํŒŒ์‹ฑ๋ฉ๋‹ˆ๋‹ค.
external_data = {
    "id": "100", 
    "name": "๋ชจ๋‹ˆํ„ฐ", 
    "price": "4500.50", 
    "is_available": "true"
}

# Pylance ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒ!
# item = Item(**external_data)
# model_validate๋ฅผ ์ด์šฉํ•ด ๊ฒ€์ฆ ๋ฐ ๋ณ€ํ™˜ ์ˆ˜ํ–‰
item = Item.model_validate(external_data)

print(item.id)           # 100 (int)
print(item.price)        # 4500.5 (float)
print(item.is_available) # True (bool)
  • BaseModel ์ƒ์†์„ ํ†ตํ•ด์„œ Pydantic ๊ธฐ๋Šฅ ์‚ฌ์šฉ.
    • Pydantic์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค์ด๋ฏ€๋กœ ํ•„์ˆ˜์ ์œผ๋กœ ์ƒ์†
  • item = Item(**external_data) Dictinary Unpacking์„ ์‚ฌ์šฉํ•˜์—ฌ๋„ ๋˜์ง€๋งŒ Pylance๋ฅผ ํ†ตํ•ด ์ฆ‰์‹œ ๊ฒ€์ฆ์ด Validation ์—๋Ÿฌ ๋ฐœ์ƒ
    • model_validate๋ฅผ ํ†ตํ•ด์„œ ๋ช…์‹œ์ ์œผ๋กœ ๋ณ€ํ™˜๋งŒ ์ง„ํ–‰ํ•˜๋„๋ก ํ•œ๋‹ค.

VSCode Pylance ํ™•์žฅ, Pydantic ์‚ฌ์šฉ์‹œ ๊ฒ€์ฆ์„ ์œ„ํ•œ ํ•„์ˆ˜ ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ

๐Ÿ‘“ model_validate ์‚ฌ์šฉ ์‹œ๊ธฐ

  • ์•ž์„  ์˜ˆ์ œ๋กœ model_validate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฒ€์ฆ ๋Œ€์‹  ๋ณ€ํ™˜๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ์ง„ํ–‰ํ•œ๋‹ค
  • ์š”์ฒญ, ์‘๋‹ต๊ณผ ๊ฐ™์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ž…, ์ถœ๋ ฅ์€ Pydantic Class ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ฝ”๋“œ๋‹จ์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ ๊ฐ€๊ณตํ•˜์—ฌ ๊ฒ€์ฆ์ด ์™„๋ฃŒ๋œ ๋ฐ์ดํ„ฐ์ธ ๊ฒฝ์šฐ์—๋Š” model_validate ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€ํ™˜๋งŒ ์ˆ˜ํ–‰ํ•œ๋‹ค

โž• Camel ๋ณ€ํ™˜

์„œ๋ฒ„๋‹จ์—์„œ ํ”ํžˆ ์‚ฌ์šฉํ•˜๋Š” snake ↔ camel ๋ณ€ํ™˜์ด๋‹ค.

python convention์— ๋”ฐ๋ผ ๋‚ด๋ถ€์—์„œ๋Š” snake case๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ,

์ธํ„ฐํŽ˜์ด์Šค ์ƒ์—์„œ๋Š” camelCase ๋ฐ์ดํ„ฐ๋ฅผ ์†ก์ˆ˜์‹  ํ•˜๋Š” ๊ฒฝ์šฐ ์œ ์šฉํ•˜๋‹ค.

def to_camel(value: str) -> str:
    head, *tail = value.split("_")
    return head + "".join(part.capitalize() for part in tail)

# CamelModel ์ •์˜
## alias_generator์— camel ๋ณ€ํ™˜ ํ•จ์ˆ˜ ์—ฐ๊ฒฐ
class CamelModel(BaseModel):
    model_config = ConfigDict(
        alias_generator=to_camel,
        populate_by_name=True,
    )

# CasemModel ์ƒ์†
class ImgProcessingForm(CamelModel):
    prc_type: PrcType
    kernel_size: int | None = None\\

 

โœ… 2. ์ค‘์ฒฉ๋œ ๋ชจ๋ธ

๋ณต์žกํ•œ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด ์ง€ํ–ฅ์ ์œผ๋กœ ์ •์˜ ๊ฐ€๋Šฅ.

from typing import List

class QuoteItem(BaseModel):
    product_name: str
    quantity: int

class Quote(BaseModel):
    quote_id: str
    vendor_name: str
    items: List[QuoteItem]  # ๋‹ค๋ฅธ Pydantic ๋ชจ๋ธ์„ ๋ฆฌ์ŠคํŠธ๋กœ ํฌํ•จ

data = {
    "quote_id": "Q-2026-001",
    "vendor_name": "์‚ผ์„ฑ์ „์ž",
    "items": [
        {"product_name": "๋…ธํŠธ๋ถ", "quantity": 5},
        {"product_name": "๋งˆ์šฐ์Šค", "quantity": 10}
    ]
}

quote = Quote(**data)
print(quote.items[0].product_name)  # '๋…ธํŠธ๋ถ'
  • ์†์„ฑ์— ๊ธฐ์กด ๋ชจ๋ธ์„ ํ™œ์šฉํ•ด ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์ •์˜ (Quote.items)

 

โœ… 3. ์ปค์Šคํ…€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

๊ธฐ๋ณธ ํƒ€์ž… ์™ธ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(์˜ˆ: ์ˆ˜๋Ÿ‰์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•จ)์„ ๊ฐ•์ œํ•  ๋•Œ ์‚ฌ์šฉ.

from pydantic import BaseModel, field_validator

class Inventory(BaseModel):
    stock: int

    @field_validator('stock')
    @classmethod
    def check_positive_stock(cls, v: int):
        if v < 0:
            raise ValueError('์žฌ๊ณ ๋Š” ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.')
        return v

# ์—๋Ÿฌ ๋ฐœ์ƒ ์˜ˆ์‹œ
try:
    Inventory(stock=-5)
except ValueError as e:
    print(e)  # ์žฌ๊ณ ๋Š” ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

 

โœ… 4. ์ง๋ ฌํ™” (Serialization)

๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ๋‹ค์‹œ JSON์ด๋‚˜ Dictionary๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ DB์— ์ €์žฅํ•˜๊ฑฐ๋‚˜ API ์‘๋‹ต์œผ๋กœ ๋ณด๋‚ผ ๋•Œ ์‚ฌ์šฉ.

# Pydantic V2 ๊ธฐ์ค€
quote_dict = quote.model_dump()      # Python dict๋กœ ๋ณ€ํ™˜
quote_json = quote.model_dump_json() # JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜

print(type(quote_dict)) # <class 'dict'>
print(quote_json)       # '{"quote_id":"Q-2026-001", ...}'

FastAPI์—์„œ๋Š” ๋ผ์šฐํ„ฐ์—์„œ BaseModel ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด JSON ์Šคํ‚ค๋งˆ๋กœ ์ž๋™ ๋ณ€ํ™˜ ๋จ.

class FileListResponse(BaseModel):
    has_more: bool = Field(..., description="๋‹ค์Œ ํŽ˜์ด์ง€ ์กด์žฌ ์—ฌ๋ถ€")
    next_cursor_uploaded_at: datetime | None = Field(None, description="๋‹ค์Œ ์ปค์„œ ๊ธฐ์ค€ ์—…๋กœ๋“œ ์‹œ๊ฐ")
    next_cursor_id: str | None = Field(None, description="๋‹ค์Œ ์ปค์„œ ํŒŒ์ผ ID")

@router.get("/image-processing")
async def get_saved_images() {
	...
	
	return FileListResponse(...)
}

 

โœ… 5. ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ

.env ํŒŒ์ผ์ด๋‚˜ ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ฝ์–ด์˜ฌ ๋•Œ ์œ ์šฉ.

BsseSettings ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ์„ค์ • ๊ตฌ์„ฑ์„ ํ•˜๋ฉด ๋œ๋‹ค.

pydantic-settings ํŒจํ‚ค์ง€๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ FastAPI ์„ค์น˜ ์‹œ ๊ธฐ๋ณธ์ ์œผ๋กœ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    app_name: str = "FastAPI Server"
    env: str = "local"
    debug: bool = True
    api_prefix: str = "/api"
    database_url: str = "postgresql://postgres:test1!@localhost:5434/postgres"

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False,
    )
    
    # .env ํŒŒ์ผ์„ ํ†ตํ•ด์„œ ์„ค์ •๋„ ๊ฐ€๋Šฅ
    # class Config:
	  #   env_file = ".env"

settings = Settings()

 

๋ถ€๋ก

OpenAPI

FastAPI์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ

Pydantic ๊ธฐ๋ฐ˜์œผ๋กœ OpenAPI ์ŠคํŽ™์„ ์ž‘์„ฑํ•ด์ฃผ๊ณ  Swagger๋ฅผ ํ†ตํ•ด ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

<host>/docs ๊ฒฝ๋กœ๋ฅผ ํ†ตํ•ด swagger ํ™”๋ฉด์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
Pydantic ์„ค์ •์— ๋”ฐ๋ฅธ Swagger ํ™”๋ฉด

 

๋งˆ๋ฌด๋ฆฌ

  • python์„ ์ปดํŒŒ์ผ ํƒ€์ž„์— ๊ฒ€์ฆ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Pydantic์ด ํ•„์ˆ˜
  • FastAPI์—์„œ๋Š” Pydantic ๊ธฐ๋ฐ˜ OpenAPI ๋ช…์„ธ ์ž๋™ํ™”๊ฐ€ ๋˜์–ด ์žˆ์–ด ๋งค์šฐ ํŽธ๋ฆฌ
  • ์•ˆ์ •์ ์ด๊ณ  AI๊ธฐ๋ฐ˜ ๋น ๋ฅธ ๊ฐœ๋ฐœ์„ ์œ„ํ•ด์„œ๋Š” Pydantic์€ ์„ ํƒ์ด ์•„๋‹Œ ํ•„์ˆ˜.

 

๋Œ“๊ธ€