Skip to content

์ถ”๊ฐ€ ๋ชจ๋ธ

์ด์ „์˜ ์˜ˆ์ œ์— ์ด์–ด์„œ, ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ํ•œ ๊ฐ€์ง€ ์ด์ƒ์˜ ๊ด€๊ณ„ ๋ชจ๋ธ์„ ๊ฐ€์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ๋ชจ๋ธ์˜ ๊ฒฝ์šฐ๊ฐ€ ํŠนํžˆ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด:

  • ์ž…๋ ฅ ๋ชจ๋ธ์€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ถœ๋ ฅ ๋ชจ๋ธ์€ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ชจ๋ธ์€ ํ•ด์‹œ ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„ํ—˜

์‚ฌ์šฉ์ž์˜ ํ‰๋ฌธ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ ˆ๋Œ€ ์ €์žฅํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ํ•ญ์ƒ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋Š” "์•ˆ์ „ํ•œ ํ•ด์‹œ"๋กœ ์ €์žฅํ•˜์‹ญ์‹œ์˜ค.

๋งŒ์•ฝ ๋‹น์‹ ์ด ๋ชจ๋ฅธ๋‹ค๋ฉด ๋ณด์•ˆ ์ฑ•ํ„ฐ์—์„œ "๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ"๊ฐ€ ๋ฌด์—‡์ธ์ง€ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณตํ•ฉ ๋ชจ๋ธ๋“ค

๋ชจ๋ธ๋“ค์ด ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•„๋“œ์™€ ๊ทธ๊ฒƒ์ด ์‚ฌ์šฉ๋˜๋Š” ์œ„์น˜์—์„œ ์–ด๋–ป๊ฒŒ ๋ณด์ผ ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•œ ์ผ๋ฐ˜์ ์ธ ์•„์ด๋””์–ด๋ฅผ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserInDB(BaseModel):
    username: str
    hashed_password: str
    email: EmailStr
    full_name: Union[str, None] = None


def fake_password_hasher(raw_password: str):
    return "supersecret" + raw_password


def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
    print("User saved! ..not really")
    return user_in_db


@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
    user_saved = fake_save_user(user_in)
    return user_saved

**user_in.dict()์— ๊ด€ํ•ด

Pydantic์—์„œ์˜ .dict()

user_in์€ UserIn ํด๋ž˜์Šค์˜ Pydantic ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.

Pydantic ๋ชจ๋ธ์€ ๋ชจ๋ธ ๋ฐ์ดํ„ฐ์˜ dict๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” .dict() ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ, ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ Pydantic ๊ฐ์ฒด์ธ user_in์„ ์ƒ์„ฑํ•ด๋ณด๋ฉด:

user_in = UserIn(username="john", password="secret", email="john.doe@example.com")

๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” ์ด๋ ‡๊ฒŒ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค:

user_dict = user_in.dict()

์šฐ๋ฆฌ๋Š” user_dict(Pydantic ๋ชจ๋ธ ๊ฐ์ฒด ๋Œ€์‹  dict ์ž…๋‹ˆ๋‹ค) ๋ณ€์ˆ˜ ๋‚ด๋ถ€์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ dict๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ์ด๋ ‡๊ฒŒ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด:

print(user_dict)

์šฐ๋ฆฌ๋Š” ํŒŒ์ด์ฌ dict๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

{
    'username': 'john',
    'password': 'secret',
    'email': 'john.doe@example.com',
    'full_name': None,
}

dict๋ฅผ ํ’€๊ธฐ

์šฐ๋ฆฌ๊ฐ€ user_dict๊ฐ™์€ dict๋ฅผ ์–ป์–ด์˜ค๊ฑฐ๋‚˜ **user_dict์˜ ํ˜•ํƒœ๋กœ ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๋กœ ์ „๋‹ฌํ• ๋•Œ, ํŒŒ์ด์ฌ์€ ์ด๊ฒƒ์„ "ํ’€๊ธฐ(unwrap)"๋ฅผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ user_dict์˜ ํ‚ค์™€ ๊ฐ’์„ ํ‚ค-๊ฐ’ ์ธ์ž(arguments)๋กœ ์ง์ ‘์ ์œผ๋กœ ๋„˜๊น๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ, ์œ„์—์„œ ์„ค๋ช…ํ–ˆ๋˜ user_dict๋กœ ๊ณ„์† ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค:

UserInDB(**user_dict)

์•„๋ž˜์™€ ๋™๋“ฑํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

UserInDB(
    username="john",
    password="secret",
    email="john.doe@example.com",
    full_name=None,
)

๋˜๋Š” ๋” ์ •ํ™•ํ•˜๊ฒŒ๋Š” ๋‚˜์ค‘์— ์–ด๋–ค๋‚ด์šฉ์„ ๋‹ด๊ฒŒ ๋˜๋”๋ผ๋„ user_dict๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค:

UserInDB(
    username = user_dict["username"],
    password = user_dict["password"],
    email = user_dict["email"],
    full_name = user_dict["full_name"],
)

๋˜ ๋‹ค๋ฅธ ๋‚ด์šฉ์œผ๋กœ๋ถ€ํ„ฐ Pydantic ๋ชจ๋ธ

์œ„์˜ ์˜ˆ์ œ์—์„œ๋Š” user_in.dict()์—์„œ user_dict๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค:

user_dict = user_in.dict()
UserInDB(**user_dict)

์ด๊ฒƒ๊ณผ ๋™๋“ฑํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค:

UserInDB(**user_in.dict())

... ์™œ๋ƒํ•˜๋ฉด user_in.dict()๋Š” dict์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” UserInDB ์•ž์— **๋ฅผ ๋ถ™์—ฌ์„œ ์ „๋‹ฌํ•จ์œผ๋กœ์จ Python์ด "ํ’€๊ธฐ"๋ฅผ ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ, ์šฐ๋ฆฌ๋Š” ๋˜๋‹ค๋ฅธ Pydantic ๋ชจ๋ธ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ์—์„œ Pydantic ๋ชจ๋ธ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

dict๋ฅผ ํ’€๊ธฐ ๊ทธ๋ฆฌ๊ณ  ์ถ”๊ฐ€ ํ‚ค์›Œ๋“œ๋“ค

๊ทธ๋ฆฌ๊ณ  ์ถ”๊ฐ€ ํ‚ค์›Œ๋“œ ์†์„ฑ์ธ hashed_password=hashed_password์„ ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค:

UserInDB(**user_in.dict(), hashed_password=hashed_password)

... ๊ฒฐ๊ตญ์—๋Š” ์ด๋ ‡๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค:

UserInDB(
    username = user_dict["username"],
    password = user_dict["password"],
    email = user_dict["email"],
    full_name = user_dict["full_name"],
    hashed_password = hashed_password,
)

๊ฒฝ๊ณ 

์ถ”๊ฐ€์ ์ธ ํ•จ์ˆ˜ ์ง€์›์€ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ๋ณด์—ฌ์ฃผ๋Š” ๋ฐ๋ชจ์ผ ๋ฟ์ด๋ฉฐ, ๋‹น์—ฐํžˆ ๊ทธ ์–ด๋–ค ์‹ค์ œ ๋ณด์•ˆ๋„ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ค‘๋ณต ์ค„์ด๊ธฐ

์ค‘๋ณต ์ฝ”๋“œ ์ค„์ด๊ธฐ๋Š” FASTAPI์˜ ํ•ต์‹ฌ ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

์ฝ”๋“œ ์ค‘๋ณต์€ ๋ฒ„๊ทธ์˜ ๊ฐ€๋Šฅ์„ฑ, ๋ณด์•ˆ ์ด์Šˆ, ์ฝ”๋“œ ๋น„๋™๊ธฐํ™” ๋ฌธ์ œ(ํ•œ ์žฅ์†Œ์—๋Š” ์—…๋ฐ์ดํŠธ ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ๊ณณ์—์„œ๋Š” ๊ทธ๋ ‡์ง€ ๋ชปํ• ๋•Œ) ๋“ฑ์„ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด๋Ÿฌํ•œ ๋ชจ๋ธ๋“ค์€ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๊ณ  ํƒ€์ž…๊ณผ ์ธ์ž ์ด๋ฆ„๋“ค์„ ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋” ์ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๋ชจ๋ธ๋“ค์˜ ๊ธฐ์ดˆ๊ฐ€ ๋˜๋Š” UserBase ๋ชจ๋ธ์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” ์ด ์ธ์ž๋“ค(ํƒ€์ž… ์„ ์–ธ, ๊ฒ€์ฆ ๋“ฑ)์„ ์ƒ์†๋ฐ›์€ ๋ชจ๋ธ์ธ ์„œ๋ธŒํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™˜, ๊ฒ€์ฆ, ๋ฌธ์„œํ™” ๋“ฑ์€ ์—ฌ์ „ํžˆ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์šฐ๋ฆฌ๋Š” ๋ชจ๋ธ๋“ค ์‚ฌ์ด์˜ ์ฐจ์ด๋งŒ์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.(ํ‰๋ฌธ password์™€ ํ•จ๊ป˜, hashed_password์™€ ํ•จ๊ป˜, ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์—†์ด)

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserBase(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserIn(UserBase):
    password: str


class UserOut(UserBase):
    pass


class UserInDB(UserBase):
    hashed_password: str


def fake_password_hasher(raw_password: str):
    return "supersecret" + raw_password


def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
    print("User saved! ..not really")
    return user_in_db


@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
    user_saved = fake_save_user(user_in)
    return user_saved

Union ๋˜๋Š” anyOf

Union์„ ํ†ตํ•ด ๋‘ ๊ฐ€์ง€ ํƒ€์ž…์˜ ์‘๋‹ต์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๋ง์€, ์‘๋‹ต์€ ๊ทธ ๋‘ ๊ฐ€์ง€ ์ค‘ ์•„๋ฌด๊ฑฐ๋‚˜ ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

์ด๋Š” OpenAPI์—์„œ anyOf๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ‘œ์ค€ ํŒŒ์ด์ฌ ํƒ€์ž… ํžŒํŠธ typing.Union๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค:

์ฐธ๊ณ 

Union์„ ์„ ์–ธํ•  ๋•Œ, ๊ฐ€์žฅ ํŠน์ •ํ•œ ํƒ€์ž…์„ ์ฒซ ๋ฒˆ์งธ๋กœ ํฌํ•จํ•˜๊ณ , ๋œ ํŠน์ •ํ•œ ํƒ€์ž…์„ ๋’ค์— ๋ถ™์ด์‹ญ์‹œ์˜ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Union[PlaneItem, CarItem]์—์„œ ๋” ํŠน์ •ํ•œ PlaneItem์€ CarItem๋ณด๋‹ค ๋” ์•ž์— ์˜ต๋‹ˆ๋‹ค.

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class BaseItem(BaseModel):
    description: str
    type: str


class CarItem(BaseItem):
    type = "car"


class PlaneItem(BaseItem):
    type = "plane"
    size: int


items = {
    "item1": {"description": "All my friends drive a low rider", "type": "car"},
    "item2": {
        "description": "Music is my aeroplane, it's my aeroplane",
        "type": "plane",
        "size": 5,
    },
}


@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
async def read_item(item_id: str):
    return items[item_id]

๋ชจ๋ธ๋“ค์˜ ๋ฆฌ์ŠคํŠธ

๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ, ๊ฐ์ฒด์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ์‘๋‹ต์œผ๋กœ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ์œ„ํ•ด์„œ ํ‘œ์ค€ ํŒŒ์ด์ฌ typing.List๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค:

from typing import List

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str


items = [
    {"name": "Foo", "description": "There comes my hero"},
    {"name": "Red", "description": "It's my aeroplane"},
]


@app.get("/items/", response_model=List[Item])
async def read_items():
    return items

์ž„์˜์˜ dict๋กœ ์‘๋‹ตํ•˜๊ธฐ

Pydantic ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋‹จ์ˆœํžˆ ํ‚ค์™€ ๊ฐ’์˜ ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•˜๋Š” ํ‰๋ฒ”ํ•œ ์ž„์˜์˜ dict๋กœ ์‘๋‹ต์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฏธ๋ฆฌ ์œ ํšจํ•œ ํ•„๋“œ์™€ ์†์„ฑ์˜ ์ด๋ฆ„(์ด๊ฒƒ์€ ์•„๋งˆ Pydantic ๋ชจ๋ธ์— ํ•„์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค)์„ ์•Œ์ง€ ๋ชปํ• ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด ์ƒํ™ฉ์—์„œ typing.Dict๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.:

from typing import Dict

from fastapi import FastAPI

app = FastAPI()


@app.get("/keyword-weights/", response_model=Dict[str, float])
async def read_keyword_weights():
    return {"foo": 2.3, "bar": 3.4}

์ •๋ฆฌ

๊ฐ๊ฐ์˜ ์ƒํ™ฉ์—์„œ ๋‹ค์ˆ˜์˜ Pydantic ๋ชจ๋ธ๋“ค์„ ์‚ฌ์šฉํ•˜๊ณ  ์ž์œ ๋กญ๊ฒŒ ์ƒ์†ํ•˜์‹ญ์‹œ์˜ค.

๋งŒ์•ฝ ๊ฐœ์ฒด๊ฐ€ ๋‹ค๋ฅธ "์ƒํƒœ"๋ฅผ ๊ฐ€์งˆ ์ˆ˜๋ฐ–์— ์—†๋‹ค๋ฉด, ๊ตณ์ด ํ•˜๋‚˜์˜ ๊ฐœ์ฒด๋‹น ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์„ ๊ฐ€์งˆ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. password, password_hash๋Š” ์žˆ์œผ๋‚˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์—†๋Š” ์‚ฌ์šฉ์ž "entity" ์ƒํ™ฉ์ด ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.