Fast API - ๊ธฐ์ด
๋ง์ ํ๋ ์ ์ํฌ๋ฅผ ์ฌ์ฉํด์์ง๋ง, ๊น์ด๊ฐ ๋ถ์กฑํ๋ค๊ณ ํญ์ ์๊ฐํ๋ค. ์ด์ ๋ ๊ทธ๊ฒ๋ค์ ๋ํ ๊น์ด๋ฅผ ๋ ์ฑ์๋ณด๊ณ ์ ํ๋ค.
๋ฌด์๋ณด๋ค ์ด๋ค ์ง๋ฌธ์ ๊ดํด์๋ ์ด์ ๋ฅผ ๋ฌผ์ ๋ ๋ฐ๋ก ๋ต ํ ์ ์๋ ์ค๋ ฅ์ ๊ฐ๊ธฐ๋ฅผ ์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ฐ์ฅ ๋จผ์ ๊น๊ฒ ํ๋ณผ ํ๋ ์์ํฌ๋ Fast API์ด๋ค.
์ด์ ๋ Python์ ์ฃผ๋ ฅ์ผ๋ก ํ๊ณ , ์ ์ผ ๋ง์ด ๋งก์ ์จ ์ญํ ์ด ๋ฐฑ์ค๋์ด๊ธฐ์, ๋น ๋ฅด๊ฒ ์ ๋ฆฌํ๊ณ ๊น์ด๋ฅผ ๋๋ฆด ์ ์๋ค๊ณ ํ๋จํ๋ค.
Fast API๋ฅผ ์ค์ฌ์ผ๋ก ํ์ฅํ๋ฉฐ ์ ๋ฆฌํ๊ฒ ๋ค.
๊ธฐ๋ณธ ๊ฐ๋
Fast API๋ Python์ async / await ๋ฌธ๋ฒ์ ๊ธฐ๋ฐ์ผ๋ก ํ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ง์
-> ์ด ๋๋ถ์ ์ฌ๋ฌ ์์ฒญ์ ๋์์ ์ฒ๋ฆฌํ ์ ์์ด, ๋์ ํธ๋ํฝ ์ํฉ์์๋ ๋น ๋ฅธ ์๋ต์ ์ ์งํ ์ ์๋ค.
Flask์ฒ๋ผ ๋๊ธฐ ํ๋ก๊ทธ๋จ์ ๊ฒฝ์ฐ ํ ๋ฒ์ ํ๋์ ์์ ๋ง ์ฒ๋ฆฌํ๊ธฐ์ ๋ถ์กฑ
"ํ ๋ฒ์ ํ๋์ ์์
"๋ง ์ฒ๋ฆฌํ๋ ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์, ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
(์ธ๋ถ API ํธ์ถ, DB ๋๊ธฐ)์ด ์์ผ๋ฉด ๊ทธ๋์ ๋ค๋ฅธ ์์ฒญ์ ๋๊ธฐํ๊ฒ ๋๋ค.
๋ฐ๋ฉด FastAPI๋ ๋น๋๊ธฐ(async) ๋ฐฉ์์ ์ฑํํ์ฌ, ์ธ๋ถ ์๋ต์ ๊ธฐ๋ค๋ฆฌ๋ ๋์์๋ ๋ค๋ฅธ ์์ฒญ์ ๋์์ ์ฒ๋ฆฌํ ์ ์์ด → ์ฒ๋ฆฌ ์๋๊ฐ ๋น ๋ฅด๊ณ , ์๋ฒ ๋ฆฌ์์ค ํจ์จ์ด ๋๋ค.
ํ๋ ์์ํฌ | ์ธ์คํด์ค ์์ฑ ๋ฐฉ์ | ์์ ์ฝ๋ |
FastAPI | app = FastAPI() | from fastapi import FastAPI app = FastAPI() |
Flask | app = Flask(name) | from flask import Flask app = Flask(name) |
Django | ์ง์ ์ธ์คํด์ค ์์ฑ X ํ๋ก์ ํธ ๊ตฌ์กฐ์ ์ค์ ํ์ผ๋ก ๊ด๋ฆฌ |
django-admin startproject ... manage.py runserver |
์ค์จ๊ฑฐ ๋ณด์
from fastapi import FastAPI
app = FastAPI() # Fastapi ์ธ์คํด์ค ์์ฑ
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/hello/{name}")
async def say_hello(name: str):
return {"message": f"Hello {name}"}
์๋ฒ ์คํ
๋ก์ปฌ ์คํ: ASGI ๊ตฌํ์ฒด ์ค ๋น๋๊ธฐ ์น ์๋ฒ์ธ Uvicorn
1. ASGI๋?
- ASGI(Asynchronous Server Gateway Interface)๋ ํ์ด์ฌ ์น ์๋ฒ์ ์น ํ๋ ์์ํฌ(์: FastAPI, Django) ์ฌ์ด์ "๋น๋๊ธฐ ํต์ "์ ์ํ ํ์ค ์ธํฐํ์ด์ค
- ๊ธฐ์กด์ WSGI(Web Server Gateway Interface)๋ ๋๊ธฐ ๋ฐฉ์๋ง ์ง์ํด์ ์ค์๊ฐ ์ฒ๋ฆฌ, ์น์์ผ, ๋น๋๊ธฐ ์์ฒญ ๋ฑ ํ๋์ ์น ๊ธฐ๋ฅ์ ํ๊ณ
2. Uvicorn์ด๋?
- Uvicorn์ ASGI ํ์ค์ ๊ตฌํํ ๋ํ์ ์ธ ํ์ด์ฌ ๋น๋๊ธฐ ์น ์๋ฒ
- FastAPI, Starlette, ์ต์ Django(ASGI ๋ชจ๋) ๋ฑ๊ณผ ํจ๊ป ์ฌ์ฉ๋๋ฉฐ, ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ํ์ํ ํ๋์ ์น ์๋น์ค์ ์ ํฉ
Flask์์ ์ฌ์ฉํ Gunicorn์ WSGI ์๋ฒ
uvicorn main:app --reload
- main = main.py
- app: app=FastAPI(), FastAPI ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๊ฐ์ฒด์ ๋ณ์ ์ด๋ฆ
- --reload: ์ฝ๋ ์์ ์ ์๋ฒ ์๋ ์ฌ์์
ํญ๋ชฉ | Swagger | ReDoc |
๊ธฐ๋ฅ ๋ชฉ์ | ๊ฐ๋ฐ ๋ฐ ํ ์คํธ ์ค์ฌ | ๋ฌธ์ํ ๋ฐ ๋ฐฐํฌ ์ค์ฌ |
๋์์ธ | ์ง๊ด์ ์ด๋ฉฐ ๊ธฐ๋ฅ ์์ฃผ UI | ์ ๋๋ ๋ฌธ์ ์คํ์ผ UI |
Try it out ๊ธฐ๋ฅ | ์์ → API ์ง์ ํ ์คํธ ๊ฐ๋ฅ | ์์ → ์ฝ๊ธฐ ์ ์ฉ ๋ฌธ์ |
์ฌ์ฉ์์ธต | ์ฃผ๋ก ๊ฐ๋ฐ์ | ๊ฐ๋ฐ์ + ๋น๊ฐ๋ฐ์(๊ธฐํ์ ๋ฑ) |
FastAPI ๊ธฐ๋ณธ ๊ฒฝ๋ก | /docs | /redoc |
๊ธฐ๋ฐ ์คํ | OpenAPI (Swagger) | OpenAPI (ReDoc ๊ธฐ๋ฐ) |
- /docs (Swagger UI): API๋ฅผ ์ง์ ์คํํด๋ณด๋ฉฐ ํ ์คํธ ๊ฐ๋ฅ → ๊ฐ๋ฐ ์ค์ ์ ์ฉํจ
- /redoc: ๊น๋ํ๊ฒ ์ ๋ฆฌ๋ ๋ฌธ์ ์ ๊ณต → ๋ฐฐํฌ ํ ์ธ๋ถ ๊ณต๊ฐ์ฉ์ ์ ํฉ
Swagger
Paths | ๊ฐ API ์๋ํฌ์ธํธ (GET, POST ๋ฑ) |
Parameters | Path, Query, Header, Cookie ๋ฑ์ ๋ค์ด๊ฐ๋ ํ๋ผ๋ฏธํฐ |
Request body | ์์ฒญ ์ ์ ์กํ JSON ์คํค๋ง (pydantic ๋ชจ๋ธ ๊ธฐ๋ฐ) |
Responses | ์๋ต ์ฝ๋๋ณ ๋ฐํ ๊ฐ ์์ ๋ฐ ์คํค๋ง |
Schemas | ์ ์ฒด API์์ ์ฐ์ด๋ ๋ฐ์ดํฐ ๋ชจ๋ธ ๊ตฌ์กฐ ์ ์ |
Try it out | ์ง์ ํ๋ผ๋ฏธํฐ ์ ๋ ฅํ๊ณ ์์ฒญ ๋ณด๋ด๊ธฐ ๊ฐ๋ฅ |
๋ผ์ฐํ
์ ์: ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ์ค๋ HTTP ์์ฒญ์ ์ ์ ํ ํจ์(handler)๋ก ์ฐ๊ฒฐํ๋ ๊ฒ
์ฆ, GET /users/123 ์์ฒญ์ด ์ค๋ฉด ์ด๋ฅผ ์ฒ๋ฆฌํ FastAPI ํจ์์ ์ฐ๊ฒฐํ๋ ๊ณผ์
๊ฒฝ๋ก ๋งค๊ฐ๋ณ์
- URL ๊ฒฝ๋ก์ ์ผ๋ถ๋ฅผ ๋ณ์์ฒ๋ผ ์ฌ์ฉ
- ๋์ ์ธ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ ๋ ์ฌ์ฉ
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"user_id": user_id}
- ์์ฒญ: GET /users/7
- ๊ฒฐ๊ณผ: { "user_id": 7 }
์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์
- URL์์ ? ์ดํ์ ๋์ค๋ key=value ํํ์ ๋งค๊ฐ๋ณ์
@app.get("/search")
def search_items(q: str = None):
return {"query": q}
- ์์ฒญ: GET /search?q=apple
- ๊ฒฐ๊ณผ: { "query": "apple" }
ํ์ ํํธ
๋ณ์๋ ํจ์์ ๋งค๊ฐ๋ณ์๋ ๋ฐํ๊ฐ์ ํ์ ์ ๋ช ์ํด์ฃผ๋ ๋ฌธ๋ฒ์ผ๋ก, ์ฝ๋ ๊ฐ๋ ์ฑ ํฅ์ + FastAPI์์๋ ์๋ ๋ฌธ์ํ ๋ฐ ์ ํจ์ฑ ๊ฒ์ฌ์ ์ฌ์ฉ
from typing import Dict
from fastapi import FastAPI
app = FastAPI()
@app.post("/create-item/")
def create_item(item: Dict[str, int]):
return item
from typing import List
@app.post("/items/")
def create_items(items: List[str]):
return {"received": items}
ํ์ด์ฌ ๋ฐ์ดํฐ ํ์
1. ๊ธฐ๋ณธ ์๋ฃํ
int | 1, -5, 100 | ์ ์ํ |
float | 3.14, -0.1 | ์ค์ํ (๋ถ๋์์์ ) |
complex | 1+2j | ๋ณต์์ํ |
bool | True, False | ๋ถ๋ฆฌ์ธํ |
str | "hello", 'a' | ๋ฌธ์์ดํ |
2. ์ํ์ค ํ์
list | [1, 2, 3] | ๊ฐ๋ณํ ์์ฐจ ์๋ฃํ |
tuple | (1, 2) | ๋ถ๋ณํ ์์ฐจ ์๋ฃํ |
range | range(5) | ์ ์ ์ํ์ค (๋ฐ๋ณต์ ์ฌ์ฉ) |
3. ๋งคํ ํ์
dict | {"a": 1} | ํค-๊ฐ ์์ ์งํฉ |
4. ์งํฉ ํ์
set | {1, 2, 3} | ์ค๋ณต ์๋ ์งํฉ |
frozenset | frozenset([1,2]) | ๋ถ๋ณ ์งํฉ |
5. ๋ฐ์ด๋๋ฆฌ ํ์
bytes | b"abc" | ๋ฐ์ดํธ ๋ฐ์ดํฐ |
bytearray | bytearray(5) | ๊ฐ๋ณ ๋ฐ์ดํธ |
memoryview | memoryview(b"abc") | ๋ฉ๋ชจ๋ฆฌ ๋ฒํผ ๋ทฐ |
6. None ํ์
NoneType | None | ๊ฐ์ด ์์์ ์๋ฏธ |
7. ํ์ ํํธ์ฉ ํ์ (typing ๋ชจ๋)
Any | ์ด๋ค ํ์ ์ด๋ ๊ฐ๋ฅ |
Union[int, str] | int ๋๋ ๋ฌธ์์ด (์ฌ๋ฌ ํ์ ์ค ํ๋) |
Optional[int] | int ๋๋ None (๊ฐ์ด ์๊ฑฐ๋ None) |
List[str] | ๋ฌธ์์ด ๋ฆฌ์คํธ |
Dict[str, int] | ๋ฌธ์์ด-์ ์ ๋์ ๋๋ฆฌ |
Tuple[int, str] | ๊ณ ์ ๋ ๊ตฌ์กฐ์ ํํ |
Callable[[int,int],int] | ๋ ์ ์ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ๊ณ ์ ์๋ฅผ ๋ฐํ (ํจ์ ํ์ ) |
Literal["A", "B"] | ํน์ ๊ฐ๋ค๋ง ํ์ฉ |
TypedDict | ๊ตฌ์กฐ ์ ์๋ ๋์ ๋๋ฆฌ |
Annotated | ํ์ + ๋ฉํ๋ฐ์ดํฐ |
8. ์ฌ์ฉ์ ์ ์ ํ์
class User:
name: str
HTTP Method(NO Pydantic)
from fastapi import FastAPI, HTTPException
from typing import Dict, List
app = FastAPI()
# ๋ฉ๋ชจ๋ฆฌ ์์ ๊ฐ์ง ๋ฐ์ดํฐ ์ ์ฅ์(์ง๊ธ DB ์์ด ํ๋๊น)
items_db: Dict[int, Dict[str, str or int or float]] = {}
# 1. POST - ์์ดํ
์ถ๊ฐ
@app.post("/items/")
def create_item(item: Dict[str, str or int or float]):
item_id = item.get("id")
if item_id in items_db:
raise HTTPException(status_code=400, detail="Item already exists")
items_db[item_id] = item
return {"message": "Item created", "item": item}
# 2. GET - ์์ดํ
์ ์ฒด ์กฐํ
@app.get("/items/")
def get_all_items():
return list(items_db.values())
# 3. GET - ํน์ ์์ดํ
์กฐํ
@app.get("/items/{item_id}")
def get_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
return items_db[item_id]
# 4. PUT - ์์ดํ
์์
@app.put("/items/{item_id}")
def update_item(item_id: int, updated_item: Dict[str, str or int or float]):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
items_db[item_id] = updated_item
return {"message": "Item updated", "item": updated_item}
# 5. DELETE - ์์ดํ
์ญ์
@app.delete("/items/{item_id}")
def delete_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
del items_db[item_id]
return {"message": f"Item {item_id} deleted"}
์ฃผ์์
- ์ ํจ์ฑ ๊ฒ์ฌ ์์ → ํด๋ผ์ด์ธํธ๊ฐ ์๋ชป๋ ํ์ ์ ๋ณด๋ด๋ ์๋ฒ๊ฐ ๋ฐ์๋ฒ๋ฆด ์ ์์
- FastAPI์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ(๋ฌธ์ ์๋ํ, ํ์ ์ฒดํฌ ๋ฑ)์ ์ถฉ๋ถํ ํ์ฉํ๋ ค๋ฉด Pydantic ์ฌ์ฉ์ ๊ถ์ฅ
Pydantic
Python์ ํ์ ํํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ๋ ฅ ๊ฐ์ ์ ํจ์ฑ ๊ฒ์ฆ๊ณผ ์๋ ๋ณํ, ์ง๋ ฌํ, ์ญ์ง๋ ฌํ๋ฅผ ๋์์ฃผ๋ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ
FastAPI๋ Pydantic์ ์ฌ์ฉํด์ ๋ค์์ ์๋์ผ๋ก ์ฒ๋ฆฌ
๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ(Validation) | ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ธ ๋ฐ์ดํฐ์ ํ์ , ํ์, ํ์ ์ฌ๋ถ๋ฅผ ๊ฒ์ฌ | price: float์ ๋ฌธ์์ด "abc"์ด ๋ค์ด์ค๋ฉด ์ค๋ฅ ๋ฐํ |
๋ฐ์ดํฐ ์ง๋ ฌํ (Serialization) | ๋ด๋ถ Python ๊ฐ์ฒด๋ฅผ JSON ๋ฑ์ผ๋ก ์๋ ๋ณํ | Python์ Item ๊ฐ์ฒด → JSON ์๋ต์ผ๋ก ๋ณํ |
๋ฐ์ดํฐ ์ญ์ง๋ ฌํ (Parsing) | JSON → Python ๊ฐ์ฒด๋ก ์๋ ๋ณํ | JSON์ผ๋ก ์จ {"id": 1} → Item(id=1) ๊ฐ์ฒด ์์ฑ |
๋ฌธ์ํ ์๋ํ (Schema ์์ฑ) | Swagger UI(/docs)์์ ๋ชจ๋ธ ๊ตฌ์กฐ ์๋ ํ์ | ํ๋ ํ์ , ํ์ ์ฌ๋ถ ๋ฑ ๋ฌธ์ํ |
๊ธฐ๋ณธ๊ฐ ๋ฐ ํ์ ํ๋ ๊ด๋ฆฌ | ์ ํ/ํ์ ํ๋ ๊ตฌ๋ถ ๊ฐ๋ฅ | name: Optional[str] = None |
ํ์ ๋ณํ ์ง์ | "10" ๊ฐ์ ๋ฌธ์์ด ์ซ์ → int๋ก ์๋ ๋ณํ | age: int์ "10" ๋ฃ์ด๋ int ์ฒ๋ฆฌ |
- ์ง๋ ฌํ
- ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ฑฐ๋ ์ ์ฅํ ์ ์๋๋ก ํน์ ํ ํฌ๋งท(์: JSON, XML, CSV ๋ฑ)์ผ๋ก ๋ณํํ๋ ๊ณผ์ .
- ์ฆ, ํ์ด์ฌ ๊ฐ์ฒด์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์์ ๋ฐ์ดํฐ๋ฅผ ํ์ผ, ๋ฐ์ดํฐ๋ฒ ์ด์ค, ๋คํธ์ํฌ ๋ฑ์ผ๋ก ๋ด๋ณด๋ผ ์ ์๊ฒ ์ผ๋ จ์ ๋ฐ์ดํธ๋ ๋ฌธ์์ด๋ก ๋ฐ๊พธ๋ ๊ฒ์ ์๋ฏธํ๋ค. ๋ฐ๋๋ก, ์ง๋ ฌํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ํ์ด์ฌ ๊ฐ์ฒด๋ก ๋ณต์ํ๋ ๊ณผ์ ์ ์ญ์ง๋ ฌํ๋ผ๊ณ ํ๋ค.
ํญ๋ชฉ | Pydantic ์ฌ์ฉ (BaseModel) | Pydantic ๋ฏธ์ฌ์ฉ (dict, ํ์ ํํธ๋ง) |
์ ๋ ฅ๊ฐ ๊ฒ์ฆ | ํ์ /๊ฐ ์๋ ๊ฒ์ฆ, ์์ธ ์๋ฌ ๋ฉ์์ง ๋ฐํ | ์๋ ๊ฒ์ฆ ํ์, ์๋ฌ ์ฒ๋ฆฌ ์ง์ ๊ตฌํ |
์๋ ๋ณํ/ํ์ฑ | JSON → ๊ฐ์ฒด ์๋ ๋ณํ | ์ง์ ํ์ฑ/๋ณํ ์ฝ๋ ์์ฑ ํ์ |
์ง๋ ฌํ/์ญ์ง๋ ฌํ | dict(), json() ๋ฑ์ผ๋ก ๊ฐ์ฒด ๋ณํ ์๋ | ์๋ ๋ณํ ํ์, ์ผ๊ด์ฑ ๋จ์ด์ง |
Swagger ๋ฌธ์ ์๋ํ | ์ ๋ ฅ/์ถ๋ ฅ ๊ตฌ์กฐ, ์ ์ฝ์กฐ๊ฑด, ์์ ์๋ ๋ฐ์ | ๋จ์ ํ์ ๋ง ํ์, ๊ตฌ์กฐํ ์ด๋ ค์ |
๋ณต์กํ ๊ตฌ์กฐ ์ง์ | ์ค์ฒฉ, ๋ฆฌ์คํธ, ์ต์ ๋ฑ ๋ณต์กํ ๊ตฌ์กฐ ์ฝ๊ฒ ์ฒ๋ฆฌ | ๋จ์ผ ๊ฐ/๊ฐ๋จ ๊ตฌ์กฐ๋ง ์ฒ๋ฆฌ |
์ฝ๋ ์ ์ง๋ณด์/ํ์ฅ์ฑ | ๋ชจ๋ธ ์ค์ฌ ๊ด๋ฆฌ, ์ฌ์ฌ์ฉ/ํ์ฅ ์ฉ์ด | ํ๋ ๋ง์์ง๋ฉด ์ฝ๋ ๋ณต์ก, ๊ด๋ฆฌ ์ด๋ ค์ |
์๋ฌ ์๋ต | 422 ๋ฑ ํ์คํ๋ ์๋ฌ ๋ฐ ์์ธ ๋ฉ์์ง ์๋ ๋ฐํ | 500 ๋ฑ ์๋ฒ ์๋ฌ, ์์ธ ํผ๋๋ฐฑ ์ด๋ ค์ |
์ ํจ์ฑ ๊ฒ์ฆ ์ปค์คํฐ๋ง์ด์ฆ | validator, Field ๋ฑ์ผ๋ก ์ธ๋ฐํ๊ฒ ์ ์ด | ์ง์ if๋ฌธ ๋ฑ์ผ๋ก ์ฒ๋ฆฌ |
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# 1. ๋ฐ์ดํฐ ๋ชจ๋ธ ์ ์
class Item(BaseModel):
id: int
name: str
price: float
# 2. POST ์์ฒญ ์ฒ๋ฆฌ
@app.post("/items/")
def create_item(item: Item):
return {"message": "Item created", "item": item}
curl -X POST "http://localhost:8000/items/" \
-H "Content-Type: application/json" \
-d '{"id": 1, "name": "apple", "price": 3.5}'
{"message":"Item created","item":{"id":1,"name":"apple","price":3.5}}
curl Tset
- -H "accept: application/json"
- ์ค๋ช : ์๋ฒ์๊ฒ "๋๋ JSON ํ์์ ์๋ต์ ๋ฐ๊ณ ์ถ๋ค."๋ผ๊ณ ์์ฒญํ๋ HTTP ํค๋
- ์ญํ : ์๋ฒ๊ฐ ์ฌ๋ฌ ๊ฐ์ง ๋ฐ์ดํฐ ํ์(XML, HTML ๋ฑ)์ผ๋ก ์๋ตํ ์ ์์ ๋, ํด๋ผ์ด์ธํธ๊ฐ ์ํ๋ ํ์์ ์ง์ ํ๋ ์ญํ
- -H "Content-Type: application/json"
- ์ค๋ช : ์๋ฒ์๊ฒ "๋ด๊ฐ ๋ณด๋ด๋ ๋ฐ์ดํฐ๋ JSON ํ์์ด๋ค."๋ผ๊ณ ์๋ฆฌ๋ HTTP ํค๋
- ์ญํ : ์๋ฒ๊ฐ ์์ฒญ ๋ณธ๋ฌธ์ ๋ด๊ธด ๋ฐ์ดํฐ์ ํ์์ ์ฌ๋ฐ๋ฅด๊ฒ ํด์ํ ์ ์๋๋ก ํ๋ค.
- -d
- ์ค๋ช : curl ๋ช ๋ น์ด์์ -d๋ "data"์ ์ฝ์๋ก, ์๋ฒ๋ก ๋ณด๋ผ ๋ฐ์ดํฐ๋ฅผ ์ง์ ํ ๋ ์ฌ์ฉ
- ์ญํ : POST, PUT ๊ฐ์ ์์ฒญ์์ ๋ณธ๋ฌธ์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋ณด๋ผ ๋ ์ฌ์ฉ
curl์ “Client URL”์ ์ค์๋ง๋ก, ํฐ๋ฏธ๋/๋ช ๋ น์ด ๊ธฐ๋ฐ HTTP ์์ฒญ ๋๊ตฌ
- REST API๋ฅผ ํ ์คํธํ ๋ ๋งค์ฐ ์ ์ฉ
- FastAPI ์๋ฒ์ ์์ฒญ์ ์ง์ ๋ ๋ฆด ์ ์์
- GUI ์์ด ๋ช ๋ น์ค์์ ๋น ๋ฅด๊ฒ ํ์ธ ๊ฐ๋ฅ
@app.post("/items")
def create_item(item: dict):
return item
curl -X GET "http://localhost:8000/users/123"
curl -X GET "http://localhost:8000/search"
curl -X POST "http://localhost:8000/items" \
-H "Content-Type: application/json" \
-d '{"name": "banana", "price": 1000}'
{"name":"banana","price":1000}
Item์ด Pydantic ๋ชจ๋ธ id, name, price๊ฐ ํ๋
ํ๋ ์ ์ฝ์กฐ๊ฑด
์ ์ฝ์กฐ๊ฑด | ์ค๋ช | ํ์ |
min_length, max_length | ๋ฌธ์์ด ๊ธธ์ด ์ ํ | str |
gt, ge, lt, le | ์ซ์ ํฌ๊ธฐ ์กฐ๊ฑด(> >= < <=) | int, float |
regex | ์ ๊ทํํ์์ผ๋ก ๋ฌธ์์ด ํ์ ์ ํ | str |
min_items, max_items | ๋ฆฌ์คํธ์ ์ต์/์ต๋ ๊ธธ์ด ์ ํ | list |
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Product(BaseModel):
name: str = Field(..., min_length=3, max_length=50)
price: float = Field(..., gt=0) # 0๋ณด๋ค ์ปค์ผ ํจ
description: str = Field(default="", max_length=200)
tags: list[str] = Field(default=[], min_items=1)
@app.post("/products/")
def create_product(product: Product):
return {"message": "์ํ ๋ฑ๋ก ์๋ฃ", "product": product}
- Field(..., ...) : ...(Ellipsis)๋ Python์์ "ํ์๊ฐ"์ ์๋ฏธ
- Field(default=...) : ๊ธฐ๋ณธ๊ฐ ์ง์
- Field(default_factory=list) : ๋ฆฌ์คํธ์ ๊ธฐ๋ณธ๊ฐ์ ๋น ๋ฆฌ์คํธ๋ก ์์ ํ๊ฒ ์ง์
์๋ฌ: (ํ์๊ฐ ๋๋ฝ, price๊ฐ 0 ์ดํ, name์ด ์งง์ ๋ฑ) -> 422 Unprocessable Entity ์๋ฌ ๋ฐ์
๋ณต์กํ ๋ฐ์ดํฐํํ
1. ์ค์ฒฉ ๋ชจ๋ธ (Nested Models) - ๋ณต์กํ JSON ๊ตฌ์กฐ๋ฅผ ํํํ ๋ ๋ชจ๋ธ ์์ ๋ค๋ฅธ ๋ชจ๋ธ์ ๋ฃ๋ ๋ฐฉ์
๊ฐ๋
- ํด๋์ค ์์ ๋ค๋ฅธ Pydantic ๋ชจ๋ธ ํด๋์ค ์ฌ์ฉ
- ์ฌ์ฌ์ฉ์ฑ, ๊ฐ๋ ์ฑ, ์ ์ง๋ณด์
from pydantic import BaseModel
from typing import List
class Address(BaseModel):
city: str
zipcode: str
class User(BaseModel):
name: str
age: int
address: Address # ์ค์ฒฉ ์ฌ์ฉ
์์ฒญ ์
{
"name": "Alice",
"age": 30,
"address": {
"city": "Seoul",
"zipcode": "12345"
}
}
2. List, Union
- List: ์ฌ๋ฌ ๊ฐ์ ํญ๋ชฉ์ ๋ด๋ ๋ฆฌ์คํธ ๊ตฌ์กฐ
- Union: ์ฌ๋ฌ ํ์ ์ค ํ๋๋ฅผ ํ์ฉ
# List → "hobbies": ["reading", "sports"] ํํ ๊ฐ๋ฅ
class User(BaseModel):
name: str
hobbies: List[str]
# Union → "value": 123 ๋ ๋๊ณ "value": "abc" ๋ ๊ฐ๋ฅ
from typing import Union
class Item(BaseModel):
value: Union[int, str]
3. ์ ๋ค๋ฆญ(Generic)
"์๋ฌด ํ์ ์ด๋ ๋ค์ด์ฌ ์ ์์"์ ์๋ฏธ
์ฃผ๋ก ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ ์ฐํ ๊ตฌ์กฐ ๋ง๋ค ๋ ์ฌ์ฉ
๊ด๋ จ ํ์ : TypeVar, Generic
- ํ์
๋ณ์(TypeVar)
- TypeVar๋ ํ์ ํํธ์์ "์์ง ๊ตฌ์ฒด์ ์ผ๋ก ์ ํด์ง์ง ์์ ํ์ "์ ํํํ ๋ ์ฌ์ฉ
- ์๋ฅผ ๋ค์ด, ํจ์๋ ํด๋์ค๊ฐ ์ฌ๋ฌ ํ์ ์ ๋ฐ์์ผ ํ ๋, ํ์ ์ ์๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ์์ฝํด๋๋ ์ญํ
- ์ผ์ชฝ T: ์ค์ ์ฝ๋์์ ์ฌ์ฉํ ํ์ ๋ณ์ ์ด๋ฆ(ํ์ ํํธ๋ก ์ฌ์ฉ)
- ์ค๋ฅธ์ชฝ "T": ๋ด๋ถ์ ์ผ๋ก ์๋ณ์ ์ญํ ์ ํ๋ ๋ฌธ์์ด(์๋ฌด ๋ฌธ์์ด์ด๋ ๊ฐ๋ฅํ์ง๋ง, ๋ณดํต ๋ณ์๋ช ๊ณผ ๋ง์ถค)
T = TypeVar("T")
- ์ ๋๋ฆญ(Generic) ํ์
- ์ ๋๋ฆญ์ ํด๋์ค๋ ํจ์๋ฅผ "ํ์ ์ ๋ฐ๋ผ ์ ์ฐํ๊ฒ" ๋์ํ๊ฒ ๋ง๋ค์ด์ค๋ค.
- ์๋ฅผ ๋ค์ด, ๋ฆฌ์คํธ(List)๋ ์ด๋ค ํ์ ์ ๊ฐ์ด๋ ๋ด์ ์ ์๋ค. → List[int], List[str]
from typing import TypeVar, Generic
from pydantic.generics import GenericModel
T = TypeVar("T") # ์ด๋ค ํ์
์ด๋ ๊ฐ๋ฅ
# Response๋ ์ด๋ค ํ์
์ data๋ ๋ด์ ์ ์๋ ์ ๋๋ฆญ ๋ชจ๋ธ
# ์ค์ ์ฌ์ฉํ ๋๋ Response[int], Response[str], Response[User] ๋ฑ์ผ๋ก ๊ตฌ์ฒดํํด์ ์ฌ์ฉ
class Response(GenericModel, Generic[T]):
code: int
data: T
class User(BaseModel):
id: int
name: str
@app.get("/user", response_model=Response[User])
def get_user():
return Response(code=200, data=User(id=1, name="Alice"))
# → T๊ฐ User๋ก ๊ตฌ์ฒดํ๋จ. Response[User]๊ฐ ๋๋ ๊ตฌ์กฐ
{
"code": 200,
"data": {
"id": 1,
"name": "Alice"
}
}
์์ฒญ
HTTP ์์ฒญ์ ๊ธฐ๋ณธ ๊ตฌ์ฑ
- method: ์์ฒญ ๋ฐฉ์ (GET, POST, PUT, PATCH, DELETE)
- url: ์์ฒญ ์ฃผ์ (์: /users/1)
- headers: ์์ฒญ ํค๋ (์: ์ธ์ฆ, Content-Type ๋ฑ)
- body: ์์ฒญ ๋ณธ๋ฌธ (POST, PUT, PATCH ๋ฑ์์ ์ฃผ๋ก ์ฌ์ฉ)
FastAPI์์ ๋ฐ์ดํฐ ์ ๋ฌ ๋ฐฉ์
1. GET ์์ฒญ
- ๋ฐ์ดํฐ ์ ๋ฌ: URL ๊ฒฝ๋ก(Path), ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์(Query), ํค๋(Header)
from fastapi import FastAPI, Query, Path, Header
app = FastAPI()
@app.get("/users/{user_id}")
def read_user(
user_id: int = Path(..., description="User ID"),
q: str = Query(None, description="๊ฒ์์ด"),
token: str = Header(None)
):
return {"user_id": user_id, "q": q, "token": token}
- user_id: URL ๊ฒฝ๋ก์์ ์ถ์ถ (Path)
- q: ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์์์ ์ถ์ถ (Query)
- token: ํค๋์์ ์ถ์ถ (Header)
2. POST, PUT, PATCH ์์ฒญ
- ๋ฐ์ดํฐ ์ ๋ฌ: ์ฃผ๋ก ์์ฒญ ๋ณธ๋ฌธ(Body)
from fastapi import FastAPI, Body
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.post("/items/")
def create_item(
item: Item = Body(...), # ๋ณธ๋ฌธ ํ์
description: str = Body(None) # ๋ณธ๋ฌธ ์ ํ
):
return {"item": item, "description": description}
๊ตฌ๋ถ | ๋ฐฉ๋ฒ | ์์ |
๊ฒฝ๋ก ๋งค๊ฐ๋ณ์ | URL ๋ด ๋ณ์ | /items/{item_id} |
์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์ | ?key=value | /items?limit=10 |
์์ฒญ ๋ณธ๋ฌธ(JSON) | Pydantic ๋ชจ๋ธ๋ก ๋ฐ์ | POST, PUT์์ ์ฃผ๋ก ์ฌ์ฉ |
ํผ ๋ฐ์ดํฐ | Form ๊ฐ์ฒด ์ฌ์ฉ | ๋ก๊ทธ์ธ, ํ์๊ฐ์ |
ํ์ผ ์ ๋ก๋ | File ๊ฐ์ฒด ์ฌ์ฉ | ์ด๋ฏธ์ง, ๋ฌธ์ ์ ๋ก๋ |
ํค๋ | Header๋ก ๋ฐ์ | ์ธ์ฆ ํ ํฐ ๋ฑ |
์ฟ ํค | Cookie๋ก ๋ฐ์ | ์ธ์ ์ฒ๋ฆฌ ๋ฑ |
from fastapi import FastAPI, Form, File, Header, Cookie
@app.post("/login")
def login(username: str = Form(...), password: str = Form(...)):
return {"username": username}
@app.post("/upload")
def upload(file: bytes = File(...)):
return {"file_size": len(file)}
@app.get("/read-header")
def read_header(user_agent: str = Header(None)):
return {"User-Agent": user_agent}
์๋ต
์๋ต ๋ชจ๋ธ
- ์๋ต ๋ชจ๋ธ์ FastAPI์์ API ์๋ต ๋ฐ์ดํฐ์ ๊ตฌ์กฐ์ ํ์ ์ ๋ช ํํ๊ฒ ์ ์ธํ๋ ๊ธฐ๋ฅ
- Pydantic์ BaseModel์ ์์๋ฐ์ ์๋ต ๋ฐ์ดํฐ์ ๊ตฌ์กฐ๋ฅผ ๋ฏธ๋ฆฌ ์ ์ํ๋ค.
- FastAPI์ @app.get, @app.post ๋ฑ ๊ฒฝ๋ก ํจ์์์ response_model=๋ชจ๋ธ๋ช ์ผ๋ก ์ง์ ํ๋ค.
- ์ด ๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ์๋ต ๋ฐ์ดํฐ์ ํ์ ๋ณํ, ์ ํจ์ฑ ๊ฒ์ฌ, ์ง๋ ฌํ, ๋ฌธ์ํ๊ฐ ์๋์ผ๋ก ์ด๋ฃจ์ด์ง๋ค.
- ๋ชจ๋ธ์ ์ง์ ํ์ง ์์ผ๋ฉด ๋ฐํ๋ ๊ฐ์ฒด๋ฅผ ๊ทธ๋๋ก JSON์ผ๋ก ๋ณํํ์ฌ ํด๋ผ์ด์ธํธ์ ๋ฐํํ๊ณ , ๋ฐ์ดํฐ๊ฐ ๋ ธ์ถ๋ ์ ์๋ค.
๊ตฌ๋ถ | ๋จ์ response (dict ๋ฑ) | ์๋ต ๋ชจ๋ธ(response_model) |
๋ฐ์ดํฐ ๊ตฌ์กฐ | ์์ ๋กญ๊ฒ ๋ฐํ, ํ์ ์ ํ ์์ | ๋ช ํํ๊ฒ ์ ์ธํ ๊ตฌ์กฐ์ ํ์ ์ผ๋ก ๋ฐํ |
๊ฒ์ฆ/๋ณํ | ์์ | ํ์ ๋ณํ, ์ ํจ์ฑ ๊ฒ์ฌ ์๋ ์ํ |
๋ฌธ์ํ | ์๋ ๋ฌธ์ํ ๋ถ๊ฐ | OpenAPI ๋ฌธ์ ์๋ ์์ฑ, ์คํค๋ง ๋ช ํ |
ํ๋ ์ ํ | ๋ชจ๋ ๋ฐ์ดํฐ ๋ฐํ(๋ฏผ๊ฐ์ ๋ณด ๋ ธ์ถ ์ํ) | ๋ชจ๋ธ์ ์ ์๋ ํ๋๋ง ๋ฐํ(ํ์ํ ์ ๋ณด๋ง ๋ ธ์ถ) |
์ง๋ ฌํ | ์๋ ์ฒ๋ฆฌ ํ์ | Pydantic์ด ์๋ ์ง๋ ฌํ(JSON ๋ณํ ๋ฑ) |
1. ๊ธฐ๋ณธ ์๋ต ๋ชจ๋ธ
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
name: str
@app.get("/user", response_model=User)
def get_user():
# password๋ User ๋ชจ๋ธ์ ์์
return {"id": 1, "name": "Alice", "password": "secret"}
- ์ค์ ๋ฐํ๊ฐ์๋ password๊ฐ ์์ง๋ง, ํด๋ผ์ด์ธํธ๋ ์๋์ฒ๋ผ ๋ฐ์
{
"id": 1,
"name": "Alice"
}
2. Generic ์๋ต ๋ชจ๋ธ
- ์ฌ๋ฌ ํ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ธ๋ ๊ณตํต ์๋ต ๊ตฌ์กฐ๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉ
- TypeVar์ GenericModel์ ํ์ฉํด, ๋ค์ํ ํ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฐํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
from typing import TypeVar, Generic
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.generics import GenericModel
app = FastAPI()
T = TypeVar("T")
class Response(GenericModel, Generic[T]):
code: int
data: T
class User(BaseModel):
id: int
name: str
@app.get("/user", response_model=Response[User])
def get_user():
return Response(code=200, data=User(id=1, name="Alice"))
{"code": 200, "data": {"id": 1, "name": "Alice"}}
3. Union ์๋ต ๋ชจ๋ธ
- ํ ์๋ํฌ์ธํธ์์ ์ฌ๋ฌ ํ์ ์ ์๋ต์ ๋ฐํํ ์ ์๋๋ก ์ ์
- Union์ ํ์ฉํ๋ฉด, ์๋ต์ด ์ฌ๋ฌ ๋ชจ๋ธ ์ค ํ๋์ผ ์ ์์์ ๋ช ์ํ ์ ์๋ค.
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class SuccessResponse(BaseModel):
result: str
class ErrorResponse(BaseModel):
error: str
@app.get("/result", response_model=Union[SuccessResponse, ErrorResponse])
def get_result(success: bool = True):
if success:
return SuccessResponse(result="ok")
else:
return ErrorResponse(error="fail")
# /result?success=true ํธ์ถ ์
{"result": "ok"}
# /result?success=false ํธ์ถ ์
{"error": "fail"}
4. List ์๋ต ๋ชจ๋ธ
- ์ค๋ช :์ฌ๋ฌ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ฆฌ์คํธ๋ก ๋ฐํํ ๋ ์ฌ์ฉ
- response_model=List[Model] ๋๋ response_model=list[Model]๋ก ์ง์ ํ๋ค.
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.get("/items", response_model=List[Item])
def get_items():
return [
{"name": "Book", "price": 10.0},
{"name": "Pen", "price": 2.5},
]
[
{"name": "Book", "price": 10.0},
{"name": "Pen", "price": 2.5}
]
์๋ต ํด๋์ค
- ์๋ต ํด๋์ค๋ ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ๋ HTTP ์๋ต์ ํ์(ํ์ )์ ์ ์
- FastAPI์์ response_class ํ๋ผ๋ฏธํฐ๋ก ์ง์ ํ๊ฑฐ๋, ๊ธฐ๋ณธ๊ฐ(JSONResponse)์ ์ฌ์ฉ
- ๋ฐํ๊ฐ์ ํด๋น ์๋ต ํด๋์ค์ ๋ง๋ ํํ(์: JSON, HTML ๋ฑ)๋ก ์๋ ๋ณํํด ์ค๋ค.
JSON (๊ธฐ๋ณธ) | ์๋ ๋ณํ | ๋์ ๋๋ฆฌ → JSON |
HTML | HTMLResponse | HTML ํ ํ๋ฆฟ ๋ฐํ |
ํ ์คํธ | PlainTextResponse | ์ผ๋ฐ ํ ์คํธ ๋ฐํ |
๋ฆฌ๋ค์ด๋ ํธ | RedirectResponse | URL ์ด๋ ์ฒ๋ฆฌ |
ํ์ผ ๋ค์ด๋ก๋ | FileResponse | ํ์ผ ๋ฐํ |
์ง์ ์ค์ | JSONResponse | ์ํ์ฝ๋, ํค๋ ์๋ ์ค์ ๊ฐ๋ฅ |
# JSON
@app.get("/json")
def get_json():
return {"message": "Hello, JSON!"}
# HTML
from fastapi.responses import HTMLResponse
@app.get("/html", response_class=HTMLResponse)
def get_html():
return "<h1>Hello, HTML!</h1>"
# Plain Text
from fastapi.responses import PlainTextResponse
@app.get("/text", response_class=PlainTextResponse)
def get_text():
return "Just plain text"
# Redirect
from fastapi.responses import RedirectResponse
@app.get("/redirect")
def redirect():
return RedirectResponse(url="/new-location")
- ์๋ต ํด๋์ค: "์ด ์๋ต์ ์ด๋ค ํ์(HTML, JSON, ํ์ผ ๋ฑ)์ผ๋ก ์ค ๊ฒ์ธ๊ฐ?"
- ์๋ต ๋ชจ๋ธ: "์ด ์๋ต ๋ฐ์ดํฐ์ ๊ตฌ์กฐ(ํ๋, ํ์ )๋ ์ด๋ป๊ฒ ์๊ฒผ๋๊ฐ?"
์์ธ์ฒ๋ฆฌ
์์ธ์ฒ๋ฆฌ๋ฅผ ํตํด ์๋ฌ ์ํฉ์ ๋์ํ๊ณ , ์๋ฌ๋ฉ์์ง์ HTTP์ํ์ฝ๋๋ก API ์ ๋ํ ์ ๋ขฐ์ฑ๊ณผ UX๋ฅผ ํฅ์
์์ธ๊ฐ ๋ฐ์ํ ์์ ์์ ํ๋ก๊ทธ๋จ์ด ๋ฐ๋ก ์ข ๋ฃ
-> ์๋ฒ API๋ผ๋ฉด 500 ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์์
→ FastAPI๋ ๋ด๋ถ์ ์ผ๋ก try-except ๊ตฌ๋ฌธ์ ์ฌ์ฉํด ์์ธ๊ฐ ๋ฐ์ํด๋ ์๋ฒ๊ฐ ๋ฉ์ถ์ง ์๊ฒ ์ฒ๋ฆฌํ๋ค.
- ๊ฐ๋ฐ์๊ฐ ์ง์ ์์ธ์ฒ๋ฆฌ๋ฅผ ํ๊ฑฐ๋, ์ปค์คํ ์์ธ ํธ๋ค๋ฌ(@app.exception_handler)๋ฅผ ๋ฑ๋กํด์ ์๋ฌ ๋ฉ์์ง์ HTTP ์ํ์ฝ๋๋ฅผ ์ํ๋ ๋๋ก ๋ฐํํ๋ค.
try:
print("์์")
result = 10 / 2
except ZeroDivisionError:
print("0์ผ๋ก ๋๋ ์ ์์ต๋๋ค.")
else:
print("์ ์์ ์ผ๋ก ์คํ๋์์ต๋๋ค:", result)
finally:
print("๋ฌด์กฐ๊ฑด ์คํ๋ฉ๋๋ค")
try | ์์ธ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ด ์๋ ์ฝ๋ |
except | ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ์ฒ๋ฆฌํ ์ฝ๋ |
else | ์์ธ๊ฐ ๋ฐ์ํ์ง ์์์ ๋ ์คํ๋๋ ์ฝ๋ |
finally | ์์ธ ๋ฐ์ ์ฌ๋ถ์ ๊ด๊ณ์์ด ํญ์ ์คํ๋๋ ์ฝ๋ |
HTTPExecption
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
if item_id == 0:
raise HTTPException(status_code=404, detail="Item not found",headers={"WWW-Authenticate": "Bearer"})
return {"item_id": item_id}
๋งค๊ฐ๋ณ์ | ์ค๋ช |
status_code | HTTP ์ํ ์ฝ๋ (ํ์) |
detail | ์๋ฌ ์์ธ ๋ฉ์์ง (ํ์) |
headers | ์ถ๊ฐ ์๋ต ํค๋ (์ ํ) |
์ฌ์ฉ์ ์ ์ ์์ธ์ฒ๋ฆฌ
class NegativeNumberError(Exception):
pass
def check_positive(n):
if n < 0:
raise NegativeNumberError("์์๋ ํ์ฉ๋์ง ์์ต๋๋ค.")
try:
check_positive(-5)
except NegativeNumberError as e:
print(f"์๋ฌ ๋ฐ์: {e}")
์ฐธ๊ณ
https://fastapi.tiangolo.com/ko/tutorial/response-model
์ง์์ ์ผ๋ก ์ ๋ฐ์ดํธ ์ค์ธ ๊ธ์ ๋๋ค.