FastAPI๋?
FastAPI๋ Python์ผ๋ก ๋ง๋ ์ต์ ์น ํ๋ ์์ํฌ์ด๋ค
๋น ๋ฅธ ์๋, ํ์
ํํธ ์ง์, ์๋ ๋ฌธ์ํ, ๋น๋๊ธฐ(Async) ์ง์ ๋ฑ์ ๊ฐ์ ์ด ์๋ค
RESTful API, ์น์์ผ ๋ฑ ๋ค์ํ ๋ฐฑ์๋ ๊ธฐ๋ฅ์ ์ฝ๊ฒ ๊ฐ๋ฐํ ์ ์๊ฒ ํด์ค๋ค
FastAPI์ ๊ด๋ จ ์ฃผ์ ์ฉ์ด
ASGI (Asynchronous Server Gateway Interface)
- Python์์ ๋น๋๊ธฐ ์น ์๋น์ค๋ฅผ ๋ง๋ค๊ธฐ ์ํ ์ธํฐํ์ด์ค (ํ๋กํ ์ฝ, ํ์ค)
- WSGI[^1]์ ๋น๋๊ธฐ ํ์ฅํ์ผ๋ก, ๋๊ธฐ(Sync)์ ๋น๋๊ธฐ(Async) ๋ชจ๋ ์ง์
- FastAPI, Starlette, Django(3.x ์ดํ) ๋ฑ์ด ASGI๋ฅผ ์ง์
Starlette
- Starlette๋ ASGI ๊ธฐ๋ฐ์ ์ด๊ฒฝ๋ ์น ํ๋ ์์ํฌ
- FastAPI๋ Starlette ์์ ๋ง๋ค์ด์ก์ผ๋ฉฐ, Starlette๊ฐ FastAPI์ ๋ผ๋ ์ญํ
- ๋ผ์ฐํ , ๋ฏธ๋ค์จ์ด, ์์ฒญ/์๋ต ์ฒ๋ฆฌ ๋ฑ ์น ํ๋ ์์ํฌ์ ํต์ฌ ๊ธฐ๋ฅ์ ์ ๊ณต
Uvicorn
- Uvicorn์ ASGI ์๋ฒ๋ก, FastAPI (๋๋ Starlette) ์ฑ์ ์คํ์์ผ์ฃผ๋ ์๋ฒ
- ๋น๋๊ธฐ (Async) ์ง์, ๋น ๋ฅด๊ณ ๊ฐ๋ฒผ์
- Flask์์์ WSGI ์๋ฒ (์: gunicorn, uwsgi ๋ฑ)์ ๋น์ทํ ์ญํ
Gunicorn
- Gunicorn์ WSGI ์๋ฒ๋ก ๋๋ฆฌ ์ฌ์ฉ๋์ด์์ผ๋, ์ต๊ทผ์๋ ASGI ์ ํ๋ฆฌ์ผ์ด์ ๋ ์ง์
- Uvicorn ์์ปค(worker)์ ์กฐํฉํด์ ์ด์ ํ๊ฒฝ์์ ๋ง์ด ์ฌ์ฉ
- Gunicorn์ ๋ฉํฐ ํ๋ก์ธ์ค ๋ฐฉ์์ ์ง์ํ์ฌ, ์ฌ๋ฌ Uvicorn ์ธ์คํด์ค๋ฅผ ๊ด๋ฆฌ
FastAPI ์ ์ฒด ๊ตฌ์กฐ
graph TD Client["ํด๋ผ์ด์ธํธ / ๋ธ๋ผ์ฐ์ ๋๋ ์ฑ)"] -->|ASGI| Uvicorn["Uvicorn - ASGI ์๋ฒ"] Uvicorn --> Starlette["Starlette - ASGI ํ๋ ์์ํฌ"] Starlette --> FastAPI["FastAPI - API ๋น์ฆ๋์ค ๋ก์ง"]
์ด์ ํ๊ฒฝ์์๋ Gunicorn์ด ์ฌ๋ฌ ๊ฐ์ Uvicorn ํ๋ก์ธ์ค๋ฅผ ๊ด๋ฆฌํ๋ ์ญํ ๋ก ์ถ๊ฐ๋ ์ ์์
์์ฝ ํ
์ฉ์ด | ์ค๋ช | FastAPI์์ ๊ด๊ณ |
---|---|---|
ASGI | Python ๋น๋๊ธฐ ์๋ฒ ์ธํฐํ์ด์ค | FastAPI์ ๋์ ๊ธฐ๋ฐ ํ๋กํ ์ฝ |
Starlette | ASGI ๊ธฐ๋ฐ ๊ฒฝ๋ ์น ํ๋ ์์ํฌ | FastAPI์ ๊ธฐ๋ฐ ๋ผ๋ |
Uvicorn | ASGI ์๋ฒ, FastAPI ์คํ ์๋ฒ | FastAPI๋ฅผ ์คํ์์ผ์ฃผ๋ ์๋ฒ |
Gunicorn | ๋ฉํฐ ํ๋ก์ธ์ค WSGI/ASGI ์๋ฒ | Uvicorn๊ณผ ์กฐํฉํ์ฌ ์ด์ |
FastAPI | Starlette ๊ธฐ๋ฐ ์ต์ API ํ๋ ์์ํฌ | ์ค์ API ์์ฑ ์์น |
FastAPI Tutorial
ํจํค์ง ์ค์น
pip install "fastapi[all]"
๊ฐ๋จํ API ๋ง๋ค๊ธฐ
from fastapi import FastAPI
# Create a FastAPI instance
app = FastAPI()
# path '/' ๋ก ๊ฐ์ GET operation ์คํํ์ ๋, ํธ์ถ๋ ํ์ด์ฌ ํจ์
@app.get("/")
def read_root():
return {"Hello": "World"}
์คํ
# ๊ฐ๋ฐ
uvicorn main:app --reload
python -m fastapi dev app/main.py
# ์ด์(๋ฐฐํฌ)
gunicorn -k uvicorn.workers.UvicornWorker main:app
- main : python script ์ด๋ฆ
Path Parameter
from fastapi import FastAPI
# Create a FastAPI instance
app = FastAPI()
# item_id๊ฐ Path Parameter. function์ argument๋ก ์ ๋ฌ
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
Query Parameter
from fastapi import FastAPI
# Create a FastAPI instance
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
# function parameter์ ํฌํจ๋์ง๋ง, Path Operation์ ํฌํจ X
# http://localhost:8000/items/?skip=0&limit=10 ์ผ๋ก ์ ๊ทผ
@app.get("/items/")
def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]
Multiple Path and Query Parameters
# multi_param.py
from typing import Union
from fastapi import FastAPI
# Create a FastAPI instance
app = FastAPI()
# Path Parameter + Query Parameter
@app.get("/users/{user_id}/items/{item_id}")
def read_user_item(user_id: int, item_id: str, q: Union[str, None] = None, short: bool = False):
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"},
)
return item
FastAPI CRUD
- CREATE / READ / UPDATE / DELETE ๊ธฐ๋ฅ์ Path Parameter, Query Parameter, Pydantic์ ํ์ฉํด ๊ตฌํ ๊ฐ๋ฅ
Path Parameter CRUD
- CREATE :
POST /users/name/{name}/nickname/{nickname}
- READ :
GET /users/name/{name}
- UPDATE :
PUT /users/name/{name}/nickname/{nickname}
- DELETE :
DELETE /users/name/{name}
from fastapi import FastAPI, HTTPException
# Create a FastAPI instance
app = FastAPI()
# User database
USER_DB = {}
# Fail response
NAME_NOT_FOUND = HTTPException(status_code=400, detail="Name not found.")
@app.post("/users/name/{name}/nickname/{nickname}")
def create_user(name: str, nickname: str):
USER_DB[name] = nickname
return {"status": "success"}
@app.get("/users/name/{name}")
def read_user(name: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
return {"nickname": USER_DB[name]}
@app.put("/users/name/{name}/nickname/{nickname}")
def update_user(name: str, nickname: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
USER_DB[name] = nickname
return {"status": "success"}
@app.delete("/users/name/{name}")
def delete_user(name: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
del USER_DB[name]
return {"status": "success"}
Query Parameter CRUD
- CREATE :
POST /users?name=hello&nickname=world
- READ :
GET /users?name=hello
- UPDATE :
PUT /users?name=hello&nickname=world2
- DELETE :
DELETE /users?name=hello
from fastapi import FastAPI, HTTPException
# Create a FastAPI instance
app = FastAPI()
# User database
USER_DB = {}
# Fail response
NAME_NOT_FOUND = HTTPException(status_code=400, detail="Name not found.")
@app.post("/users")
def create_user(name: str, nickname: str):
USER_DB[name] = nickname
return {"status": "success"}
@app.get("/users")
def read_user(name: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
return {"nickname": USER_DB[name]}
@app.put("/users")
def update_user(name: str, nickname: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
USER_DB[name] = nickname
return {"status": "success"}
@app.delete("/users")
def delete_user(name: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
del USER_DB[name]
return {"status": "success"}
Pydantic CRUD
- ๊ธฐ๋ฅ
- ๋ฐ์ดํฐ ๊ฒ์ฆ ๋ฐ ํ์
์์ ์ฑ (BaseModel)
- CreateIn : ์ ๋ ฅ๋ฐ๋ ๋ฐ์ดํฐ ํํ ์ง์
- CreateOut : ๋ฐํํ๊ณ ์ ํ๋ ๋ฐ์ดํฐ ํํ ์ง์
- ์ฝ๋ ๊ฐ๋ ์ฑ ๋ฐ ์ ์ง๋ณด์์ฑ ํฅ์
- ๋ณด์ ๋ฐ ์ ๋ณด ๋ ธ์ถ ์ต์ํ : Response Model ๋ณ๋ ์ง์ ํด์ ๋น๋ฐ๋ฒํธ์ ๊ฐ์ ๋ฏผ๊ฐํ ์ ๋ณด ์๋ต์์ ์ ์ธ ๊ฐ๋ฅ
- ์๋ ๋ณํ ๋ฐ ์ง๋ ฌํ (Python โ JSON)
- ๋ฐ์ดํฐ ๊ฒ์ฆ ๋ฐ ํ์
์์ ์ฑ (BaseModel)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
class CreateIn(BaseModel):
name: str
nickname: str
class CreateOut(BaseModel):
status: str
id: int
# Create a FastAPI instance
app = FastAPI()
# User database
USER_DB = {}
# Fail response
NAME_NOT_FOUND = HTTPException(status_code=400, detail="Name not found.")
@app.post("/users", response_model=CreateOut)
def create_user(user: CreateIn):
USER_DB[user.name] = user.nickname
user_dict = user.dict()
user_dict["status"] = "success"
user_dict["id"] = len(USER_DB)
return user_dict
@app.get("/users")
def read_user(name: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
return {"nickname": USER_DB[name]}
@app.put("/users")
def update_user(name: str, nickname: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
USER_DB[name] = nickname
return {"status": "success"}
@app.delete("/users")
def delete_user(name: str):
if name not in USER_DB:
raise NAME_NOT_FOUND
del USER_DB[name]
return {"status": "success"}