///
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`model` ํค๋ OpenAPI์ ์ผ๋ถ๊ฐ ์๋๋๋ค.
///
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`responses` ํ๋ผ๋ฏธํฐ์์ ๋ค๋ฅธ ๋ฏธ๋์ด ํ์
์ ๋ช
์์ ์ผ๋ก ์ง์ ํ์ง ์๋ ํ, FastAPI๋ ์๋ต์ด ์ฃผ์ ์๋ต ํด๋์ค์ ๋์ผํ ๋ฏธ๋์ด ํ์
(๊ธฐ๋ณธ๊ฐ `application/json`)์ ๊ฐ์ง๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
์ด ๋์์ 0.118.0์์ ๋๋๋ ค์ ธ, `yield` ์ดํ์ ์ข
๋ฃ ์ฝ๋๊ฐ ์๋ต์ด ์ ์ก๋ ๋ค ์คํ๋๋๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์๋์์ ๋ณด์๊ฒ ์ง๋ง, ์ด๋ 0.106.0 ๋ฒ์ ์ด์ ์ ๋์๊ณผ ๋งค์ฐ ๋น์ทํ์ง๋ง, ์ฌ๋ฌ ๊ฐ์ ์ฌํญ๊ณผ ์ฝ๋ ์ผ์ด์ค์ ๋ํ ๋ฒ๊ทธ ์์ ์ด ํฌํจ๋์ด ์์ต๋๋ค.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`response_class` ๋งค๊ฐ๋ณ์๋ ์๋ต์ "๋ฏธ๋์ด ํ์
"์ ์ ์ํ๋ ๋ฐ์๋ ์ฌ์ฉ๋ฉ๋๋ค.
///
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
๋ฌผ๋ก ์ค์ `Content-Type` ํค๋, ์ํ ์ฝ๋ ๋ฑ์ ๋ฐํ๋ `Response` ๊ฐ์ฒด์์ ๊ฐ์ ธ์ต๋๋ค.
### `StreamingResponse` { #streamingresponse }
-๋น๋๊ธฐ ์ ๋๋ ์ดํฐ ๋๋ ์ผ๋ฐ ์ ๋๋ ์ดํฐ/์ดํฐ๋ ์ดํฐ(`yield`๊ฐ ์๋ ํจ์)๋ฅผ ๋ฐ์ ์๋ต ๋ณธ๋ฌธ์ ์คํธ๋ฆฌ๋ฐํฉ๋๋ค.
+๋น๋๊ธฐ ์ ๋๋ ์ดํฐ ๋๋ ์ผ๋ฐ ์ ๋๋ ์ดํฐ/์ดํฐ๋ ์ดํฐ(`yield`๊ฐ ์๋ ํจ์`)๋ฅผ ๋ฐ์ ์๋ต ๋ณธ๋ฌธ์ ์คํธ๋ฆฌ๋ฐํฉ๋๋ค.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
์ด๋ Pydantic ๋ชจ๋ธ์ ์ฌ์ฉํ ๋์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก๋ ๋ด๋ถ์ ์ผ๋ก๋ Pydantic์ ์ฌ์ฉํด ๊ฐ์ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋ฉ๋๋ค.
-/// info
+/// note
dataclasses๋ Pydantic ๋ชจ๋ธ์ด ํ ์ ์๋ ๋ชจ๋ ๊ฒ์ ํ ์๋ ์๋ค๋ ์ ์ ๊ธฐ์ตํ์ธ์.
์ฌ๊ธฐ์ `shutdown` ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์๋ ํ
์คํธ ํ ์ค `"Application shutdown"`์ `log.txt` ํ์ผ์ ๊ธฐ๋กํฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`open()` ํจ์์์ `mode="a"`๋ "append"(์ถ๊ฐ)๋ฅผ ์๋ฏธํ๋ฏ๋ก, ๊ธฐ์กด ๋ด์ฉ์ ๋ฎ์ด์ฐ์ง ์๊ณ ํ์ผ์ ์๋ ๋ด์ฉ ๋ค์ ์ค์ด ์ถ๊ฐ๋ฉ๋๋ค.
ํธ๊ธฐ์ฌ ๋ง์ ๋ถ๋ค์ ์ํ ๊ธฐ์ ์ ์ธ ์ธ๋ถ์ฌํญ์
๋๋ค. ๐ค
-๋ด๋ถ์ ์ผ๋ก ASGI ๊ธฐ์ ์ฌ์์์๋ ์ด๊ฒ์ด [Lifespan Protocol](https://asgi.readthedocs.io/en/latest/specs/lifespan.html)์ ์ผ๋ถ์ด๋ฉฐ, `startup`๊ณผ `shutdown`์ด๋ผ๋ ์ด๋ฒคํธ๋ฅผ ์ ์ํฉ๋๋ค.
+๋ด๋ถ์ ์ผ๋ก ASGI ๊ธฐ์ ์ฌ์์์๋ ์ด๊ฒ์ด [Lifespan ํ๋กํ ์ฝ](https://asgi.readthedocs.io/en/latest/specs/lifespan.html)์ ์ผ๋ถ์ด๋ฉฐ, `startup`๊ณผ `shutdown`์ด๋ผ๋ ์ด๋ฒคํธ๋ฅผ ์ ์ํฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
Starlette `lifespan` ํธ๋ค๋ฌ์ ๋ํด์๋ [Starlette์ Lifespan ๋ฌธ์](https://www.starlette.dev/lifespan/)์์ ๋ ์ฝ์ด๋ณผ ์ ์์ต๋๋ค.
**FastAPI**๋ **OpenAPI** ์ฌ์์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฏ๋ก, FastAPI์ API๋ ๋ง์ ๋๊ตฌ๊ฐ ์ดํดํ ์ ์๋ ํ์ค ํ์์ผ๋ก ์ค๋ช
ํ ์ ์์ต๋๋ค.
-รซ\8d\95รซยถ\84รฌ\97\90 รฌ\97ยฌรซ\9fยฌ รฌ\96ยธรฌ\96ยดรฌ\9aยฉ รญ\81ยดรซ\9dยผรฌ\9dยดรฌ\96ยธรญ\8aยธ รซ\9dยผรฌ\9dยดรซยธ\8cรซ\9fยฌรซยฆยฌ(<abbr title="Software Development Kits - รฌ\86\8cรญ\94\84รญ\8aยธรฌ\9bยจรฌ\96ยด รชยฐ\9cรซยฐ\9c รญ\82ยครญ\8aยธ">**SDKs**</abbr>), รฌยต\9cรฌ\8bย **รซยฌยธรฌ\84\9c**, ๊ทธ๋ฆฌ๊ณ ์ฝ๋์ ๋๊ธฐํ๋ **ํ
์คํธ** ๋๋ **์๋ํ ์ํฌํ๋ก**๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค.
+รซ\8d\95รซยถ\84รฌ\97\90 รฌยต\9cรฌ\8bย **รซยฌยธรฌ\84\9c**, รฌ\97ยฌรซ\9fยฌ รฌ\96ยธรฌ\96ยดรฌ\9aยฉ รญ\81ยดรซ\9dยผรฌ\9dยดรฌ\96ยธรญ\8aยธ รซ\9dยผรฌ\9dยดรซยธ\8cรซ\9fยฌรซยฆยฌ(<abbr title="Software Development Kits - รฌ\86\8cรญ\94\84รญ\8aยธรฌ\9bยจรฌ\96ยด รชยฐ\9cรซยฐ\9c รญ\82ยครญ\8aยธ">**SDKs**</abbr>), ๊ทธ๋ฆฌ๊ณ ์ฝ๋์ ๋๊ธฐํ๋ **ํ
์คํธ** ๋๋ **์๋ํ ์ํฌํ๋ก**๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค.
์ด ๊ฐ์ด๋์์๋ FastAPI ๋ฐฑ์๋์ฉ **TypeScript SDK**๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋๋ค.
์๋ฅผ ๋ค์ด ๋ค์์ ์ฌ์ฉํด ๋ณผ ์ ์์ต๋๋ค:
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
-* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
์ด ์ค ์ผ๋ถ๋ ์คํ ์์ค์ด๊ฑฐ๋ ๋ฌด๋ฃ ํฐ์ด๋ฅผ ์ ๊ณตํ๋ฏ๋ก, ๋น์ฉ ๋ถ๋ด ์์ด ์ฌ์ฉํด ๋ณผ ์ ์์ต๋๋ค. ๋ค๋ฅธ ์์ฉ SDK ์์ฑ๊ธฐ๋ ์์ผ๋ฉฐ ์จ๋ผ์ธ์์ ์ฐพ์ ์ ์์ต๋๋ค. ๐ค
์ด ์์ ์์, ์์์ ๋ง๋ ์ฝ๋ฐฑ ๋ผ์ฐํฐ ์์ *์ฝ๋ฐฑ ๊ฒฝ๋ก ์ฒ๋ฆฌ(๋ค)*(์ฆ *external developer*๊ฐ *external API*์ ๊ตฌํํด์ผ ํ๋ ๊ฒ๋ค)์ ์ค๋นํ์ต๋๋ค.
-์ด์ *์ฌ๋ฌ๋ถ์ API ๊ฒฝ๋ก ์ฒ๋ฆฌ ๋ฐ์ฝ๋ ์ดํฐ*์์ `callbacks` ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํด, ๊ทธ ์ฝ๋ฐฑ ๋ผ์ฐํฐ์ `.routes` ์์ฑ(์ค์ ๋ก๋ routes/*๊ฒฝ๋ก ์ฒ๋ฆฌ*์ `list`)์ ์ ๋ฌํฉ๋๋ค:
+์ด์ *์ฌ๋ฌ๋ถ์ API ๊ฒฝ๋ก ์ฒ๋ฆฌ ๋ฐ์ฝ๋ ์ดํฐ*์์ `callbacks` ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํด, ๊ทธ ์ฝ๋ฐฑ ๋ผ์ฐํฐ์ `.routes` ์์ฑ์ ์ ๋ฌํฉ๋๋ค:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | ํ
-`callback=`์ ๋ผ์ฐํฐ ์์ฒด(`invoices_callback_router`)๋ฅผ ๋๊ธฐ๋ ๊ฒ์ด ์๋๋ผ, `invoices_callback_router.routes`์ฒ๋ผ `.routes` ์์ฑ์ ๋๊ธด๋ค๋ ์ ์ ์ฃผ๋ชฉํ์ธ์.
+`callbacks=`์ ๋ผ์ฐํฐ ์์ฒด(`invoices_callback_router`)๋ฅผ ๋๊ธฐ๋ ๊ฒ์ด ์๋๋ผ, `invoices_callback_router.routes`์ฒ๋ผ `.routes` ์์ฑ์ ๋๊ธด๋ค๋ ์ ์ ์ฃผ๋ชฉํ์ธ์. FastAPI๋ ์ด ๋ผ์ฐํธ๋ค์ ์ฌ์ฉํ์ฌ ์ฝ๋ฐฑ OpenAPI ๋ฌธ์๋ฅผ ์์ฑํฉ๋๋ค.
///
์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ฉ์๊ฐ ์ฌ๋ฌ๋ถ์ **webhook** ์์ฒญ์ ๋ฐ๊ธฐ ์ํด **์์ ๋ค์ API๋ฅผ ๊ตฌํ**ํ๊ธฐ๊ฐ ํจ์ฌ ์ฌ์์ง๊ณ , ๊ฒฝ์ฐ์ ๋ฐ๋ผ์๋ ์์ ์ API ์ฝ๋ ์ผ๋ถ๋ฅผ ์๋ ์์ฑํ ์๋ ์์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
Webhooks๋ OpenAPI 3.1.0 ์ด์์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, FastAPI `0.99.0` ์ด์์์ ์ง์๋ฉ๋๋ค.
์ฌ๋ฌ๋ถ์ด ์ ์ํ webhook์ **OpenAPI** ์คํค๋ง์ ์๋ **docs UI**์ ํฌํจ๋ฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`app.webhooks` ๊ฐ์ฒด๋ ์ค์ ๋ก `APIRouter`์ผ ๋ฟ์ด๋ฉฐ, ์ฌ๋ฌ ํ์ผ๋ก ์ฑ์ ๊ตฌ์กฐํํ ๋ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๋์ผํ ํ์
์
๋๋ค.
## OpenAPI operationId { #openapi-operationid }
-/// warning | ๊ฒฝ๊ณ
+/// warning
OpenAPI โ์ ๋ฌธ๊ฐโ๊ฐ ์๋๋ผ๋ฉด, ์๋ง ์ด ๋ด์ฉ์ ํ์ํ์ง ์์ ๊ฒ์
๋๋ค.
### *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์* ์ด๋ฆ์ operationId๋ก ์ฌ์ฉํ๊ธฐ { #using-the-path-operation-function-name-as-the-operationid }
-API์ ํจ์ ์ด๋ฆ์ `operationId`๋ก ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด, ๋ชจ๋ API๋ฅผ ์ํํ๋ฉด์ `APIRoute.name`์ ์ฌ์ฉํด ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ `operation_id`๋ฅผ ๋ฎ์ด์ธ ์ ์์ต๋๋ค.
+API์ ํจ์ ์ด๋ฆ์ `operationId`๋ก ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด, `FastAPI`์ ์ฌ์ฉ์ ์ ์ `generate_unique_id_function`์ ์ ๋ฌํ ์ ์์ต๋๋ค.
-๋ชจ๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์ถ๊ฐํ ๋ค์ ์ํํด์ผ ํฉ๋๋ค.
+์ด ํจ์๋ ๊ฐ `APIRoute`๋ฅผ ๋ฐ์ ๊ทธ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ์ฌ์ฉํ `operationId`๋ฅผ ๋ฐํํฉ๋๋ค.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2,5:6,9] *}
-/// tip | ํ
-
-`app.openapi()`๋ฅผ ์๋์ผ๋ก ํธ์ถํ๋ค๋ฉด, ๊ทธ ์ ์ `operationId`๋ค์ ์
๋ฐ์ดํธํด์ผ ํฉ๋๋ค.
-
-///
-
-/// warning | ๊ฒฝ๊ณ
+/// warning
์ด๋ ๊ฒ ํ ๊ฒฝ์ฐ, ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์ ์ด๋ฆ์ด ๊ณ ์ ํ๋๋ก ๋ณด์ฅํด์ผ ํฉ๋๋ค.
์ด *๊ฒฝ๋ก ์ฒ๋ฆฌ* ์ ์ฉ OpenAPI ์คํค๋ง๋ ๋ณดํต **FastAPI**๊ฐ ์๋์ผ๋ก ์์ฑํ์ง๋ง, ํ์ฅํ ์๋ ์์ต๋๋ค.
-/// tip | ํ
+/// tip
์ด๋ ์ ์์ค ํ์ฅ ์ง์ ์
๋๋ค.
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
-/// tip | ํ
+/// tip
์ฌ๊ธฐ์๋ ๊ฐ์ Pydantic ๋ชจ๋ธ์ ์ฌ์ฌ์ฉํฉ๋๋ค.
`Response` ๋๋ ๊ทธ ํ์ ํด๋์ค๋ฅผ ๋ฐํํ ์ ์์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`JSONResponse` ์์ฒด๋ `Response`์ ํ์ ํด๋์ค์
๋๋ค.
* `instagram_basic` ๋ Facebook/Instagram์์ ์ฌ์ฉํฉ๋๋ค.
* `https://www.googleapis.com/auth/drive` ๋ Google์์ ์ฌ์ฉํฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
OAuth2์์ โ์ค์ฝํโ๋ ํ์ํ ํน์ ๊ถํ์ ์ ์ธํ๋ ๋ฌธ์์ด์ผ ๋ฟ์
๋๋ค.
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
-/// info | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
`Security`๋ ์ค์ ๋ก `Depends`์ ์๋ธํด๋์ค์ด๋ฉฐ, ๋์ค์ ๋ณด๊ฒ ๋ ์ถ๊ฐ ๋งค๊ฐ๋ณ์ ํ๋๋ง ๋ ์์ต๋๋ค.
ํ์ง๋ง ์์ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๋ ๋ฌธ์์ด์ ์คํธ๋ฆฌ๋ฐํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ๋ฉด ๋ฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
FastAPI 0.134.0์ ์ถ๊ฐ๋์์ต๋๋ค.
๋ํ ๋์คํฌ๋ ๋คํธ์ํฌ์์ ์ฝ๊ธฐ ๋๋ฌธ์, ๋ง์ ๊ฒฝ์ฐ ์ฝ๊ธฐ ์์
์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ๋ง์ ์ ์๋ ๋ธ๋กํน ์ฐ์ฐ์
๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์์ ์์๋ ์์ธ์ ์ธ ๊ฒฝ์ฐ์
๋๋ค. `io.BytesIO` ๊ฐ์ฒด๋ ์ด๋ฏธ ๋ฉ๋ชจ๋ฆฌ์ ์์ผ๋ฏ๋ก ์ฝ๊ธฐ๊ฐ ์๋ฌด ๊ฒ๋ ์ฐจ๋จํ์ง ์์ต๋๋ค.
์ด ์ค์ ์ ์ฌ์ฉํ๋ฉด `Content-Type` ํค๋๊ฐ ์๋ ์์ฒญ๋ ๋ณธ๋ฌธ์ด JSON์ผ๋ก ํ์ฑ๋ฉ๋๋ค. ์ด๋ ์ด์ ๋ฒ์ ์ FastAPI์ ๋์ผํ ๋์์
๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์ด ๋์๊ณผ ์ค์ ์ FastAPI 0.132.0์ ์ถ๊ฐ๋์์ต๋๋ค.
{* ../../docs_src/websockets_/tutorial002_an_py310.py hl[68:69,82] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
WebSocket์ด๊ธฐ ๋๋ฌธ์ `HTTPException`์ ๋ฐ์์ํค๋ ๊ฒ์ ์ ์ ํ์ง ์์ต๋๋ค. ๋์ `WebSocketException`์ ๋ฐ์์ํต๋๋ค.
## `WSGIMiddleware` ์ฌ์ฉํ๊ธฐ { #using-wsgimiddleware }
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์ด๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด `a2wsgi`๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค. ์: `pip install a2wsgi`
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
-# If running behind a proxy like Nginx or Traefik add --proxy-headers
+# Nginx๋ Traefik ๊ฐ์ ํ๋ก์ ๋ค์์ ์คํํ๋ค๋ฉด --proxy-headers๋ฅผ ์ถ๊ฐํ์ธ์
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
```
</div>
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
ํจํค์ง ์์กด์ฑ์ ์ ์ํ๊ณ ์ค์นํ๋ ๋ค๋ฅธ ํ์๊ณผ ๋๊ตฌ๋ ์์ต๋๋ค.
**์ฌ๋ฌ ์ปจํ
์ด๋**๊ฐ ์๊ณ ๊ฐ ์ปจํ
์ด๋๊ฐ ๋ณดํต **๋จ์ผ ํ๋ก์ธ์ค**๋ฅผ ์คํํ๋ค๋ฉด(์: **Kubernetes** ํด๋ฌ์คํฐ), ๋ณต์ ๋ ์์ปค ์ปจํ
์ด๋๋ฅผ ์คํํ๊ธฐ **์ ์**, ๋จ์ผ ์ปจํ
์ด๋์์ ๋จ์ผ ํ๋ก์ธ์ค๋ก **์์ ์ ์ฌ์ ๋จ๊ณ**๋ฅผ ์ํํ๋ **๋ณ๋์ ์ปจํ
์ด๋**๋ฅผ ๋๊ณ ์ถ์ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
Kubernetes๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ์ด๋ ์๋ง๋ [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)์ผ ๊ฒ์
๋๋ค.
# FastAPI Cloud { #fastapi-cloud }
-**ํ ๋ฒ์ ๋ช
๋ น**์ผ๋ก FastAPI ์ฑ์ [FastAPI Cloud](https://fastapicloud.com)์ ๋ฐฐํฌํ ์ ์์ต๋๋ค. ์์ง์ด๋ผ๋ฉด ๋๊ธฐ์ ๋ช
๋จ์ ๋ฑ๋กํด ๋ณด์ธ์. ๐
-
-## ๋ก๊ทธ์ธํ๊ธฐ { #login }
-
-๋จผ์ **FastAPI Cloud** ๊ณ์ ์ด ์ด๋ฏธ ์๋์ง ํ์ธํ์ธ์(๋๊ธฐ์ ๋ช
๋จ์์ ์ด๋ํด ๋๋ ธ์ ๊ฑฐ์์ ๐).
-
-๊ทธ๋ค์ ๋ก๊ทธ์ธํฉ๋๋ค:
-
-<div class="termy">
-
-```console
-$ fastapi login
-
-You are logged in to FastAPI Cloud ๐
-```
-
-</div>
-
-## ๋ฐฐํฌํ๊ธฐ { #deploy }
-
-์ด์ **ํ ๋ฒ์ ๋ช
๋ น**์ผ๋ก ์ฑ์ ๋ฐฐํฌํฉ๋๋ค:
+**ํ ๋ฒ์ ๋ช
๋ น**์ผ๋ก FastAPI ์ฑ์ [FastAPI Cloud](https://fastapicloud.com)์ ๋ฐฐํฌํ ์ ์์ต๋๋ค. ๐
<div class="termy">
</div>
+CLI๊ฐ FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ์๋์ผ๋ก ๊ฐ์งํ์ฌ ํด๋ผ์ฐ๋์ ๋ฐฐํฌํฉ๋๋ค. ๋ก๊ทธ์ธ๋์ด ์์ง ์๋ค๋ฉด, ์ธ์ฆ์ ์๋ฃํ ์ ์๋๋ก ๋ธ๋ผ์ฐ์ ๊ฐ ์๋์ผ๋ก ์ด๋ฆฝ๋๋ค.
+
์ด๊ฒ ์ ๋ถ์
๋๋ค! ์ด์ ํด๋น URL์์ ์ฑ์ ์ ๊ทผํ ์ ์์ต๋๋ค. โจ
## FastAPI Cloud ์๊ฐ { #about-fastapi-cloud }
* [Hypercorn](https://hypercorn.readthedocs.io/): HTTP/2 ๋ฐ Trio ๋ฑ ์ฌ๋ฌ ๊ธฐ๋ฅ๊ณผ ํธํ๋๋ ASGI ์๋ฒ.
* [Daphne](https://github.com/django/daphne): Django Channels๋ฅผ ์ํด ๋ง๋ค์ด์ง ASGI ์๋ฒ.
* [Granian](https://github.com/emmett-framework/granian): Python ์ ํ๋ฆฌ์ผ์ด์
์ ์ํ Rust HTTP ์๋ฒ.
-* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit์ ๊ฐ๋ณ๊ณ ๋ค์ฉ๋๋ก ์ฌ์ฉํ ์ ์๋ ์น ์ ํ๋ฆฌ์ผ์ด์
๋ฐํ์์
๋๋ค.
## ์๋ฒ ๋จธ์ ๊ณผ ์๋ฒ ํ๋ก๊ทธ๋จ { #server-machine-and-server-program }
์ฌ๊ธฐ์๋ `fastapi` ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ `uvicorn` ๋ช
๋ น์ด๋ฅผ ์ง์ ์ฌ์ฉํด์, **์์ปค ํ๋ก์ธ์ค**์ ํจ๊ป **Uvicorn**์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
Docker๋ Kubernetes ๊ฐ์ ์ปจํ
์ด๋๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, ๋ค์ ์ฅ์ธ [์ปจํ
์ด๋์์์ FastAPI - ๋์ปค](docker.md)์์ ๋ ์์ธํ ์ค๋ช
ํ๊ฒ ์ต๋๋ค.
* `openapi_version`: ์ฌ์ฉ๋๋ OpenAPI ์คํ ๋ฒ์ . ๊ธฐ๋ณธ๊ฐ์ ์ต์ ์ธ `3.1.0`.
* `summary`: API์ ๋ํ ์งง์ ์์ฝ.
* `description`: API ์ค๋ช
. markdown์ ํฌํจํ ์ ์์ผ๋ฉฐ ๋ฌธ์์ ํ์๋ฉ๋๋ค.
-* `routes`: ๋ผ์ฐํธ ๋ชฉ๋ก. ๊ฐ๊ฐ ๋ฑ๋ก๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์
๋๋ค. `app.routes`์์ ๊ฐ์ ธ์ต๋๋ค.
+* `routes`: ์ ํ๋ฆฌ์ผ์ด์
์ ๋ผ์ฐํธ. `app.routes`์์ ๊ฐ์ ธ์ต๋๋ค. FastAPI๋ ์ด๋ฅผ ์ฌ์ฉํด ๋ฑ๋ก๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์์งํ๋ฉฐ, ํฌํจ๋ ๋ผ์ฐํฐ์ ๊ฒ๊น์ง ํฌํจํฉ๋๋ค.
-/// info | ์ ๋ณด
+/// tip | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+`app.routes`๋ ๋ ํ์ ์์ค์ ๋ผ์ฐํธ ํธ๋ฆฌ์
๋๋ค. ํฌํจ๋ ๋ผ์ฐํฐ๋ฅผ ์ํด FastAPI๊ฐ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉํ๋ ๋ผ์ฐํธ ํ๋ณด๋ค์ ํฌํจํ ์ ์์ผ๋ฉฐ, ์ต์ข
`APIRoute` ๊ฐ์ฒด๋ง ์๋ ๊ฒ์ ์๋๋๋ค.
+
+`app.routes`๋ฅผ ๊ทธ๋๋ก `get_openapi()`์ ์ ๋ฌํด๋ ๋ฉ๋๋ค. FastAPI๊ฐ ๊ทธ ๋ผ์ฐํธ ํธ๋ฆฌ๋ฅผ ์ํํ์ฌ ์ค์ ์ ํจํ ๊ฒฝ๋ก ์ฒ๋ฆฌ๋ค์ ์์งํฉ๋๋ค.
+
+///
+
+/// note | ์ฐธ๊ณ
`summary` ํ๋ผ๋ฏธํฐ๋ OpenAPI 3.1.0 ์ด์์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, FastAPI 0.99.0 ์ด์์์ ์ง์๋ฉ๋๋ค.
-# ์
๋ ฅ๊ณผ ์ถ๋ ฅ์ ๋ํด OpenAPI ์คํค๋ง๋ฅผ ๋ถ๋ฆฌํ ์ง ์ฌ๋ถ { #separate-openapi-schemas-for-input-and-output-or-not }
+# ์
๋ ฅ๊ณผ ์ถ๋ ฅ์ ๋ํด OpenAPI ์คํคะผะฐ๋ฅผ ๋ถ๋ฆฌํ ์ง ์ฌ๋ถ { #separate-openapi-schemas-for-input-and-output-or-not }
**Pydantic v2**๊ฐ ๋ฆด๋ฆฌ์ค๋ ์ดํ, ์์ฑ๋๋ OpenAPI๋ ์ด์ ๋ณด๋ค ์กฐ๊ธ ๋ ์ ํํ๊ณ **์ฌ๋ฐ๋ฅด๊ฒ** ๋ง๋ค์ด์ง๋๋ค. ๐
ํ์ง๋ง `Item-Output`์์๋ `description`์ด **ํ์์ด๋ฉฐ**, ๋นจ๊ฐ ๋ณํ๊ฐ ์์ต๋๋ค.
<div class="screenshot">
-<img src="/img/tutorial/separate-openapi_schemas/image04.png">
+<img src="/img/tutorial/separate-openapi-schemas/image04.png">
</div>
**Pydantic v2**์ ์ด ๊ธฐ๋ฅ ๋๋ถ์ API ๋ฌธ์๋ ๋ **์ ๋ฐ**ํด์ง๊ณ , ์๋ ์์ฑ๋ ํด๋ผ์ด์ธํธ์ SDK๊ฐ ์๋ค๋ฉด ๊ทธ๊ฒ๋ค๋ ๋ ์ ๋ฐํด์ ธ์ ๋ ๋์ **developer experience**์ ์ผ๊ด์ฑ์ ์ ๊ณตํ ์ ์์ต๋๋ค. ๐
๊ทธ๋ฐ ๊ฒฝ์ฐ์๋, **FastAPI**์์ `separate_input_output_schemas=False` ํ๋ผ๋ฏธํฐ๋ก ์ด ๊ธฐ๋ฅ์ ๋นํ์ฑํํ ์ ์์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`separate_input_output_schemas` ์ง์์ FastAPI `0.102.0`์ ์ถ๊ฐ๋์์ต๋๋ค. ๐ค
---
-"_ํ๋ก๋์
Python API๋ฅผ ๋ง๋ค๊ณ ์ ํ๋ค๋ฉด, ์ ๋ **FastAPI**๋ฅผ ๊ฐ๋ ฅํ ์ถ์ฒํฉ๋๋ค. **์๋ฆ๋ต๊ฒ ์ค๊ณ**๋์๊ณ , **์ฌ์ฉ์ด ๊ฐ๋จ**ํ๋ฉฐ, **ํ์ฅ์ฑ์ด ๋งค์ฐ ๋ฐ์ด๋** ์ฐ๋ฆฌ์ API ์ฐ์ ๊ฐ๋ฐ ์ ๋ต์์ **ํต์ฌ ๊ตฌ์ฑ ์์**๊ฐ ๋์์ต๋๋ค._"
+"_ํ๋ก๋์
Python API๋ฅผ ๋ง๋ค๊ณ ์ ํ๋ค๋ฉด, ์ ๋ **FastAPI**๋ฅผ ๊ฐ๋ ฅํ ์ถ์ฒํฉ๋๋ค. **์๋ฆ๋ต๊ฒ ์ค๊ณ**๋์๊ณ , **์ฌ์ฉ์ด ๊ฐ๋จ**ํ๋ฉฐ, **ํ์ฅ์ฑ์ด ๋งค์ฐ ๋ฐ์ด๋** ์ฐ๋ฆฌ์ API ์ฐ์ ๊ฐ๋ฐ ์ ๋ต์์ **ํต์ฌ ๊ตฌ์ฑ ์์**๊ฐ ๋์๊ณ , ์ฐ๋ฆฌ์ Virtual TAC Engineer์ ๊ฐ์ ์ฌ๋ฌ ์๋ํ์ ์๋น์ค๋ค์ ์ถ์งํ๊ณ ์์ต๋๋ค._"
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/"><small>(ref)</small></a></div>
### ์ฑ ๋ฐฐํฌํ๊ธฐ(์ ํ ์ฌํญ) { #deploy-your-app-optional }
-์ ํ์ ์ผ๋ก FastAPI ์ฑ์ [FastAPI Cloud](https://fastapicloud.com)์ ๋ฐฐํฌํ ์ ์์ต๋๋ค. ์์ง์ด๋ผ๋ฉด ๋๊ธฐ์ ๋ช
๋จ์ ๋ฑ๋กํด ๋ณด์ธ์. ๐
-
-์ด๋ฏธ **FastAPI Cloud** ๊ณ์ ์ด ์๋ค๋ฉด(๋๊ธฐ์ ๋ช
๋จ์์ ์ด๋ํด ๋๋ ธ์ต๋๋ค ๐), ํ ๋ฒ์ ๋ช
๋ น์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฐฐํฌํ ์ ์์ต๋๋ค.
+์ ํ์ ์ผ๋ก FastAPI ์ฑ์ ํ ๋ฒ์ ๋ช
๋ น์ด๋ก [FastAPI Cloud](https://fastapicloud.com)์ ๋ฐฐํฌํ ์ ์์ต๋๋ค. ๐
<div class="termy">
</div>
+CLI๊ฐ ์ฌ๋ฌ๋ถ์ FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ์๋์ผ๋ก ๊ฐ์งํ์ฌ ํด๋ผ์ฐ๋์ ๋ฐฐํฌํฉ๋๋ค. ๋ก๊ทธ์ธ๋์ด ์์ง ์๋ค๋ฉด, ์ธ์ฆ์ ์๋ฃํ๊ธฐ ์ํด ๋ธ๋ผ์ฐ์ ๊ฐ ์ด๋ฆฝ๋๋ค.
+
์ด๊ฒ ์ ๋ถ์
๋๋ค! ์ด์ ํด๋น URL์์ ์ฑ์ ์ ๊ทผํ ์ ์์ต๋๋ค. โจ
#### FastAPI Cloud ์๊ฐ { #about-fastapi-cloud }
/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
-๋ด๋ถ์ ์ผ๋ก๋ `APIRouter`์ ์ ์ธ๋ ๊ฐ *path operation*๋ง๋ค *path operation*์ ์ค์ ๋ก ์์ฑํฉ๋๋ค.
+FastAPI๋ ๋ฉ์ธ ์ ํ๋ฆฌ์ผ์ด์
์ router๋ฅผ ํฌํจํด๋ ์๋์ `APIRouter`์ ๊ทธ `APIRoute`๋ค์ ํ์ฑ ์ํ๋ก ์ ์งํฉ๋๋ค.
-์ฆ, ๋ด๋ถ์ ์ผ๋ก๋ ๋ชจ๋ ๊ฒ์ด ๋์ผํ ํ๋์ ์ฑ์ธ ๊ฒ์ฒ๋ผ ๋์ํฉ๋๋ค.
+์ฆ, ์ปค์คํ
`APIRouter`์ `APIRoute` ์๋ธํด๋์ค๊ฐ ํฌํจ๋ ์ดํ์๋ ๊ณ์ ์๋ํ ์ ์์ต๋๋ค.
///
router๋ฅผ ํฌํจ(include)ํ ๋ ์ฑ๋ฅ์ ๊ฑฑ์ ํ ํ์๋ ์์ต๋๋ค.
-์ด ์์
์ ๋ง์ดํฌ๋ก์ด ๋จ์์ด๋ฉฐ ์์ ์์๋ง ๋ฐ์ํฉ๋๋ค.
+์ด ๊ธฐ๋ฅ์ ๋งค์ฐ ๊ฐ๋ณ๊ฒ ์ค๊ณ๋์๊ณ ๊ฐ ์์ฒญ์ ์ค๋ฒํค๋๋ฅผ ์ถ๊ฐํ์ง ์๋๋ก ๋์ด ์์ต๋๋ค.
๋ฐ๋ผ์ ์ฑ๋ฅ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. โก
`APIRouter`๋ "mount"๋๋ ๊ฒ์ด ์๋๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์
์ ๋๋จธ์ง ๋ถ๋ถ๊ณผ ๊ฒฉ๋ฆฌ๋์ด ์์ง ์์ต๋๋ค.
-รฌ\9dยดรซ\8a\94 OpenAPI รฌ\8aยครญ\82ยครซยง\88รฌ\99\80 รฌ\82ยฌรฌ\9aยฉรฌ\9e\90 รฌ\9dยธรญ\84ยฐรญ\8e\98รฌ\9dยดรฌ\8aยครฌ\97\90 รชยทยธรซ\93ยครฌ\9d\98 *path operations*รซยฅยผ รญ\8fยฌรญ\95ยจรฌ\8b\9cรญ\82ยครชยณย รฌ\8bยถรชยธยฐ รซ\95\8cรซยฌยธ์
๋๋ค.
+รฌ\9dยดรซ\8a\94 OpenAPI รฌ\8aยครญ\82ยครซยง\88รฌ\99\80 รฌ\82ยฌรฌ\9aยฉรฌ\9e\90 รฌ\9dยธรญ\84ยฐรญ\8e\98รฌ\9dยดรฌ\8aยครฌ\97\90 รชยทยธรซ\93ยครฌ\9d\98 *path operations*รซยฅยผ รญ\8fยฌรญ\95ยจรฌ\8b\9cรญ\82ยครชยธยฐ รฌ\9c\84รญ\95ยจ์
๋๋ค.
-รซ\82\98รซยจยธรฌยง\80รฌ\99\80 รซ\8f\85รซยฆยฝรฌย \81รฌ\9cยผรซยก\9c รชยฒยฉรซยฆยฌรญ\95ยด "mount"รญ\95ย รฌ\88\98 รฌ\97\86รฌ\9cยผรซยฏ\80รซยก\9c, *path operations*รซ\8a\94 รฌยง\81รฌย \91 รญ\8fยฌรญ\95ยจรซ\90\98รซ\8a\94 รชยฒ\83รฌ\9dยด รฌ\95\84รซ\8b\88รซ\9dยผ "clone"(รฌ\9eยฌรฌ\83\9dรฌ\84ยฑ)รซ\90ยฉรซ\8b\88รซ\8bยค.
+FastAPIรซ\8a\94 รฌ\9b\90รซ\9e\98รฌ\9d\98 routerรฌ\99\80 *path operations*รซยฅยผ รญ\99\9cรฌ\84ยฑ รฌ\83\81รญ\83\9cรซยก\9c รฌ\9cย รฌยง\80รญ\95\98รชยณย , รฌ\9a\94รฌยฒยญรฌ\9d\84 รฌยฒ\98รซยฆยฌรญ\95\98รชยณย OpenAPIรซยฅยผ รฌ\83\9dรฌ\84ยฑรญ\95ย รซ\95\8c routerรฌ\9d\98 prefix, dependencies, tags, responses รซยฐ\8f รชยธยฐรญ\83\80 รซยฉ\94รญ\83\80รซ\8dยฐรฌ\9dยดรญ\84ยฐรซยฅยผ รชยฒยฐรญ\95ยฉรญ\95ยฉรซ\8b\88รซ\8bยค.
///
router.include_router(other_router)
```
-`FastAPI` ์ฑ์ `router`๋ฅผ ํฌํจํ๊ธฐ ์ ์ ์ํํด์ผ ํ๋ฉฐ, ๊ทธ๋์ผ `other_router`์ *path operations*๋ ํจ๊ป ํฌํจ๋ฉ๋๋ค.
+`router`๋ฅผ `FastAPI` ์ฑ์ ํฌํจํ๊ธฐ ์ ์ด๋ ํ๋ , ์ด๋ ์์ ์ ํด๋ ๋ฉ๋๋ค. FastAPI๋ ๋ผ์ฐํ
๊ณผ OpenAPI์ `other_router`์ *path operations*๋ ํฌํจํฉ๋๋ค.
+
+๋์ค์ router๋ค์ ์ถ๊ฐ๋ *path operations*๋ ๋์ผํ๊ฒ ์ ์ฉ๋ฉ๋๋ค. ์ด์ ์ ์ํํ ํฌํจ์ ํตํด์๋ ๋ณด์ด๊ฒ ๋ฉ๋๋ค.
+
+/// warning | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+router๋ฅผ ํฌํจํ ๋ค์ `router.routes`๋ฅผ ์ง์ ๋ณํํ๋ ๊ฒ์ ํผํ์ธ์. FastAPI๋ router ํฌํจ์ ์ค์๊ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ฏ๋ก, ์๋ router์ ๊ทธ routes๋ ๋ผ์ฐํ
๊ณผ OpenAPI ์์ฑ์ ์ผ๋ถ๋ก ๋จ์ ์์ต๋๋ค.
+
+๊ฒฝ๋ก์ router๋ฅผ ์ถ๊ฐํ ๋๋ path operation ๋ฐ์ฝ๋ ์ดํฐ์ `.include_router()` ๊ฐ์ ๋ฌธ์ํ๋ API๋ฅผ ์ฌ์ฉํ์ธ์.
+
+`router.routes`๋ ์ต์ข
*path operations*์ ํํํ๋ ๋ชฉ๋ก์ด ์๋๋ผ, route ์ ์์ ํฌํจ๋ router๋ฅผ ๋ด๋ ํ์ ์์ค์ ํธ๋ฆฌ๋ก ์ทจ๊ธํ๊ณ , ์ฌ๊ธฐ์ ์์กดํ์ง ๋ง์ธ์.
+
+///
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`Body` ๋ํ `Query`, `Path` ๊ทธ๋ฆฌ๊ณ ์ดํ์ ๋ณผ ๋ค๋ฅธ ๊ฒ๋ค๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ๋์ผํ ์ถ๊ฐ ๊ฒ์ฆ๊ณผ ๋ฉํ๋ฐ์ดํฐ ๋งค๊ฐ๋ณ์๋ฅผ ๋ชจ๋ ๊ฐ๊ณ ์์ต๋๋ค.
ํ์ง๋ง ์ถ๊ฐ ๋ณธ๋ฌธ ๋งค๊ฐ๋ณ์๋ฅผ ์ ์ธํ ๋์ฒ๋ผ, `item` ํค๋ฅผ ๊ฐ์ง๊ณ ๊ทธ ์์ ๋ชจ๋ธ ๋ด์ฉ์ด ๋ค์ด ์๋ JSON์ ์์ํ๊ฒ ํ๋ ค๋ฉด, `Body`์ ํน๋ณํ ๋งค๊ฐ๋ณ์ `embed`๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
```Python
-item: Item = Body(embed=True)
+item: Annotated[Item, Body(embed=True)]
```
๋ค์๊ณผ ๊ฐ์ด์:
}
```
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`images` ํค๊ฐ ์ด์ ์ด๋ฏธ์ง ๊ฐ์ฒด ๋ฆฌ์คํธ๋ฅผ ๊ฐ๋์ง ์ฃผ๋ชฉํ์ธ์.
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`Offer`๊ฐ `Item`์ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ง๊ณ , ๊ทธ `Item`์ด ๋ค์ ์ ํ ์ฌํญ์ธ `Image` ๋ฆฌ์คํธ๋ฅผ ๊ฐ๋์ง ์ฃผ๋ชฉํ์ธ์
**์์ฒญ** ๋ณธ๋ฌธ์ ์ ์ธํ๊ธฐ ์ํด์ ๋ชจ๋ ๊ฐ๋ ฅํจ๊ณผ ์ด์ ์ ๊ฐ์ถ [Pydantic](https://docs.pydantic.dev/) ๋ชจ๋ธ์ ์ฌ์ฉํฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ธฐ ์ํด, (์ข ๋ ๋ณดํธ์ ์ธ) `POST`, `PUT`, `DELETE` ํน์ `PATCH` ์ค์ ํ๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
<img src="/img/tutorial/cookie-param-models/image01.png">
</div>
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
๋ช
์ฌํ์ธ์, ๋ด๋ถ์ ์ผ๋ก **๋ธ๋ผ์ฐ์ ๋ ์ฟ ํค๋ฅผ ํน๋ณํ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ**ํ๊ธฐ ๋๋ฌธ์ **์๋ฐ์คํฌ๋ฆฝํธ**๊ฐ ์ฝ๊ฒ ์ฟ ํค๋ฅผ ๊ฑด๋๋ฆด ์ **์์ต๋๋ค**.
///
-/// info
+/// note
์ฟ ํค๋ฅผ ์ ์ธํ๊ธฐ ์ํด์๋ `Cookie`๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ํด๋น ๋งค๊ฐ๋ณ์๋ฅผ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์๋ก ํด์ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
///
-/// info
+/// note
**๋ธ๋ผ์ฐ์ ๋ ์ฟ ํค๋ฅผ** ๋ด๋ถ์ ์ผ๋ก ํน๋ณํ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์, **JavaScript**๊ฐ ์ฝ๊ฒ ์ฟ ํค๋ฅผ ๋ค๋ฃจ๋๋ก ํ์ฉํ์ง ์๋๋ค๋ ์ ์ ์ผ๋์ ๋์ธ์.
///
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์ด ์์์์ `X-Key`์ `X-Token`์ด๋ผ๋ ์ปค์คํ
ํค๋๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ์ต๋๋ค.
end
```
-/// info
+/// note
ํด๋ผ์ด์ธํธ์๋ **ํ๋์ ์๋ต**๋ง ์ ์ก๋ฉ๋๋ค. ์ด๋ ์ค๋ฅ ์๋ต ์ค ํ๋์ผ ์๋ ์๊ณ , *๊ฒฝ๋ก ์ฒ๋ฆฌ*์์ ์์ฑ๋ ์๋ต์ผ ์๋ ์์ต๋๋ค.
๊ทธ ํ ์์ ๊ฐ์ ํฌํจํ `dict` ์๋ฃํ์ผ๋ก ๋ฐํํ ๋ฟ์
๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
FastAPI๋ 0.95.0 ๋ฒ์ ๋ถํฐ `Annotated`์ ๋ํ ์ง์์ (๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ์ฌ์ฉํ๊ธฐ ๊ถ์ฅํฉ๋๋ค) ์ถ๊ฐํ์ต๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ๊ณต์ฉ ์ฝ๋๋ฅผ ํ๋ฒ๋ง ์ ์ด๋ ๋๋ฉฐ, **FastAPI**๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ์ํด ์ด์ ๋ํ ํธ์ถ์ ์ฒ๋ฆฌํฉ๋๋ค.
-/// check | ํ์ธ
+/// tip | ํ
ํน๋ณํ ํด๋์ค๋ฅผ ๋ง๋ค์ง ์์๋ ๋๋ฉฐ, ์ด๋ฌํ ๊ฒ ํน์ ๋น์ทํ ์ข
๋ฅ๋ฅผ **FastAPI**์ "๋ฑ๋ก"ํ๊ธฐ ์ํด ์ด๋ค ๊ณณ์ ๋๊ฒจ์ฃผ์ง ์์๋ ๋ฉ๋๋ค.
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
*๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์์๋ `query_or_cookie_extractor`๋ผ๋ ์์กด์ฑ ํ๋๋ง ์ ์ธํ๊ณ ์๋ค๋ ์ ์ ์ฃผ๋ชฉํ์ธ์.
from backend.main import app
```
-### `fastapi dev`์ ๊ฒฝ๋ก ์ง์ ํ๊ธฐ { #fastapi-dev-with-path }
+### `fastapi dev`๋ฅผ ๊ฒฝ๋ก ๋๋ `--entrypoint` CLI ์ต์
๊ณผ ํจ๊ป ์ฌ์ฉํ๊ธฐ { #fastapi-dev-with-path-or-with-entrypoint-cli-option }
`fastapi dev` ๋ช
๋ น์ด์ ํ์ผ ๊ฒฝ๋ก๋ฅผ ์ ๋ฌํ ์๋ ์์ผ๋ฉฐ, ๊ทธ๋ฌ๋ฉด ์ฌ์ฉํ FastAPI app ๊ฐ์ฒด๋ฅผ ์ถ์ ํฉ๋๋ค:
$ fastapi dev main.py
```
-ํ์ง๋ง ๋งค๋ฒ `fastapi` ๋ช
๋ น์ด๋ฅผ ํธ์ถํ ๋๋ง๋ค ์ฌ๋ฐ๋ฅธ ๊ฒฝ๋ก๋ฅผ ์ ๋ฌํด์ผ ํฉ๋๋ค.
-
-๋ํ ๋ค๋ฅธ ๋๊ตฌ๋ค, ์๋ฅผ ๋ค์ด [VS Code ํ์ฅ](../editor-support.md)์ด๋ [FastAPI Cloud](https://fastapicloud.com)๊ฐ ์ด๋ฅผ ์ฐพ์ง ๋ชปํ ์ ์์ผ๋ฏ๋ก, `pyproject.toml`์ `entrypoint`๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
-
-### ์ฑ ๋ฐฐํฌํ๊ธฐ(์ ํ ์ฌํญ) { #deploy-your-app-optional }
-
-์ ํ์ ์ผ๋ก FastAPI ์ฑ์ [FastAPI Cloud](https://fastapicloud.com)์ ๋ฐฐํฌํ ์ ์์ต๋๋ค. ์์ง ๋๊ธฐ์ ๋ช
๋จ์ ๋ฑ๋กํ์ง ์์๋ค๋ฉด, ๋ฑ๋กํ๋ฌ ๊ฐ์ธ์. ๐
-
-์ด๋ฏธ **FastAPI Cloud** ๊ณ์ ์ด ์๋ค๋ฉด(๋๊ธฐ์ ๋ช
๋จ์์ ์ด๋ํด ๋๋ ธ์ต๋๋ค ๐), ํ ๋ฒ์ ๋ช
๋ น์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฐฐํฌํ ์ ์์ต๋๋ค.
-
-๋ฐฐํฌํ๊ธฐ ์ ์, ๋ก๊ทธ์ธ๋์ด ์๋์ง ํ์ธํ์ธ์:
-
-<div class="termy">
+๋๋ `fastapi dev` ๋ช
๋ น์ด์ `--entrypoint` ์ต์
์ ์ ๋ฌํ ์๋ ์์ต๋๋ค:
```console
-$ fastapi login
-
-You are logged in to FastAPI Cloud ๐
+$ fastapi dev --entrypoint main:app
```
-</div>
+ํ์ง๋ง ๋งค๋ฒ `fastapi` ๋ช
๋ น์ด๋ฅผ ํธ์ถํ ๋๋ง๋ค ์ฌ๋ฐ๋ฅธ path\entrypoint๋ฅผ ์ ๋ฌํด์ผ ํฉ๋๋ค.
-๊ทธ ๋ค์ ์ฑ์ ๋ฐฐํฌํฉ๋๋ค:
+๋ํ ๋ค๋ฅธ ๋๊ตฌ๋ค, ์๋ฅผ ๋ค์ด [VS Code ํ์ฅ](../editor-support.md)์ด๋ [FastAPI Cloud](https://fastapicloud.com)๊ฐ ์ด๋ฅผ ์ฐพ์ง ๋ชปํ ์ ์์ผ๋ฏ๋ก, `pyproject.toml`์ `entrypoint`๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
+
+### ์ฑ ๋ฐฐํฌํ๊ธฐ(์ ํ ์ฌํญ) { #deploy-your-app-optional }
+
+์ ํ์ ์ผ๋ก FastAPI ์ฑ์ [FastAPI Cloud](https://fastapicloud.com)์ ๋จ ํ ๋ฒ์ ๋ช
๋ น์ผ๋ก ๋ฐฐํฌํ ์ ์์ต๋๋ค. ๐
<div class="termy">
</div>
+CLI๊ฐ ์ฌ๋ฌ๋ถ์ FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ์๋์ผ๋ก ๊ฐ์งํ๊ณ ํด๋ผ์ฐ๋์ ๋ฐฐํฌํฉ๋๋ค. ๋ก๊ทธ์ธ๋์ด ์์ง ์๋ค๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ์ด๋ ค ์ธ์ฆ ๊ณผ์ ์ ์๋ฃํฉ๋๋ค.
+
์ด๊ฒ ์ ๋ถ์
๋๋ค! ์ด์ ํด๋น URL์์ ์ฑ์ ์ ๊ทผํ ์ ์์ต๋๋ค. โจ
## ๋จ๊ณ๋ณ ์์ฝ { #recap-step-by-step }
```
/items/foo
```
-
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
"๊ฒฝ๋ก"๋ ์ผ๋ฐ์ ์ผ๋ก "์๋ํฌ์ธํธ" ๋๋ "๋ผ์ฐํธ"๋ผ๊ณ ๋ ๋ถ๋ฆฝ๋๋ค.
* ๊ฒฝ๋ก `/`
* <dfn title="HTTP GET ๋ฉ์๋"><code>get</code> ์๋</dfn> ์ฌ์ฉ
-/// info | `@decorator` ์ ๋ณด
+/// note | `@decorator` ์ ๋ณด
์ด `@something` ๋ฌธ๋ฒ์ ํ์ด์ฌ์์ "๋ฐ์ฝ๋ ์ดํฐ"๋ผ ๋ถ๋ฆ
๋๋ค.
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
ํ๊ทธ์ ๋ํ ์์ธํ ๋ด์ฉ์ [๊ฒฝ๋ก ์ฒ๋ฆฌ ๊ตฌ์ฑ](path-operation-configuration.md#tags)์์ ์ฝ์ด๋ณด์ธ์.
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`response_description`์ ๊ตฌ์ฒด์ ์ผ๋ก ์๋ต์ ์ง์นญํ๋ฉฐ, `description`์ ์ผ๋ฐ์ ์ธ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์ง์นญํฉ๋๋ค.
///
-/// check | ํ์ธ
+/// tip | ํ
OpenAPI๋ ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๊ฐ ์๋ต์ ๊ดํ ์ค๋ช
์ ์๊ตฌํ ๊ฒ์ ๋ช
์ํฉ๋๋ค.
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
FastAPI๋ 0.95.0 ๋ฒ์ ์์ `Annotated` ์ง์์ ์ถ๊ฐํ๊ณ (๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ๊ถ์ฅํ๊ธฐ ์์ํ์ต๋๋ค).
* `lt`: `l`ess `t`han
* `le`: `l`ess than or `e`qual
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`Query`, `Path`, ๊ทธ๋ฆฌ๊ณ ๋์ค์ ๋ณด๊ฒ ๋ ๋ค๋ฅธ ํด๋์ค๋ค์ ๊ณตํต `Param` ํด๋์ค์ ์๋ธํด๋์ค์
๋๋ค.
์์ ์์์์, `item_id`๋ `int`๋ก ์ ์ธ๋์์ต๋๋ค.
-/// check | ํ์ธ
+/// tip | ํ
์ด ๊ธฐ๋ฅ์ ํจ์ ๋ด์์ ์ค๋ฅ ๊ฒ์ฌ, ์๋์์ฑ ๋ฑ์ ํธ์ง๊ธฐ ๊ธฐ๋ฅ์ ํ์ฉํ ์ ์๊ฒ ํด์ค๋๋ค.
{"item_id":3}
```
-/// check | ํ์ธ
+/// tip | ํ
ํจ์๊ฐ ๋ฐ์(๋ฐํ๋ ํ๋) ๊ฐ์ ๋ฌธ์์ด `"3"`์ด ์๋๋ผ ํ์ด์ฌ `int` ํ์ธ `3`์
๋๋ค.
`int` ๋์ `float`์ ์ ๊ณตํ๋ฉด(์: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)) ๋์ผํ ์ค๋ฅ๊ฐ ๋ํ๋ฉ๋๋ค.
-/// check | ํ์ธ
+/// tip | ํ
์ฆ, ํ์ด์ฌ ํ์
์ ์ธ์ ํ๋ฉด **FastAPI**๋ ๋ฐ์ดํฐ ๊ฒ์ฆ์ ํฉ๋๋ค.
<img src="/img/tutorial/path-params/image01.png">
-/// check | ํ์ธ
+/// tip | ํ
๋ค์ ํ ๋ฒ, ๋์ผํ ํ์ด์ฌ ํ์
์ ์ธ๋ง์ผ๋ก **FastAPI**๋ ์๋ ๋ํํ ๋ฌธ์(Swagger UI ํตํฉ)๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ด ๊ฒฝ์ฐ ํจ์ ๋งค๊ฐ๋ณ์ `q`๋ ์ ํ์ ์ด๋ฉฐ ๊ธฐ๋ณธ๊ฐ์ผ๋ก `None` ๊ฐ์ด ๋ฉ๋๋ค.
-/// check
+/// tip | ํ
๋ํ **FastAPI**๋ `item_id`๊ฐ ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์์ด๊ณ `q`๋ ๊ฒฝ๋ก ๋งค๊ฐ๋ณ์๊ฐ ์๋๋ผ์ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์๋ผ๋ ๊ฒ์ ์ ์ ๋๋ก ์ถฉ๋ถํ ๋๋ํ๋ค๋ ์ ๋ ํ์ธํ์ธ์.
* `skip`, ๊ธฐ๋ณธ๊ฐ์ด `0`์ธ `int`.
* `limit`, ์ ํ์ ์ธ `int`.
-/// tip
+/// tip | ํ
[๊ฒฝ๋ก ๋งค๊ฐ๋ณ์](path-params.md#predefined-values)์ ๋ง์ฐฌ๊ฐ์ง๋ก `Enum`์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
`File`์ ์ฌ์ฉํ์ฌ ํด๋ผ์ด์ธํธ๊ฐ ์
๋ก๋ํ ํ์ผ๋ค์ ์ ์ํ ์ ์์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์
๋ก๋๋ ํ์ผ์ ์ ๋ฌ๋ฐ๊ธฐ ์ํด ๋จผ์ [`python-multipart`](https://github.com/Kludex/python-multipart)๋ฅผ ์ค์นํด์ผํฉ๋๋ค.
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`File` ์ `Form` ์ผ๋ก๋ถํฐ ์ง์ ์์๋ ํด๋์ค์
๋๋ค.
FastAPI์์ **Pydantic ๋ชจ๋ธ**์ ์ด์ฉํ์ฌ **ํผ ํ๋**๋ฅผ ์ ์ธํ ์ ์์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
ํผ์ ์ฌ์ฉํ๋ ค๋ฉด, ๋จผ์ [`python-multipart`](https://github.com/Kludex/python-multipart)๋ฅผ ์ค์นํ์ธ์.
`File` ๊ณผ `Form` ์ ์ฌ์ฉํ์ฌ ํ์ผ๊ณผ ํผ ํ๋๋ฅผ ๋์์ ์ ์ํ ์ ์์ต๋๋ค.
-/// info
+/// note
์
๋ก๋๋ ํ์ผ ๋ฐ/๋๋ ํผ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ผ๋ ค๋ฉด ๋จผ์ [`python-multipart`](https://github.com/Kludex/python-multipart)๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค.
JSON ๋์ ํผ ํ๋๋ฅผ ๋ฐ์์ผ ํ๋ ๊ฒฝ์ฐ `Form`์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
ํผ์ ์ฌ์ฉํ๋ ค๋ฉด, ๋จผ์ [`python-multipart`](https://github.com/Kludex/python-multipart)๋ฅผ ์ค์นํ์ธ์.
<dfn title="์ฌ์">์ฌ์</dfn>์์๋ ํ๋ ์ด๋ฆ์ด `username` ๋ฐ `password`๋ก ์ ํํ๊ฒ ๋ช
๋ช
๋์ด์ผ ํ๊ณ , JSON์ด ์๋ ํผ ํ๋๋ก ์ ์กํด์ผ ํฉ๋๋ค.
-`Form`์ ์ฌ์ฉํ๋ฉด ์ ํจ์ฑ ๊ฒ์ฌ, ์์ , ๋ณ์นญ(์: `username` ๋์ `user-name`) ๋ฑ์ ํฌํจํ์ฌ `Body`(๋ฐ `Query`, `Path`, `Cookie`)์ ๋์ผํ ๊ตฌ์ฑ์ ์ ์ธํ ์ ์์ต๋๋ค.
+`Form`์ ์ฌ์ฉํ๋ฉด ์ ํจ์ฑ ๊ฒ์ฌ, ์์ , ๋ณ์นญ(์: `user-name` ๋์ `username`) ๋ฑ์ ํฌํจํ์ฌ `Body`(๋ฐ `Query`, `Path`, `Cookie`)์ ๋์ผํ ๊ตฌ์ฑ์ ์ ์ธํ ์ ์์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`Form`์ `Body`์์ ์ง์ ์์๋๋ ํด๋์ค์
๋๋ค.
๊ทธ๋ฌ๋ ํผ์ ํ์ผ์ด ํฌํจ๋ ๊ฒฝ์ฐ, `multipart/form-data`๋ก ์ธ์ฝ๋ฉํฉ๋๋ค. ๋ค์ ์ฅ์์ ํ์ผ ์ฒ๋ฆฌ์ ๋ํด ์ฝ์ ๊ฒ๋๋ค.
-์ด๋ฌํ ์ธ์ฝ๋ฉ ๋ฐ ํผ ํ๋์ ๋ํด ๋ ์ฝ๊ณ ์ถ๋ค๋ฉด, [`POST`์ ๋ํ <abbr title="Mozilla Developer Network - Mozilla ๊ฐ๋ฐ์ ๋คํธ์ํฌ">MDN</abbr> ์น ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
+์ด๋ฌํ ์ธ์ฝ๋ฉ ๋ฐ ํผ ํ๋์ ๋ํด ๋ ์ฝ๊ณ ์ถ๋ค๋ฉด, [`POST`์ ๋ํ <abbr title="Mozilla Developer Network - ๋ชจ์ง๋ผ ๊ฐ๋ฐ์ ๋คํธ์ํฌ">MDN</abbr> ์น ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`EmailStr`์ ์ฌ์ฉํ๋ ค๋ฉด ๋จผ์ [`email-validator`](https://github.com/JoshData/python-email-validator)๋ฅผ ์ค์นํ์ธ์.
ํ์ง๋ง ์ ํจํ Pydantic ํ์
์ด ์๋ ๋ค๋ฅธ ์์์ ๊ฐ์ฒด(์: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ฒด)๋ฅผ ๋ฐํํ๊ณ , ํจ์์์ ๊ทธ๋ ๊ฒ ์ด๋
ธํ
์ด์
ํ๋ฉด, FastAPI๋ ๊ทธ ํ์
์ด๋
ธํ
์ด์
์ผ๋ก๋ถํฐ Pydantic ์๋ต ๋ชจ๋ธ์ ๋ง๋ค๋ ค๊ณ ์๋ํ๋ค๊ฐ ์คํจํฉ๋๋ค.
-๋ํ, ์ ํจํ Pydantic ํ์
์ด ์๋ ํ์
์ด ํ๋ ์ด์ ํฌํจ๋ ์ฌ๋ฌ ํ์
๊ฐ์ <dfn title="์ฌ๋ฌ ํ์
๊ฐ์ union์ '์ด ํ์
๋ค ์ค ์๋ฌด๊ฑฐ๋'๋ฅผ ์๋ฏธํฉ๋๋ค.">union</dfn>์ด ์๋ ๊ฒฝ์ฐ์๋ ๋์ผํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์๋๋ ์คํจํฉ๋๋ค ๐ฅ:
+๋ํ, ์ ํจํ Pydantic ํ์
์ด ์๋ ํ์
์ด ํ๋ ์ด์ ํฌํจ๋ ์ฌ๋ฌ ํ์
๊ฐ์ <dfn title="์ฌ๋ฌ ํ์
๊ฐ์ ์ ๋์จ์ '์ด ํ์
๋ค ์ค ์๋ฌด๊ฑฐ๋'๋ฅผ ์๋ฏธํฉ๋๋ค.">์ ๋์จ</dfn>์ด ์๋ ๊ฒฝ์ฐ์๋ ๋์ผํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์๋๋ ์คํจํฉ๋๋ค ๐ฅ:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
-...์ด๋ ํ์
์ด๋
ธํ
์ด์
์ด Pydantic ํ์
์ด ์๋๊ณ , ๋จ์ผ `Response` ํด๋์ค/์๋ธํด๋์ค๋ ์๋๋ฉฐ, `Response`์ `dict` ๊ฐ union(๋ ์ค ์๋ฌด๊ฑฐ๋)์ด๊ธฐ ๋๋ฌธ์ ์คํจํฉ๋๋ค.
+...์ด๋ ํ์
์ด๋
ธํ
์ด์
์ด Pydantic ํ์
์ด ์๋๊ณ , ๋จ์ผ `Response` ํด๋์ค/์๋ธํด๋์ค๋ ์๋๋ฉฐ, `Response`์ `dict` ๊ฐ ์ ๋์จ(๋ ์ค ์๋ฌด๊ฑฐ๋)์ด๊ธฐ ๋๋ฌธ์ ์คํจํฉ๋๋ค.
### ์๋ต ๋ชจ๋ธ ๋นํ์ฑํ { #disable-response-model }
}
```
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
๋ค์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค:
/// note | ์ฐธ๊ณ
-`status_code` รซ\8a\94 "รซ\8dยฐรฌยฝ\94รซย \88รฌ\9dยดรญ\84ยฐ" รซยฉ\94รฌ\86\8cรซ\93\9c(`get`, `post` รซ\93ยฑ)รฌ\9d\98 รซยงยครชยฐ\9cรซยณ\80รฌ\88\98รฌ\9e\85รซ\8b\88รซ\8bยค. รซยชยจรซ\93ย รซยงยครชยฐ\9cรซยณ\80รฌ\88\98รซ\93ยครชยณยผ รซยณยธรซยฌยธรฌยฒ\98รซ\9fยผ *รชยฒยฝรซยก\9c รฌยฒ\98รซยฆยฌ รญ\95ยจรฌ\88\98*๊ฐ ์๋๋๋ค.
+`status_code` รซ\8a\94 "รซ\8dยฐรฌยฝ\94รซย \88รฌ\9dยดรญ\84ยฐ" รซยฉ\94รฌ\86\8cรซ\93\9c(`get`, `post` รซ\93ยฑ)รฌ\9d\98 รซยงยครชยฐ\9cรซยณ\80รฌ\88\98รฌ\9e\85รซ\8b\88รซ\8bยค. รซ\8bยครซยฅยธ รซยงยครชยฐ\9cรซยณ\80รฌ\88\98รซ\82\98 รซยณยธรซยฌยธรชยณยผ รซ\8bยฌรซยฆยฌ, *รชยฒยฝรซยก\9c รฌยฒ\98รซยฆยฌ รญ\95ยจรฌ\88\98*รฌ\9d\98 รซยงยครชยฐ\9cรซยณ\80รฌ\88\98๊ฐ ์๋๋๋ค.
///
`status_code` ๋งค๊ฐ๋ณ์๋ HTTP ์ํ ์ฝ๋๋ฅผ ์ซ์๋ก ์
๋ ฅ๋ฐ์ต๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`status_code` ๋ ํ์ด์ฌ์ [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus) ์ ๊ฐ์ `IntEnum` ์ ์
๋ ฅ๋ฐ์ ์๋ ์์ต๋๋ค.
///
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
(FastAPI 0.99.0๋ถํฐ ์ฐ์ด๊ธฐ ์์ํ) OpenAPI 3.1.0์ **JSON ์คํค๋ง** ํ์ค์ ์ผ๋ถ์ธ `examples`์ ๋ํ ์ง์์ ์ถ๊ฐํ์ต๋๋ค.
* `File()`
* `Form()`
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์ด ์์ OpenAPI-ํนํ `examples` ๋งค๊ฐ๋ณ์๋ ์ด์ FastAPI `0.103.0`๋ถํฐ `openapi_examples`์
๋๋ค.
JSON ์คํค๋ง์ ์๋ก์ด `examples` ํ๋๋ ์์ ์ **๋จ์ํ `list`**์ผ ๋ฟ์ด๋ฉฐ, (์์์ ์์ ํ ๊ฒ์ฒ๋ผ) OpenAPI์ ๋ค๋ฅธ ๊ณณ์ ์กด์ฌํ๋ ์ถ๊ฐ ๋ฉํ๋ฐ์ดํฐ๊ฐ ์๋ dict๊ฐ ์๋๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
๋ ์ฝ๊ณ ์๋ก์ด JSON ์คํค๋ง์์ ํตํฉ๊ณผ ํจ๊ป OpenAPI 3.1.0๊ฐ ๋ฐฐํฌ๋์์ง๋ง, ์ ์๋์ ์๋ ๋ฌธ์ ์์ฑ์ ์ ๊ณตํ๋ ๋๊ตฌ์ธ Swagger UI๋ OpenAPI 3.1.0์ ์ง์ํ์ง ์์์ต๋๋ค (5.0.0 ๋ฒ์ ๋ถํฐ ์ง์ํฉ๋๋ค ๐).
## ์คํํ๊ธฐ { #run-it }
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
[`python-multipart`](https://github.com/Kludex/python-multipart) ํจํค์ง๋ `pip install "fastapi[standard]"` ๋ช
๋ น์ ์คํํ๋ฉด **FastAPI**์ ํจ๊ป ์๋์ผ๋ก ์ค์น๋ฉ๋๋ค.
<img src="/img/tutorial/security/image01.png">
-/// check | Authorize ๋ฒํผ!
+/// tip | Authorize ๋ฒํผ!
๋ฐ์ง์ด๋ ์ "Authorize" ๋ฒํผ์ด ์ด๋ฏธ ์์ต๋๋ค.
์ด ์์ ์์๋ **OAuth2**์ **Password** ํ๋ก์ฐ์ **Bearer** token์ ์ฌ์ฉํฉ๋๋ค. ์ด๋ฅผ ์ํด `OAuth2PasswordBearer` ํด๋์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
"bearer" token๋ง์ด ์ ์ผํ ์ ํ์ง๋ ์๋๋๋ค.
๊ณง ์ค์ ๊ฒฝ๋ก ์ฒ๋ฆฌ๋ฅผ ๋ง๋ค ๊ฒ์
๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์๊ฒฉํ "Pythonista"๋ผ๋ฉด `token_url` ๋์ `tokenUrl` ๊ฐ์ ํ๋ผ๋ฏธํฐ ์ด๋ฆ ์คํ์ผ์ด ๋ง์์ ๋ค์ง ์์ ์๋ ์์ต๋๋ค.
**FastAPI**๋ ์ด ์์กด์ฑ์ ์ฌ์ฉํด OpenAPI ์คํค๋ง(๋ฐ ์๋ API ๋ฌธ์)์ "security scheme"๋ฅผ ์ ์ํ ์ ์๋ค๋ ๊ฒ์ ์๊ฒ ๋ฉ๋๋ค.
-/// info | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
**FastAPI**๋ (์์กด์ฑ์ ์ ์ธ๋) `OAuth2PasswordBearer` ํด๋์ค๋ฅผ ์ฌ์ฉํด OpenAPI์์ ๋ณด์ ์คํด์ ์ ์ํ ์ ์๋ค๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค. ์ด๋ `OAuth2PasswordBearer`๊ฐ `fastapi.security.oauth2.OAuth2`๋ฅผ ์์ํ๊ณ , ์ด๊ฒ์ด ๋ค์ `fastapi.security.base.SecurityBase`๋ฅผ ์์ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
///
-/// check | ํ์ธ
+/// tip | ํ
์ด ์์กด์ฑ ์์คํ
์ด ์ค๊ณ๋ ๋ฐฉ์์ ๋ชจ๋ `User` ๋ชจ๋ธ์ ๋ฐํํ๋ ์๋ก ๋ค๋ฅธ ์์กด์ฑ(์๋ก ๋ค๋ฅธ "dependables")์ ๊ฐ์ง ์ ์๋๋ก ํฉ๋๋ค.
# ํจ์ค์๋(ํด์ฑ ํฌํจ)๋ฅผ ์ฌ์ฉํ๋ OAuth2, JWT ํ ํฐ์ ์ฌ์ฉํ๋ Bearer { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
-๋ชจ๋ ๋ณด์ ํ๋ฆ์ ๊ตฌ์ฑํ์ผ๋ฏ๋ก, ์ด์ <abbr title="JSON ์น ํ ํฐ">JWT</abbr> ํ ํฐ๊ณผ ์์ ํ ํจ์ค์๋ ํด์ฑ์ ์ฌ์ฉํด ์ ํ๋ฆฌ์ผ์ด์
์ ์ค์ ๋ก ์์ ํ๊ฒ ๋ง๋ค๊ฒ ์ต๋๋ค.
+๋ชจ๋ ๋ณด์ ํ๋ฆ์ ๊ตฌ์ฑํ์ผ๋ฏ๋ก, ์ด์ <abbr title="JSON Web Tokens - JSON ์น ํ ํฐ">JWT</abbr> ํ ํฐ๊ณผ ์์ ํ ํจ์ค์๋ ํด์ฑ์ ์ฌ์ฉํด ์ ํ๋ฆฌ์ผ์ด์
์ ์ค์ ๋ก ์์ ํ๊ฒ ๋ง๋ค๊ฒ ์ต๋๋ค.
์ด ์ฝ๋๋ ์ค์ ๋ก ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ํจ์ค์๋ ํด์๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๋ ๋ฑ์ ์์
์ ํ์ฉํ ์ ์์ต๋๋ค.
</div>
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
RSA๋ ECDSA ๊ฐ์ ์ ์ ์๋ช
์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ ๊ณํ์ด๋ผ๋ฉด, cryptography ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์กด์ฑ์ธ `pyjwt[crypto]`๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค.
Username: `johndoe`
Password: `secret`
-/// check | ํ์ธ
+/// tip | ํ
์ฝ๋ ์ด๋์๋ ํ๋ฌธ ํจ์ค์๋ "`secret`"์ ์๊ณ , ํด์๋ ๋ฒ์ ๋ง ์๋ค๋ ์ ์ ์ ์ํ์ญ์์ค.
* `instagram_basic`์ ํ์ด์ค๋ถ/์ธ์คํ๊ทธ๋จ์์ ์ฌ์ฉํฉ๋๋ค.
* `https://www.googleapis.com/auth/drive`๋ Google์์ ์ฌ์ฉํฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
OAuth2์์ "๋ฒ์"๋ ํ์ํ ํน์ ๊ถํ์ ์ ์ธํ๋ ๋ฌธ์์ด์
๋๋ค.
* `client_id`(์ ํ์ ์ผ๋ก ์ฌ์ฉ) (์์ ์์๋ ํ์ํ์ง ์์ต๋๋ค).
* `client_secret`(์ ํ์ ์ผ๋ก ์ฌ์ฉ) (์์ ์์๋ ํ์ํ์ง ์์ต๋๋ค).
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`OAuth2PasswordRequestForm`์ `OAuth2PasswordBearer`์ ๊ฐ์ด **FastAPI**์ ๋ํ ํน์ ํด๋์ค๊ฐ ์๋๋๋ค.
)
```
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`**user_dict`์ ๋ํ ์์ธํ ์ค๋ช
์ [**์ถ๊ฐ ๋ชจ๋ธ** ๋ฌธ์](../extra-models.md#about-user-in-dict)๋ฅผ ๋ค์ ํ์ธํด๋ณด์ธ์.
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
์ฌ๊ธฐ์ ๋ฐํํ๋ ๊ฐ์ด `Bearer`์ธ ์ถ๊ฐ ํค๋ `WWW-Authenticate`๋ ์ฌ์์ ์ผ๋ถ์
๋๋ค.
์ด๋ [JSON Lines ์คํธ๋ฆฌ๋ฐ](stream-json-lines.md)๊ณผ ๋น์ทํ์ง๋ง, ๋ธ๋ผ์ฐ์ ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource)๋ฅผ ํตํด ์ง์ํ๋ `text/event-stream` ํ์์ ์ฌ์ฉํฉ๋๋ค.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
FastAPI 0.135.0์ ์ถ๊ฐ๋์์ต๋๋ค.
์ฐ์๋ ๋ฐ์ดํฐ๋ฅผ "**์คํธ๋ฆผ**"์ผ๋ก ๋ณด๋ด๊ณ ์ถ๋ค๋ฉด **JSON Lines**๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
-/// info
+/// note
FastAPI 0.134.0์ ์ถ๊ฐ๋์์ต๋๋ค.
JSON ๋ฐฐ์ด(Python์ list์ ํด๋น)๊ณผ ๋งค์ฐ ๋น์ทํ์ง๋ง, ํญ๋ชฉ๋ค์ `[]`๋ก ๊ฐ์ธ๊ณ ํญ๋ชฉ ์ฌ์ด์ `,`๋ฅผ ๋ฃ๋ ๋์ , ์ค๋ง๋ค ํ๋์ JSON ๊ฐ์ฒด๊ฐ ์๊ณ , ์ ์ค ๋ฌธ์๋ก ๊ตฌ๋ถ๋ฉ๋๋ค.
-/// info
+/// note
ํต์ฌ์ ์ ํ๋ฆฌ์ผ์ด์
์ด ๊ฐ ์ค์ ์ฐจ๋ก๋ก ์์ฑํ๋ ๋์, ํด๋ผ์ด์ธํธ๋ ์ด์ ์ค์ ์๋นํ ์ ์๋ค๋ ์ ์
๋๋ค.
## `TestClient` ์ฌ์ฉํ๊ธฐ { #using-testclient }
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`TestClient` ์ฌ์ฉํ๋ ค๋ฉด, ์ฐ์ [`httpx`](https://www.python-httpx.org)๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค.
๋ฐฑ์๋๋ก ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ๋ณด๋ด๋์ง ์ ๋ณด๋ฅผ ๋ ์ป์ผ๋ ค๋ฉด (`httpx` ํน์ `TestClient`๋ฅผ ์ด์ฉํด์) [HTTPX ๋ฌธ์](https://www.python-httpx.org)๋ฅผ ํ์ธํ์ธ์.
-/// info | ์ ๋ณด
+/// note | ์ฐธ๊ณ
`TestClient`๋ Pydantic ๋ชจ๋ธ์ด ์๋๋ผ JSON์ผ๋ก ๋ณํ๋ ์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ต๋๋ค.