๐ ์ฝ 2๋ถ ์ฝ๋ ๋ฐ ์์๋ฉ๋๋ค
์ด์ ๊ธ ๋ณด๊ธฐ(FastAPI MySQL ์ฐ๋ ๋ฐ CRUD)
๋ฐฑ์๋ ๊ฐ๋ฐ์์ ํ ์คํธ๋ ๋จ์ํ ์ ํ์ด ์๋ ์์กด์ด๋ค. ๋ฒ๊ทธ๋ฅผ ์ค์ด๊ณ , ์ ์ฒด ์๋น์ค์ ์์ ์ฑ๊ณผ ์ ๋ขฐ์ฑ์ ํ๋ณดํ ์ ์๊ฒ ๋์์ฃผ๋ ์ค์ํ ๋๊ตฌ๋ค. ํนํ FastAPI๋ ํ ์คํธ ์นํ์ ์ธ ํ๋ ์์ํฌ๋ก, ์์ฝ๊ฒ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์๋ํํ ์ ์๋๋ก ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ์ด ๊ธ์์๋ Pytest๋ฅผ ์ค์ฌ์ผ๋ก FastAPI ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํธ๋ฅผ ์ด๋ป๊ฒ ์์ฑํ๊ณ ์คํํ ์ ์๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ํตํด ์ด๋ค ์ด์ ์ ์ป์ ์ ์๋์ง๋ฅผ Windows ํ๊ฒฝ ๊ธฐ์ค์ผ๋ก ์ ๋ฆฌํด๋ณด์.
๐งฐ ํ ์คํธ ํ๊ฒฝ ๊ตฌ์ถํ๊ธฐ
FastAPI์์๋ ํ
์คํธ๋ฅผ ์ํ ๋ ๊ฐ์ง ๋๊ตฌ๋ฅผ ํ์ฉํ๋ค. ํ๋๋ pytest
, ๋ค๋ฅธ ํ๋๋ FastAPI์์ ์ ๊ณตํ๋ TestClient
์ด๋ค. ๋ค์ ๋ช
๋ น์ด๋ฅผ ํตํด ํ์ํ ํจํค์ง๋ฅผ ์ค์นํ์.
pip install pytest httpx
pytest
: Python์์ ๊ฐ์ฅ ๋๋ฆฌ ์ฐ์ด๋ ํ ์คํธ ํ๋ ์์ํฌ์ด๋ค.httpx
: TestClient๊ฐ ๋ด๋ถ์ ์ผ๋ก ์์กดํ๋ ๋น๋๊ธฐ HTTP ํด๋ผ์ด์ธํธ์ด๋ค.
์ค์น๊ฐ ์๋ฃ๋๋ฉด ํ ์คํธ๋ฅผ ์ํ ์ค๋น๋ ๋์ด๋ค. ์ด์ ์ค์ ์ฝ๋๋ฅผ ํ ์คํธํด๋ณด์.
๐งช ํ ์คํธํ ๊ธฐ๋ณธ API ๋ง๋ค๊ธฐ
์๋๋ ์์ ๋ก ์ฌ์ฉํ FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ด๋ค. main.py
๋ผ๋ ์ด๋ฆ์ ํ์ผ๋ก ์์ฑํ๋ค.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
์ด์ ์ด API์ ๊ธฐ๋ฅ์ pytest
๋ก ํ
์คํธํด๋ณผ ๊ฒ์ด๋ค.
๐งช TestClient๋ก API ํ ์คํธํ๊ธฐ
FastAPI๋ Starlette ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก๊ธฐ ๋๋ฌธ์, ์์ฒด์ ์ผ๋ก TestClient
๋ฅผ ์ ๊ณตํ๋ค. ์ด ๊ฐ์ฒด๋ฅผ ํ์ฉํ๋ฉด ๋ณ๋๋ก ์๋ฒ๋ฅผ ๋์ฐ์ง ์๊ณ ๋ API ์์ฒญ์ ํ๋ด๋ผ ์ ์๋ค. ์๋๋ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ํ
์คํธ ์ฝ๋์ด๋ค. test_main.py
ํ์ผ์ ์์ฑํ์ฌ ์์ฑํ๋ค.
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
TestClient(app)
: ์ ํ๋ฆฌ์ผ์ด์ ์ธ์คํด์ค๋ฅผ ์ธ์๋ก ๋๊ฒจ์ค๋ค.client.get("/")
: ์ค์ GET ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ์ฒ๋ผ ํ๋ํ๋ค.assert
: ์๋ต์ด ๊ธฐ๋ํ ํํ์ธ์ง ๊ฒ์ฆํ๋ค.
๐ ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์ ๋ฐ ์ฟผ๋ฆฌ ํ ์คํธ
๋ค์์ ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์์ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ฅผ ํ ์คํธํ๋ ์์์ด๋ค.
def test_read_item():
response = client.get("/items/42?q=fastapi")
assert response.status_code == 200
assert response.json() == {"item_id": 42, "q": "fastapi"}
๋งค๊ฐ๋ณ์๊ฐ ์ ๋๋ก ๋ฐ์ธ๋ฉ๋๋์ง ํ์ธํ ์ ์์ผ๋ฉฐ, ์ฟผ๋ฆฌ ๋ฌธ์์ด๋ ํจ๊ป ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
โ๏ธ POST ์์ฒญ ํ ์คํธ
POST ์์ฒญ์์๋ json=
ํ๋ผ๋ฏธํฐ๋ฅผ ํ์ฉํ์ฌ ๋ฐ์ดํฐ ๋ฐ๋๋ฅผ ํจ๊ป ๋ณด๋ผ ์ ์๋ค. ์๋๋ POST API์ ์ด์ ๋ํ ํ
์คํธ ์์์ด๋ค.
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
@app.post("/items/")
def create_item(item: Item):
return {"name": item.name, "price": item.price}
์ด์ ํด๋น API์ ๋ํ ํ ์คํธ๋ฅผ ์์ฑํด๋ณด์.
def test_create_item():
response = client.post("/items/", json={"name": "Keyboard", "price": 49.99})
assert response.status_code == 200
assert response.json() == {"name": "Keyboard", "price": 49.99}
FastAPI๋ ์์ฒญ ๋ณธ๋ฌธ์ ์๋์ผ๋ก Pydantic ๋ชจ๋ธ๋ก ํ์ฑํ๊ณ ๊ฒ์ฆํ๊ธฐ ๋๋ฌธ์ ํ ์คํธ ์ญ์ ์ง๊ด์ ์ผ๋ก ์์ฑํ ์ ์๋ค.
๐งต ๋น๋๊ธฐ ํจ์ ํ ์คํธ
FastAPI๋ ๋น๋๊ธฐ๋ฅผ ์ง์ํ๋ฏ๋ก async def
๋ก ์์ฑ๋ ์๋ํฌ์ธํธ๋ ๋ง๋ค. ๋คํํ๋ TestClient
๋ ์ด ๋ถ๋ถ๋ ๋ฌธ์ ์์ด ์ฒ๋ฆฌํ๋ค. ์๋ฅผ ๋ค์ด ๋ค์์ฒ๋ผ ์์ฑ๋ ๋น๋๊ธฐ API๋ ๋์ผํ ๋ฐฉ์์ผ๋ก ํ
์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
@app.get("/async-endpoint")
async def async_endpoint():
return {"message": "๋น๋๊ธฐ ์๋ ์๋ฃ!"}
def test_async_endpoint():
response = client.get("/async-endpoint")
assert response.status_code == 200
assert response.json() == {"message": "๋น๋๊ธฐ ์๋ ์๋ฃ!"}
๐ ๋ฐ๋ณต ํ ์คํธ – ํ๋ผ๋ฏธํฐํ
๊ฐ์ ๊ตฌ์กฐ์ ํ
์คํธ๋ฅผ ์ฌ๋ฌ ์ผ์ด์ค๋ก ๋ฐ๋ณตํ๊ณ ์ถ๋ค๋ฉด pytest.mark.parametrize
๋ฅผ ํ์ฉํ ์ ์๋ค.
import pytest
@pytest.mark.parametrize("item_id, q", [
(1, "apple"),
(2, "banana"),
(3, "cherry")
])
def test_read_item_param(item_id, q):
response = client.get(f"/items/{item_id}?q={q}")
assert response.status_code == 200
assert response.json() == {"item_id": item_id, "q": q}
์ด ๋ฐฉ์์ ์ฌ๋ฌ ์ ๋ ฅ๊ฐ์ ๋ํด ๋์ผํ ๋ก์ง์ ํ ์คํธ๋ฅผ ๋ฐ๋ณต ์ํํ ์ ์์ด ์์ฐ์ฑ์ด ๋งค์ฐ ๋๋ค.
๐งพ ํ ์คํธ ์คํํ๊ธฐ
์์ฑํ ํ ์คํธ๋ ๋ค์ ๋ช ๋ น์ด๋ก ์คํํ๋ค. Windows ํ๊ฒฝ์์๋ PowerShell ๋๋ CMD์์ ์๋์ฒ๋ผ ์ ๋ ฅํ๋ฉด ๋๋ค.
pytest
๋ณด๋ค ์์ธํ ๋ก๊ทธ๋ฅผ ์ํ๋ค๋ฉด -v
์ต์
์ ์ถ๊ฐํ๋ค.
pytest -v
๐งผ ํ ์คํธ ๋๋ ํฐ๋ฆฌ ๊ตฌ์ฑ ํ
์ค๋ฌด์์๋ ํ ์คํธ ํ์ผ์ด ๋ง์์ง๋ฏ๋ก ๋ค์๊ณผ ๊ฐ์ด ๋๋ ํฐ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๋ ๊ฒ์ด ์ข๋ค.
project/
โโโ app/
โ โโโ main.py
โโโ tests/
โ โโโ test_main.py
์ด ๊ตฌ์กฐ๋ก ํ
์คํธ๋ฅผ ์์ฑํ๋ฉด pytest
๋ tests/
๋๋ ํฐ๋ฆฌ ์๋์ ๋ชจ๋ test_*.py
ํ์ผ์ ์๋์ผ๋ก ์ธ์ํ๊ณ ์คํํ๋ค.
๐ฏ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
FastAPI์ ํ ์คํธ๋ ์์์ด ์ฝ๊ณ , ํ์ฅ๋ ๋งค์ฐ ์ ์ฐํ๋ค. Pytest์ TestClient๋ฅผ ์ ์ ํ ํ์ฉํ๋ฉด API์ ๋์ ์ฌ๋ถ๋ฅผ ๋น ๋ฅด๊ฒ ๊ฒ์ฆํ ์ ์๊ณ , ๋ฆฌํฉํ ๋ง์ด๋ ๊ธฐ๋ฅ ์ถ๊ฐ์๋ ์์ ๊ฐ์ ๊ฐ์ง ์ ์๋ค. ํ ์คํธ๋ ์ฝ๋๋ฅผ ๋ฏฟ๊ฒ ๋ง๋๋ ์ ์ผํ ๋ฐฉ๋ฒ์ด๋ค. ๊ฐ๋ฐ ์ด๊ธฐ๋ถํฐ ํ ์คํธ๋ฅผ ์ต๊ด์ฒ๋ผ ์์ฑํ๋ ๊ฒ์ด ๋์ ํ์ง์ ๋ฐฑ์๋๋ฅผ ๋ง๋๋ ๊ฐ์ฅ ๋น ๋ฅธ ๊ธธ์ด๋ค.
FastAPI ๊ณต์ ๋ฌธ์ : https://fastapi.tiangolo.com/ko/
