TL;DR
- pydantic์ Python์ ํ์ ํํธ๋ฅผ ๋ฐํ์์ ๊ฐ์ ํ๋ ๋ฐ์ดํฐ ๊ฒ์ฆ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- dataclass๋ ํ์ ํํธ๊ฐ ํํธ์ผ ๋ฟ์ด์ง๋ง, pydantic์ ์ค์ ๋ก ๊ฒ์ฆํ๊ณ ๋ณํํ๋ค
- Python fastapi์ ์์ฒญ/์๋ต ์ฒ๋ฆฌ, ์ค์ ๊ด๋ฆฌ, ์ธ๋ถ ๋ฐ์ดํฐ ํ์ฑ ๋ฑ์์ ํต์ฌ์ ์ผ๋ก ์ฌ์ฉ๋๋ค
AI-assisted
Sources
1. pydantic์ด๋
pydantic์ Python์ ํ์ ํํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ ๊ฒ์ฆ๊ณผ ์๋ ๋ณํ์ ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค. ํต์ฌ ์ฐจ์ด๋ฅผ ์ฝ๋๋ก ๋ณด๋ฉด ๋ฐ๋ก ์ดํด๋๋ค.
dataclass โ ํ์ ํํธ๋ โํํธโ์ผ ๋ฟ
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
u = User(name="alice", age="30")
print(u.age) # "30" โ ๋ฌธ์์ด ๊ทธ๋๋ก
print(type(u.age)) # <class 'str'>age: int๋ผ๊ณ ์ ์์ง๋ง ๋ฌธ์์ด "30"์ด ๊ทธ๋๋ก ๋ค์ด๊ฐ๋ค. ๋ฐํ์์์ ํ์
์ ํ์ธํ์ง ์๋๋ค.
pydantic โ ํ์ ์ โ๊ฐ์ โํ๋ค
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
u = User(name="alice", age="30")
print(u.age) # 30 โ int๋ก ๋ณํ๋จ
print(type(u.age)) # <class 'int'>๋ฌธ์์ด "30"์ ๋ฃ์ด๋ int๋ก ์๋ ๋ณํ๋๋ค. ๋ณํ ๋ถ๊ฐ๋ฅํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
User(name="alice", age="abc")
# ValidationError: 1 validation error for User
# age
# Input should be a valid integer, unable to parse string as an integer์ค์น
pydantic์ ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค. ๋ณ๋ ์ค์น๊ฐ ํ์ํ๋ค.
pip install pydantic
2. ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
๋ชจ๋ธ ์ ์
BaseModel์ ์์ํ๊ณ ํ๋๋ฅผ ํ์
์ด๋
ธํ
์ด์
์ผ๋ก ์ ์ธํ๋ค.
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float
quantity: int = 0 # ๊ธฐ๋ณธ๊ฐ
tags: list[str] = [] # mutable ๊ธฐ๋ณธ๊ฐ๋ ์์ (dataclass์ ๋ฌ๋ฆฌ field() ๋ถํ์)p = Product(name="ํค๋ณด๋", price="89000") # ๋ฌธ์์ด โ float ์๋ ๋ณํ
print(p)
# name='ํค๋ณด๋' price=89000.0 quantity=0 tags=[]์๋ ๋ณํ ๊ท์น
pydantic์ ๊ฐ๋ฅํ ํ ์ ์ธ๋ ํ์ ์ผ๋ก ๋ณํ์ ์๋ํ๋ค.
class Example(BaseModel):
a: int
b: float
c: str
d: bool
e = Example(a="42", b="3.14", c=123, d="yes")
print(e)
# a=42 b=3.14 c='123' d=True| ์ ๋ ฅ | ์ ์ธ ํ์ | ๊ฒฐ๊ณผ |
|---|---|---|
"42" | int | 42 |
"3.14" | float | 3.14 |
123 | str | "123" |
"yes" | bool | True |
"abc" | int | ValidationError |
ValidationError
๊ฒ์ฆ ์คํจ ์ ValidationError๊ฐ ๋ฐ์ํ๋ค. ์ด๋ค ํ๋์์ ์ด๋ค ์ด์ ๋ก ์คํจํ๋์ง ์์ธ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ค.
from pydantic import ValidationError
class User(BaseModel):
name: str
age: int
email: str
try:
User(name=123, age="abc", email="alice@example.com")
except ValidationError as e:
print(e.error_count()) # 1 โ age๋ง ์คํจ (name์ "123"์ผ๋ก ๋ณํ ๊ฐ๋ฅ)
print(e.errors())
# [{'type': 'int_parsing', 'loc': ('age',), 'msg': 'Input should be a valid integer...'}]3. Field()
Field()๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ๋ณธ๊ฐ, ๋ณ์นญ, ๊ฒ์ฆ ์ ์ฝ ์กฐ๊ฑด ๋ฑ์ ํ๋ ๋จ์๋ก ์ค์ ํ ์ ์๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(min_length=1, max_length=50, description="์ฌ์ฉ์ ์ด๋ฆ")
age: int = Field(ge=0, le=150, description="๋์ด")
email: str = Field(pattern=r"^[\w.-]+@[\w.-]+\.\w+$")User(name="", age=30, email="alice@example.com")
# ValidationError โ name์ด ๋น ๋ฌธ์์ด (min_length=1 ์๋ฐ)
User(name="alice", age=-1, email="alice@example.com")
# ValidationError โ age๊ฐ ์์ (ge=0 ์๋ฐ)
User(name="alice", age=30, email="not-an-email")
# ValidationError โ pattern ๋ถ์ผ์นField() ์ฃผ์ ์ต์
| ์ต์ | ์ฉ๋ | ์์ |
|---|---|---|
default | ๊ธฐ๋ณธ๊ฐ | Field(default=0) |
default_factory | mutable ๊ธฐ๋ณธ๊ฐ | Field(default_factory=list) |
alias | JSON ํค ์ด๋ฆ ๋งคํ | Field(alias="userName") |
min_length / max_length | ๋ฌธ์์ด ๊ธธ์ด ์ ํ | Field(min_length=1) |
ge / gt / le / lt | ์ซ์ ๋ฒ์ | Field(ge=0, le=100) |
pattern | ์ ๊ท์ ๊ฒ์ฆ | Field(pattern=r"^\d{3}-\d{4}$") |
description | JSON Schema ์ค๋ช | Field(description="์ฌ์ฉ์ ID") |
exclude | ์ง๋ ฌํ ์ ์ ์ธ | Field(exclude=True) |
alias โ ์ธ๋ถ JSON ํค์ Python ํ๋๋ช ๋งคํ
์ธ๋ถ API์ JSON ํค๊ฐ Python ๋ค์ด๋ฐ ์ปจ๋ฒค์ ๊ณผ ๋ค๋ฅผ ๋ ์ ์ฉํ๋ค.
class User(BaseModel):
user_name: str = Field(alias="userName")
created_at: str = Field(alias="createdAt")
# JSON์์๋ camelCase๋ก ๋ฐ๊ณ
data = {"userName": "alice", "createdAt": "2026-04-12"}
u = User(**data)
# Python์์๋ snake_case๋ก ์ ๊ทผ
print(u.user_name) # alice
print(u.created_at) # 2026-04-124. ๊ฒ์ฆ์ (Validator)
Field()์ ์ ์ฝ ์กฐ๊ฑด๋ง์ผ๋ก ๋ถ์กฑํ ๋, ์ปค์คํ
๊ฒ์ฆ ๋ก์ง์ ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ ์ํ ์ ์๋ค.
@field_validator โ ํ๋ ๋จ์ ๊ฒ์ฆ
from pydantic import BaseModel, field_validator
class User(BaseModel):
username: str
password: str
@field_validator("username")
@classmethod
def username_must_be_alphanumeric(cls, v: str) -> str:
if not v.isalnum():
raise ValueError("์๋ฌธ๊ณผ ์ซ์๋ง ํ์ฉ๋ฉ๋๋ค")
return v
@field_validator("password")
@classmethod
def password_must_be_strong(cls, v: str) -> str:
if len(v) < 8:
raise ValueError("๋น๋ฐ๋ฒํธ๋ 8์ ์ด์์ด์ด์ผ ํฉ๋๋ค")
if not any(c.isupper() for c in v):
raise ValueError("๋๋ฌธ์๊ฐ ์ต์ 1๊ฐ ํฌํจ๋์ด์ผ ํฉ๋๋ค")
return vUser(username="alice123", password="MyPass123") # OK
User(username="alice!@#", password="MyPass123")
# ValidationError โ ์๋ฌธ๊ณผ ์ซ์๋ง ํ์ฉ๋ฉ๋๋ค
User(username="alice", password="short")
# ValidationError โ ๋น๋ฐ๋ฒํธ๋ 8์ ์ด์์ด์ด์ผ ํฉ๋๋ค@field_validator์ ๊ฐ ๋ณํ
validator์์ ๊ฐ์ ๋ณํํด์ ๋ฐํํ ์๋ ์๋ค. ๊ฒ์ฆ๊ณผ ์ ๊ทํ๋ฅผ ๋์์ ์ฒ๋ฆฌํ๋ค.
class Tag(BaseModel):
name: str
@field_validator("name")
@classmethod
def normalize(cls, v: str) -> str:
return v.strip().lower()
print(Tag(name=" Python ")) # name='python'@model_validator โ ๋ชจ๋ธ ๋จ์ ๊ฒ์ฆ (์ฌ๋ฌ ํ๋ ์กฐํฉ)
ํ๋ ๊ฐ ๊ด๊ณ๋ฅผ ๊ฒ์ฆํ ๋ ์ฌ์ฉํ๋ค.
from pydantic import BaseModel, model_validator
class DateRange(BaseModel):
start: str
end: str
@model_validator(mode="after")
def check_date_order(self):
if self.start >= self.end:
raise ValueError("start๋ end๋ณด๋ค ์ด์ ์ด์ด์ผ ํฉ๋๋ค")
return selfDateRange(start="2026-01-01", end="2026-12-31") # OK
DateRange(start="2026-12-31", end="2026-01-01") # ValidationErrormode=โbeforeโ vs mode=โafterโ
class User(BaseModel):
name: str
age: int
# before: ํ์
๋ณํ ์ ์ ์คํ (raw ์
๋ ฅ๊ฐ์ ๋ฐ์)
@model_validator(mode="before")
@classmethod
def preprocess(cls, data):
if isinstance(data, dict) and "full_name" in data:
data["name"] = data.pop("full_name")
return data
# after: ํ์
๋ณํ ํ์ ์คํ (๊ฒ์ฆ๋ ๋ชจ๋ธ ์ธ์คํด์ค๋ฅผ ๋ฐ์)
@model_validator(mode="after")
def postprocess(self):
self.name = self.name.title()
return selfu = User(**{"full_name": "alice", "age": 30})
print(u.name) # Alice โ before์์ ํค ๋ณํ, after์์ title() ์ ์ฉ5. ์ง๋ ฌํ/์ญ์ง๋ ฌํ
pydantic์ ์ง๋ ฌํ ๋ฉ์๋๋ฅผ ๋ด์ฅํ๊ณ ์๋ค. asdict + json.dumps ์กฐํฉ์ด ํ์ํ dataclass์ ๋ฌ๋ฆฌ ํ ์ค๋ก ์ฒ๋ฆฌ๋๋ค.
Python ๊ฐ์ฒด โ dict / JSON
class Address(BaseModel):
city: str
zipcode: str
class User(BaseModel):
name: str
age: int
address: Address
u = User(name="alice", age=30, address=Address(city="์์ธ", zipcode="06000"))
# dict๋ก ๋ณํ
print(u.model_dump())
# {'name': 'alice', 'age': 30, 'address': {'city': '์์ธ', 'zipcode': '06000'}}
# JSON ๋ฌธ์์ด๋ก ๋ณํ
print(u.model_dump_json(indent=2))
# {
# "name": "alice",
# "age": 30,
# "address": {
# "city": "์์ธ",
# "zipcode": "06000"
# }
# }dict / JSON โ Python ๊ฐ์ฒด
# dict์์ ์์ฑ
data = {"name": "bob", "age": 25, "address": {"city": "๋ถ์ฐ", "zipcode": "48000"}}
u = User.model_validate(data)
# JSON ๋ฌธ์์ด์์ ์์ฑ
json_str = '{"name": "bob", "age": 25, "address": {"city": "๋ถ์ฐ", "zipcode": "48000"}}'
u = User.model_validate_json(json_str)์ค์ฒฉ๋ ๋ชจ๋ธ๋ ์๋์ผ๋ก ํ์ฑ๋๋ค. dict ์์ {"city": "๋ถ์ฐ", "zipcode": "48000"}์ด Address ๊ฐ์ฒด๋ก ๋ณํ๋๋ค.
์ง๋ ฌํ ์ต์
class User(BaseModel):
name: str
password: str = Field(exclude=True) # ์ง๋ ฌํ ์ ์ ์ธ
age: int
u = User(name="alice", password="secret123", age=30)
print(u.model_dump())
# {'name': 'alice', 'age': 30} โ password ์ ์ธ๋จ# ํน์ ํ๋๋ง ํฌํจ/์ ์ธ
u.model_dump(include={"name", "age"}) # {'name': 'alice', 'age': 30}
u.model_dump(exclude={"age"}) # {'name': 'alice'}dataclass์ ๋น๊ต
| ์์ | dataclass | pydantic |
|---|---|---|
| dict ๋ณํ | asdict(obj) | obj.model_dump() |
| JSON ๋ณํ | json.dumps(asdict(obj)) | obj.model_dump_json() |
| dict โ ๊ฐ์ฒด | ์๋ ๋งคํ ํ์ | Model.model_validate(dict) |
| JSON โ ๊ฐ์ฒด | json.loads() + ์๋ ๋งคํ | Model.model_validate_json(str) |
| ์ค์ฒฉ ๊ฐ์ฒด ํ์ฑ | ์๋ | ์๋ |
6. ๋ชจ๋ธ ์ค์ (model_config)
model_config๋ฅผ ํตํด ๋ชจ๋ธ ์ ์ฒด์ ๋์์ ์ ์ดํ ์ ์๋ค.
from pydantic import ConfigDictstrict โ ์๋ ๋ณํ ๋๊ธฐ
๊ธฐ๋ณธ์ ์ผ๋ก pydantic์ "30" โ 30์ฒ๋ผ ๋ณํ์ ์๋ํ๋ค. strict=True๋ก ์ค์ ํ๋ฉด ์ ํํ ํ์
๋ง ํ์ฉํ๋ค.
class StrictUser(BaseModel):
model_config = ConfigDict(strict=True)
name: str
age: int
StrictUser(name="alice", age=30) # OK
StrictUser(name="alice", age="30") # ValidationError โ str์ int๊ฐ ์๋frozen โ ๋ถ๋ณ ๋ชจ๋ธ
dataclass์ frozen=True์ ๋์ผํ๋ค.
class Config(BaseModel):
model_config = ConfigDict(frozen=True)
host: str
port: int
c = Config(host="localhost", port=8080)
c.host = "0.0.0.0" # ValidationError โ frozen์ด๋ผ ๋ณ๊ฒฝ ๋ถ๊ฐextra โ ์ ์ํ์ง ์์ ํ๋ ์ฒ๋ฆฌ
# forbid: ์ ์ํ์ง ์์ ํ๋๊ฐ ์ค๋ฉด ์๋ฌ (๊ธฐ๋ณธ๊ฐ์ ignore)
class StrictModel(BaseModel):
model_config = ConfigDict(extra="forbid")
name: str
StrictModel(name="alice", unknown_field="?")
# ValidationError โ extra inputs are not permitted# allow: ์ ์ํ์ง ์์ ํ๋๋ ์ ์ฅ
class FlexModel(BaseModel):
model_config = ConfigDict(extra="allow")
name: str
m = FlexModel(name="alice", custom="value")
print(m.custom) # valuepopulate_by_name โ alias์ ํ๋๋ช ๋ ๋ค ํ์ฉ
class User(BaseModel):
model_config = ConfigDict(populate_by_name=True)
user_name: str = Field(alias="userName")
# ๋ ๋ค ๊ฐ๋ฅ
User(userName="alice") # OK (alias)
User(user_name="alice") # OK (ํ๋๋ช
)7. ์ค์ฒฉ ๋ชจ๋ธ๊ณผ JSON Schema
์ค์ฒฉ ๋ชจ๋ธ ์๋ ํ์ฑ
pydantic์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋๋ค. JSON์ ์ค์ฒฉ ๊ตฌ์กฐ๋ฅผ ๋ชจ๋ธ ์ ์๋ง์ผ๋ก ์๋ ํ์ฑํ๋ค.
class Address(BaseModel):
city: str
zipcode: str
class Company(BaseModel):
name: str
address: Address
class User(BaseModel):
name: str
age: int
company: Company
hobbies: list[str] = []# ๊น๊ฒ ์ค์ฒฉ๋ dict๋ฅผ ๋ฃ์ด๋ ์๋์ผ๋ก ๊ฐ ๋ชจ๋ธ๋ก ๋ณํ๋๋ค
data = {
"name": "alice",
"age": 30,
"company": {
"name": "Acme",
"address": {
"city": "์์ธ",
"zipcode": "06000"
}
},
"hobbies": ["python", "coffee"]
}
u = User(**data)
print(type(u.company)) # <class 'Company'>
print(type(u.company.address)) # <class 'Address'>
print(u.company.address.city) # ์์ธdataclass์์ ๊ฐ์ ์ผ์ ํ๋ ค๋ฉด ์ค์ฒฉ๋ dict๋ฅผ ์๋์ผ๋ก ๋งคํํด์ผ ํ๋ค.
JSON Schema ์๋ ์์ฑ
pydantic ๋ชจ๋ธ์์ JSON Schema๋ฅผ ์๋์ผ๋ก ์์ฑํ ์ ์๋ค. Python fastapi๋ ์ด ๊ธฐ๋ฅ์ ์ด์ฉํด API ๋ฌธ์(Swagger UI)๋ฅผ ์๋ ์์ฑํ๋ค.
import json
schema = User.model_json_schema()
print(json.dumps(schema, indent=2))
# {
# "properties": {
# "name": { "type": "string", "title": "Name" },
# "age": { "type": "integer", "title": "Age" },
# "company": { "$ref": "#/$defs/Company" },
# "hobbies": {
# "items": { "type": "string" },
# "type": "array",
# "default": [],
# "title": "Hobbies"
# }
# },
# "required": ["name", "age", "company"],
# ...
# }8. FastAPI์์ ์ฐ๋
Python fastapi๋ ๋ด๋ถ์ ์ผ๋ก pydantic์ ์ฌ์ฉํ๋ค. ์์ฒญ body๋ฅผ pydantic ๋ชจ๋ธ๋ก ์ ์ธํ๋ฉด ๊ฒ์ฆ, ๋ณํ, ๋ฌธ์ํ๊ฐ ์๋์ผ๋ก ์ฒ๋ฆฌ๋๋ค.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class CreateUserRequest(BaseModel):
name: str = Field(min_length=1, max_length=50)
age: int = Field(ge=0, le=150)
email: str
class UserResponse(BaseModel):
id: int
name: str
email: str
@app.post("/users", response_model=UserResponse)
async def create_user(req: CreateUserRequest):
# req๋ ์ด๋ฏธ ๊ฒ์ฆ/๋ณํ์ด ์๋ฃ๋ pydantic ๋ชจ๋ธ
# name์ด ๋น ๋ฌธ์์ด์ด๊ฑฐ๋ age๊ฐ ์์๋ฉด ์ฌ๊ธฐ๊น์ง ์ค์ง ์๋๋ค (422 ์๋ฌ)
return UserResponse(id=1, name=req.name, email=req.email)์ด ์ฝ๋๋ง์ผ๋ก ๋ค์์ด ์๋์ผ๋ก ๋์ํ๋ค:
- ์์ฒญ JSON โ
CreateUserRequest๊ฒ์ฆ + ๋ณํ - ๊ฒ์ฆ ์คํจ ์ 422 Unprocessable Entity ์๋ต (์๋ฌ ๋ฉ์์ง ํฌํจ)
- ์๋ต์
UserResponse์คํค๋ง๋ก ์ง๋ ฌํ - Swagger UI(
/docs)์ ์์ฒญ/์๋ต ์คํค๋ง ์๋ ํ์
Query Parameter ๊ฒ์ฆ
body๋ฟ ์๋๋ผ query parameter์๋ pydantic ๊ฒ์ฆ์ ์ ์ฉํ ์ ์๋ค.
from fastapi import Query
@app.get("/users")
async def list_users(
page: int = Query(ge=1, default=1),
size: int = Query(ge=1, le=100, default=20),
sort: str = Query(pattern=r"^(name|age|created)$", default="name"),
):
return {"page": page, "size": size, "sort": sort}9. dataclass vs pydantic ์ ๋ฆฌ
| ๊ธฐ์ค | dataclass | pydantic |
|---|---|---|
| ์ค์น | ๋ถํ์ (ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ) | pip install pydantic |
| ํ์ ๊ฒ์ฆ | X (ํํธ๋ง) | O (๋ฐํ์ ๊ฒ์ฆ + ์๋ ๋ณํ) |
| ๊ธฐ๋ณธ๊ฐ (mutable) | field(default_factory=list) | [] ๊ทธ๋๋ก ๊ฐ๋ฅ |
| ํ๋ ์ ์ฝ ์กฐ๊ฑด | __post_init__์์ ์ง์ ๊ตฌํ | Field(ge=0, max_length=50) |
| ์ปค์คํ ๊ฒ์ฆ | __post_init__ | @field_validator, @model_validator |
| dict ๋ณํ | asdict() | .model_dump() |
| JSON ๋ณํ | asdict() + json.dumps() | .model_dump_json() |
| JSON โ ๊ฐ์ฒด | ์๋ ๋งคํ | .model_validate_json() |
| ์ค์ฒฉ ๊ฐ์ฒด ํ์ฑ | ์๋ | ์๋ |
| JSON Schema | X | .model_json_schema() |
| ๋ถ๋ณ ๋ชจ๋ธ | frozen=True | model_config = ConfigDict(frozen=True) |
| ์ฑ๋ฅ | ๋น ๋ฆ (boilerplate ์์ฑ์ผ ๋ฟ) | v2์์ ํฌ๊ฒ ๊ฐ์ (Rust ๊ธฐ๋ฐ ์ฝ์ด) |
| FastAPI ์ฐ๋ | X | ๊ธฐ๋ณธ ํตํฉ |
ํ๋จ ๊ธฐ์ค
์ธ๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ ๊ฒ์ฆํด์ผ ํ๋๊ฐ?
โโโ Yes โ pydantic
โ (API ์
๋ ฅ, JSON ํ์ฑ, ํ๊ฒฝ๋ณ์, ์ฌ์ฉ์ ์
๋ ฅ)
โโโ No โ ๋ด๋ถ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ์กฐํ๋ง ํ๋ฉด ๋๋๊ฐ?
โโโ Yes โ dataclass (๊ฐ๋ณ๊ณ ํ์ค)
โโโ ๊ฒ์ฆ๋ ์ฝ๊ฐ ํ์ โ pydantic ๋๋ attrs
๊ฐ์ด ์ฐ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค
ํ๋์ ํ๋ก์ ํธ์์ ๋์ ์์ด ์ฐ๋ ๊ฒ์ ์์ฐ์ค๋ฝ๋ค. API ๊ฒฝ๊ณ(์ ์ถ๋ ฅ)์์๋ pydantic์ผ๋ก ๊ฒ์ฆํ๊ณ , ๋ด๋ถ ๋ก์ง์์๋ dataclass๋ก ๊ฐ๋ณ๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ํจํด์ด ์ผ๋ฐ์ ์ด๋ค.