--- /dev/null
+# LLM ํ
์คํธ ํ์ผ { #llm-test-file }
+
+์ด ๋ฌธ์๋ ๋ฌธ์๋ฅผ ๋ฒ์ญํ๋ <abbr title="Large Language Model - ๋๊ท๋ชจ ์ธ์ด ๋ชจ๋ธ">LLM</abbr>์ด `scripts/translate.py`์ `general_prompt`์ `docs/{language code}/llm-prompt.md`์ ์ธ์ด๋ณ ํ๋กฌํํธ๋ฅผ ์ดํดํ๋์ง ํ
์คํธํฉ๋๋ค. ์ธ์ด๋ณ ํ๋กฌํํธ๋ `general_prompt`์ ์ถ๊ฐ๋ฉ๋๋ค.
+
+์ฌ๊ธฐ์ ์ถ๊ฐ๋ ํ
์คํธ๋ ์ธ์ด๋ณ ํ๋กฌํํธ๋ฅผ ์ค๊ณํ๋ ๋ชจ๋ ์ฌ๋์ด ๋ณด๊ฒ ๋ฉ๋๋ค.
+
+์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* ์ธ์ด๋ณ ํ๋กฌํํธ `docs/{language code}/llm-prompt.md`๋ฅผ ์ค๋นํฉ๋๋ค.
+* ์ด ๋ฌธ์๋ฅผ ์ํ๋ ๋์ ์ธ์ด๋ก ์๋ก ๋ฒ์ญํฉ๋๋ค(์: `translate.py`์ `translate-page` ๋ช
๋ น). ๊ทธ๋ฌ๋ฉด `docs/{language code}/docs/_llm-test.md` ์๋์ ๋ฒ์ญ์ด ์์ฑ๋ฉ๋๋ค.
+* ๋ฒ์ญ์์ ๋ฌธ์ ๊ฐ ์๋์ง ํ์ธํฉ๋๋ค.
+* ํ์ํ๋ค๋ฉด ์ธ์ด๋ณ ํ๋กฌํํธ, ์ผ๋ฐ ํ๋กฌํํธ, ๋๋ ์์ด ๋ฌธ์๋ฅผ ๊ฐ์ ํฉ๋๋ค.
+* ๊ทธ๋ฐ ๋ค์ ๋ฒ์ญ์์ ๋จ์ ์๋ ๋ฌธ์ ๋ฅผ ์๋์ผ๋ก ์์ ํด ์ข์ ๋ฒ์ญ์ด ๋๊ฒ ํฉ๋๋ค.
+* ์ข์ ๋ฒ์ญ์ ๋ ์ํ์์ ๋ค์ ๋ฒ์ญํฉ๋๋ค. ์ด์์ ์ธ ๊ฒฐ๊ณผ๋ LLM์ด ๋ ์ด์ ๋ฒ์ญ์ ๋ณ๊ฒฝ์ ๋ง๋ค์ง ์๋ ๊ฒ์
๋๋ค. ์ด๋ ์ผ๋ฐ ํ๋กฌํํธ์ ์ธ์ด๋ณ ํ๋กฌํํธ๊ฐ ๊ฐ๋ฅํ ํ ์ต์ ์ด๋ผ๋ ๋ป์
๋๋ค(๋๋๋ก ๋ช ๊ฐ์ง seemingly random ๋ณ๊ฒฝ์ ํ ์ ์๋๋ฐ, ๊ทธ ์ด์ ๋ <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">LLM์ ๊ฒฐ์ ๋ก ์ ์๊ณ ๋ฆฌ์ฆ์ด ์๋๊ธฐ ๋๋ฌธ</a>์
๋๋ค).
+
+ํ
์คํธ:
+
+## ์ฝ๋ ์ค๋ํซ { #code-snippets }
+
+//// tab | ํ
์คํธ
+
+๋ค์์ ์ฝ๋ ์ค๋ํซ์
๋๋ค: `foo`. ๊ทธ๋ฆฌ๊ณ ์ด๊ฒ์ ๋ ๋ค๋ฅธ ์ฝ๋ ์ค๋ํซ์
๋๋ค: `bar`. ๊ทธ๋ฆฌ๊ณ ๋ ํ๋: `baz quux`.
+
+////
+
+//// tab | ์ ๋ณด
+
+์ฝ๋ ์ค๋ํซ์ ๋ด์ฉ์ ๊ทธ๋๋ก ๋์ด์ผ ํฉ๋๋ค.
+
+`scripts/translate.py`์ ์ผ๋ฐ ํ๋กฌํํธ์์ `### Content of code snippets` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+////
+
+## ๋ฐ์ดํ { #quotes }
+
+//// tab | ํ
์คํธ
+
+์ด์ ์ ์น๊ตฌ๊ฐ ์ด๋ ๊ฒ ์ผ์ต๋๋ค: "If you spell incorrectly correctly, you have spelled it incorrectly". ์ด์ ์ ๋ ์ด๋ ๊ฒ ๋ตํ์ต๋๋ค: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"".
+
+/// note | ์ฐธ๊ณ
+
+LLM์ ์๋ง ์ด๊ฒ์ ์๋ชป ๋ฒ์ญํ ๊ฒ์
๋๋ค. ํฅ๋ฏธ๋ก์ด ์ ์ ์ฌ๋ฒ์ญํ ๋ ๊ณ ์ ๋ ๋ฒ์ญ์ ์ ์งํ๋์ง ์ฌ๋ถ๋ฟ์
๋๋ค.
+
+///
+
+////
+
+//// tab | ์ ๋ณด
+
+ํ๋กฌํํธ ์ค๊ณ์๋ ์ค๋ฆฝ ๋ฐ์ดํ๋ฅผ ํ์ดํฌ๊ทธ๋ํผ ๋ฐ์ดํ๋ก ๋ณํํ ์ง ์ ํํ ์ ์์ต๋๋ค. ๊ทธ๋๋ก ๋์ด๋ ๊ด์ฐฎ์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด `docs/de/llm-prompt.md`์ `### Quotes` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+////
+
+## ์ฝ๋ ์ค๋ํซ์ ๋ฐ์ดํ { #quotes-in-code-snippets }
+
+//// tab | ํ
์คํธ
+
+`pip install "foo[bar]"`
+
+์ฝ๋ ์ค๋ํซ์์ ๋ฌธ์์ด ๋ฆฌํฐ๋ด์ ์: `"this"`, `'that'`.
+
+์ฝ๋ ์ค๋ํซ์์ ๋ฌธ์์ด ๋ฆฌํฐ๋ด์ ์ด๋ ค์ด ์: `f"I like {'oranges' if orange else "apples"}"`
+
+ํ๋์ฝ์ด: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
+
+////
+
+//// tab | ์ ๋ณด
+
+... ํ์ง๋ง ์ฝ๋ ์ค๋ํซ ์์ ๋ฐ์ดํ๋ ๊ทธ๋๋ก ์ ์ง๋์ด์ผ ํฉ๋๋ค.
+
+////
+
+## ์ฝ๋ ๋ธ๋ก { #code-blocks }
+
+//// tab | ํ
์คํธ
+
+Bash ์ฝ๋ ์์...
+
+```bash
+# ์ฐ์ฃผ์ ์ธ์ฌ๋ง ์ถ๋ ฅ
+echo "Hello universe"
+```
+
+...๊ทธ๋ฆฌ๊ณ ์ฝ์ ์ฝ๋ ์์...
+
+```console
+$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
+<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting server
+ Searching for package file structure
+```
+
+...๊ทธ๋ฆฌ๊ณ ๋ ๋ค๋ฅธ ์ฝ์ ์ฝ๋ ์์...
+
+```console
+// "Code" ๋๋ ํฐ๋ฆฌ ์์ฑ
+$ mkdir code
+// ํด๋น ๋๋ ํฐ๋ฆฌ๋ก ์ด๋
+$ cd code
+```
+
+...๊ทธ๋ฆฌ๊ณ Python ์ฝ๋ ์์...
+
+```Python
+wont_work() # ์ด๊ฑด ๋์ํ์ง ์์ต๋๋ค ๐ฑ
+works(foo="bar") # ์ด๊ฑด ๋์ํฉ๋๋ค ๐
+```
+
+...์ด์์
๋๋ค.
+
+////
+
+//// tab | ์ ๋ณด
+
+์ฝ๋ ๋ธ๋ก์ ์ฝ๋๋(์ฃผ์์ ์ ์ธํ๊ณ ) ์์ ํ๋ฉด ์ ๋ฉ๋๋ค.
+
+`scripts/translate.py`์ ์ผ๋ฐ ํ๋กฌํํธ์์ `### Content of code blocks` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+////
+
+## ํญ๊ณผ ์์ ๋ฐ์ค { #tabs-and-colored-boxes }
+
+//// tab | ํ
์คํธ
+
+/// info | ์ ๋ณด
+์ผ๋ถ ํ
์คํธ
+///
+
+/// note | ์ฐธ๊ณ
+์ผ๋ถ ํ
์คํธ
+///
+
+/// note Technical details | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+์ผ๋ถ ํ
์คํธ
+///
+
+/// check | ํ์ธ
+์ผ๋ถ ํ
์คํธ
+///
+
+/// tip | ํ
+์ผ๋ถ ํ
์คํธ
+///
+
+/// warning | ๊ฒฝ๊ณ
+์ผ๋ถ ํ
์คํธ
+///
+
+/// danger | ์ํ
+์ผ๋ถ ํ
์คํธ
+///
+
+////
+
+//// tab | ์ ๋ณด
+
+ํญ๊ณผ `Info`/`Note`/`Warning`/๋ฑ์ ๋ธ๋ก์ ์ ๋ชฉ ๋ฒ์ญ์ ์์ง ๋ง๋(`|`) ๋ค์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
+
+`scripts/translate.py`์ ์ผ๋ฐ ํ๋กฌํํธ์์ `### Special blocks`์ `### Tab blocks` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+////
+
+## ์น ๋ฐ ๋ด๋ถ ๋งํฌ { #web-and-internal-links }
+
+//// tab | ํ
์คํธ
+
+๋งํฌ ํ
์คํธ๋ ๋ฒ์ญ๋์ด์ผ ํ๊ณ , ๋งํฌ ์ฃผ์๋ ๋ณ๊ฒฝ๋์ง ์์์ผ ํฉ๋๋ค:
+
+* [์์ ์ ๋ชฉ์ผ๋ก ๊ฐ๋ ๋งํฌ](#code-snippets)
+* [๋ด๋ถ ๋งํฌ](index.md#installation){.internal-link target=_blank}
+* <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">์ธ๋ถ ๋งํฌ</a>
+* <a href="https://fastapi.tiangolo.com/css/styles.css" class="external-link" target="_blank">์คํ์ผ๋ก ๊ฐ๋ ๋งํฌ</a>
+* <a href="https://fastapi.tiangolo.com/js/logic.js" class="external-link" target="_blank">์คํฌ๋ฆฝํธ๋ก ๊ฐ๋ ๋งํฌ</a>
+* <a href="https://fastapi.tiangolo.com/img/foo.jpg" class="external-link" target="_blank">์ด๋ฏธ์ง๋ก ๊ฐ๋ ๋งํฌ</a>
+
+๋งํฌ ํ
์คํธ๋ ๋ฒ์ญ๋์ด์ผ ํ๊ณ , ๋งํฌ ์ฃผ์๋ ๋ฒ์ญ ํ์ด์ง๋ฅผ ๊ฐ๋ฆฌ์ผ์ผ ํฉ๋๋ค:
+
+* <a href="https://fastapi.tiangolo.com/ko/" class="external-link" target="_blank">FastAPI ๋งํฌ</a>
+
+////
+
+//// tab | ์ ๋ณด
+
+๋งํฌ๋ ๋ฒ์ญ๋์ด์ผ ํ์ง๋ง, ์ฃผ์๋ ๋ณ๊ฒฝ๋์ง ์์์ผ ํฉ๋๋ค. ์์ธ๋ FastAPI ๋ฌธ์ ํ์ด์ง๋ก ํฅํ๋ ์ ๋ ๋งํฌ์ด๋ฉฐ, ์ด ๊ฒฝ์ฐ ๋ฒ์ญ ํ์ด์ง๋ก ์ฐ๊ฒฐ๋์ด์ผ ํฉ๋๋ค.
+
+`scripts/translate.py`์ ์ผ๋ฐ ํ๋กฌํํธ์์ `### Links` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+////
+
+## HTML "abbr" ์์ { #html-abbr-elements }
+
+//// tab | ํ
์คํธ
+
+์ฌ๊ธฐ HTML "abbr" ์์๋ก ๊ฐ์ผ ๋ช ๊ฐ์ง๊ฐ ์์ต๋๋ค(์ผ๋ถ๋ ์์๋ก ๋ง๋ ๊ฒ์
๋๋ค):
+
+### abbr๊ฐ ์ ์ฒด ๋ฌธ๊ตฌ๋ฅผ ์ ๊ณต { #the-abbr-gives-a-full-phrase }
+
+* <abbr title="Getting Things Done - ์ผ์ ๋๋ด๋ ๋ฐฉ๋ฒ๋ก ">GTD</abbr>
+* <abbr title="less than - ๋ณด๋ค ์์"><code>lt</code></abbr>
+* <abbr title="XML Web Token - XML ์น ํ ํฐ">XWT</abbr>
+* <abbr title="Parallel Server Gateway Interface - ๋ณ๋ ฌ ์๋ฒ ๊ฒ์ดํธ์จ์ด ์ธํฐํ์ด์ค">PSGI</abbr>
+
+### abbr๊ฐ ์ค๋ช
์ ์ ๊ณต { #the-abbr-gives-an-explanation }
+
+* <abbr title="์ด๋ค ๋ฐฉ์์ผ๋ก๋ ์๋ก ์ฐ๊ฒฐ๋๊ณ ํจ๊ป ์๋ํ๋๋ก ๊ตฌ์ฑ๋ ๋จธ์ ๋ค์ ์งํฉ์
๋๋ค.">cluster</abbr>
+* <abbr title="์
๋ ฅ๊ณผ ์ถ๋ ฅ ๊ณ์ธต ์ฌ์ด์ ์๋ง์ ์๋ ๊ณ์ธต์ ๋ ์ธ๊ณต ์ ๊ฒฝ๋ง์ ์ฌ์ฉํ๋ ๋จธ์ ๋ฌ๋ ๋ฐฉ๋ฒ์ผ๋ก, ์ด๋ฅผ ํตํด ํฌ๊ด์ ์ธ ๋ด๋ถ ๊ตฌ์กฐ๋ฅผ ํ์ฑํฉ๋๋ค">Deep Learning</abbr>
+
+### abbr๊ฐ ์ ์ฒด ๋ฌธ๊ตฌ์ ์ค๋ช
์ ์ ๊ณต { #the-abbr-gives-a-full-phrase-and-an-explanation }
+
+* <abbr title="Mozilla Developer Network - ๋ชจ์ง๋ผ ๊ฐ๋ฐ์ ๋คํธ์ํฌ: Firefox๋ฅผ ๋ง๋๋ ์ฌ๋๋ค์ด ์์ฑํ ๊ฐ๋ฐ์์ฉ ๋ฌธ์">MDN</abbr>
+* <abbr title="Input/Output - ์
๋ ฅ/์ถ๋ ฅ: ๋์คํฌ ์ฝ๊ธฐ ๋๋ ์ฐ๊ธฐ, ๋คํธ์ํฌ ํต์ .">I/O</abbr>.
+
+////
+
+//// tab | ์ ๋ณด
+
+"abbr" ์์์ "title" ์์ฑ์ ๋ช ๊ฐ์ง ๊ตฌ์ฒด์ ์ธ ์ง์นจ์ ๋ฐ๋ผ ๋ฒ์ญ๋ฉ๋๋ค.
+
+๋ฒ์ญ์์๋(์์ด ๋จ์ด๋ฅผ ์ค๋ช
ํ๊ธฐ ์ํด) ์์ฒด "abbr" ์์๋ฅผ ์ถ๊ฐํ ์ ์์ผ๋ฉฐ, LLM์ ์ด๋ฅผ ์ ๊ฑฐํ๋ฉด ์ ๋ฉ๋๋ค.
+
+`scripts/translate.py`์ ์ผ๋ฐ ํ๋กฌํํธ์์ `### HTML abbr elements` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+////
+
+## ์ ๋ชฉ { #headings }
+
+//// tab | ํ
์คํธ
+
+### ์น์ฑ ๊ฐ๋ฐํ๊ธฐ - ํํ ๋ฆฌ์ผ { #develop-a-webapp-a-tutorial }
+
+์๋
ํ์ธ์.
+
+### ํ์
ํํธ์ -์ ๋ํ
์ด์
{ #type-hints-and-annotations }
+
+๋ค์ ์๋
ํ์ธ์.
+
+### super- ๋ฐ subclasses { #super-and-subclasses }
+
+๋ค์ ์๋
ํ์ธ์.
+
+////
+
+//// tab | ์ ๋ณด
+
+์ ๋ชฉ์ ๋ํ ์ ์ผํ ๊ฐํ ๊ท์น์, LLM์ด ์ค๊ดํธ ์์ ํด์ ๋ถ๋ถ์ ๋ณ๊ฒฝํ์ง ์์ ๋งํฌ๊ฐ ๊นจ์ง์ง ์๊ฒ ํ๋ ๊ฒ์
๋๋ค.
+
+`scripts/translate.py`์ ์ผ๋ฐ ํ๋กฌํํธ์์ `### Headings` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+์ธ์ด๋ณ ์ง์นจ์ ์๋ฅผ ๋ค์ด `docs/de/llm-prompt.md`์ `### Headings` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+////
+
+## ๋ฌธ์์์ ์ฌ์ฉ๋๋ ์ฉ์ด { #terms-used-in-the-docs }
+
+//// tab | ํ
์คํธ
+
+* ๋น์
+* ๋น์ ์
+
+* ์: (e.g.)
+* ๋ฑ (etc.)
+
+* `int`๋ก์์ `foo`
+* `str`๋ก์์ `bar`
+* `list`๋ก์์ `baz`
+
+* ํํ ๋ฆฌ์ผ - ์ฌ์ฉ์ ๊ฐ์ด๋
+* ๊ณ ๊ธ ์ฌ์ฉ์ ๊ฐ์ด๋
+* SQLModel ๋ฌธ์
+* API ๋ฌธ์
+* ์๋ ๋ฌธ์
+
+* Data Science
+* Deep Learning
+* Machine Learning
+* Dependency Injection
+* HTTP Basic authentication
+* HTTP Digest
+* ISO format
+* JSON Schema ํ์ค
+* JSON schema
+* schema definition
+* Password Flow
+* Mobile
+
+* deprecated
+* designed
+* invalid
+* on the fly
+* standard
+* default
+* case-sensitive
+* case-insensitive
+
+* ์ ํ๋ฆฌ์ผ์ด์
์ ์๋นํ๋ค
+* ํ์ด์ง๋ฅผ ์๋นํ๋ค
+
+* ์ฑ
+* ์ ํ๋ฆฌ์ผ์ด์
+
+* ์์ฒญ
+* ์๋ต
+* ์ค๋ฅ ์๋ต
+
+* ๊ฒฝ๋ก ์ฒ๋ฆฌ
+* ๊ฒฝ๋ก ์ฒ๋ฆฌ ๋ฐ์ฝ๋ ์ดํฐ
+* ๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์
+
+* body
+* ์์ฒญ body
+* ์๋ต body
+* JSON body
+* form body
+* file body
+* ํจ์ body
+
+* parameter
+* body parameter
+* path parameter
+* query parameter
+* cookie parameter
+* header parameter
+* form parameter
+* function parameter
+
+* event
+* startup event
+* ์๋ฒ startup
+* shutdown event
+* lifespan event
+
+* handler
+* event handler
+* exception handler
+* ์ฒ๋ฆฌํ๋ค
+
+* model
+* Pydantic model
+* data model
+* database model
+* form model
+* model object
+
+* class
+* base class
+* parent class
+* subclass
+* child class
+* sibling class
+* class method
+
+* header
+* headers
+* authorization header
+* `Authorization` header
+* forwarded header
+
+* dependency injection system
+* dependency
+* dependable
+* dependant
+
+* I/O bound
+* CPU bound
+* concurrency
+* parallelism
+* multiprocessing
+
+* env var
+* environment variable
+* `PATH`
+* `PATH` variable
+
+* authentication
+* authentication provider
+* authorization
+* authorization form
+* authorization provider
+* ์ฌ์ฉ์๊ฐ ์ธ์ฆํ๋ค
+* ์์คํ
์ด ์ฌ์ฉ์๋ฅผ ์ธ์ฆํ๋ค
+
+* CLI
+* command line interface
+
+* server
+* client
+
+* cloud provider
+* cloud service
+
+* development
+* development stages
+
+* dict
+* dictionary
+* enumeration
+* enum
+* enum member
+
+* encoder
+* decoder
+* encodeํ๋ค
+* decodeํ๋ค
+
+* exception
+* raiseํ๋ค
+
+* expression
+* statement
+
+* frontend
+* backend
+
+* GitHub discussion
+* GitHub issue
+
+* performance
+* performance optimization
+
+* return type
+* return value
+
+* security
+* security scheme
+
+* task
+* background task
+* task function
+
+* template
+* template engine
+
+* type annotation
+* type hint
+
+* server worker
+* Uvicorn worker
+* Gunicorn Worker
+* worker process
+* worker class
+* workload
+
+* deployment
+* deployํ๋ค
+
+* SDK
+* software development kit
+
+* `APIRouter`
+* `requirements.txt`
+* Bearer Token
+* breaking change
+* bug
+* button
+* callable
+* code
+* commit
+* context manager
+* coroutine
+* database session
+* disk
+* domain
+* engine
+* fake X
+* HTTP GET method
+* item
+* library
+* lifespan
+* lock
+* middleware
+* mobile application
+* module
+* mounting
+* network
+* origin
+* override
+* payload
+* processor
+* property
+* proxy
+* pull request
+* query
+* RAM
+* remote machine
+* status code
+* string
+* tag
+* web framework
+* wildcard
+* returnํ๋ค
+* validateํ๋ค
+
+////
+
+//// tab | ์ ๋ณด
+
+์ด๊ฒ์ ๋ฌธ์์์ ๋ณด์ด๋ (๋๋ถ๋ถ) ๊ธฐ์ ์ฉ์ด์ ๋ถ์์ ํ๊ณ ๋น๊ท๋ฒ์ ์ธ ๋ชฉ๋ก์
๋๋ค. ํ๋กฌํํธ ์ค๊ณ์๊ฐ ์ด๋ค ์ฉ์ด์ ๋ํด LLM์ ์ถ๊ฐ์ ์ธ ๋์์ด ํ์ํ์ง ํ์
ํ๋ ๋ฐ ์ ์ฉํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ข์ ๋ฒ์ญ์ ๊ณ์ ๋ ์ข์ ๋ฒ์ญ์ผ๋ก ๋๋๋ฆด ๋, ๋๋ ์ธ์ด์์ ์ฉ์ด์ ํ์ฉ/๋ณํ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ ๋ฌธ์ ๊ฐ ์์ ๋ ๋์์ด ๋ฉ๋๋ค.
+
+์๋ฅผ ๋ค์ด `docs/de/llm-prompt.md`์ `### List of English terms and their preferred German translations` ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+
+////
--- /dev/null
+# OpenAPI์์ ์ถ๊ฐ ์๋ต { #additional-responses-in-openapi }
+
+/// warning | ๊ฒฝ๊ณ
+
+์ด๋ ๊ฝค ๊ณ ๊ธ ์ฃผ์ ์
๋๋ค.
+
+**FastAPI**๋ฅผ ๋ง ์์ํ๋ค๋ฉด, ์ด ๋ด์ฉ์ด ํ์ ์์ ์๋ ์์ต๋๋ค.
+
+///
+
+์ถ๊ฐ ์ํ ์ฝ๋, ๋ฏธ๋์ด ํ์
, ์ค๋ช
๋ฑ์ ํฌํจํ ์ถ๊ฐ ์๋ต์ ์ ์ธํ ์ ์์ต๋๋ค.
+
+์ด๋ฌํ ์ถ๊ฐ ์๋ต์ OpenAPI ์คํค๋ง์ ํฌํจ๋๋ฏ๋ก API ๋ฌธ์์๋ ํ์๋ฉ๋๋ค.
+
+ํ์ง๋ง ์ด๋ฌํ ์ถ๊ฐ ์๋ต์ ๊ฒฝ์ฐ, ์ํ ์ฝ๋์ ์ฝํ
์ธ ๋ฅผ ํฌํจํ์ฌ `JSONResponse` ๊ฐ์ `Response`๋ฅผ ์ง์ ๋ฐํํ๋๋ก ๋ฐ๋์ ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค.
+
+## `model`์ ์ฌ์ฉํ ์ถ๊ฐ ์๋ต { #additional-response-with-model }
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ ๋ฐ์ฝ๋ ์ดํฐ*์ `responses` ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค.
+
+์ด๋ `dict`๋ฅผ ๋ฐ์ต๋๋ค. ํค๋ ๊ฐ ์๋ต์ ์ํ ์ฝ๋(์: `200`)์ด๊ณ , ๊ฐ์ ๊ฐ ์๋ต์ ๋ํ ์ ๋ณด๋ฅผ ๋ด์ ๋ค๋ฅธ `dict`์
๋๋ค.
+
+๊ฐ ์๋ต `dict`์๋ `response_model`์ฒ๋ผ Pydantic ๋ชจ๋ธ์ ๋ด๋ `model` ํค๊ฐ ์์ ์ ์์ต๋๋ค.
+
+**FastAPI**๋ ๊ทธ ๋ชจ๋ธ์ ์ฌ์ฉํด JSON Schema๋ฅผ ์์ฑํ๊ณ , OpenAPI์ ์ฌ๋ฐ๋ฅธ ์์น์ ํฌํจํฉ๋๋ค.
+
+์๋ฅผ ๋ค์ด, ์ํ ์ฝ๋ `404`์ Pydantic ๋ชจ๋ธ `Message`๋ฅผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ์๋ต์ ์ ์ธํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
+
+/// note | ์ฐธ๊ณ
+
+`JSONResponse`๋ฅผ ์ง์ ๋ฐํํด์ผ ํ๋ค๋ ์ ์ ๊ธฐ์ตํ์ธ์.
+
+///
+
+/// info | ์ ๋ณด
+
+`model` ํค๋ OpenAPI์ ์ผ๋ถ๊ฐ ์๋๋๋ค.
+
+**FastAPI**๋ ์ฌ๊ธฐ์์ Pydantic ๋ชจ๋ธ์ ๊ฐ์ ธ์ JSON Schema๋ฅผ ์์ฑํ๊ณ ์ฌ๋ฐ๋ฅธ ์์น์ ๋ฃ์ต๋๋ค.
+
+์ฌ๋ฐ๋ฅธ ์์น๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* ๊ฐ์ผ๋ก ๋ ๋ค๋ฅธ JSON ๊ฐ์ฒด(`dict`)๋ฅผ ๊ฐ์ง๋ `content` ํค ์์:
+ * ๋ฏธ๋์ด ํ์
(์: `application/json`)์ ํค๋ก ๊ฐ์ง๋ฉฐ, ๊ฐ์ผ๋ก ๋ ๋ค๋ฅธ JSON ๊ฐ์ฒด๋ฅผ ํฌํจํ๊ณ :
+ * `schema` ํค๊ฐ ์๊ณ , ๊ทธ ๊ฐ์ด ๋ชจ๋ธ์์ ์์ฑ๋ JSON Schema์
๋๋ค. ์ด๊ฒ์ด ์ฌ๋ฐ๋ฅธ ์์น์
๋๋ค.
+ * **FastAPI**๋ ์ด๋ฅผ ์ง์ ํฌํจํ๋ ๋์ , OpenAPI์ ๋ค๋ฅธ ์์น์ ์๋ ์ ์ญ JSON Schemas๋ฅผ ์ฐธ์กฐํ๋๋ก ์ฌ๊ธฐ์์ reference๋ฅผ ์ถ๊ฐํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์
๊ณผ ํด๋ผ์ด์ธํธ๊ฐ ๊ทธ JSON Schema๋ฅผ ์ง์ ์ฌ์ฉํ ์ ์๊ณ , ๋ ๋์ ์ฝ๋ ์์ฑ ๋๊ตฌ ๋ฑ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
+
+///
+
+์ด *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๋ํด OpenAPI์ ์์ฑ๋๋ ์๋ต์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+```JSON hl_lines="3-12"
+{
+ "responses": {
+ "404": {
+ "description": "Additional Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Message"
+ }
+ }
+ }
+ },
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+์คํค๋ง๋ OpenAPI ์คํค๋ง ๋ด๋ถ์ ๋ค๋ฅธ ์์น๋ฅผ ์ฐธ์กฐํฉ๋๋ค:
+
+```JSON hl_lines="4-16"
+{
+ "components": {
+ "schemas": {
+ "Message": {
+ "title": "Message",
+ "required": [
+ "message"
+ ],
+ "type": "object",
+ "properties": {
+ "message": {
+ "title": "Message",
+ "type": "string"
+ }
+ }
+ },
+ "Item": {
+ "title": "Item",
+ "required": [
+ "id",
+ "value"
+ ],
+ "type": "object",
+ "properties": {
+ "id": {
+ "title": "Id",
+ "type": "string"
+ },
+ "value": {
+ "title": "Value",
+ "type": "string"
+ }
+ }
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": [
+ "loc",
+ "msg",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string"
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string"
+ }
+ }
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+## ์ฃผ์ ์๋ต์ ๋ํ ์ถ๊ฐ ๋ฏธ๋์ด ํ์
{ #additional-media-types-for-the-main-response }
+
+๊ฐ์ `responses` ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํด ๋์ผํ ์ฃผ์ ์๋ต์ ๋ํด ๋ค๋ฅธ ๋ฏธ๋์ด ํ์
์ ์ถ๊ฐํ ์๋ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด, *๊ฒฝ๋ก ์ฒ๋ฆฌ*๊ฐ JSON ๊ฐ์ฒด(๋ฏธ๋์ด ํ์
`application/json`) ๋๋ PNG ์ด๋ฏธ์ง(๋ฏธ๋์ด ํ์
`image/png`)๋ฅผ ๋ฐํํ ์ ์๋ค๊ณ ์ ์ธํ๊ธฐ ์ํด `image/png`๋ผ๋ ์ถ๊ฐ ๋ฏธ๋์ด ํ์
์ ์ถ๊ฐํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
+
+/// note | ์ฐธ๊ณ
+
+์ด๋ฏธ์ง๋ `FileResponse`๋ฅผ ์ฌ์ฉํด ์ง์ ๋ฐํํด์ผ ํ๋ค๋ ์ ์ ์ ์ํ์ธ์.
+
+///
+
+/// info | ์ ๋ณด
+
+`responses` ํ๋ผ๋ฏธํฐ์์ ๋ค๋ฅธ ๋ฏธ๋์ด ํ์
์ ๋ช
์์ ์ผ๋ก ์ง์ ํ์ง ์๋ ํ, FastAPI๋ ์๋ต์ด ์ฃผ์ ์๋ต ํด๋์ค์ ๋์ผํ ๋ฏธ๋์ด ํ์
(๊ธฐ๋ณธ๊ฐ `application/json`)์ ๊ฐ์ง๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
+
+ํ์ง๋ง ์ปค์คํ
์๋ต ํด๋์ค๋ฅผ ์ง์ ํ๋ฉด์ ๋ฏธ๋์ด ํ์
์ `None`์ผ๋ก ์ค์ ํ๋ค๋ฉด, FastAPI๋ ์ฐ๊ฒฐ๋ ๋ชจ๋ธ์ด ์๋ ๋ชจ๋ ์ถ๊ฐ ์๋ต์ ๋ํด `application/json`์ ์ฌ์ฉํฉ๋๋ค.
+
+///
+
+## ์ ๋ณด ๊ฒฐํฉํ๊ธฐ { #combining-information }
+
+`response_model`, `status_code`, `responses` ํ๋ผ๋ฏธํฐ๋ฅผ ํฌํจํด ์ฌ๋ฌ ์์น์ ์๋ต ์ ๋ณด๋ฅผ ๊ฒฐํฉํ ์๋ ์์ต๋๋ค.
+
+๊ธฐ๋ณธ ์ํ ์ฝ๋ `200`(๋๋ ํ์ํ๋ค๋ฉด ์ปค์คํ
์ฝ๋)์ ์ฌ์ฉํ์ฌ `response_model`์ ์ ์ธํ๊ณ , ๊ทธ์ ๋์ผํ ์๋ต์ ๋ํ ์ถ๊ฐ ์ ๋ณด๋ฅผ `responses`์์ OpenAPI ์คํค๋ง์ ์ง์ ์ ์ธํ ์ ์์ต๋๋ค.
+
+**FastAPI**๋ `responses`์ ์ถ๊ฐ ์ ๋ณด๋ฅผ ์ ์งํ๊ณ , ๋ชจ๋ธ์ JSON Schema์ ๊ฒฐํฉํฉ๋๋ค.
+
+์๋ฅผ ๋ค์ด, Pydantic ๋ชจ๋ธ์ ์ฌ์ฉํ๊ณ ์ปค์คํ
`description`์ ๊ฐ์ง ์ํ ์ฝ๋ `404` ์๋ต์ ์ ์ธํ ์ ์์ต๋๋ค.
+
+๋ํ `response_model`์ ์ฌ์ฉํ๋ ์ํ ์ฝ๋ `200` ์๋ต์ ์ ์ธํ๋, ์ปค์คํ
`example`์ ํฌํจํ ์๋ ์์ต๋๋ค:
+
+{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
+
+์ด ๋ชจ๋ ๋ด์ฉ์ OpenAPI์ ๊ฒฐํฉ๋์ด ํฌํจ๋๊ณ , API ๋ฌธ์์ ํ์๋ฉ๋๋ค:
+
+<img src="/img/tutorial/additional-responses/image01.png">
+
+## ๋ฏธ๋ฆฌ ์ ์๋ ์๋ต๊ณผ ์ปค์คํ
์๋ต ๊ฒฐํฉํ๊ธฐ { #combine-predefined-responses-and-custom-ones }
+
+์ฌ๋ฌ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ์ ์ฉ๋๋ ๋ฏธ๋ฆฌ ์ ์๋ ์๋ต์ด ํ์ํ ์๋ ์์ง๋ง, ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ง๋ค ํ์ํ ์ปค์คํ
์๋ต๊ณผ ๊ฒฐํฉํ๊ณ ์ถ์ ์๋ ์์ต๋๋ค.
+
+๊ทธ๋ฐ ๊ฒฝ์ฐ Python์ `dict` โunpackingโ ๊ธฐ๋ฒ์ธ `**dict_to_unpack`์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+```Python
+old_dict = {
+ "old key": "old value",
+ "second old key": "second old value",
+}
+new_dict = {**old_dict, "new key": "new value"}
+```
+
+์ฌ๊ธฐ์ `new_dict`๋ `old_dict`์ ๋ชจ๋ ํค-๊ฐ ์์ ๋ํด ์ ํค-๊ฐ ์๊น์ง ํฌํจํฉ๋๋ค:
+
+```Python
+{
+ "old key": "old value",
+ "second old key": "second old value",
+ "new key": "new value",
+}
+```
+
+์ด ๊ธฐ๋ฒ์ ์ฌ์ฉํด *๊ฒฝ๋ก ์ฒ๋ฆฌ*์์ ์ผ๋ถ ๋ฏธ๋ฆฌ ์ ์๋ ์๋ต์ ์ฌ์ฌ์ฉํ๊ณ , ์ถ๊ฐ ์ปค์คํ
์๋ต๊ณผ ๊ฒฐํฉํ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด:
+
+{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
+
+## OpenAPI ์๋ต์ ๋ํ ์ถ๊ฐ ์ ๋ณด { #more-information-about-openapi-responses }
+
+์๋ต์ ์ ํํ ๋ฌด์์ ํฌํจํ ์ ์๋์ง ๋ณด๋ ค๋ฉด, OpenAPI ์ฌ์์ ๋ค์ ์น์
์ ํ์ธํ์ธ์:
+
+* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">OpenAPI Responses Object</a>: `Response Object`๋ฅผ ํฌํจํฉ๋๋ค.
+* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">OpenAPI Response Object</a>: `responses` ํ๋ผ๋ฏธํฐ ์์ ๊ฐ ์๋ต์ ์ด๊ฒ์ ์ด๋ค ํญ๋ชฉ์ด๋ ์ง์ ํฌํจํ ์ ์์ต๋๋ค. `description`, `headers`, `content`(์ฌ๊ธฐ์์ ์๋ก ๋ค๋ฅธ ๋ฏธ๋์ด ํ์
๊ณผ JSON Schema๋ฅผ ์ ์ธํฉ๋๋ค), `links` ๋ฑ์ ํฌํจํ ์ ์์ต๋๋ค.
--- /dev/null
+# ํ๋ก์ ๋ค์์ ์คํํ๊ธฐ { #behind-a-proxy }
+
+๋ง์ ๊ฒฝ์ฐ FastAPI ์ฑ ์๋จ์ Traefik์ด๋ Nginx ๊ฐ์ **ํ๋ก์(proxy)**๋ฅผ ๋๊ณ ์ฌ์ฉํฉ๋๋ค.
+
+์ด๋ฐ ํ๋ก์๋ HTTPS ์ธ์ฆ์ ์ฒ๋ฆฌ ๋ฑ ์ฌ๋ฌ ์์
์ ๋ด๋นํ ์ ์์ต๋๋ค.
+
+## ํ๋ก์ ์ ๋ฌ ํค๋ { #proxy-forwarded-headers }
+
+์ ํ๋ฆฌ์ผ์ด์
์๋จ์ **ํ๋ก์**๋ ๋ณดํต **์๋ฒ**๋ก ์์ฒญ์ ๋ณด๋ด๊ธฐ ์ ์, ํด๋น ์์ฒญ์ด ํ๋ก์์ ์ํด **์ ๋ฌ(forwarded)**๋์๋ค๋ ๊ฒ์ ์๋ฒ๊ฐ ์ ์ ์๋๋ก ๋ช๋ช ํค๋๋ฅผ ๋์ ์ผ๋ก ์ค์ ํฉ๋๋ค. ์ด๋ฅผ ํตํด ์๋ฒ๋ ๋๋ฉ์ธ์ ํฌํจํ ์๋์ (๊ณต๊ฐ) URL, HTTPS ์ฌ์ฉ ์ฌ๋ถ ๋ฑ ์ ๋ณด๋ฅผ ์ ์ ์์ต๋๋ค.
+
+**์๋ฒ** ํ๋ก๊ทธ๋จ(์: **FastAPI CLI**๋ฅผ ํตํด ์คํ๋๋ **Uvicorn**)์ ์ด๋ฐ ํค๋๋ฅผ ํด์ํ ์ ์๊ณ , ๊ทธ ์ ๋ณด๋ฅผ ์ ํ๋ฆฌ์ผ์ด์
์ผ๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ๋ณด์์, ์๋ฒ๋ ์์ ์ด ์ ๋ขฐํ ์ ์๋ ํ๋ก์ ๋ค์ ์๋ค๋ ๊ฒ์ ๋ชจ๋ฅด๋ฉด ํด๋น ํค๋๋ฅผ ํด์ํ์ง ์์ต๋๋ค.
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+ํ๋ก์ ํค๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
+* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
+* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
+
+///
+
+### ํ๋ก์ ์ ๋ฌ ํค๋ ํ์ฑํํ๊ธฐ { #enable-proxy-forwarded-headers }
+
+FastAPI CLI๋ฅผ *CLI ์ต์
* `--forwarded-allow-ips`๋ก ์คํํ๊ณ , ์ ๋ฌ ํค๋๋ฅผ ์ฝ์ ์ ์๋๋ก ์ ๋ขฐํ IP ์ฃผ์๋ค์ ๋๊ธธ ์ ์์ต๋๋ค.
+
+`--forwarded-allow-ips="*"`๋ก ์ค์ ํ๋ฉด ๋ค์ด์ค๋ ๋ชจ๋ IP๋ฅผ ์ ๋ขฐํฉ๋๋ค.
+
+**์๋ฒ**๊ฐ ์ ๋ขฐํ ์ ์๋ **ํ๋ก์** ๋ค์ ์๊ณ ํ๋ก์๋ง ์๋ฒ์ ์ ๊ทผํ๋ค๋ฉด, ์ด๋ ํด๋น **ํ๋ก์**์ IP๊ฐ ๋ฌด์์ด๋ ๊ฐ์ ๋ฐ์๋ค์ด๊ฒ ๋ฉ๋๋ค.
+
+<div class="termy">
+
+```console
+$ fastapi run --forwarded-allow-ips="*"
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+```
+
+</div>
+
+### HTTPS์์ ๋ฆฌ๋๋ ์
{ #redirects-with-https }
+
+์๋ฅผ ๋ค์ด, *๊ฒฝ๋ก ์ฒ๋ฆฌ* `/items/`๋ฅผ ์ ์ํ๋ค๊ณ ํด๋ด
์๋ค:
+
+{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
+
+ํด๋ผ์ด์ธํธ๊ฐ `/items`๋ก ์ ๊ทผํ๋ฉด, ๊ธฐ๋ณธ์ ์ผ๋ก `/items/`๋ก ๋ฆฌ๋๋ ์
๋ฉ๋๋ค.
+
+ํ์ง๋ง *CLI ์ต์
* `--forwarded-allow-ips`๋ฅผ ์ค์ ํ๊ธฐ ์ ์๋ `http://localhost:8000/items/`๋ก ๋ฆฌ๋๋ ์
๋ ์ ์์ต๋๋ค.
+
+๊ทธ๋ฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์
์ด `https://mysuperapp.com`์ ํธ์คํ
๋์ด ์๊ณ , ๋ฆฌ๋๋ ์
๋ `https://mysuperapp.com/items/`๋ก ๋์ด์ผ ํ ์ ์์ต๋๋ค.
+
+์ด๋ `--proxy-headers`๋ฅผ ์ค์ ํ๋ฉด FastAPI๊ฐ ์ฌ๋ฐ๋ฅธ ์์น๋ก ๋ฆฌ๋๋ ์
ํ ์ ์์ต๋๋ค. ๐
+
+```
+https://mysuperapp.com/items/
+```
+
+/// tip | ํ
+
+HTTPS์ ๋ํด ๋ ์์๋ณด๋ ค๋ฉด ๊ฐ์ด๋ [HTTPS์ ๋ํ์ฌ](../deployment/https.md){.internal-link target=_blank}๋ฅผ ํ์ธํ์ธ์.
+
+///
+
+### ํ๋ก์ ์ ๋ฌ ํค๋๊ฐ ๋์ํ๋ ๋ฐฉ์ { #how-proxy-forwarded-headers-work }
+
+๋ค์์ **ํ๋ก์**๊ฐ ํด๋ผ์ด์ธํธ์ **์ ํ๋ฆฌ์ผ์ด์
์๋ฒ** ์ฌ์ด์์ ์ ๋ฌ ํค๋๋ฅผ ์ถ๊ฐํ๋ ๊ณผ์ ์ ์๊ฐ์ ์ผ๋ก ๋ํ๋ธ ๊ฒ์
๋๋ค:
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant Proxy as Proxy/Load Balancer
+ participant Server as FastAPI Server
+
+ Client->>Proxy: HTTPS Request<br/>Host: mysuperapp.com<br/>Path: /items
+
+ Note over Proxy: Proxy adds forwarded headers
+
+ Proxy->>Server: HTTP Request<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
+
+ Note over Server: Server interprets headers<br/>(if --forwarded-allow-ips is set)
+
+ Server->>Proxy: HTTP Response<br/>with correct HTTPS URLs
+
+ Proxy->>Client: HTTPS Response
+```
+
+**ํ๋ก์**๋ ์๋์ ํด๋ผ์ด์ธํธ ์์ฒญ์ ๊ฐ๋ก์ฑ๊ณ , **์ ํ๋ฆฌ์ผ์ด์
์๋ฒ**๋ก ์์ฒญ์ ์ ๋ฌํ๊ธฐ ์ ์ ํน์ํ *forwarded* ํค๋(`X-Forwarded-*`)๋ฅผ ์ถ๊ฐํฉ๋๋ค.
+
+์ด ํค๋๋ค์ ๊ทธ๋ ์ง ์์ผ๋ฉด ์ฌ๋ผ์ง ์ ์๋ ์๋ ์์ฒญ์ ์ ๋ณด๋ฅผ ๋ณด์กดํฉ๋๋ค:
+
+* **X-Forwarded-For**: ์๋ ํด๋ผ์ด์ธํธ์ IP ์ฃผ์
+* **X-Forwarded-Proto**: ์๋ ํ๋กํ ์ฝ(`https`)
+* **X-Forwarded-Host**: ์๋ ํธ์คํธ(`mysuperapp.com`)
+
+**FastAPI CLI**๋ฅผ `--forwarded-allow-ips`๋ก ์ค์ ํ๋ฉด, ์ด ํค๋๋ฅผ ์ ๋ขฐํ๊ณ ์ฌ์ฉํฉ๋๋ค. ์๋ฅผ ๋ค์ด ๋ฆฌ๋๋ ์
์์ ์ฌ๋ฐ๋ฅธ URL์ ์์ฑํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
+
+## ์ ๊ฑฐ๋ ๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ๊ฐ์ง ํ๋ก์ { #proxy-with-a-stripped-path-prefix }
+
+์ ํ๋ฆฌ์ผ์ด์
์ ๊ฒฝ๋ก ์ ๋์ฌ(prefix)๋ฅผ ์ถ๊ฐํ๋ ํ๋ก์๋ฅผ ๋ ์๋ ์์ต๋๋ค.
+
+์ด๋ฐ ๊ฒฝ์ฐ `root_path`๋ฅผ ์ฌ์ฉํด ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
+
+`root_path`๋ (FastAPI๊ฐ Starlette๋ฅผ ํตํด ๊ธฐ๋ฐ์ผ๋ก ํ๋) ASGI ์ฌ์์์ ์ ๊ณตํ๋ ๋ฉ์ปค๋์ฆ์
๋๋ค.
+
+`root_path`๋ ์ด๋ฌํ ํน์ ์ฌ๋ก๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
+
+๋ํ ์๋ธ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง์ดํธํ ๋ ๋ด๋ถ์ ์ผ๋ก๋ ์ฌ์ฉ๋ฉ๋๋ค.
+
+๊ฒฝ๋ก ์ ๋์ฌ๊ฐ ์ ๊ฑฐ(stripped)๋๋ ํ๋ก์๊ฐ ์๋ค๋ ๊ฒ์, ์ฝ๋์์๋ `/app`์ ๊ฒฝ๋ก๋ฅผ ์ ์ธํ์ง๋ง, ์์ ํ ๊ฒน(ํ๋ก์)์ ์ถ๊ฐํด **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
์ `/api/v1` ๊ฐ์ ๊ฒฝ๋ก ์๋์ ๋๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
+
+์ด ๊ฒฝ์ฐ ์๋ ๊ฒฝ๋ก `/app`์ ์ค์ ๋ก `/api/v1/app`์์ ์๋น์ค๋ฉ๋๋ค.
+
+์ฝ๋๋ ๋ชจ๋ `/app`๋ง ์๋ค๊ณ ๊ฐ์ ํ๊ณ ์์ฑ๋์ด ์๋๋ฐ๋ ๋ง์
๋๋ค.
+
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
+
+๊ทธ๋ฆฌ๊ณ ํ๋ก์๋ ์์ฒญ์ ์ฑ ์๋ฒ(์๋ง FastAPI CLI๋ฅผ ํตํด ์คํ๋๋ Uvicorn)๋ก ์ ๋ฌํ๊ธฐ ์ ์, ๋์ ์ผ๋ก **๊ฒฝ๋ก ์ ๋์ฌ**๋ฅผ **"์ ๊ฑฐ"**ํฉ๋๋ค. ๊ทธ๋์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฌ์ ํ `/app`์์ ์๋น์ค๋๋ค๊ณ ๋ฏฟ๊ฒ ๋๊ณ , ์ฝ๋ ์ ์ฒด๋ฅผ `/api/v1` ์ ๋์ฌ๋ฅผ ํฌํจํ๋๋ก ์์ ํ ํ์๊ฐ ์์ด์ง๋๋ค.
+
+์ฌ๊ธฐ๊น์ง๋ ๋ณดํต ์ ์์ ์ผ๋ก ๋์ํฉ๋๋ค.
+
+ํ์ง๋ง ํตํฉ ๋ฌธ์ UI(ํ๋ก ํธ์๋)๋ฅผ ์ด๋ฉด, OpenAPI ์คํค๋ง๋ฅผ `/api/v1/openapi.json`์ด ์๋๋ผ `/openapi.json`์์ ๊ฐ์ ธ์ค๋ ค๊ณ ํฉ๋๋ค.
+
+๊ทธ๋์ ๋ธ๋ผ์ฐ์ ์์ ์คํ๋๋ ํ๋ก ํธ์๋๋ `/openapi.json`์ ์ ๊ทผํ๋ ค๊ณ ์๋ํ์ง๋ง OpenAPI ์คํค๋ง๋ฅผ ์ป์ง ๋ชปํฉ๋๋ค.
+
+์ฑ์ ๋ํด `/api/v1` ๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ๊ฐ์ง ํ๋ก์๊ฐ ์์ผ๋ฏ๋ก, ํ๋ก ํธ์๋๋ `/api/v1/openapi.json`์์ OpenAPI ์คํค๋ง๋ฅผ ๊ฐ์ ธ์์ผ ํฉ๋๋ค.
+
+```mermaid
+graph LR
+
+browser("Browser")
+proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
+server["Server on http://127.0.0.1:8000/app"]
+
+browser --> proxy
+proxy --> server
+```
+
+/// tip | ํ
+
+IP `0.0.0.0`์ ๋ณดํต ํด๋น ๋จธ์ /์๋ฒ์์ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ IP์์ ํ๋ก๊ทธ๋จ์ด ๋ฆฌ์จํ๋ค๋ ์๋ฏธ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
+
+///
+
+๋ฌธ์ UI๋ ๋ํ OpenAPI ์คํค๋ง์์ ์ด API `server`๊ฐ `/api/v1`(ํ๋ก์ ๋ค) ์์น์ ์๋ค๊ณ ์ ์ธํด์ผ ํฉ๋๋ค. ์:
+
+```JSON hl_lines="4-8"
+{
+ "openapi": "3.1.0",
+ // More stuff here
+ "servers": [
+ {
+ "url": "/api/v1"
+ }
+ ],
+ "paths": {
+ // More stuff here
+ }
+}
+```
+
+์ด ์์์์ "Proxy"๋ **Traefik** ๊ฐ์ ๊ฒ์ด๊ณ , ์๋ฒ๋ **Uvicorn**์ผ๋ก ์คํ๋๋ FastAPI CLI์ฒ๋ผ, FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ์คํํ๋ ๊ตฌ์ฑ์ผ ์ ์์ต๋๋ค.
+
+### `root_path` ์ ๊ณตํ๊ธฐ { #providing-the-root-path }
+
+์ด๋ฅผ ๋ฌ์ฑํ๋ ค๋ฉด ๋ค์์ฒ๋ผ ์ปค๋งจ๋ ๋ผ์ธ ์ต์
`--root-path`๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+<div class="termy">
+
+```console
+$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+```
+
+</div>
+
+Hypercorn์ ์ฌ์ฉํ๋ค๋ฉด, Hypercorn์๋ `--root-path` ์ต์
์ด ์์ต๋๋ค.
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+ASGI ์ฌ์์ ์ด ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ํด `root_path`๋ฅผ ์ ์ํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ปค๋งจ๋ ๋ผ์ธ ์ต์
`--root-path`๊ฐ ๊ทธ `root_path`๋ฅผ ์ ๊ณตํฉ๋๋ค.
+
+///
+
+### ํ์ฌ `root_path` ํ์ธํ๊ธฐ { #checking-the-current-root-path }
+
+์์ฒญ๋ง๋ค ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฉ ์ค์ธ ํ์ฌ `root_path`๋ฅผ ์ป์ ์ ์๋๋ฐ, ์ด๋ `scope` ๋์
๋๋ฆฌ(ASGI ์ฌ์์ ์ผ๋ถ)์ ํฌํจ๋์ด ์์ต๋๋ค.
+
+์ฌ๊ธฐ์๋ ๋ฐ๋ชจ ๋ชฉ์ ์ ์ํด ๋ฉ์์ง์ ํฌํจํ๊ณ ์์ต๋๋ค.
+
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
+
+๊ทธ ๋ค์ Uvicorn์ ๋ค์๊ณผ ๊ฐ์ด ์์ํ๋ฉด:
+
+<div class="termy">
+
+```console
+$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+```
+
+</div>
+
+์๋ต์ ๋ค์๊ณผ ๋น์ทํ ๊ฒ์
๋๋ค:
+
+```JSON
+{
+ "message": "Hello World",
+ "root_path": "/api/v1"
+}
+```
+
+### FastAPI ์ฑ์์ `root_path` ์ค์ ํ๊ธฐ { #setting-the-root-path-in-the-fastapi-app }
+
+๋๋ `--root-path` ๊ฐ์ ์ปค๋งจ๋ ๋ผ์ธ ์ต์
(๋๋ ๋๋ฑํ ๋ฐฉ๋ฒ)์ ์ ๊ณตํ ์ ์๋ ๊ฒฝ์ฐ, FastAPI ์ฑ์ ์์ฑํ ๋ `root_path` ํ๋ผ๋ฏธํฐ๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
+
+`FastAPI`์ `root_path`๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ Uvicorn์ด๋ Hypercorn์ ์ปค๋งจ๋ ๋ผ์ธ ์ต์
`--root-path`๋ฅผ ์ ๋ฌํ๋ ๊ฒ๊ณผ ๋์ผํฉ๋๋ค.
+
+### `root_path`์ ๋ํ์ฌ { #about-root-path }
+
+์๋ฒ(Uvicorn)๋ ๊ทธ `root_path`๋ฅผ ์ฑ์ ์ ๋ฌํ๋ ๊ฒ ์ธ์๋ ๋ค๋ฅธ ์ฉ๋๋ก ์ฌ์ฉํ์ง ์๋๋ค๋ ์ ์ ๊ธฐ์ตํ์ธ์.
+
+ํ์ง๋ง ๋ธ๋ผ์ฐ์ ๋ก <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>์ ์ ์ํ๋ฉด ์ ์ ์๋ต์ ๋ณผ ์ ์์ต๋๋ค:
+
+```JSON
+{
+ "message": "Hello World",
+ "root_path": "/api/v1"
+}
+```
+
+๋ฐ๋ผ์ `http://127.0.0.1:8000/api/v1/app`๋ก ์ ๊ทผ๋ ๊ฒ์ด๋ผ๊ณ ๊ธฐ๋ํ์ง๋ ์์ต๋๋ค.
+
+Uvicorn์ ํ๋ก์๊ฐ `http://127.0.0.1:8000/app`์์ Uvicorn์ ์ ๊ทผํ ๊ฒ์ ๊ธฐ๋ํ๊ณ , ๊ทธ ์์ `/api/v1` ์ ๋์ฌ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ ํ๋ก์์ ์ฑ
์์
๋๋ค.
+
+## ์ ๊ฑฐ๋ ๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ๊ฐ์ง ํ๋ก์์ ๋ํ์ฌ { #about-proxies-with-a-stripped-path-prefix }
+
+๊ฒฝ๋ก ์ ๋์ฌ๊ฐ ์ ๊ฑฐ๋๋ ํ๋ก์๋ ๊ตฌ์ฑ ๋ฐฉ๋ฒ ์ค ํ๋์ผ ๋ฟ์ด๋ผ๋ ์ ์ ๊ธฐ์ตํ์ธ์.
+
+๋ง์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ ํ๋ก์๊ฐ ๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ์ ๊ฑฐํ์ง ์๋ ๋ฐฉ์์ผ ๊ฒ์
๋๋ค.
+
+๊ทธ๋ฐ ๊ฒฝ์ฐ(๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ์ ๊ฑฐํ์ง ์๋ ๊ฒฝ์ฐ) ํ๋ก์๋ `https://myawesomeapp.com` ๊ฐ์ ๊ณณ์์ ๋ฆฌ์จํ๊ณ , ๋ธ๋ผ์ฐ์ ๊ฐ `https://myawesomeapp.com/api/v1/app`๋ก ์ ๊ทผํ๋ฉด, ์๋ฒ(์: Uvicorn)๊ฐ `http://127.0.0.1:8000`์์ ๋ฆฌ์จํ๊ณ ์์ ๋ ํ๋ก์(๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ์ ๊ฑฐํ์ง ์๋)๋ ๋์ผํ ๊ฒฝ๋ก๋ก Uvicorn์ ์ ๊ทผํฉ๋๋ค: `http://127.0.0.1:8000/api/v1/app`.
+
+## Traefik์ผ๋ก ๋ก์ปฌ ํ
์คํธํ๊ธฐ { #testing-locally-with-traefik }
+
+<a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>์ ์ฌ์ฉํ๋ฉด, ๊ฒฝ๋ก ์ ๋์ฌ๊ฐ ์ ๊ฑฐ๋๋ ๊ตฌ์ฑ์ ๋ก์ปฌ์์ ์ฝ๊ฒ ์คํํ ์ ์์ต๋๋ค.
+
+<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Traefik ๋ค์ด๋ก๋</a>๋ ๋จ์ผ ๋ฐ์ด๋๋ฆฌ์ด๋ฉฐ, ์์ถ ํ์ผ์ ํ๊ณ ํฐ๋ฏธ๋์์ ๋ฐ๋ก ์คํํ ์ ์์ต๋๋ค.
+
+๊ทธ ๋ค์ ๋ค์ ๋ด์ฉ์ ๊ฐ์ง `traefik.toml` ํ์ผ์ ์์ฑํ์ธ์:
+
+```TOML hl_lines="3"
+[entryPoints]
+ [entryPoints.http]
+ address = ":9999"
+
+[providers]
+ [providers.file]
+ filename = "routes.toml"
+```
+
+์ด๋ Traefik์ด 9999 ํฌํธ์์ ๋ฆฌ์จํ๊ณ , ๋ค๋ฅธ ํ์ผ `routes.toml`์ ์ฌ์ฉํ๋๋ก ์ง์ํฉ๋๋ค.
+
+/// tip | ํ
+
+ํ์ค HTTP ํฌํธ 80 ๋์ 9999 ํฌํธ๋ฅผ ์ฌ์ฉํด์, ๊ด๋ฆฌ์(`sudo`) ๊ถํ์ผ๋ก ์คํํ์ง ์์๋ ๋๊ฒ ํ์ต๋๋ค.
+
+///
+
+์ด์ ๋ค๋ฅธ ํ์ผ `routes.toml`์ ์์ฑํ์ธ์:
+
+```TOML hl_lines="5 12 20"
+[http]
+ [http.middlewares]
+
+ [http.middlewares.api-stripprefix.stripPrefix]
+ prefixes = ["/api/v1"]
+
+ [http.routers]
+
+ [http.routers.app-http]
+ entryPoints = ["http"]
+ service = "app"
+ rule = "PathPrefix(`/api/v1`)"
+ middlewares = ["api-stripprefix"]
+
+ [http.services]
+
+ [http.services.app]
+ [http.services.app.loadBalancer]
+ [[http.services.app.loadBalancer.servers]]
+ url = "http://127.0.0.1:8000"
+```
+
+์ด ํ์ผ์ Traefik์ด ๊ฒฝ๋ก ์ ๋์ฌ `/api/v1`์ ์ฌ์ฉํ๋๋ก ์ค์ ํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ Traefik์ ์์ฒญ์ `http://127.0.0.1:8000`์์ ์คํ ์ค์ธ Uvicorn์ผ๋ก ์ ๋ฌํฉ๋๋ค.
+
+์ด์ Traefik์ ์์ํ์ธ์:
+
+<div class="termy">
+
+```console
+$ ./traefik --configFile=traefik.toml
+
+INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
+```
+
+</div>
+
+๊ทธ๋ฆฌ๊ณ `--root-path` ์ต์
์ ์ฌ์ฉํด ์ฑ์ ์์ํ์ธ์:
+
+<div class="termy">
+
+```console
+$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+```
+
+</div>
+
+### ์๋ต ํ์ธํ๊ธฐ { #check-the-responses }
+
+์ด์ Uvicorn์ ํฌํธ๋ก ๋ URL์ธ <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>๋ก ์ ์ํ๋ฉด ์ ์ ์๋ต์ ๋ณผ ์ ์์ต๋๋ค:
+
+```JSON
+{
+ "message": "Hello World",
+ "root_path": "/api/v1"
+}
+```
+
+/// tip | ํ
+
+`http://127.0.0.1:8000/app`๋ก ์ ๊ทผํ๋๋ฐ๋ `/api/v1`์ `root_path`๊ฐ ํ์๋๋ ๊ฒ์ ์ฃผ์ํ์ธ์. ์ด๋ ์ต์
`--root-path`์์ ๊ฐ์ ธ์จ ๊ฐ์
๋๋ค.
+
+///
+
+์ด์ Traefik์ ํฌํธ๊ฐ ํฌํจ๋๊ณ ๊ฒฝ๋ก ์ ๋์ฌ๊ฐ ํฌํจ๋ URL <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>์ ์ฌ์ธ์.
+
+๋์ผํ ์๋ต์ ์ป์ต๋๋ค:
+
+```JSON
+{
+ "message": "Hello World",
+ "root_path": "/api/v1"
+}
+```
+
+ํ์ง๋ง ์ด๋ฒ์๋ ํ๋ก์๊ฐ ์ ๊ณตํ ์ ๋์ฌ ๊ฒฝ๋ก `/api/v1`์ด ํฌํจ๋ URL์์์ ์๋ต์
๋๋ค.
+
+๋ฌผ๋ก ์ฌ๊ธฐ์์ ์์ด๋์ด๋ ๋ชจ๋๊ฐ ํ๋ก์๋ฅผ ํตํด ์ฑ์ ์ ๊ทผํ๋ค๋ ๊ฒ์ด๋ฏ๋ก, `/api/v1` ๊ฒฝ๋ก ์ ๋์ฌ๊ฐ ์๋ ๋ฒ์ ์ด "์ฌ๋ฐ๋ฅธ" ์ ๊ทผ์
๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๊ฒฝ๋ก ์ ๋์ฌ๊ฐ ์๋ ๋ฒ์ (`http://127.0.0.1:8000/app`)์ Uvicorn์ด ์ง์ ์ ๊ณตํ๋ ๊ฒ์ด๋ฉฐ, ์ค์ง _ํ๋ก์_(Traefik)๊ฐ ์ ๊ทผํ๊ธฐ ์ํ ์ฉ๋์
๋๋ค.
+
+์ด๋ ํ๋ก์(Traefik)๊ฐ ๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์๋ฒ(Uvicorn)๊ฐ ์ต์
`--root-path`๋ก๋ถํฐ์ `root_path`๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง๋ฅผ ๋ณด์ฌ์ค๋๋ค.
+
+### ๋ฌธ์ UI ํ์ธํ๊ธฐ { #check-the-docs-ui }
+
+ํ์ง๋ง ์ฌ๋ฏธ์๋ ๋ถ๋ถ์ ์ฌ๊ธฐ์
๋๋ค. โจ
+
+์ฑ์ ์ ๊ทผํ๋ "๊ณต์" ๋ฐฉ๋ฒ์ ์ฐ๋ฆฌ๊ฐ ์ ์ํ ๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ๊ฐ์ง ํ๋ก์๋ฅผ ํตํด์์
๋๋ค. ๋ฐ๋ผ์ ๊ธฐ๋ํ๋ ๋๋ก, URL์ ๊ฒฝ๋ก ์ ๋์ฌ๊ฐ ์๋ ์ํ์์ Uvicorn์ด ์ง์ ์ ๊ณตํ๋ docs UI๋ฅผ ์๋ํ๋ฉด, ํ๋ก์๋ฅผ ํตํด ์ ๊ทผ๋๋ค๊ณ ๊ฐ์ ํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋์ํ์ง ์์ต๋๋ค.
+
+<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>์์ ํ์ธํ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/behind-a-proxy/image01.png">
+
+ํ์ง๋ง ํ๋ก์(ํฌํธ `9999`)๋ฅผ ์ฌ์ฉํด "๊ณต์" URL์ธ `/api/v1/docs`์์ docs UI์ ์ ๊ทผํ๋ฉด, ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํฉ๋๋ค! ๐
+
+<a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>์์ ํ์ธํ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/behind-a-proxy/image02.png">
+
+์ํ๋ ๊ทธ๋๋ก์
๋๋ค. โ๏ธ
+
+์ด๋ FastAPI๊ฐ ์ด `root_path`๋ฅผ ์ฌ์ฉํด, OpenAPI์์ ๊ธฐ๋ณธ `server`๋ฅผ `root_path`๊ฐ ์ ๊ณตํ URL๋ก ์์ฑํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+## ์ถ๊ฐ ์๋ฒ { #additional-servers }
+
+/// warning | ๊ฒฝ๊ณ
+
+์ด๋ ๋ ๊ณ ๊ธ ์ฌ์ฉ ์ฌ๋ก์
๋๋ค. ๊ฑด๋๋ฐ์ด๋ ๊ด์ฐฎ์ต๋๋ค.
+
+///
+
+๊ธฐ๋ณธ์ ์ผ๋ก **FastAPI**๋ OpenAPI ์คํค๋ง์์ `root_path`์ URL๋ก `server`๋ฅผ ์์ฑํฉ๋๋ค.
+
+ํ์ง๋ง ์๋ฅผ ๋ค์ด ๋์ผํ docs UI๊ฐ ์คํ
์ด์ง๊ณผ ํ๋ก๋์
ํ๊ฒฝ ๋ชจ๋์ ์ํธ์์ฉํ๋๋ก ํ๋ ค๋ฉด, ๋ค๋ฅธ ๋์ `servers`๋ฅผ ์ ๊ณตํ ์๋ ์์ต๋๋ค.
+
+์ฌ์ฉ์ ์ ์ `servers` ๋ฆฌ์คํธ๋ฅผ ์ ๋ฌํ๊ณ `root_path`(API๊ฐ ํ๋ก์ ๋ค์ ์๊ธฐ ๋๋ฌธ)๊ฐ ์๋ค๋ฉด, **FastAPI**๋ ๋ฆฌ์คํธ์ ๋งจ ์์ ์ด `root_path`๋ฅผ ๊ฐ์ง "server"๋ฅผ ์ฝ์
ํฉ๋๋ค.
+
+์:
+
+{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
+
+๋ค์๊ณผ ๊ฐ์ OpenAPI ์คํค๋ง๋ฅผ ์์ฑํฉ๋๋ค:
+
+```JSON hl_lines="5-7"
+{
+ "openapi": "3.1.0",
+ // More stuff here
+ "servers": [
+ {
+ "url": "/api/v1"
+ },
+ {
+ "url": "https://stag.example.com",
+ "description": "Staging environment"
+ },
+ {
+ "url": "https://prod.example.com",
+ "description": "Production environment"
+ }
+ ],
+ "paths": {
+ // More stuff here
+ }
+}
+```
+
+/// tip | ํ
+
+`root_path`์์ ๊ฐ์ ธ์จ ๊ฐ์ธ `/api/v1`์ `url` ๊ฐ์ ๊ฐ์ง, ์๋ ์์ฑ๋ server์ ์ฃผ๋ชฉํ์ธ์.
+
+///
+
+<a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>์ docs UI์์๋ ๋ค์์ฒ๋ผ ๋ณด์
๋๋ค:
+
+<img src="/img/tutorial/behind-a-proxy/image03.png">
+
+/// tip | ํ
+
+docs UI๋ ์ ํํ server์ ์ํธ์์ฉํฉ๋๋ค.
+
+///
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+OpenAPI ์ฌ์์์ `servers` ์์ฑ์ ์ ํ ์ฌํญ์
๋๋ค.
+
+`servers` ํ๋ผ๋ฏธํฐ๋ฅผ ์ง์ ํ์ง ์๊ณ `root_path`๊ฐ `/`์ ๊ฐ๋ค๋ฉด, ์์ฑ๋ OpenAPI ์คํค๋ง์ `servers` ์์ฑ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์์ ํ ์๋ต๋๋ฉฐ, ์ด๋ `url` ๊ฐ์ด `/`์ธ ๋จ์ผ server์ ๋๋ฑํฉ๋๋ค.
+
+///
+
+### `root_path`์์ ์๋ server ๋นํ์ฑํํ๊ธฐ { #disable-automatic-server-from-root-path }
+
+**FastAPI**๊ฐ `root_path`๋ฅผ ์ฌ์ฉํ ์๋ server๋ฅผ ํฌํจํ์ง ์๊ฒ ํ๋ ค๋ฉด, `root_path_in_servers=False` ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
+
+๊ทธ๋ฌ๋ฉด OpenAPI ์คํค๋ง์ ํฌํจ๋์ง ์์ต๋๋ค.
+
+## ์๋ธ ์ ํ๋ฆฌ์ผ์ด์
๋ง์ดํธํ๊ธฐ { #mounting-a-sub-application }
+
+ํ๋ก์์์ `root_path`๋ฅผ ์ฌ์ฉํ๋ฉด์๋, [์๋ธ ์ ํ๋ฆฌ์ผ์ด์
- ๋ง์ดํธ](sub-applications.md){.internal-link target=_blank}์ ์ค๋ช
๋ ๊ฒ์ฒ๋ผ ์๋ธ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง์ดํธํด์ผ ํ๋ค๋ฉด, ๊ธฐ๋ํ๋ ๋๋ก ์ผ๋ฐ์ ์ผ๋ก ์ํํ ์ ์์ต๋๋ค.
+
+FastAPI๊ฐ ๋ด๋ถ์ ์ผ๋ก `root_path`๋ฅผ ๋๋ํ๊ฒ ์ฌ์ฉํ๋ฏ๋ก, ๊ทธ๋ฅ ๋์ํฉ๋๋ค. โจ
--- /dev/null
+# Dataclasses ์ฌ์ฉํ๊ธฐ { #using-dataclasses }
+
+FastAPI๋ **Pydantic** ์์ ๊ตฌ์ถ๋์ด ์์ผ๋ฉฐ, ์ง๊ธ๊น์ง๋ Pydantic ๋ชจ๋ธ์ ์ฌ์ฉํด ์์ฒญ๊ณผ ์๋ต์ ์ ์ธํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ๋๋ ธ์ต๋๋ค.
+
+ํ์ง๋ง FastAPI๋ <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>๋ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์ ์ง์ํฉ๋๋ค:
+
+{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
+
+์ด๋ **Pydantic** ๋๋ถ์ ์ฌ์ ํ ์ง์๋๋๋ฐ, Pydantic์ด <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">`dataclasses`์ ๋ํ ๋ด๋ถ ์ง์</a>์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+๋ฐ๋ผ์ ์ ์ฝ๋์ฒ๋ผ Pydantic์ ๋ช
์์ ์ผ๋ก ์ฌ์ฉํ์ง ์๋๋ผ๋, FastAPI๋ Pydantic์ ์ฌ์ฉํด ํ์ค dataclasses๋ฅผ Pydantic์ dataclasses ๋ณํ์ผ๋ก ๋ณํํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋ฌผ๋ก ๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ๋ ๋์ผํ๊ฒ ์ง์ํฉ๋๋ค:
+
+* ๋ฐ์ดํฐ ๊ฒ์ฆ
+* ๋ฐ์ดํฐ ์ง๋ ฌํ
+* ๋ฐ์ดํฐ ๋ฌธ์ํ ๋ฑ
+
+์ด๋ Pydantic ๋ชจ๋ธ์ ์ฌ์ฉํ ๋์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก๋ ๋ด๋ถ์ ์ผ๋ก๋ Pydantic์ ์ฌ์ฉํด ๊ฐ์ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋ฉ๋๋ค.
+
+/// info | ์ ๋ณด
+
+dataclasses๋ Pydantic ๋ชจ๋ธ์ด ํ ์ ์๋ ๋ชจ๋ ๊ฒ์ ํ ์๋ ์๋ค๋ ์ ์ ๊ธฐ์ตํ์ธ์.
+
+๊ทธ๋์ ์ฌ์ ํ Pydantic ๋ชจ๋ธ์ ์ฌ์ฉํด์ผ ํ ์๋ ์์ต๋๋ค.
+
+ํ์ง๋ง ์ด๋ฏธ ์ฌ๋ฌ dataclasses๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด, ์ด๊ฒ์ FastAPI๋ก ์น API๋ฅผ ๊ตฌ๋ํ๋ ๋ฐ ๊ทธ๊ฒ๋ค์ ํ์ฉํ ์ ์๋ ์ข์ ๋ฐฉ๋ฒ์
๋๋ค. ๐ค
+
+///
+
+## `response_model`์์ Dataclasses ์ฌ์ฉํ๊ธฐ { #dataclasses-in-response-model }
+
+`response_model` ๋งค๊ฐ๋ณ์์์๋ `dataclasses`๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
+
+dataclass๋ ์๋์ผ๋ก Pydantic dataclass๋ก ๋ณํ๋ฉ๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด ํด๋น ์คํค๋ง๊ฐ API docs ์ฌ์ฉ์ ์ธํฐํ์ด์ค์ ํ์๋ฉ๋๋ค:
+
+<img src="/img/tutorial/dataclasses/image01.png">
+
+## ์ค์ฒฉ ๋ฐ์ดํฐ ๊ตฌ์กฐ์์ Dataclasses ์ฌ์ฉํ๊ธฐ { #dataclasses-in-nested-data-structures }
+
+`dataclasses`๋ฅผ ๋ค๋ฅธ ํ์
์ ๋ํ
์ด์
๊ณผ ์กฐํฉํด ์ค์ฒฉ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค ์๋ ์์ต๋๋ค.
+
+์ผ๋ถ ๊ฒฝ์ฐ์๋ Pydantic ๋ฒ์ ์ `dataclasses`๋ฅผ ์ฌ์ฉํด์ผ ํ ์๋ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ์๋ ์์ฑ๋ API ๋ฌธ์์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ์
๋๋ค.
+
+๊ทธ๋ฐ ๊ฒฝ์ฐ ํ์ค `dataclasses`๋ฅผ ๋๋กญ์ธ ๋์ฒด์ฌ์ธ `pydantic.dataclasses`๋ก ๊ฐ๋จํ ๋ฐ๊พธ๋ฉด ๋ฉ๋๋ค:
+
+{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
+
+1. ํ์ค `dataclasses`์์ `field`๋ฅผ ๊ณ์ ์ํฌํธํฉ๋๋ค.
+
+2. `pydantic.dataclasses`๋ `dataclasses`์ ๋๋กญ์ธ ๋์ฒด์ฌ์
๋๋ค.
+
+3. `Author` dataclass์๋ `Item` dataclasses์ ๋ฆฌ์คํธ๊ฐ ํฌํจ๋ฉ๋๋ค.
+
+4. `Author` dataclass๊ฐ `response_model` ๋งค๊ฐ๋ณ์๋ก ์ฌ์ฉ๋ฉ๋๋ค.
+
+5. ์์ฒญ ๋ณธ๋ฌธ์ผ๋ก dataclasses์ ํจ๊ป ๋ค๋ฅธ ํ์ค ํ์
์ ๋ํ
์ด์
์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+ ์ด ๊ฒฝ์ฐ์๋ `Item` dataclasses์ ๋ฆฌ์คํธ์
๋๋ค.
+
+6. ์ฌ๊ธฐ์๋ dataclasses ๋ฆฌ์คํธ์ธ `items`๋ฅผ ํฌํจํ๋ ๋์
๋๋ฆฌ๋ฅผ ๋ฐํํฉ๋๋ค.
+
+ FastAPI๋ ์ฌ์ ํ ๋ฐ์ดํฐ๋ฅผ JSON์ผ๋ก <abbr title="converting the data to a format that can be transmitted - ๋ฐ์ดํฐ๋ฅผ ์ ์ก ๊ฐ๋ฅํ ํ์์ผ๋ก ๋ณํํ๋ ๊ฒ">serializing</abbr>ํ ์ ์์ต๋๋ค.
+
+7. ์ฌ๊ธฐ์ `response_model`์ `Author` dataclasses ๋ฆฌ์คํธ์ ๋ํ ํ์
์ ๋ํ
์ด์
์ ์ฌ์ฉํฉ๋๋ค.
+
+ ๋ค์ ๋งํด, `dataclasses`๋ฅผ ํ์ค ํ์
์ ๋ํ
์ด์
๊ณผ ์กฐํฉํ ์ ์์ต๋๋ค.
+
+8. ์ด *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*๋ `async def` ๋์ ์ผ๋ฐ `def`๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ ์ ์ ์ฃผ๋ชฉํ์ธ์.
+
+ ์ธ์ ๋์ฒ๋ผ FastAPI์์๋ ํ์์ ๋ฐ๋ผ `def`์ `async def`๋ฅผ ์กฐํฉํด ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+ ์ด๋ค ๊ฒ์ ์ธ์ ์ฌ์ฉํด์ผ ํ๋์ง ๋ค์ ํ์ธํ๊ณ ์ถ๋ค๋ฉด, [`async`์ `await`](../async.md#in-a-hurry){.internal-link target=_blank} ๋ฌธ์์ _"๊ธํ์ ๊ฐ์?"_ ์น์
์ ํ์ธํ์ธ์.
+
+9. ์ด *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*๋ dataclasses๋ฅผ(๋ฌผ๋ก ๋ฐํํ ์๋ ์์ง๋ง) ๋ฐํํ์ง ์๊ณ , ๋ด๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋์
๋๋ฆฌ๋ค์ ๋ฆฌ์คํธ๋ฅผ ๋ฐํํฉ๋๋ค.
+
+ FastAPI๋ `response_model` ๋งค๊ฐ๋ณ์(dataclasses ํฌํจ)๋ฅผ ์ฌ์ฉํด ์๋ต์ ๋ณํํฉ๋๋ค.
+
+`dataclasses`๋ ๋ค๋ฅธ ํ์
์ ๋ํ
์ด์
๊ณผ ๋งค์ฐ ๋ค์ํ ์กฐํฉ์ผ๋ก ๊ฒฐํฉํด ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
+
+๋ ๊ตฌ์ฒด์ ์ธ ๋ด์ฉ์ ์ ์ฝ๋ ๋ด ์ ๋ํ
์ด์
ํ์ ํ์ธํ์ธ์.
+
+## ๋ ์์๋ณด๊ธฐ { #learn-more }
+
+`dataclasses`๋ฅผ ๋ค๋ฅธ Pydantic ๋ชจ๋ธ๊ณผ ์กฐํฉํ๊ฑฐ๋, ์ด๋ฅผ ์์ํ๊ฑฐ๋, ์ฌ๋ฌ๋ถ์ ๋ชจ๋ธ์ ํฌํจํ๋ ๋ฑ์ ์์
๋ ํ ์ ์์ต๋๋ค.
+
+์์ธํ ๋ด์ฉ์ <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">dataclasses์ ๊ดํ Pydantic ๋ฌธ์</a>๋ฅผ ์ฐธ๊ณ ํ์ธ์.
+
+## ๋ฒ์ { #version }
+
+์ด ๊ธฐ๋ฅ์ FastAPI `0.67.0` ๋ฒ์ ๋ถํฐ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๐
--- /dev/null
+# SDK ์์ฑํ๊ธฐ { #generating-sdks }
+
+**FastAPI**๋ **OpenAPI** ์ฌ์์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฏ๋ก, FastAPI์ API๋ ๋ง์ ๋๊ตฌ๊ฐ ์ดํดํ ์ ์๋ ํ์ค ํ์์ผ๋ก ์ค๋ช
ํ ์ ์์ต๋๋ค.
+
+๋๋ถ์ ์ฌ๋ฌ ์ธ์ด์ฉ ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(<abbr title="Software Development Kits - ์ํํธ์จ์ด ๊ฐ๋ฐ ํคํธ">**SDKs**</abbr>), ์ต์ **๋ฌธ์**, ๊ทธ๋ฆฌ๊ณ ์ฝ๋์ ๋๊ธฐํ๋ **ํ
์คํธ** ๋๋ **์๋ํ ์ํฌํ๋ก**๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค.
+
+์ด ๊ฐ์ด๋์์๋ FastAPI ๋ฐฑ์๋์ฉ **TypeScript SDK**๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋๋ค.
+
+## ์คํ ์์ค SDK ์์ฑ๊ธฐ { #open-source-sdk-generators }
+
+๋ค์ํ๊ฒ ํ์ฉํ ์ ์๋ ์ต์
์ผ๋ก <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>๊ฐ ์์ผ๋ฉฐ, **๋ค์ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด**๋ฅผ ์ง์ํ๊ณ OpenAPI ์ฌ์์ผ๋ก๋ถํฐ SDK๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
+
+**TypeScript ํด๋ผ์ด์ธํธ**์ ๊ฒฝ์ฐ <a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a>๋ TypeScript ์ํ๊ณ์ ์ต์ ํ๋ ๊ฒฝํ์ ์ ๊ณตํ๋ ๋ชฉ์ ์ ๋ง๊ฒ ์ค๊ณ๋ ์๋ฃจ์
์
๋๋ค.
+
+๋ ๋ง์ SDK ์์ฑ๊ธฐ๋ <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a>์์ ํ์ธํ ์ ์์ต๋๋ค.
+
+/// tip | ํ
+
+FastAPI๋ **OpenAPI 3.1** ์ฌ์์ ์๋์ผ๋ก ์์ฑํ๋ฏ๋ก, ์ฌ์ฉํ๋ ๋๊ตฌ๋ ์ด ๋ฒ์ ์ ์ง์ํด์ผ ํฉ๋๋ค.
+
+///
+
+## FastAPI ์คํฐ์์ SDK ์์ฑ๊ธฐ { #sdk-generators-from-fastapi-sponsors }
+
+์ด ์น์
์์๋ FastAPI๋ฅผ ํ์ํ๋ ํ์ฌ๋ค์ด ์ ๊ณตํ๋ **๋ฒค์ฒ ํฌ์ ๊ธฐ๋ฐ** ๋ฐ **๊ธฐ์
์ง์** ์๋ฃจ์
์ ์๊ฐํฉ๋๋ค. ์ด ์ ํ๋ค์ ๊ณ ํ์ง๋ก ์์ฑ๋ SDK์ ๋ํด **์ถ๊ฐ ๊ธฐ๋ฅ**๊ณผ **ํตํฉ**์ ์ ๊ณตํฉ๋๋ค.
+
+โจ [**FastAPI ํ์ํ๊ธฐ**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} โจ๋ฅผ ํตํด, ์ด ํ์ฌ๋ค์ ํ๋ ์์ํฌ์ ๊ทธ **์ํ๊ณ**๊ฐ ๊ฑด๊ฐํ๊ณ **์ง์ ๊ฐ๋ฅ**ํ๊ฒ ์ ์ง๋๋๋ก ๋์ต๋๋ค.
+
+๋ํ ์ด๋ค์ ํ์์ FastAPI **์ปค๋ฎค๋ํฐ**(์ฌ๋ฌ๋ถ)์ ๋ํ ๊ฐํ ํ์ ์ ๋ณด์ฌ์ฃผ๋ฉฐ, **์ข์ ์๋น์ค**๋ฅผ ์ ๊ณตํ๋ ๊ฒ๋ฟ ์๋๋ผ, ๊ฒฌ๊ณ ํ๊ณ ํ๋ฐํ ํ๋ ์์ํฌ์ธ FastAPI๋ฅผ ์ง์ํ๋ ๋ฐ์๋ ๊ด์ฌ์ด ์์์ ๋ํ๋
๋๋ค. ๐
+
+์๋ฅผ ๋ค์ด ๋ค์์ ์ฌ์ฉํด ๋ณผ ์ ์์ต๋๋ค:
+
+* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
+* <a href="https://www.stainless.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
+* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
+
+์ด ์ค ์ผ๋ถ๋ ์คํ ์์ค์ด๊ฑฐ๋ ๋ฌด๋ฃ ํฐ์ด๋ฅผ ์ ๊ณตํ๋ฏ๋ก, ๋น์ฉ ๋ถ๋ด ์์ด ์ฌ์ฉํด ๋ณผ ์ ์์ต๋๋ค. ๋ค๋ฅธ ์์ฉ SDK ์์ฑ๊ธฐ๋ ์์ผ๋ฉฐ ์จ๋ผ์ธ์์ ์ฐพ์ ์ ์์ต๋๋ค. ๐ค
+
+## TypeScript SDK ๋ง๋ค๊ธฐ { #create-a-typescript-sdk }
+
+๊ฐ๋จํ FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ผ๋ก ์์ํด ๋ณด๊ฒ ์ต๋๋ค:
+
+{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
+
+*path operation*์์ ์์ฒญ ํ์ด๋ก๋์ ์๋ต ํ์ด๋ก๋์ ์ฌ์ฉํ๋ ๋ชจ๋ธ์ `Item`, `ResponseMessage` ๋ชจ๋ธ๋ก ์ ์ํ๊ณ ์๋ค๋ ์ ์ ์ฃผ๋ชฉํ์ธ์.
+
+### API ๋ฌธ์ { #api-docs }
+
+`/docs`๋ก ์ด๋ํ๋ฉด, ์์ฒญ์ผ๋ก ๋ณด๋ผ ๋ฐ์ดํฐ์ ์๋ต์ผ๋ก ๋ฐ์ ๋ฐ์ดํฐ์ ๋ํ **์คํค๋ง(schemas)**๊ฐ ์๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/generate-clients/image01.png">
+
+์ด ์คํค๋ง๋ ์ฑ์์ ๋ชจ๋ธ๋ก ์ ์ธ๋์๊ธฐ ๋๋ฌธ์ ๋ณผ ์ ์์ต๋๋ค.
+
+๊ทธ ์ ๋ณด๋ ์ฑ์ **OpenAPI ์คํค๋ง**์์ ์ฌ์ฉํ ์ ์๊ณ , ์ดํ API ๋ฌธ์์ ํ์๋ฉ๋๋ค.
+
+OpenAPI์ ํฌํจ๋ ๋ชจ๋ธ์ ๋์ผํ ์ ๋ณด๊ฐ **ํด๋ผ์ด์ธํธ ์ฝ๋ ์์ฑ**์ ์ฌ์ฉ๋ ์ ์์ต๋๋ค.
+
+### Hey API { #hey-api }
+
+๋ชจ๋ธ์ด ํฌํจ๋ FastAPI ์ฑ์ด ์ค๋น๋๋ฉด, Hey API๋ฅผ ์ฌ์ฉํด TypeScript ํด๋ผ์ด์ธํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ๊ฐ์ฅ ๋น ๋ฅธ ๋ฐฉ๋ฒ์ npx๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
+
+```sh
+npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
+```
+
+์ด ๋ช
๋ น์ `./src/client`์ TypeScript SDK๋ฅผ ์์ฑํฉ๋๋ค.
+
+<a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">`@hey-api/openapi-ts` ์ค์น ๋ฐฉ๋ฒ</a>๊ณผ <a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">์์ฑ๋ ๊ฒฐ๊ณผ๋ฌผ</a>์ ํด๋น ์น์ฌ์ดํธ์์ ํ์ธํ ์ ์์ต๋๋ค.
+
+### SDK ์ฌ์ฉํ๊ธฐ { #using-the-sdk }
+
+์ด์ ํด๋ผ์ด์ธํธ ์ฝ๋๋ฅผ importํด์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋์ฒ๋ผ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ๋ฉ์๋์ ๋ํ ์๋ ์์ฑ์ด ์ ๊ณต๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/generate-clients/image02.png">
+
+๋ณด๋ผ ํ์ด๋ก๋์ ๋ํด์๋ ์๋ ์์ฑ์ด ์ ๊ณต๋ฉ๋๋ค:
+
+<img src="/img/tutorial/generate-clients/image03.png">
+
+/// tip | ํ
+
+`name`๊ณผ `price`์ ๋ํ ์๋ ์์ฑ์ FastAPI ์ ํ๋ฆฌ์ผ์ด์
์์ `Item` ๋ชจ๋ธ์ ์ ์๋ ๋ด์ฉ์
๋๋ค.
+
+///
+
+์ ์กํ๋ ๋ฐ์ดํฐ์ ๋ํด ์ธ๋ผ์ธ ์ค๋ฅ๋ ํ์๋ฉ๋๋ค:
+
+<img src="/img/tutorial/generate-clients/image04.png">
+
+์๋ต ๊ฐ์ฒด๋ ์๋ ์์ฑ์ ์ ๊ณตํฉ๋๋ค:
+
+<img src="/img/tutorial/generate-clients/image05.png">
+
+## ํ๊ทธ๊ฐ ์๋ FastAPI ์ฑ { #fastapi-app-with-tags }
+
+๋๋ถ๋ถ์ ๊ฒฝ์ฐ FastAPI ์ฑ์ ๋ ์ปค์ง๊ณ , ์๋ก ๋ค๋ฅธ *path operations* ๊ทธ๋ฃน์ ๋ถ๋ฆฌํ๊ธฐ ์ํด ํ๊ทธ๋ฅผ ์ฌ์ฉํ๊ฒ ๋ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
+
+์๋ฅผ ๋ค์ด **items** ์น์
๊ณผ **users** ์น์
์ด ์๊ณ , ์ด๋ฅผ ํ๊ทธ๋ก ๋ถ๋ฆฌํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
+
+### ํ๊ทธ๋ก TypeScript ํด๋ผ์ด์ธํธ ์์ฑํ๊ธฐ { #generate-a-typescript-client-with-tags }
+
+ํ๊ทธ๋ฅผ ์ฌ์ฉํ๋ FastAPI ์ฑ์ ๋ํด ํด๋ผ์ด์ธํธ๋ฅผ ์์ฑํ๋ฉด, ์ผ๋ฐ์ ์ผ๋ก ์์ฑ๋ ํด๋ผ์ด์ธํธ ์ฝ๋๋ ํ๊ทธ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ถ๋ฆฌ๋ฉ๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด ํด๋ผ์ด์ธํธ ์ฝ๋์์ ํญ๋ชฉ๋ค์ด ์ฌ๋ฐ๋ฅด๊ฒ ์ ๋ ฌ๋๊ณ ๊ทธ๋ฃนํ๋ฉ๋๋ค:
+
+<img src="/img/tutorial/generate-clients/image06.png">
+
+์ด ๊ฒฝ์ฐ ๋ค์์ด ์์ต๋๋ค:
+
+* `ItemsService`
+* `UsersService`
+
+### ํด๋ผ์ด์ธํธ ๋ฉ์๋ ์ด๋ฆ { #client-method-names }
+
+ํ์ฌ `createItemItemsPost` ๊ฐ์ ์์ฑ๋ ๋ฉ์๋ ์ด๋ฆ์ ๊ทธ๋ค์ง ๊น๋ํ์ง ์์ต๋๋ค:
+
+```TypeScript
+ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
+```
+
+...์ด๋ ํด๋ผ์ด์ธํธ ์์ฑ๊ธฐ๊ฐ ๊ฐ *path operation*์ ๋ํด OpenAPI ๋ด๋ถ์ **operation ID**๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+OpenAPI๋ ๋ชจ๋ *path operations* ์ ์ฒด์์ operation ID๊ฐ ๊ฐ๊ฐ ์ ์ผํด์ผ ํ๋ค๊ณ ์๊ตฌํฉ๋๋ค. ๊ทธ๋์ FastAPI๋ operation ID๊ฐ ์ ์ผํ๋๋ก **ํจ์ ์ด๋ฆ**, **๊ฒฝ๋ก**, **HTTP method/operation**์ ์กฐํฉํด operation ID๋ฅผ ์์ฑํฉ๋๋ค.
+
+ํ์ง๋ง ๋ค์์์ ์ด๋ฅผ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค. ๐ค
+
+## ์ปค์คํ
Operation ID์ ๋ ๋์ ๋ฉ์๋ ์ด๋ฆ { #custom-operation-ids-and-better-method-names }
+
+ํด๋ผ์ด์ธํธ์์ **๋ ๋จ์ํ ๋ฉ์๋ ์ด๋ฆ**์ ๊ฐ๋๋ก, operation ID๊ฐ **์์ฑ๋๋ ๋ฐฉ์**์ **์์ **ํ ์ ์์ต๋๋ค.
+
+์ด ๊ฒฝ์ฐ operation ID๊ฐ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก๋ **์ ์ผ**ํ๋๋ก ๋ณด์ฅํด์ผ ํฉ๋๋ค.
+
+์๋ฅผ ๋ค์ด ๊ฐ *path operation*์ด ํ๊ทธ๋ฅผ ๊ฐ๋๋ก ํ ๋ค์, **ํ๊ทธ**์ *path operation* **์ด๋ฆ**(ํจ์ ์ด๋ฆ)์ ๊ธฐ๋ฐ์ผ๋ก operation ID๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
+
+### ์ ์ผ ID ์์ฑ ํจ์ ์ปค์คํฐ๋ง์ด์ง { #custom-generate-unique-id-function }
+
+FastAPI๋ ๊ฐ *path operation*์ ๋ํด **์ ์ผ ID**๋ฅผ ์ฌ์ฉํ๋ฉฐ, ์ด๋ **operation ID** ๋ฐ ์์ฒญ/์๋ต์ ํ์ํ ์ปค์คํ
๋ชจ๋ธ ์ด๋ฆ์๋ ์ฌ์ฉ๋ฉ๋๋ค.
+
+์ด ํจ์๋ฅผ ์ปค์คํฐ๋ง์ด์งํ ์ ์์ต๋๋ค. ์ด ํจ์๋ `APIRoute`๋ฅผ ๋ฐ์ ๋ฌธ์์ด์ ๋ฐํํฉ๋๋ค.
+
+์๋ฅผ ๋ค์ด ์๋์์๋ ์ฒซ ๋ฒ์งธ ํ๊ทธ(๋๋ถ๋ถ ํ๊ทธ๋ ํ๋๋ง ์์ ๊ฒ์
๋๋ค)์ *path operation* ์ด๋ฆ(ํจ์ ์ด๋ฆ)์ ์ฌ์ฉํฉ๋๋ค.
+
+๊ทธ ๋ค์ ์ด ์ปค์คํ
ํจ์๋ฅผ `generate_unique_id_function` ๋งค๊ฐ๋ณ์๋ก **FastAPI**์ ์ ๋ฌํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
+
+### ์ปค์คํ
Operation ID๋ก TypeScript ํด๋ผ์ด์ธํธ ์์ฑํ๊ธฐ { #generate-a-typescript-client-with-custom-operation-ids }
+
+์ด์ ํด๋ผ์ด์ธํธ๋ฅผ ๋ค์ ์์ฑํ๋ฉด, ๊ฐ์ ๋ ๋ฉ์๋ ์ด๋ฆ์ ํ์ธํ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/generate-clients/image07.png">
+
+๋ณด์๋ค์ํผ, ์ด์ ๋ฉ์๋ ์ด๋ฆ์ ํ๊ทธ ๋ค์์ ํจ์ ์ด๋ฆ์ด ์ค๋ฉฐ, URL ๊ฒฝ๋ก์ HTTP operation์ ์ ๋ณด๋ ํฌํจํ์ง ์์ต๋๋ค.
+
+### ํด๋ผ์ด์ธํธ ์์ฑ๊ธฐ๋ฅผ ์ํ OpenAPI ์ฌ์ ์ ์ฒ๋ฆฌ { #preprocess-the-openapi-specification-for-the-client-generator }
+
+์์ฑ๋ ์ฝ๋์๋ ์ฌ์ ํ ์ผ๋ถ **์ค๋ณต ์ ๋ณด**๊ฐ ์์ต๋๋ค.
+
+`ItemsService`(ํ๊ทธ์์ ๊ฐ์ ธ์ด)์ ์ด๋ฏธ **items**๊ฐ ํฌํจ๋์ด ์์ด ์ด ๋ฉ์๋๊ฐ items์ ๊ด๋ จ๋์ด ์์์ ์ ์ ์์ง๋ง, ๋ฉ์๋ ์ด๋ฆ์๋ ํ๊ทธ ์ด๋ฆ์ด ์ ๋์ฌ๋ก ๋ถ์ด ์์ต๋๋ค. ๐
+
+OpenAPI ์ ๋ฐ์์๋ operation ID๊ฐ **์ ์ผ**ํ๋ค๋ ๊ฒ์ ๋ณด์ฅํ๊ธฐ ์ํด ์ด ๋ฐฉ์์ ์ ์งํ๊ณ ์ถ์ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ์์ฑ๋ ํด๋ผ์ด์ธํธ์์๋, ํด๋ผ์ด์ธํธ๋ฅผ ์์ฑํ๊ธฐ ์ง์ ์ OpenAPI operation ID๋ฅผ **์์ **ํด์ ๋ฉ์๋ ์ด๋ฆ์ ๋ ๋ณด๊ธฐ ์ข๊ณ **๊น๋ํ๊ฒ** ๋ง๋ค ์ ์์ต๋๋ค.
+
+OpenAPI JSON์ `openapi.json` ํ์ผ๋ก ๋ค์ด๋ก๋ํ ๋ค, ์๋์ ๊ฐ์ ์คํฌ๋ฆฝํธ๋ก **์ ๋์ฌ ํ๊ทธ๋ฅผ ์ ๊ฑฐ**ํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/generate_clients/tutorial004_py39.py *}
+
+//// tab | Node.js
+
+```Javascript
+{!> ../../docs_src/generate_clients/tutorial004.js!}
+```
+
+////
+
+์ด๋ ๊ฒ ํ๋ฉด operation ID๊ฐ `items-get_items` ๊ฐ์ ํํ์์ `get_items`๋ก ๋ณ๊ฒฝ๋์ด, ํด๋ผ์ด์ธํธ ์์ฑ๊ธฐ๊ฐ ๋ ๋จ์ํ ๋ฉ์๋ ์ด๋ฆ์ ์์ฑํ ์ ์์ต๋๋ค.
+
+### ์ ์ฒ๋ฆฌ๋ OpenAPI๋ก TypeScript ํด๋ผ์ด์ธํธ ์์ฑํ๊ธฐ { #generate-a-typescript-client-with-the-preprocessed-openapi }
+
+์ด์ ์ต์ข
๊ฒฐ๊ณผ๊ฐ `openapi.json` ํ์ผ์ ์์ผ๋ฏ๋ก, ์
๋ ฅ ์์น๋ฅผ ์
๋ฐ์ดํธํด์ผ ํฉ๋๋ค:
+
+```sh
+npx @hey-api/openapi-ts -i ./openapi.json -o src/client
+```
+
+์ ํด๋ผ์ด์ธํธ๋ฅผ ์์ฑํ ํ์๋ **๊น๋ํ ๋ฉ์๋ ์ด๋ฆ**์ ๊ฐ์ง๋ฉด์๋, **์๋ ์์ฑ**, **์ธ๋ผ์ธ ์ค๋ฅ** ๋ฑ์ ๊ทธ๋๋ก ์ ๊ณต๋ฉ๋๋ค:
+
+<img src="/img/tutorial/generate-clients/image08.png">
+
+## ์ฅ์ { #benefits }
+
+์๋์ผ๋ก ์์ฑ๋ ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค์์ ๋ํด **์๋ ์์ฑ**์ ๋ฐ์ ์ ์์ต๋๋ค:
+
+* ๋ฉ์๋
+* ๋ณธ๋ฌธ(body)์ ์์ฒญ ํ์ด๋ก๋, ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ๋ฑ
+* ์๋ต ํ์ด๋ก๋
+
+๋ํ ๋ชจ๋ ๊ฒ์ ๋ํด **์ธ๋ผ์ธ ์ค๋ฅ**๋ ํ์ธํ ์ ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋ฐฑ์๋ ์ฝ๋๋ฅผ ์
๋ฐ์ดํธํ ๋ค ํ๋ก ํธ์๋๋ฅผ **์ฌ์์ฑ(regenerate)**ํ๋ฉด, ์ *path operations*๊ฐ ๋ฉ์๋๋ก ์ถ๊ฐ๋๊ณ ๊ธฐ์กด ๊ฒ์ ์ ๊ฑฐ๋๋ฉฐ, ๊ทธ ๋ฐ์ ๋ณ๊ฒฝ ์ฌํญ๋ ์์ฑ๋ ์ฝ๋์ ๋ฐ์๋ฉ๋๋ค. ๐ค
+
+์ด๋ ๋ฌด์ธ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๊ทธ ๋ณ๊ฒฝ์ด ํด๋ผ์ด์ธํธ ์ฝ๋์๋ ์๋์ผ๋ก **๋ฐ์**๋๋ค๋ ๋ป์
๋๋ค. ๋ํ ํด๋ผ์ด์ธํธ๋ฅผ **๋น๋(build)**ํ๋ฉด ์ฌ์ฉ๋ ๋ฐ์ดํฐ๊ฐ **๋ถ์ผ์น(mismatch)**ํ ๊ฒฝ์ฐ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
+
+๋ฐ๋ผ์ ์ด์ ํ๊ฒฝ์์ ์ต์ข
์ฌ์ฉ์์๊ฒ ์ค๋ฅ๊ฐ ๋
ธ์ถ๋ ๋ค ๋ฌธ์ ๋ฅผ ์ถ์ ํ๋ ๋์ , ๊ฐ๋ฐ ์ฌ์ดํด ์ด๊ธฐ์ **๋ง์ ์ค๋ฅ๋ฅผ ๋งค์ฐ ๋นจ๋ฆฌ ๊ฐ์ง**ํ ์ ์์ต๋๋ค. โจ
--- /dev/null
+# ๊ณ ๊ธ Middleware { #advanced-middleware }
+
+๋ฉ์ธ ํํ ๋ฆฌ์ผ์์ ์ ํ๋ฆฌ์ผ์ด์
์ [์ปค์คํ
Middleware](../tutorial/middleware.md){.internal-link target=_blank}๋ฅผ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ ์ฝ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ [`CORSMiddleware`๋ก CORS ์ฒ๋ฆฌํ๊ธฐ](../tutorial/cors.md){.internal-link target=_blank}๋ ์ฝ์์ต๋๋ค.
+
+์ด ์น์
์์๋ ๋ค๋ฅธ middleware๋ค์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+## ASGI middleware ์ถ๊ฐํ๊ธฐ { #adding-asgi-middlewares }
+
+**FastAPI**๋ Starlette๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ณ <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr> ์ฌ์์ ๊ตฌํํ๋ฏ๋ก, ์ด๋ค ASGI middleware๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+ASGI ์ฌ์์ ๋ฐ๋ฅด๊ธฐ๋ง ํ๋ฉด, FastAPI๋ Starlette๋ฅผ ์ํด ๋ง๋ค์ด์ง middleware๊ฐ ์๋์ด๋ ๋์ํฉ๋๋ค.
+
+์ผ๋ฐ์ ์ผ๋ก ASGI middleware๋ ์ฒซ ๋ฒ์งธ ์ธ์๋ก ASGI ์ฑ์ ๋ฐ๋๋ก ๊ธฐ๋ํ๋ ํด๋์ค์
๋๋ค.
+
+๊ทธ๋์ ์๋ํํฐ ASGI middleware ๋ฌธ์์์๋ ์๋ง ๋ค์๊ณผ ๊ฐ์ด ํ๋ผ๊ณ ์๋ดํ ๊ฒ์
๋๋ค:
+
+```Python
+from unicorn import UnicornMiddleware
+
+app = SomeASGIApp()
+
+new_app = UnicornMiddleware(app, some_config="rainbow")
+```
+
+ํ์ง๋ง FastAPI(์ ํํ๋ Starlette)๋ ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ฉฐ, ์ด๋ฅผ ํตํด ๋ด๋ถ middleware๊ฐ ์๋ฒ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์ปค์คํ
์์ธ ํธ๋ค๋ฌ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
+
+์ด๋ฅผ ์ํด(๊ทธ๋ฆฌ๊ณ CORS ์์ ์์์ฒ๋ผ) `app.add_middleware()`๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+```Python
+from fastapi import FastAPI
+from unicorn import UnicornMiddleware
+
+app = FastAPI()
+
+app.add_middleware(UnicornMiddleware, some_config="rainbow")
+```
+
+`app.add_middleware()`๋ ์ฒซ ๋ฒ์งธ ์ธ์๋ก middleware ํด๋์ค๋ฅผ ๋ฐ๊ณ , ๊ทธ ๋ค์๋ middleware์ ์ ๋ฌํ ์ถ๊ฐ ์ธ์๋ค์ ๋ฐ์ต๋๋ค.
+
+## ํตํฉ middleware { #integrated-middlewares }
+
+**FastAPI**์๋ ์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ํ ์ฌ๋ฌ middleware๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. ๋ค์์์ ์ด๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+๋ค์ ์์ ์์๋ `from starlette.middleware.something import SomethingMiddleware`๋ฅผ ์ฌ์ฉํด๋ ๋ฉ๋๋ค.
+
+**FastAPI**๋ ๊ฐ๋ฐ์ ํธ์๋ฅผ ์ํด `fastapi.middleware`์ ์ฌ๋ฌ middleware๋ฅผ ์ ๊ณตํ์ง๋ง, ์ฌ์ฉ ๊ฐ๋ฅํ ๋๋ถ๋ถ์ middleware๋ Starlette์์ ์ง์ ์ ๊ณต๋ฉ๋๋ค.
+
+///
+
+## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
+
+๋ค์ด์ค๋ ๋ชจ๋ ์์ฒญ์ด `https` ๋๋ `wss`์ฌ์ผ ํ๋๋ก ๊ฐ์ ํฉ๋๋ค.
+
+`http` ๋๋ `ws`๋ก ๋ค์ด์ค๋ ๋ชจ๋ ์์ฒญ์ ๋์ ๋ณด์ ์คํด์ผ๋ก ๋ฆฌ๋๋ ์
๋ฉ๋๋ค.
+
+{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
+
+## `TrustedHostMiddleware` { #trustedhostmiddleware }
+
+HTTP Host Header ๊ณต๊ฒฉ์ ๋ฐฉ์ดํ๊ธฐ ์ํด, ๋ค์ด์ค๋ ๋ชจ๋ ์์ฒญ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ค์ ๋ `Host` ํค๋๊ฐ ์์ด์ผ ํ๋๋ก ๊ฐ์ ํฉ๋๋ค.
+
+{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
+
+๋ค์ ์ธ์๋ค์ ์ง์ํฉ๋๋ค:
+
+* `allowed_hosts` - ํธ์คํธ๋ช
์ผ๋ก ํ์ฉํ ๋๋ฉ์ธ ์ด๋ฆ ๋ชฉ๋ก์
๋๋ค. `*.example.com` ๊ฐ์ ์์ผ๋์นด๋ ๋๋ฉ์ธ์ผ๋ก ์๋ธ๋๋ฉ์ธ์ ๋งค์นญํ๋ ๊ฒ๋ ์ง์ํฉ๋๋ค. ์ด๋ค ํธ์คํธ๋ช
์ด๋ ํ์ฉํ๋ ค๋ฉด `allowed_hosts=["*"]`๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ middleware๋ฅผ ์๋ตํ์ธ์.
+* `www_redirect` - True๋ก ์ค์ ํ๋ฉด, ํ์ฉ๋ ํธ์คํธ์ non-www ๋ฒ์ ์ผ๋ก ๋ค์ด์ค๋ ์์ฒญ์ www ๋ฒ์ ์ผ๋ก ๋ฆฌ๋๋ ์
ํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ `True`์
๋๋ค.
+
+๋ค์ด์ค๋ ์์ฒญ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๊ฒ์ฆ๋์ง ์์ผ๋ฉด `400` ์๋ต์ด ์ ์ก๋ฉ๋๋ค.
+
+## `GZipMiddleware` { #gzipmiddleware }
+
+`Accept-Encoding` ํค๋์ `"gzip"`์ด ํฌํจ๋ ์ด๋ค ์์ฒญ์ด๋ GZip ์๋ต์ ์ฒ๋ฆฌํฉ๋๋ค.
+
+์ด middleware๋ ์ผ๋ฐ ์๋ต๊ณผ ์คํธ๋ฆฌ๋ฐ ์๋ต์ ๋ชจ๋ ์ฒ๋ฆฌํฉ๋๋ค.
+
+{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
+
+๋ค์ ์ธ์๋ค์ ์ง์ํฉ๋๋ค:
+
+* `minimum_size` - ๋ฐ์ดํธ ๋จ์๋ก ์ง์ ํ ์ต์ ํฌ๊ธฐ๋ณด๋ค ์์ ์๋ต์ GZip์ผ๋ก ์์ถํ์ง ์์ต๋๋ค. ๊ธฐ๋ณธ๊ฐ์ `500`์
๋๋ค.
+* `compresslevel` - GZip ์์ถ ์ค์ ์ฌ์ฉ๋ฉ๋๋ค. 1๋ถํฐ 9๊น์ง์ ์ ์์
๋๋ค. ๊ธฐ๋ณธ๊ฐ์ `9`์
๋๋ค. ๊ฐ์ด ๋ฎ์์๋ก ์์ถ์ ๋ ๋น ๋ฅด์ง๋ง ํ์ผ ํฌ๊ธฐ๋ ๋ ์ปค์ง๊ณ , ๊ฐ์ด ๋์์๋ก ์์ถ์ ๋ ๋๋ฆฌ์ง๋ง ํ์ผ ํฌ๊ธฐ๋ ๋ ์์์ง๋๋ค.
+
+## ๋ค๋ฅธ middleware { #other-middlewares }
+
+๋ค๋ฅธ ASGI middleware๋ ๋ง์ด ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด:
+
+* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Uvicorn์ `ProxyHeadersMiddleware`</a>
+* <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>
+
+์ฌ์ฉ ๊ฐ๋ฅํ ๋ค๋ฅธ middleware๋ฅผ ๋ณด๋ ค๋ฉด <a href="https://www.starlette.dev/middleware/" class="external-link" target="_blank">Starlette์ Middleware ๋ฌธ์</a>์ <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">ASGI Awesome List</a>๋ฅผ ํ์ธํ์ธ์.
--- /dev/null
+# OpenAPI ์ฝ๋ฐฑ { #openapi-callbacks }
+
+๋ค๋ฅธ ์ฌ๋์ด ๋ง๋ *external API*(์๋ง๋ ๋น์ ์ API๋ฅผ *์ฌ์ฉ*ํ ๋์ผํ ๊ฐ๋ฐ์)๊ฐ ์์ฒญ์ ํธ๋ฆฌ๊ฑฐํ๋๋ก ๋ง๋๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ๊ฐ์ง API๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
+
+๋น์ ์ API ์ฑ์ด *external API*๋ฅผ ํธ์ถํ ๋ ์ผ์ด๋๋ ๊ณผ์ ์ "callback"์ด๋ผ๊ณ ํฉ๋๋ค. ์ธ๋ถ ๊ฐ๋ฐ์๊ฐ ์์ฑํ ์ํํธ์จ์ด๊ฐ ๋น์ ์ API๋ก ์์ฒญ์ ๋ณด๋ธ ๋ค์, ๋น์ ์ API๊ฐ ๋ค์ *external API*๋ก ์์ฒญ์ ๋ณด๋ด *๋๋๋ ค ํธ์ถ*ํ๊ธฐ ๋๋ฌธ์
๋๋ค(์๋ง๋ ๊ฐ์ ๊ฐ๋ฐ์๊ฐ ๋ง๋ API์ผ ๊ฒ์
๋๋ค).
+
+์ด ๊ฒฝ์ฐ, ๊ทธ *external API*๊ฐ ์ด๋ค ํํ์ฌ์ผ ํ๋์ง ๋ฌธ์ํํ๊ณ ์ถ์ ์ ์์ต๋๋ค. ์ด๋ค *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ๊ฐ์ ธ์ผ ํ๋์ง, ์ด๋ค body๋ฅผ ๊ธฐ๋ํ๋์ง, ์ด๋ค ์๋ต์ ๋ฐํํด์ผ ํ๋์ง ๋ฑ์
๋๋ค.
+
+## ์ฝ๋ฐฑ์ด ์๋ ์ฑ { #an-app-with-callbacks }
+
+์์๋ก ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
+
+์ฒญ๊ตฌ์๋ฅผ ์์ฑํ ์ ์๋ ์ฑ์ ๊ฐ๋ฐํ๋ค๊ณ ๊ฐ์ ํด ๋ณด์ธ์.
+
+์ด ์ฒญ๊ตฌ์๋ `id`, `title`(์ ํ ์ฌํญ), `customer`, `total`์ ๊ฐ์ต๋๋ค.
+
+๋น์ ์ API ์ฌ์ฉ์(์ธ๋ถ ๊ฐ๋ฐ์)๋ POST ์์ฒญ์ผ๋ก ๋น์ ์ API์์ ์ฒญ๊ตฌ์๋ฅผ ์์ฑํฉ๋๋ค.
+
+๊ทธ ๋ค์ ๋น์ ์ API๋(๊ฐ์ ํด ๋ณด๋ฉด):
+
+* ์ฒญ๊ตฌ์๋ฅผ ์ธ๋ถ ๊ฐ๋ฐ์์ ๊ณ ๊ฐ์๊ฒ ์ ์กํฉ๋๋ค.
+* ๋์ ์๊ธํฉ๋๋ค.
+* API ์ฌ์ฉ์(์ธ๋ถ ๊ฐ๋ฐ์)์ API๋ก ๋ค์ ์๋ฆผ์ ๋ณด๋
๋๋ค.
+ * ์ด๋ (๋น์ ์ API์์) ๊ทธ ์ธ๋ถ ๊ฐ๋ฐ์๊ฐ ์ ๊ณตํ๋ ์ด๋ค *external API*๋ก POST ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ์์ผ๋ก ์ํ๋ฉ๋๋ค(์ด๊ฒ์ด "callback"์
๋๋ค).
+
+## ์ผ๋ฐ์ ์ธ **FastAPI** ์ฑ { #the-normal-fastapi-app }
+
+๋จผ์ ์ฝ๋ฐฑ์ ์ถ๊ฐํ๊ธฐ ์ , ์ผ๋ฐ์ ์ธ API ์ฑ์ด ์ด๋ป๊ฒ ์๊ฒผ๋์ง ๋ณด๊ฒ ์ต๋๋ค.
+
+`Invoice` body๋ฅผ ๋ฐ๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์, ์ฝ๋ฐฑ์ ์ํ URL์ ๋ด๋ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ `callback_url`์ด ์์ ๊ฒ์
๋๋ค.
+
+์ด ๋ถ๋ถ์ ๊ฝค ์ผ๋ฐ์ ์ด๋ฉฐ, ๋๋ถ๋ถ์ ์ฝ๋๋ ์ด๋ฏธ ์ต์ํ ๊ฒ์
๋๋ค:
+
+{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
+
+/// tip | ํ
+
+`callback_url` ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ Pydantic์ <a href="https://docs.pydantic.dev/latest/api/networks/" class="external-link" target="_blank">Url</a> ํ์
์ ์ฌ์ฉํฉ๋๋ค.
+
+///
+
+์ ์ผํ๊ฒ ์๋ก์ด ๊ฒ์ *๊ฒฝ๋ก ์ฒ๋ฆฌ ๋ฐ์ฝ๋ ์ดํฐ*์ ์ธ์๋ก `callbacks=invoices_callback_router.routes`๊ฐ ๋ค์ด๊ฐ๋ค๋ ์ ์
๋๋ค. ์ด๊ฒ์ด ๋ฌด์์ธ์ง ๋ค์์์ ๋ณด๊ฒ ์ต๋๋ค.
+
+## ์ฝ๋ฐฑ ๋ฌธ์ํํ๊ธฐ { #documenting-the-callback }
+
+์ค์ ์ฝ๋ฐฑ ์ฝ๋๋ ๋น์ ์ API ์ฑ์ ํฌ๊ฒ ์์กดํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ฑ๋ง๋ค ๋ง์ด ๋ฌ๋ผ์ง ์ ์์ต๋๋ค.
+
+๋ค์์ฒ๋ผ ํ๋ ์ค์ ์ฝ๋์ผ ์๋ ์์ต๋๋ค:
+
+```Python
+callback_url = "https://example.com/api/v1/invoices/events/"
+httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
+```
+
+ํ์ง๋ง ์ฝ๋ฐฑ์์ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์, ๋น์ ์ API ์ฌ์ฉ์(์ธ๋ถ ๊ฐ๋ฐ์)๊ฐ ์ฝ๋ฐฑ ์์ฒญ body๋ก *๋น์ ์ API*๊ฐ ๋ณด๋ผ ๋ฐ์ดํฐ ๋ฑ์ ๋ง์ถฐ *external API*๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌํํ๋๋ก ๋ณด์ฅํ๋ ๊ฒ์
๋๋ค.
+
+๊ทธ๋์ ๋ค์์ผ๋ก ํ ์ผ์, *๋น์ ์ API*์์ ๋ณด๋ด๋ ์ฝ๋ฐฑ์ ๋ฐ๊ธฐ ์ํด ๊ทธ *external API*๊ฐ ์ด๋ค ํํ์ฌ์ผ ํ๋์ง ๋ฌธ์ํํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์
๋๋ค.
+
+๊ทธ ๋ฌธ์๋ ๋น์ ์ API์์ `/docs`์ Swagger UI์ ํ์๋๋ฉฐ, ์ธ๋ถ ๊ฐ๋ฐ์๋ค์ด *external API*๋ฅผ ์ด๋ป๊ฒ ๋ง๋ค์ด์ผ ํ๋์ง ์ ์ ์๊ฒ ํด์ค๋๋ค.
+
+์ด ์์๋ ์ฝ๋ฐฑ ์์ฒด(ํ ์ค ์ฝ๋๋ก๋ ๋ ์ ์์)๋ฅผ ๊ตฌํํ์ง ์๊ณ , ๋ฌธ์ํ ๋ถ๋ถ๋ง ๊ตฌํํฉ๋๋ค.
+
+/// tip | ํ
+
+์ค์ ์ฝ๋ฐฑ์ ๋จ์ง HTTP ์์ฒญ์
๋๋ค.
+
+์ฝ๋ฐฑ์ ์ง์ ๊ตฌํํ ๋๋ <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>๋ <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a> ๊ฐ์ ๊ฒ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+///
+
+## ์ฝ๋ฐฑ ๋ฌธ์ํ ์ฝ๋ ์์ฑํ๊ธฐ { #write-the-callback-documentation-code }
+
+์ด ์ฝ๋๋ ์ฑ์์ ์คํ๋์ง ์์ต๋๋ค. ๊ทธ *external API*๊ฐ ์ด๋ค ํํ์ฌ์ผ ํ๋์ง *๋ฌธ์ํ*ํ๋ ๋ฐ๋ง ํ์ํฉ๋๋ค.
+
+ํ์ง๋ง **FastAPI**๋ก API์ ์๋ ๋ฌธ์๋ฅผ ์ฝ๊ฒ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ด๋ฏธ ์๊ณ ์์ต๋๋ค.
+
+๋ฐ๋ผ์ ๊ทธ์ ๊ฐ์ ์ง์์ ์ฌ์ฉํด *external API*๊ฐ ์ด๋ป๊ฒ ์๊ฒจ์ผ ํ๋์ง ๋ฌธ์ํํ ๊ฒ์
๋๋ค... ์ฆ ์ธ๋ถ API๊ฐ ๊ตฌํํด์ผ ํ๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ(๋ค)*(๋น์ ์ API๊ฐ ํธ์ถํ ๊ฒ๋ค)์ ๋ง๋ค์ด์ ๋ง์
๋๋ค.
+
+/// tip | ํ
+
+์ฝ๋ฐฑ์ ๋ฌธ์ํํ๋ ์ฝ๋๋ฅผ ์์ฑํ ๋๋, ์์ ์ด ๊ทธ *์ธ๋ถ ๊ฐ๋ฐ์*๋ผ๊ณ ์์ํ๋ ๊ฒ์ด ์ ์ฉํ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ง๊ธ์ *๋น์ ์ API*๊ฐ ์๋๋ผ *external API*๋ฅผ ๊ตฌํํ๊ณ ์๋ค๊ณ ์๊ฐํด ๋ณด์ธ์.
+
+์ด ๊ด์ (์ธ๋ถ ๊ฐ๋ฐ์์ ๊ด์ )์ ์ ์ ์ฑํํ๋ฉด, ๊ทธ *external API*๋ฅผ ์ํด ํ๋ผ๋ฏธํฐ, body์ฉ Pydantic ๋ชจ๋ธ, ์๋ต ๋ฑ์ ์ด๋์ ๋์ด์ผ ํ๋์ง๊ฐ ๋ ๋ช
ํํ๊ฒ ๋๊ปด์ง ์ ์์ต๋๋ค.
+
+///
+
+### ์ฝ๋ฐฑ `APIRouter` ์์ฑํ๊ธฐ { #create-a-callback-apirouter }
+
+๋จผ์ ํ๋ ์ด์์ ์ฝ๋ฐฑ์ ๋ด์ ์ `APIRouter`๋ฅผ ๋ง๋ญ๋๋ค.
+
+{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
+
+### ์ฝ๋ฐฑ *๊ฒฝ๋ก ์ฒ๋ฆฌ* ์์ฑํ๊ธฐ { #create-the-callback-path-operation }
+
+์ฝ๋ฐฑ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ๋ง๋ค๋ ค๋ฉด ์์์ ๋ง๋ ๋์ผํ `APIRouter`๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+์ผ๋ฐ์ ์ธ FastAPI *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ฒ๋ผ ๋ณด์ผ ๊ฒ์
๋๋ค:
+
+* ์๋ง๋ ๋ฐ์์ผ ํ body ์ ์ธ์ด ์์ ๊ฒ์
๋๋ค(์: `body: InvoiceEvent`).
+* ๊ทธ๋ฆฌ๊ณ ๋ฐํํด์ผ ํ ์๋ต ์ ์ธ๋ ์์ ์ ์์ต๋๋ค(์: `response_model=InvoiceEventReceived`).
+
+{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
+
+์ผ๋ฐ์ ์ธ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์์ ์ฃผ์ ์ฐจ์ด์ ์ 2๊ฐ์ง์
๋๋ค:
+
+* ์ค์ ์ฝ๋๋ฅผ ๊ฐ์ง ํ์๊ฐ ์์ต๋๋ค. ๋น์ ์ ์ฑ์ ์ด ์ฝ๋๋ฅผ ์ ๋ ํธ์ถํ์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค. ์ด๋ *external API*๋ฅผ ๋ฌธ์ํํ๋ ๋ฐ๋ง ์ฌ์ฉ๋ฉ๋๋ค. ๋ฐ๋ผ์ ํจ์๋ ๊ทธ๋ฅ `pass`๋ง ์์ด๋ ๋ฉ๋๋ค.
+* *path*์๋ <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">OpenAPI 3 expression</a>(์์ธํ ๋ด์ฉ์ ์๋ ์ฐธ๊ณ )์ด ํฌํจ๋ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด *๋น์ ์ API*๋ก ๋ณด๋ด์ง ์๋ ์์ฒญ์ ํ๋ผ๋ฏธํฐ์ ์ผ๋ถ ๊ฐ์ ๋ณ์๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+### ์ฝ๋ฐฑ ๊ฒฝ๋ก ํํ์ { #the-callback-path-expression }
+
+์ฝ๋ฐฑ *path*๋ *๋น์ ์ API*๋ก ๋ณด๋ด์ง ์๋ ์์ฒญ์ ์ผ๋ถ๋ฅผ ํฌํจํ ์ ์๋ <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">OpenAPI 3 expression</a>์ ๊ฐ์ง ์ ์์ต๋๋ค.
+
+์ด ๊ฒฝ์ฐ, ๋ค์ `str`์
๋๋ค:
+
+```Python
+"{$callback_url}/invoices/{$request.body.id}"
+```
+
+๋ฐ๋ผ์ ๋น์ ์ API ์ฌ์ฉ์(์ธ๋ถ ๊ฐ๋ฐ์)๊ฐ *๋น์ ์ API*๋ก ๋ค์ ์์ฒญ์ ๋ณด๋ด๊ณ :
+
+```
+https://yourapi.com/invoices/?callback_url=https://www.external.org/events
+```
+
+JSON body๊ฐ ๋ค์๊ณผ ๊ฐ๋ค๋ฉด:
+
+```JSON
+{
+ "id": "2expen51ve",
+ "customer": "Mr. Richie Rich",
+ "total": "9999"
+}
+```
+
+๊ทธ๋ฌ๋ฉด *๋น์ ์ API*๋ ์ฒญ๊ตฌ์๋ฅผ ์ฒ๋ฆฌํ๊ณ , ๋์ค์ ์ด๋ ์์ ์์ `callback_url`(์ฆ *external API*)๋ก ์ฝ๋ฐฑ ์์ฒญ์ ๋ณด๋
๋๋ค:
+
+```
+https://www.external.org/events/invoices/2expen51ve
+```
+
+๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ JSON body๋ฅผ ํฌํจํ ๊ฒ์
๋๋ค:
+
+```JSON
+{
+ "description": "Payment celebration",
+ "paid": true
+}
+```
+
+๋ํ ๊ทธ *external API*๋ก๋ถํฐ ๋ค์๊ณผ ๊ฐ์ JSON body ์๋ต์ ๊ธฐ๋ํฉ๋๋ค:
+
+```JSON
+{
+ "ok": true
+}
+```
+
+/// tip | ํ
+
+์ฝ๋ฐฑ URL์๋ `callback_url` ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ URL(`https://www.external.org/events`)๋ฟ ์๋๋ผ, JSON body ์์ ์ฒญ๊ตฌ์ `id`(`2expen51ve`)๋ ํจ๊ป ์ฌ์ฉ๋๋ค๋ ์ ์ ์ฃผ๋ชฉํ์ธ์.
+
+///
+
+### ์ฝ๋ฐฑ ๋ผ์ฐํฐ ์ถ๊ฐํ๊ธฐ { #add-the-callback-router }
+
+์ด ์์ ์์, ์์์ ๋ง๋ ์ฝ๋ฐฑ ๋ผ์ฐํฐ ์์ *์ฝ๋ฐฑ ๊ฒฝ๋ก ์ฒ๋ฆฌ(๋ค)*(์ฆ *external developer*๊ฐ *external API*์ ๊ตฌํํด์ผ ํ๋ ๊ฒ๋ค)์ ์ค๋นํ์ต๋๋ค.
+
+์ด์ *๋น์ ์ API ๊ฒฝ๋ก ์ฒ๋ฆฌ ๋ฐ์ฝ๋ ์ดํฐ*์์ `callbacks` ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํด, ๊ทธ ์ฝ๋ฐฑ ๋ผ์ฐํฐ์ `.routes` ์์ฑ(์ค์ ๋ก๋ routes/*๊ฒฝ๋ก ์ฒ๋ฆฌ*์ `list`)์ ์ ๋ฌํฉ๋๋ค:
+
+{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
+
+/// tip | ํ
+
+`callback=`์ ๋ผ์ฐํฐ ์์ฒด(`invoices_callback_router`)๋ฅผ ๋๊ธฐ๋ ๊ฒ์ด ์๋๋ผ, `invoices_callback_router.routes`์ฒ๋ผ `.routes` ์์ฑ์ ๋๊ธด๋ค๋ ์ ์ ์ฃผ๋ชฉํ์ธ์.
+
+///
+
+### ๋ฌธ์ ํ์ธํ๊ธฐ { #check-the-docs }
+
+์ด์ ์ฑ์ ์คํํ๊ณ <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>๋ก ์ด๋ํ์ธ์.
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๋ํด "Callbacks" ์น์
์ ํฌํจํ ๋ฌธ์๊ฐ ํ์๋๋ฉฐ, *external API*๊ฐ ์ด๋ค ํํ์ฌ์ผ ํ๋์ง ํ์ธํ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/openapi-callbacks/image01.png">
--- /dev/null
+# OpenAPI Webhooks { #openapi-webhooks }
+
+์ฑ์ด ์ด๋ค ๋ฐ์ดํฐ์ ํจ๊ป (์์ฒญ์ ๋ณด๋ด์) *์ฌ์ฉ์์* ์ฑ์ ํธ์ถํ ์ ์๊ณ , ๋ณดํต ์ด๋ค **์ด๋ฒคํธ**๋ฅผ **์๋ฆฌ๊ธฐ** ์ํด ๊ทธ๋ ๊ฒ ํ ์ ์๋ค๋ ๊ฒ์ API **์ฌ์ฉ์**์๊ฒ ์๋ ค์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
+
+์ด๋ ์ฌ์ฉ์๊ฐ ์ฌ๋ฌ๋ถ์ API๋ก ์์ฒญ์ ๋ณด๋ด๋ ์ผ๋ฐ์ ์ธ ๊ณผ์ ๋์ , **์ฌ๋ฌ๋ถ์ API**(๋๋ ์ฑ)๊ฐ **์ฌ์ฉ์์ ์์คํ
**(์ฌ์ฉ์์ API, ์ฌ์ฉ์์ ์ฑ)์ผ๋ก **์์ฒญ์ ๋ณด๋ผ ์ ์๋ค**๋ ์๋ฏธ์
๋๋ค.
+
+์ด๋ฅผ ๋ณดํต **webhook**์ด๋ผ๊ณ ํฉ๋๋ค.
+
+## Webhooks ๋จ๊ณ { #webhooks-steps }
+
+์ผ๋ฐ์ ์ธ ๊ณผ์ ์, ์ฌ๋ฌ๋ถ์ด ์ฝ๋์์ ๋ณด๋ผ ๋ฉ์์ง, ์ฆ **์์ฒญ ๋ณธ๋ฌธ(body)**์ด ๋ฌด์์ธ์ง **์ ์**ํ๋ ๊ฒ์
๋๋ค.
+
+๋ํ ์ฌ๋ฌ๋ถ์ ์ฑ์ด ์ด๋ค **์์ **์ ๊ทธ ์์ฒญ(๋๋ ์ด๋ฒคํธ)์ ๋ณด๋ผ์ง๋ ์ด๋ค ๋ฐฉ์์ผ๋ก๋ ์ ์ํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ **์ฌ์ฉ์**๋ (์: ์ด๋๊ฐ์ ์น ๋์๋ณด๋์์) ์ฌ๋ฌ๋ถ์ ์ฑ์ด ๊ทธ ์์ฒญ์ ๋ณด๋ด์ผ ํ **URL**์ ์ด๋ค ๋ฐฉ์์ผ๋ก๋ ์ ์ํฉ๋๋ค.
+
+webhook์ URL์ ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ๊ณผ ์ค์ ๋ก ๊ทธ ์์ฒญ์ ๋ณด๋ด๋ ์ฝ๋์ ๋ํ ๋ชจ๋ **๋ก์ง**์ ์ฌ๋ฌ๋ถ์๊ฒ ๋ฌ๋ ค ์์ต๋๋ค. **์ฌ๋ฌ๋ถ์ ์ฝ๋**์์ ์ํ๋ ๋ฐฉ์์ผ๋ก ์์ฑํ๋ฉด ๋ฉ๋๋ค.
+
+## **FastAPI**์ OpenAPI๋ก webhooks ๋ฌธ์ํํ๊ธฐ { #documenting-webhooks-with-fastapi-and-openapi }
+
+**FastAPI**์์๋ OpenAPI๋ฅผ ์ฌ์ฉํด, ์ด๋ฌํ webhook์ ์ด๋ฆ, ์ฌ๋ฌ๋ถ์ ์ฑ์ด ๋ณด๋ผ ์ ์๋ HTTP ์์
ํ์
(์: `POST`, `PUT` ๋ฑ), ๊ทธ๋ฆฌ๊ณ ์ฌ๋ฌ๋ถ์ ์ฑ์ด ๋ณด๋ผ ์์ฒญ **๋ณธ๋ฌธ(body)**์ ์ ์ํ ์ ์์ต๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ฉ์๊ฐ ์ฌ๋ฌ๋ถ์ **webhook** ์์ฒญ์ ๋ฐ๊ธฐ ์ํด **์์ ๋ค์ API๋ฅผ ๊ตฌํ**ํ๊ธฐ๊ฐ ํจ์ฌ ์ฌ์์ง๊ณ , ๊ฒฝ์ฐ์ ๋ฐ๋ผ์๋ ์์ ์ API ์ฝ๋ ์ผ๋ถ๋ฅผ ์๋ ์์ฑํ ์๋ ์์ต๋๋ค.
+
+/// info | ์ ๋ณด
+
+Webhooks๋ OpenAPI 3.1.0 ์ด์์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, FastAPI `0.99.0` ์ด์์์ ์ง์๋ฉ๋๋ค.
+
+///
+
+## webhooks๊ฐ ์๋ ์ฑ { #an-app-with-webhooks }
+
+**FastAPI** ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค๋ฉด, *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์ ์ํ๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก(์: `@app.webhooks.post()`), *webhooks*๋ฅผ ์ ์ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ `webhooks` ์์ฑ์ด ์์ต๋๋ค.
+
+{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
+
+์ฌ๋ฌ๋ถ์ด ์ ์ํ webhook์ **OpenAPI** ์คํค๋ง์ ์๋ **docs UI**์ ํฌํจ๋ฉ๋๋ค.
+
+/// info | ์ ๋ณด
+
+`app.webhooks` ๊ฐ์ฒด๋ ์ค์ ๋ก `APIRouter`์ผ ๋ฟ์ด๋ฉฐ, ์ฌ๋ฌ ํ์ผ๋ก ์ฑ์ ๊ตฌ์กฐํํ ๋ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๋์ผํ ํ์
์
๋๋ค.
+
+///
+
+webhook์์๋ ์ค์ ๋ก(`/items/` ๊ฐ์) *๊ฒฝ๋ก(path)*๋ฅผ ์ ์ธํ์ง ์๋๋ค๋ ์ ์ ์ ์ํ์ธ์. ๊ทธ๊ณณ์ ์ ๋ฌํ๋ ํ
์คํธ๋ webhook์ **์๋ณ์**(์ด๋ฒคํธ ์ด๋ฆ)์ผ ๋ฟ์
๋๋ค. ์๋ฅผ ๋ค์ด `@app.webhooks.post("new-subscription")`์์ webhook ์ด๋ฆ์ `new-subscription`์
๋๋ค.
+
+์ด๋ **์ฌ์ฉ์**๊ฐ webhook ์์ฒญ์ ๋ฐ๊ณ ์ถ์ ์ค์ **URL ๊ฒฝ๋ก**๋ฅผ ๋ค๋ฅธ ๋ฐฉ์(์: ์น ๋์๋ณด๋)์ผ๋ก ์ ์ํ ๊ฒ์ด๋ผ๊ณ ๊ธฐ๋ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+### ๋ฌธ์ ํ์ธํ๊ธฐ { #check-the-docs }
+
+์ด์ ์ฑ์ ์คํํ๊ณ <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>๋ก ์ด๋ํ์ธ์.
+
+๋ฌธ์์ ์ผ๋ฐ์ ์ธ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๊ฐ ๋ณด์ด๊ณ , ์ด์ ๋ ์ผ๋ถ **webhooks**๋ ํจ๊ป ๋ณด์ผ ๊ฒ์
๋๋ค:
+
+<img src="/img/tutorial/openapi-webhooks/image01.png">
--- /dev/null
+# ๊ฒฝ๋ก ์ฒ๋ฆฌ ๊ณ ๊ธ ๊ตฌ์ฑ { #path-operation-advanced-configuration }
+
+## OpenAPI operationId { #openapi-operationid }
+
+/// warning | ๊ฒฝ๊ณ
+
+OpenAPI โ์ ๋ฌธ๊ฐโ๊ฐ ์๋๋ผ๋ฉด, ์๋ง ์ด ๋ด์ฉ์ ํ์ํ์ง ์์ ๊ฒ์
๋๋ค.
+
+///
+
+๋งค๊ฐ๋ณ์ `operation_id`๋ฅผ ์ฌ์ฉํด *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ์ฌ์ฉํ OpenAPI `operationId`๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
+
+๊ฐ ์์
๋ง๋ค ๊ณ ์ ํ๋๋ก ๋ณด์ฅํด์ผ ํฉ๋๋ค.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
+
+### *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์* ์ด๋ฆ์ operationId๋ก ์ฌ์ฉํ๊ธฐ { #using-the-path-operation-function-name-as-the-operationid }
+
+API์ ํจ์ ์ด๋ฆ์ `operationId`๋ก ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด, ๋ชจ๋ API๋ฅผ ์ํํ๋ฉด์ `APIRoute.name`์ ์ฌ์ฉํด ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ `operation_id`๋ฅผ ๋ฎ์ด์ธ ์ ์์ต๋๋ค.
+
+๋ชจ๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์ถ๊ฐํ ๋ค์ ์ํํด์ผ ํฉ๋๋ค.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
+
+/// tip | ํ
+
+`app.openapi()`๋ฅผ ์๋์ผ๋ก ํธ์ถํ๋ค๋ฉด, ๊ทธ ์ ์ `operationId`๋ค์ ์
๋ฐ์ดํธํด์ผ ํฉ๋๋ค.
+
+///
+
+/// warning | ๊ฒฝ๊ณ
+
+์ด๋ ๊ฒ ํ ๊ฒฝ์ฐ, ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์ ์ด๋ฆ์ด ๊ณ ์ ํ๋๋ก ๋ณด์ฅํด์ผ ํฉ๋๋ค.
+
+์๋ก ๋ค๋ฅธ ๋ชจ๋(ํ์ด์ฌ ํ์ผ)์ ์์ด๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค.
+
+///
+
+## OpenAPI์์ ์ ์ธํ๊ธฐ { #exclude-from-openapi }
+
+์์ฑ๋ OpenAPI ์คํค๋ง(๋ฐ๋ผ์ ์๋ ๋ฌธ์ํ ์์คํ
)์์ ํน์ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์ ์ธํ๋ ค๋ฉด, `include_in_schema` ๋งค๊ฐ๋ณ์๋ฅผ `False`๋ก ์ค์ ํ์ธ์:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
+
+## docstring์์ ๊ณ ๊ธ ์ค๋ช
๊ฐ์ ธ์ค๊ธฐ { #advanced-description-from-docstring }
+
+OpenAPI์ ์ฌ์ฉํ *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์ docstring ์ค ์๋ฅผ ์ ํํ ์ ์์ต๋๋ค.
+
+`\f`(์ด์ค์ผ์ดํ๋ "form feed" ๋ฌธ์)๋ฅผ ์ถ๊ฐํ๋ฉด **FastAPI**๋ ์ด ์ง์ ์์ OpenAPI์ ์ฌ์ฉํ ์ถ๋ ฅ ๋ด์ฉ์ ์๋ผ๋
๋๋ค.
+
+๋ฌธ์์๋ ํ์๋์ง ์์ง๋ง, Sphinx ๊ฐ์ ๋ค๋ฅธ ๋๊ตฌ๋ ๋๋จธ์ง ๋ถ๋ถ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
+
+## ์ถ๊ฐ ์๋ต { #additional-responses }
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๋ํด `response_model`๊ณผ `status_code`๋ฅผ ์ ์ธํ๋ ๋ฐฉ๋ฒ์ ์ด๋ฏธ ๋ณด์
จ์ ๊ฒ์
๋๋ค.
+
+์ด๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๊ธฐ๋ณธ ์๋ต์ ๋ํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ์ํฉ๋๋ค.
+
+๋ชจ๋ธ, ์ํ ์ฝ๋ ๋ฑ๊ณผ ํจ๊ป ์ถ๊ฐ ์๋ต๋ ์ ์ธํ ์ ์์ต๋๋ค.
+
+์ด์ ๋ํ ๋ฌธ์์ ์ ์ฒด ์ฅ์ด ์์ผ๋, [OpenAPI์ ์ถ๊ฐ ์๋ต](additional-responses.md){.internal-link target=_blank}์์ ์ฝ์ด๋ณด์ธ์.
+
+## OpenAPI Extra { #openapi-extra }
+
+์ ํ๋ฆฌ์ผ์ด์
์์ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์ ์ธํ๋ฉด, **FastAPI**๋ OpenAPI ์คํค๋ง์ ํฌํจ๋ ํด๋น *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๊ด๋ จ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ์์ฑํฉ๋๋ค.
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+OpenAPI ๋ช
์ธ์์๋ ์ด๋ฅผ <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Operation Object</a>๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
+
+///
+
+์ฌ๊ธฐ์๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๋ํ ๋ชจ๋ ์ ๋ณด๊ฐ ์์ผ๋ฉฐ, ์๋ ๋ฌธ์๋ฅผ ์์ฑํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
+
+`tags`, `parameters`, `requestBody`, `responses` ๋ฑ์ด ํฌํจ๋ฉ๋๋ค.
+
+์ด *๊ฒฝ๋ก ์ฒ๋ฆฌ* ์ ์ฉ OpenAPI ์คํค๋ง๋ ๋ณดํต **FastAPI**๊ฐ ์๋์ผ๋ก ์์ฑํ์ง๋ง, ํ์ฅํ ์๋ ์์ต๋๋ค.
+
+/// tip | ํ
+
+์ด๋ ์ ์์ค ํ์ฅ ์ง์ ์
๋๋ค.
+
+์ถ๊ฐ ์๋ต๋ง ์ ์ธํ๋ฉด ๋๋ค๋ฉด, ๋ ํธ๋ฆฌํ ๋ฐฉ๋ฒ์ [OpenAPI์ ์ถ๊ฐ ์๋ต](additional-responses.md){.internal-link target=_blank}์ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
+
+///
+
+`openapi_extra` ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํด *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ OpenAPI ์คํค๋ง๋ฅผ ํ์ฅํ ์ ์์ต๋๋ค.
+
+### OpenAPI ํ์ฅ { #openapi-extensions }
+
+์๋ฅผ ๋ค์ด `openapi_extra`๋ [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions)๋ฅผ ์ ์ธํ๋ ๋ฐ ๋์์ด ๋ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
+
+์๋ API ๋ฌธ์๋ฅผ ์ด๋ฉด, ํด๋น ํน์ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ํ๋จ์ ํ์ฅ์ด ํ์๋ฉ๋๋ค.
+
+<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
+
+๋ํ API์ `/openapi.json`์์ ๊ฒฐ๊ณผ OpenAPI๋ฅผ ๋ณด๋ฉด, ํน์ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ์ผ๋ถ๋ก ํ์ฅ์ด ํฌํจ๋ ๊ฒ๋ ํ์ธํ ์ ์์ต๋๋ค:
+
+```JSON hl_lines="22"
+{
+ "openapi": "3.1.0",
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0"
+ },
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {}
+ }
+ }
+ }
+ },
+ "x-aperture-labs-portal": "blue"
+ }
+ }
+ }
+}
+```
+
+### ์ฌ์ฉ์ ์ ์ OpenAPI *๊ฒฝ๋ก ์ฒ๋ฆฌ* ์คํค๋ง { #custom-openapi-path-operation-schema }
+
+`openapi_extra`์ ๋์
๋๋ฆฌ๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๋ํด ์๋์ผ๋ก ์์ฑ๋ OpenAPI ์คํค๋ง์ ๊น๊ฒ ๋ณํฉ๋ฉ๋๋ค.
+
+๋ฐ๋ผ์ ์๋ ์์ฑ๋ ์คํค๋ง์ ์ถ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ํ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด Pydantic๊ณผ ํจ๊ป FastAPI์ ์๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ์๊ณ , ์์ฒด ์ฝ๋๋ก ์์ฒญ์ ์ฝ๊ณ ๊ฒ์ฆํ๊ธฐ๋ก ๊ฒฐ์ ํ ์๋ ์์ง๋ง, OpenAPI ์คํค๋ง์๋ ์ฌ์ ํ ๊ทธ ์์ฒญ์ ์ ์ํ๊ณ ์ถ์ ์ ์์ต๋๋ค.
+
+๊ทธ๋ด ๋ `openapi_extra`๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
+
+์ด ์์์์๋ ์ด๋ค Pydantic ๋ชจ๋ธ๋ ์ ์ธํ์ง ์์์ต๋๋ค. ์ฌ์ค ์์ฒญ ๋ฐ๋๋ JSON์ผ๋ก <abbr title="converted from some plain format, like bytes, into Python objects - bytes ๊ฐ์ ์ผ๋ฐ ํ์์์ Python ๊ฐ์ฒด๋ก ๋ณํ">parsed</abbr>๋์ง๋ ์๊ณ , `bytes`๋ก ์ง์ ์ฝ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ํจ์ `magic_data_reader()`๊ฐ ์ด๋ค ๋ฐฉ์์ผ๋ก๋ ์ด๋ฅผ ํ์ฑํ๋ ์ญํ ์ ๋ด๋นํฉ๋๋ค.
+
+๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ , ์์ฒญ ๋ฐ๋์ ๋ํด ๊ธฐ๋ํ๋ ์คํค๋ง๋ฅผ ์ ์ธํ ์ ์์ต๋๋ค.
+
+### ์ฌ์ฉ์ ์ ์ OpenAPI ์ฝํ
์ธ ํ์
{ #custom-openapi-content-type }
+
+๊ฐ์ ํธ๋ฆญ์ ์ฌ์ฉํ๋ฉด, Pydantic ๋ชจ๋ธ์ ์ด์ฉํด JSON Schema๋ฅผ ์ ์ํ๊ณ ์ด๋ฅผ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ์ฌ์ฉ์ ์ ์ OpenAPI ์คํค๋ง ์น์
์ ํฌํจ์ํฌ ์ ์์ต๋๋ค.
+
+์์ฒญ์ ๋ฐ์ดํฐ ํ์
์ด JSON์ด ์๋๋๋ผ๋ ์ด๋ ๊ฒ ํ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด ์ด ์ ํ๋ฆฌ์ผ์ด์
์์๋ Pydantic ๋ชจ๋ธ์์ JSON Schema๋ฅผ ์ถ์ถํ๋ FastAPI์ ํตํฉ ๊ธฐ๋ฅ๋, JSON์ ๋ํ ์๋ ๊ฒ์ฆ๋ ์ฌ์ฉํ์ง ์์ต๋๋ค. ์ค์ ๋ก ์์ฒญ ์ฝํ
์ธ ํ์
์ JSON์ด ์๋๋ผ YAML๋ก ์ ์ธํฉ๋๋ค:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
+
+๊ทธ๋ผ์๋ ๊ธฐ๋ณธ ํตํฉ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ์๋๋ผ๋, YAML๋ก ๋ฐ๊ณ ์ ํ๋ ๋ฐ์ดํฐ์ ๋ํ JSON Schema๋ฅผ ์๋์ผ๋ก ์์ฑํ๊ธฐ ์ํด Pydantic ๋ชจ๋ธ์ ์ฌ์ ํ ์ฌ์ฉํฉ๋๋ค.
+
+๊ทธ ๋ค์ ์์ฒญ์ ์ง์ ์ฌ์ฉํ๊ณ , ๋ฐ๋๋ฅผ `bytes`๋ก ์ถ์ถํฉ๋๋ค. ์ด๋ FastAPI๊ฐ ์์ฒญ ํ์ด๋ก๋๋ฅผ JSON์ผ๋ก ํ์ฑํ๋ ค๊ณ ์๋์กฐ์ฐจ ํ์ง ์๋๋ค๋ ๋ป์
๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ฝ๋์์ YAML ์ฝํ
์ธ ๋ฅผ ์ง์ ํ์ฑํ ๋ค, ๋ค์ ๊ฐ์ Pydantic ๋ชจ๋ธ์ ์ฌ์ฉํด YAML ์ฝํ
์ธ ๋ฅผ ๊ฒ์ฆํฉ๋๋ค:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
+
+/// tip | ํ
+
+์ฌ๊ธฐ์๋ ๊ฐ์ Pydantic ๋ชจ๋ธ์ ์ฌ์ฌ์ฉํฉ๋๋ค.
+
+ํ์ง๋ง ๋ง์ฐฌ๊ฐ์ง๋ก, ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๊ฒ์ฆํ ์๋ ์์ต๋๋ค.
+
+///
--- /dev/null
+# HTTP Basic Auth { #http-basic-auth }
+
+๊ฐ์ฅ ๋จ์ํ ๊ฒฝ์ฐ์๋ HTTP Basic Auth๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+HTTP Basic Auth์์๋ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๊ฐ ๋ค์ด ์๋ ํค๋๋ฅผ ๊ธฐ๋ํฉ๋๋ค.
+
+์ด๋ฅผ ๋ฐ์ง ๋ชปํ๋ฉด HTTP 401 "Unauthorized" ์ค๋ฅ๋ฅผ ๋ฐํํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๊ฐ์ด `Basic`์ด๊ณ ์ ํ์ ์ผ๋ก `realm` ํ๋ผ๋ฏธํฐ๋ฅผ ํฌํจํ๋ `WWW-Authenticate` ํค๋๋ฅผ ๋ฐํํฉ๋๋ค.
+
+์ด๋ ๋ธ๋ผ์ฐ์ ๊ฐ ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ๋ ํตํฉ ํ๋กฌํํธ๋ฅผ ํ์ํ๋๋ก ์๋ ค์ค๋๋ค.
+
+๊ทธ๋ค์ ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ๋ฉด, ๋ธ๋ผ์ฐ์ ๊ฐ ์๋์ผ๋ก ํด๋น ๊ฐ์ ํค๋์ ๋ด์ ์ ์กํฉ๋๋ค.
+
+## ๊ฐ๋จํ HTTP Basic Auth { #simple-http-basic-auth }
+
+* `HTTPBasic`๊ณผ `HTTPBasicCredentials`๋ฅผ ์ํฌํธํฉ๋๋ค.
+* `HTTPBasic`์ ์ฌ์ฉํด "`security` scheme"์ ์์ฑํฉ๋๋ค.
+* *๊ฒฝ๋ก ์ฒ๋ฆฌ*์์ dependency๋ก ํด๋น `security`๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+* `HTTPBasicCredentials` ํ์
์ ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค:
+ * ์ ์ก๋ `username`๊ณผ `password`๋ฅผ ํฌํจํฉ๋๋ค.
+
+{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
+
+์ฒ์์ผ๋ก URL์ ์ด์ด๋ณด๋ฉด(๋๋ ๋ฌธ์์์ "Execute" ๋ฒํผ์ ํด๋ฆญํ๋ฉด) ๋ธ๋ผ์ฐ์ ๊ฐ ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฌผ์ด๋ด
๋๋ค:
+
+<img src="/img/tutorial/security/image12.png">
+
+## ์ฌ์ฉ์๋ช
ํ์ธํ๊ธฐ { #check-the-username }
+
+๋ ์์ ํ ์์์
๋๋ค.
+
+dependency๋ฅผ ์ฌ์ฉํด ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ์ธํ์ธ์.
+
+์ด๋ฅผ ์ํด Python ํ์ค ๋ชจ๋ <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a>๋ฅผ ์ฌ์ฉํด ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํฉ๋๋ค.
+
+`secrets.compare_digest()`๋ `bytes` ๋๋ ASCII ๋ฌธ์(์์ด์์ ์ฌ์ฉํ๋ ๋ฌธ์)๋ง ํฌํจํ `str`์ ๋ฐ์์ผ ํฉ๋๋ค. ์ฆ, `Sebastiรกn`์ `รก` ๊ฐ์ ๋ฌธ์๊ฐ ์์ผ๋ฉด ๋์ํ์ง ์์ต๋๋ค.
+
+์ด๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ๋จผ์ `username`๊ณผ `password`๋ฅผ UTF-8๋ก ์ธ์ฝ๋ฉํด์ `bytes`๋ก ๋ณํํฉ๋๋ค.
+
+๊ทธ๋ฐ ๋ค์ `secrets.compare_digest()`๋ฅผ ์ฌ์ฉํด `credentials.username`์ด `"stanleyjobson"`์ด๊ณ `credentials.password`๊ฐ `"swordfish"`์ธ์ง ํ์คํ ํ์ธํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
+
+์ด๋ ๋ค์๊ณผ ๋น์ทํฉ๋๋ค:
+
+```Python
+if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
+ # Return some error
+ ...
+```
+
+ํ์ง๋ง `secrets.compare_digest()`๋ฅผ ์ฌ์ฉํ๋ฉด "timing attacks"๋ผ๊ณ ๋ถ๋ฆฌ๋ ํ ์ ํ์ ๊ณต๊ฒฉ์ ๋ํด ์์ ํด์ง๋๋ค.
+
+### Timing Attacks { #timing-attacks }
+
+๊ทธ๋ ๋ค๋ฉด "timing attack"์ด๋ ๋ฌด์์ผ๊น์?
+
+๊ณต๊ฒฉ์๋ค์ด ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์ถ์ธกํ๋ ค๊ณ ํ๋ค๊ณ ๊ฐ์ ํด๋ด
์๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์๋ช
`johndoe`, ๋น๋ฐ๋ฒํธ `love123`์ผ๋ก ์์ฒญ์ ๋ณด๋
๋๋ค.
+
+๊ทธ๋ฌ๋ฉด ์ ํ๋ฆฌ์ผ์ด์
์ Python ์ฝ๋๋ ๋๋ต ๋ค์๊ณผ ๊ฐ์ ๊ฒ์
๋๋ค:
+
+```Python
+if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
+ ...
+```
+
+ํ์ง๋ง Python์ด `johndoe`์ ์ฒซ ๊ธ์ `j`๋ฅผ `stanleyjobson`์ ์ฒซ ๊ธ์ `s`์ ๋น๊ตํ๋ ์๊ฐ, ๋ ๋ฌธ์์ด์ด ๊ฐ์ง ์๋ค๋ ๊ฒ์ ์ด๋ฏธ ์๊ฒ ๋์ด `False`๋ฅผ ๋ฐํํฉ๋๋ค. ์ด๋ โ๋๋จธ์ง ๊ธ์๋ค์ ๋น๊ตํ๋๋ผ ๊ณ์ฐ์ ๋ ๋ญ๋นํ ํ์๊ฐ ์๋คโ๊ณ ํ๋จํ๊ธฐ ๋๋ฌธ์
๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ ํ๋ฆฌ์ผ์ด์
์ "Incorrect username or password"๋ผ๊ณ ๋งํฉ๋๋ค.
+
+๊ทธ๋ฐ๋ฐ ๊ณต๊ฒฉ์๋ค์ด ์ฌ์ฉ์๋ช
์ `stanleyjobsox`, ๋น๋ฐ๋ฒํธ๋ฅผ `love123`์ผ๋ก ๋ค์ ์๋ํฉ๋๋ค.
+
+๊ทธ๋ฌ๋ฉด ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋๋ ๋ค์๊ณผ ๋น์ทํ๊ฒ ๋์ํฉ๋๋ค:
+
+```Python
+if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
+ ...
+```
+
+Python์ ๋ ๋ฌธ์์ด์ด ๊ฐ์ง ์๋ค๋ ๊ฒ์ ์์์ฐจ๋ฆฌ๊ธฐ ์ ๊น์ง `stanleyjobsox`์ `stanleyjobson` ์์ชฝ์ `stanleyjobso` ์ ์ฒด๋ฅผ ๋น๊ตํด์ผ ํฉ๋๋ค. ๊ทธ๋์ "Incorrect username or password"๋ผ๊ณ ์๋ตํ๊ธฐ๊น์ง ์ถ๊ฐ๋ก ๋ช ๋ง์ดํฌ๋ก์ด๊ฐ ๋ ๊ฑธ๋ฆด ๊ฒ์
๋๋ค.
+
+#### ์๋ต ์๊ฐ์ ๊ณต๊ฒฉ์์๊ฒ ๋์์ด ๋ฉ๋๋ค { #the-time-to-answer-helps-the-attackers }
+
+์ด ์์ ์์ ์๋ฒ๊ฐ "Incorrect username or password" ์๋ต์ ๋ณด๋ด๋ ๋ฐ ๋ช ๋ง์ดํฌ๋ก์ด ๋ ๊ฑธ๋ ธ๋ค๋ ๊ฒ์ ์์์ฑ๋ฉด, ๊ณต๊ฒฉ์๋ค์ _๋ฌด์ธ๊ฐ_ ๋ง์๋ค๋ ๊ฒ(์ด๊ธฐ ๋ช ๊ธ์๊ฐ ๋ง์๋ค๋ ๊ฒ)์ ์๊ฒ ๋ฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ `johndoe`๋ณด๋ค๋ `stanleyjobsox`์ ๋ ๊ฐ๊น์ด ๊ฐ์ ์๋ํด์ผ ํ๋ค๋ ๊ฒ์ ์๊ณ ๋ค์ ์๋ํ ์ ์์ต๋๋ค.
+
+#### "์ ๋ฌธ์ ์ธ" ๊ณต๊ฒฉ { #a-professional-attack }
+
+๋ฌผ๋ก ๊ณต๊ฒฉ์๋ค์ ์ด๋ฐ ์์
์ ์์ผ๋ก ํ์ง ์์ต๋๋ค. ๋ณดํต ์ด๋น ์์ฒ~์๋ฐฑ๋ง ๋ฒ ํ
์คํธํ ์ ์๋ ํ๋ก๊ทธ๋จ์ ์์ฑํ ๊ฒ์ด๊ณ , ํ ๋ฒ์ ์ ๋ต ๊ธ์ ํ๋์ฉ ์ถ๊ฐ๋ก ์ป์ด๋ผ ์ ์์ต๋๋ค.
+
+๊ทธ๋ ๊ฒ ํ๋ฉด ๋ช ๋ถ ๋๋ ๋ช ์๊ฐ ๋ง์, ์๋ต์ ๊ฑธ๋ฆฐ ์๊ฐ๋ง์ ์ด์ฉํด(์ฐ๋ฆฌ ์ ํ๋ฆฌ์ผ์ด์
์ โ๋์โ์ ๋ฐ์) ์ฌ๋ฐ๋ฅธ ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์ถ์ธกํ ์ ์๊ฒ ๋ฉ๋๋ค.
+
+#### `secrets.compare_digest()`๋ก ํด๊ฒฐํ๊ธฐ { #fix-it-with-secrets-compare-digest }
+
+ํ์ง๋ง ์ฐ๋ฆฌ ์ฝ๋๋ ์ค์ ๋ก `secrets.compare_digest()`๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
+
+์์ฝํ๋ฉด, `stanleyjobsox`์ `stanleyjobson`์ ๋น๊ตํ๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ `johndoe`์ `stanleyjobson`์ ๋น๊ตํ๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ๊ณผ ๊ฐ์์ง๋๋ค. ๋น๋ฐ๋ฒํธ๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค.
+
+์ด๋ ๊ฒ ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋์์ `secrets.compare_digest()`๋ฅผ ์ฌ์ฉํ๋ฉด, ์ด๋ฌํ ๋ฒ์์ ๋ณด์ ๊ณต๊ฒฉ ์ ๋ฐ์ ๋ํด ์์ ํด์ง๋๋ค.
+
+### ์ค๋ฅ ๋ฐํํ๊ธฐ { #return-the-error }
+
+์๊ฒฉ ์ฆ๋ช
์ด ์ฌ๋ฐ๋ฅด์ง ์๋ค๊ณ ํ๋จ๋๋ฉด, ์ํ ์ฝ๋ 401(์๊ฒฉ ์ฆ๋ช
์ด ์ ๊ณต๋์ง ์์์ ๋์ ๋์ผ)์ ์ฌ์ฉํ๋ `HTTPException`์ ๋ฐํํ๊ณ ๋ธ๋ผ์ฐ์ ๊ฐ ๋ก๊ทธ์ธ ํ๋กฌํํธ๋ฅผ ๋ค์ ํ์ํ๋๋ก `WWW-Authenticate` ํค๋๋ฅผ ์ถ๊ฐํ์ธ์:
+
+{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
--- /dev/null
+# ๊ณ ๊ธ ๋ณด์ { #advanced-security }
+
+## ์ถ๊ฐ ๊ธฐ๋ฅ { #additional-features }
+
+[ํํ ๋ฆฌ์ผ - ์ฌ์ฉ์ ๊ฐ์ด๋: ๋ณด์](../../tutorial/security/index.md){.internal-link target=_blank}์์ ๋ค๋ฃฌ ๋ด์ฉ ์ธ์๋, ๋ณด์์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ์ถ๊ฐ ๊ธฐ๋ฅ์ด ์์ต๋๋ค.
+
+/// tip | ํ
+
+๋ค์ ์น์
๋ค์ **๋ฐ๋์ "๊ณ ๊ธ"์ด๋ผ๊ณ ํ ์๋ ์์ต๋๋ค**.
+
+๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ ์ฌ๋ก์ ๋ฐ๋ผ, ๊ทธ์ค ํ๋์ ํด๊ฒฐ์ฑ
์ด ์์ ์๋ ์์ต๋๋ค.
+
+///
+
+## ๋จผ์ ํํ ๋ฆฌ์ผ ์ฝ๊ธฐ { #read-the-tutorial-first }
+
+๋ค์ ์น์
์ ์ฃผ์ [ํํ ๋ฆฌ์ผ - ์ฌ์ฉ์ ๊ฐ์ด๋: ๋ณด์](../../tutorial/security/index.md){.internal-link target=_blank}์ ์ด๋ฏธ ์ฝ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
+
+๋ชจ๋ ๋์ผํ ๊ฐ๋
์ ๊ธฐ๋ฐ์ผ๋ก ํ์ง๋ง, ๋ช ๊ฐ์ง ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๊ฒ ํด์ค๋๋ค.
--- /dev/null
+# OAuth2 ์ค์ฝํ { #oauth2-scopes }
+
+**FastAPI**์์ OAuth2 ์ค์ฝํ๋ฅผ ์ง์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์์ฐ์ค๋ฝ๊ฒ ๋์ํ๋๋ก ํตํฉ๋์ด ์์ต๋๋ค.
+
+์ด๋ฅผ ํตํด OAuth2 ํ์ค์ ๋ฐ๋ฅด๋ ๋ ์ธ๋ฐํ ๊ถํ ์์คํ
์ OpenAPI ์ ํ๋ฆฌ์ผ์ด์
(๋ฐ API ๋ฌธ์)์ ํตํฉํ ์ ์์ต๋๋ค.
+
+์ค์ฝํ๋ฅผ ์ฌ์ฉํ๋ OAuth2๋ Facebook, Google, GitHub, Microsoft, X(Twitter) ๋ฑ ๋ง์ ๋ํ ์ธ์ฆ ์ ๊ณต์๊ฐ ์ฌ์ฉํ๋ ๋ฉ์ปค๋์ฆ์
๋๋ค. ์ด๋ค์ ์ด๋ฅผ ํตํด ์ฌ์ฉ์์ ์ ํ๋ฆฌ์ผ์ด์
์ ํน์ ๊ถํ์ ์ ๊ณตํฉ๋๋ค.
+
+Facebook, Google, GitHub, Microsoft, X(Twitter)๋ก โ๋ก๊ทธ์ธโํ ๋๋ง๋ค, ํด๋น ์ ํ๋ฆฌ์ผ์ด์
์ ์ค์ฝํ๊ฐ ์๋ OAuth2๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
+
+์ด ์น์
์์๋ **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
์์ ๋์ผํ โ์ค์ฝํ๊ฐ ์๋ OAuth2โ๋ก ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ(Authorization)๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ํ์ธํฉ๋๋ค.
+
+/// warning | ๊ฒฝ๊ณ
+
+์ด ์น์
์ ๋ค์ ๊ณ ๊ธ ๋ด์ฉ์
๋๋ค. ์ด์ ๋ง ์์ํ๋ค๋ฉด ๊ฑด๋๋ฐ์ด๋ ๋ฉ๋๋ค.
+
+OAuth2 ์ค์ฝํ๊ฐ ๋ฐ๋์ ํ์ํ ๊ฒ์ ์๋๋ฉฐ, ์ธ์ฆ๊ณผ ์ธ๊ฐ๋ ์ํ๋ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ์ค์ฝํ๊ฐ ์๋ OAuth2๋ (OpenAPI์ ํจ๊ป) API ๋ฐ API ๋ฌธ์์ ๊น๋ํ๊ฒ ํตํฉ๋ ์ ์์ต๋๋ค.
+
+๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ , ํด๋น ์ค์ฝํ(๋๋ ๊ทธ ๋ฐ์ ์ด๋ค ๋ณด์/์ธ๊ฐ ์๊ตฌ์ฌํญ์ด๋ )๋ ์ฝ๋์์ ํ์์ ๋ง๊ฒ ์ง์ ๊ฐ์ ํด์ผ ํฉ๋๋ค.
+
+๋ง์ ๊ฒฝ์ฐ ์ค์ฝํ๊ฐ ์๋ OAuth2๋ ๊ณผํ ์ ํ์ผ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ํ์ํ๋ค๊ณ ์๊ณ ์๊ฑฐ๋ ๊ถ๊ธํ๋ค๋ฉด ๊ณ์ ์ฝ์ด๋ณด์ธ์.
+
+///
+
+## OAuth2 ์ค์ฝํ์ OpenAPI { #oauth2-scopes-and-openapi }
+
+OAuth2 ์ฌ์์ โ์ค์ฝํ(scopes)โ๋ฅผ ๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ๋ ๋ฌธ์์ด ๋ชฉ๋ก์ผ๋ก ์ ์ํฉ๋๋ค.
+
+๊ฐ ๋ฌธ์์ด์ ๋ด์ฉ์ ์ด๋ค ํ์์ด๋ ๋ ์ ์์ง๋ง, ๊ณต๋ฐฑ์ ํฌํจํ๋ฉด ์ ๋ฉ๋๋ค.
+
+์ด ์ค์ฝํ๋ค์ โ๊ถํโ์ ๋ํ๋
๋๋ค.
+
+OpenAPI(์: API ๋ฌธ์)์์๋ โsecurity schemesโ๋ฅผ ์ ์ํ ์ ์์ต๋๋ค.
+
+์ด security scheme ์ค ํ๋๊ฐ OAuth2๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ์ค์ฝํ๋ ์ ์ธํ๊ณ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+๊ฐ โ์ค์ฝํโ๋ (๊ณต๋ฐฑ ์๋) ๋ฌธ์์ด์ผ ๋ฟ์
๋๋ค.
+
+๋ณดํต ๋ค์๊ณผ ๊ฐ์ด ํน์ ๋ณด์ ๊ถํ์ ์ ์ธํ๋ ๋ฐ ์ฌ์ฉํฉ๋๋ค:
+
+* `users:read` ๋๋ `users:write` ๋ ํํ ์์์
๋๋ค.
+* `instagram_basic` ๋ Facebook/Instagram์์ ์ฌ์ฉํฉ๋๋ค.
+* `https://www.googleapis.com/auth/drive` ๋ Google์์ ์ฌ์ฉํฉ๋๋ค.
+
+/// info | ์ ๋ณด
+
+OAuth2์์ โ์ค์ฝํโ๋ ํ์ํ ํน์ ๊ถํ์ ์ ์ธํ๋ ๋ฌธ์์ด์ผ ๋ฟ์
๋๋ค.
+
+`:` ๊ฐ์ ๋ค๋ฅธ ๋ฌธ์๊ฐ ์๊ฑฐ๋ URL์ด์ด๋ ์๊ด์์ต๋๋ค.
+
+๊ทธ๋ฐ ์ธ๋ถ์ฌํญ์ ๊ตฌํ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค.
+
+OAuth2 ์
์ฅ์์๋ ๊ทธ์ ๋ฌธ์์ด์
๋๋ค.
+
+///
+
+## ์ ์ฒด ๊ฐ์ { #global-view }
+
+๋จผ์ , ๋ฉ์ธ **ํํ ๋ฆฌ์ผ - ์ฌ์ฉ์ ๊ฐ์ด๋**์ [๋น๋ฐ๋ฒํธ(๋ฐ ํด์ฑ)๋ฅผ ์ฌ์ฉํ๋ OAuth2, JWT ํ ํฐ์ ์ฌ์ฉํ๋ Bearer](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank} ์์ ์์ ์ด๋ค ๋ถ๋ถ์ด ๋ฐ๋๋์ง ๋น ๋ฅด๊ฒ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ด์ OAuth2 ์ค์ฝํ๋ฅผ ์ฌ์ฉํฉ๋๋ค:
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
+
+์ด์ ๋ณ๊ฒฝ ์ฌํญ์ ๋จ๊ณ๋ณ๋ก ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+## OAuth2 ๋ณด์ ์คํด { #oauth2-security-scheme }
+
+์ฒซ ๋ฒ์งธ ๋ณ๊ฒฝ ์ฌํญ์ ์ด์ ์ฌ์ฉ ๊ฐ๋ฅํ ์ค์ฝํ 2๊ฐ(`me`, `items`)๋ก OAuth2 ๋ณด์ ์คํด์ ์ ์ธํ๋ค๋ ์ ์
๋๋ค.
+
+`scopes` ๋งค๊ฐ๋ณ์๋ ๊ฐ ์ค์ฝํ๋ฅผ ํค๋ก ํ๊ณ , ์ค๋ช
์ ๊ฐ์ผ๋ก ํ๋ `dict`๋ฅผ ๋ฐ์ต๋๋ค:
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
+
+์ด์ ์ค์ฝํ๋ฅผ ์ ์ธํ๊ธฐ ๋๋ฌธ์, ๋ก๊ทธ์ธ/์ธ๊ฐํ ๋ API ๋ฌธ์์ ์ค์ฝํ๊ฐ ํ์๋ฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ ๊ทผ์ ํ์ฉํ ์ค์ฝํ๋ฅผ ์ ํํ ์ ์๊ฒ ๋ฉ๋๋ค: `me`์ `items`.
+
+์ด๋ Facebook, Google, GitHub ๋ฑ์ผ๋ก ๋ก๊ทธ์ธํ๋ฉด์ ๊ถํ์ ๋ถ์ฌํ ๋ ์ฌ์ฉ๋๋ ๊ฒ๊ณผ ๋์ผํ ๋ฉ์ปค๋์ฆ์
๋๋ค:
+
+<img src="/img/tutorial/security/image11.png">
+
+## ์ค์ฝํ๋ฅผ ํฌํจํ JWT ํ ํฐ { #jwt-token-with-scopes }
+
+์ด์ ํ ํฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์์ ํด, ์์ฒญ๋ ์ค์ฝํ๋ฅผ ๋ฐํํ๋๋ก ํฉ๋๋ค.
+
+์ฌ์ ํ ๋์ผํ `OAuth2PasswordRequestForm`์ ์ฌ์ฉํฉ๋๋ค. ์ฌ๊ธฐ์๋ ์์ฒญ์์ ๋ฐ์ ๊ฐ ์ค์ฝํ๋ฅผ ๋ด๋ `scopes` ์์ฑ์ด ์์ผ๋ฉฐ, ํ์
์ `str`์ `list`์
๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ JWT ํ ํฐ์ ์ผ๋ถ๋ก ์ค์ฝํ๋ฅผ ๋ฐํํฉ๋๋ค.
+
+/// danger | ์ํ
+
+๋จ์ํ๋ฅผ ์ํด, ์ฌ๊ธฐ์๋ ์์ฒญ์ผ๋ก ๋ฐ์ ์ค์ฝํ๋ฅผ ๊ทธ๋๋ก ํ ํฐ์ ์ถ๊ฐํ๊ณ ์์ต๋๋ค.
+
+ํ์ง๋ง ์ค์ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ๋ณด์์ ์ํด, ์ฌ์ฉ์๊ฐ ์ค์ ๋ก ๊ฐ์ง ์ ์๋ ์ค์ฝํ๋ง(๋๋ ๋ฏธ๋ฆฌ ์ ์ํ ๊ฒ๋ง) ์ถ๊ฐํ๋๋ก ๋ฐ๋์ ํ์ธํด์ผ ํฉ๋๋ค.
+
+///
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
+
+## *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ์์กด์ฑ์์ ์ค์ฝํ ์ ์ธํ๊ธฐ { #declare-scopes-in-path-operations-and-dependencies }
+
+์ด์ `/users/me/items/`์ ๋ํ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๊ฐ ์ค์ฝํ `items`๋ฅผ ์๊ตฌํ๋ค๊ณ ์ ์ธํฉ๋๋ค.
+
+์ด๋ฅผ ์ํด `fastapi`์์ `Security`๋ฅผ importํ์ฌ ์ฌ์ฉํฉ๋๋ค.
+
+`Security`๋ (`Depends`์ฒ๋ผ) ์์กด์ฑ์ ์ ์ธํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ง๋ง, `Security`๋ ์ค์ฝํ(๋ฌธ์์ด) ๋ชฉ๋ก์ ๋ฐ๋ `scopes` ๋งค๊ฐ๋ณ์๋ ๋ฐ์ต๋๋ค.
+
+์ด ๊ฒฝ์ฐ, ์์กด์ฑ ํจ์ `get_current_active_user`๋ฅผ `Security`์ ์ ๋ฌํฉ๋๋ค(`Depends`๋ก ํ ๋์ ๊ฐ์ ๋ฐฉ์).
+
+ํ์ง๋ง ์ค์ฝํ `list`๋ ํจ๊ป ์ ๋ฌํฉ๋๋ค. ์ฌ๊ธฐ์๋ ์ค์ฝํ ํ๋๋ง: `items`(๋ ๋ง์ ์๋ ์์ต๋๋ค).
+
+๋ํ ์์กด์ฑ ํจ์ `get_current_active_user`๋ `Depends`๋ฟ ์๋๋ผ `Security`๋ก๋ ํ์ ์์กด์ฑ์ ์ ์ธํ ์ ์์ต๋๋ค. ์์ฒด ํ์ ์์กด์ฑ ํจ์(`get_current_user`)์ ์ถ๊ฐ ์ค์ฝํ ์๊ตฌ์ฌํญ์ ์ ์ธํฉ๋๋ค.
+
+์ด ๊ฒฝ์ฐ์๋ ์ค์ฝํ `me`๋ฅผ ์๊ตฌํฉ๋๋ค(์ฌ๋ฌ ์ค์ฝํ๋ฅผ ์๊ตฌํ ์๋ ์์ต๋๋ค).
+
+/// note | ์ฐธ๊ณ
+
+๋ฐ๋์ ์๋ก ๋ค๋ฅธ ๊ณณ์ ์๋ก ๋ค๋ฅธ ์ค์ฝํ๋ฅผ ์ถ๊ฐํด์ผ ํ๋ ๊ฒ์ ์๋๋๋ค.
+
+์ฌ๊ธฐ์๋ **FastAPI**๊ฐ ์๋ก ๋ค๋ฅธ ๋ ๋ฒจ์์ ์ ์ธ๋ ์ค์ฝํ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์ง ๋ณด์ฌ์ฃผ๊ธฐ ์ํด ์ด๋ ๊ฒ ํฉ๋๋ค.
+
+///
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
+
+/// info | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+`Security`๋ ์ค์ ๋ก `Depends`์ ์๋ธํด๋์ค์ด๋ฉฐ, ๋์ค์ ๋ณด๊ฒ ๋ ์ถ๊ฐ ๋งค๊ฐ๋ณ์ ํ๋๋ง ๋ ์์ต๋๋ค.
+
+ํ์ง๋ง `Depends` ๋์ `Security`๋ฅผ ์ฌ์ฉํ๋ฉด, **FastAPI**๋ ๋ณด์ ์ค์ฝํ๋ฅผ ์ ์ธํ ์ ์์์ ์๊ณ ๋ด๋ถ์ ์ผ๋ก ์ด๋ฅผ ์ฌ์ฉํ๋ฉฐ, OpenAPI๋ก API๋ฅผ ๋ฌธ์ํํ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง `fastapi`์์ `Query`, `Path`, `Depends`, `Security` ๋ฑ์ importํ ๋, ์ด๊ฒ๋ค์ ์ค์ ๋ก ํน์ํ ํด๋์ค๋ฅผ ๋ฐํํ๋ ํจ์์
๋๋ค.
+
+///
+
+## `SecurityScopes` ์ฌ์ฉํ๊ธฐ { #use-securityscopes }
+
+์ด์ ์์กด์ฑ `get_current_user`๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
+
+์ด๋ ์์ ์์กด์ฑ๋ค์ด ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
+
+์ฌ๊ธฐ์์ ์์ ๋ง๋ ๋์ผํ OAuth2 ์คํด์ ์์กด์ฑ์ผ๋ก ์ ์ธํ์ฌ ์ฌ์ฉํฉ๋๋ค: `oauth2_scheme`.
+
+์ด ์์กด์ฑ ํจ์ ์์ฒด์๋ ์ค์ฝํ ์๊ตฌ์ฌํญ์ด ์๊ธฐ ๋๋ฌธ์, `oauth2_scheme`์ ํจ๊ป `Depends`๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ณด์ ์ค์ฝํ๋ฅผ ์ง์ ํ ํ์๊ฐ ์์ ๋๋ `Security`๋ฅผ ์ธ ํ์๊ฐ ์์ต๋๋ค.
+
+๋ํ `fastapi.security`์์ importํ `SecurityScopes` ํ์
์ ํน๋ณํ ๋งค๊ฐ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค.
+
+์ด `SecurityScopes` ํด๋์ค๋ `Request`์ ๋น์ทํฉ๋๋ค(`Request`๋ ์์ฒญ ๊ฐ์ฒด๋ฅผ ์ง์ ์ป๊ธฐ ์ํด ์ฌ์ฉํ์ต๋๋ค).
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
+
+## `scopes` ์ฌ์ฉํ๊ธฐ { #use-the-scopes }
+
+๋งค๊ฐ๋ณ์ `security_scopes`์ ํ์
์ `SecurityScopes`์
๋๋ค.
+
+์ฌ๊ธฐ์๋ `scopes` ์์ฑ์ด ์์ผ๋ฉฐ, ์๊ธฐ ์์ ๊ณผ ์ด ํจ์๋ฅผ ํ์ ์์กด์ฑ์ผ๋ก ์ฌ์ฉํ๋ ๋ชจ๋ ์์กด์ฑ์ด ์๊ตฌํ๋ ์ค์ฝํ ์ ์ฒด๋ฅผ ๋ด์ `list`๋ฅผ ๊ฐ์ง๋๋ค. ์ฆ, ๋ชจ๋ โdependantsโ... ๋ค์ ํท๊ฐ๋ฆด ์ ์๋๋ฐ, ์๋์์ ๋ค์ ์ค๋ช
ํฉ๋๋ค.
+
+`security_scopes` ๊ฐ์ฒด(`SecurityScopes` ํด๋์ค)์๋ ๋ํ `scope_str` ์์ฑ์ด ์๋๋ฐ, ๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ๋ ๋จ์ผ ๋ฌธ์์ด๋ก ์ค์ฝํ๋ค์ ๋ด๊ณ ์์ต๋๋ค(์ด๋ฅผ ์ฌ์ฉํ ๊ฒ์
๋๋ค).
+
+๋์ค์ ์ฌ๋ฌ ์ง์ ์์ ์ฌ์ฌ์ฉ(`raise`)ํ ์ ์๋ `HTTPException`์ ์์ฑํฉ๋๋ค.
+
+์ด ์์ธ์๋ ํ์ํ ์ค์ฝํ(์๋ค๋ฉด)๋ฅผ ๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ๋ ๋ฌธ์์ด(`scope_str`)๋ก ํฌํจํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ค์ฝํ ๋ฌธ์์ด์ `WWW-Authenticate` ํค๋์ ๋ฃ์ต๋๋ค(์ด๋ ์ฌ์์ ์ผ๋ถ์
๋๋ค).
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
+
+## `username`๊ณผ ๋ฐ์ดํฐ ํํ ๊ฒ์ฆํ๊ธฐ { #verify-the-username-and-data-shape }
+
+`username`์ ์ป์๋์ง ํ์ธํ๊ณ , ์ค์ฝํ๋ฅผ ์ถ์ถํฉ๋๋ค.
+
+๊ทธ๋ฐ ๋ค์ Pydantic ๋ชจ๋ธ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ฆํฉ๋๋ค(`ValidationError` ์์ธ๋ฅผ ์ก์ต๋๋ค). JWT ํ ํฐ์ ์ฝ๊ฑฐ๋ Pydantic์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ฆํ๋ ๊ณผ์ ์์ ์ค๋ฅ๊ฐ ๋๋ฉด, ์์์ ๋ง๋ `HTTPException`์ raiseํฉ๋๋ค.
+
+์ด๋ฅผ ์ํด Pydantic ๋ชจ๋ธ `TokenData`์ ์ ์์ฑ `scopes`๋ฅผ ์ถ๊ฐํฉ๋๋ค.
+
+Pydantic์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ฆํ๋ฉด, ์๋ฅผ ๋ค์ด ์ค์ฝํ๊ฐ ์ ํํ `str`์ `list`์ด๊ณ `username`์ด `str`์ธ์ง ๋ฑ์ ๋ณด์ฅํ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด `dict`๋ ๋ค๋ฅธ ํํ๋ผ๋ฉด, ๋์ค์ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ด๋ ์์ ์ ๊นจ์ง๋ฉด์ ๋ณด์ ์ํ์ด ๋ ์ ์์ต๋๋ค.
+
+๋ํ ํด๋น username์ ๊ฐ์ง ์ฌ์ฉ์๊ฐ ์๋์ง ํ์ธํ๊ณ , ์๋ค๋ฉด ์์์ ๋ง๋ ๋์ผํ ์์ธ๋ฅผ raiseํฉ๋๋ค.
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
+
+## `scopes` ๊ฒ์ฆํ๊ธฐ { #verify-the-scopes }
+
+์ด์ ์ด ์์กด์ฑ๊ณผ ๋ชจ๋ dependant( *๊ฒฝ๋ก ์ฒ๋ฆฌ* ํฌํจ)๊ฐ ์๊ตฌํ๋ ๋ชจ๋ ์ค์ฝํ๊ฐ, ๋ฐ์ ํ ํฐ์ ์ค์ฝํ์ ํฌํจ๋์ด ์๋์ง ํ์ธํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด `HTTPException`์ raiseํฉ๋๋ค.
+
+์ด๋ฅผ ์ํด, ๋ชจ๋ ์ค์ฝํ๋ฅผ `str`๋ก ๋ด๊ณ ์๋ `security_scopes.scopes`๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
+
+## ์์กด์ฑ ํธ๋ฆฌ์ ์ค์ฝํ { #dependency-tree-and-scopes }
+
+์ด ์์กด์ฑ ํธ๋ฆฌ์ ์ค์ฝํ๋ฅผ ๋ค์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+`get_current_active_user` ์์กด์ฑ์ `get_current_user`๋ฅผ ํ์ ์์กด์ฑ์ผ๋ก ๊ฐ์ง๋ฏ๋ก, `get_current_active_user`์์ ์ ์ธ๋ ์ค์ฝํ `"me"`๋ `get_current_user`์ ์ ๋ฌ๋๋ `security_scopes.scopes`์ ์๊ตฌ ์ค์ฝํ ๋ชฉ๋ก์ ํฌํจ๋ฉ๋๋ค.
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ* ์์ฒด๋ ์ค์ฝํ `"items"`๋ฅผ ์ ์ธํ๋ฏ๋ก, ์ด๊ฒ ๋ํ `get_current_user`์ ์ ๋ฌ๋๋ `security_scopes.scopes` ๋ชฉ๋ก์ ํฌํจ๋ฉ๋๋ค.
+
+์์กด์ฑ๊ณผ ์ค์ฝํ์ ๊ณ์ธต ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* *๊ฒฝ๋ก ์ฒ๋ฆฌ* `read_own_items`๋:
+ * ์์กด์ฑ๊ณผ ํจ๊ป ์๊ตฌ ์ค์ฝํ `["items"]`๋ฅผ ๊ฐ์ง๋๋ค:
+ * `get_current_active_user`:
+ * ์์กด์ฑ ํจ์ `get_current_active_user`๋:
+ * ์์กด์ฑ๊ณผ ํจ๊ป ์๊ตฌ ์ค์ฝํ `["me"]`๋ฅผ ๊ฐ์ง๋๋ค:
+ * `get_current_user`:
+ * ์์กด์ฑ ํจ์ `get_current_user`๋:
+ * ์์ฒด์ ์ผ๋ก๋ ์๊ตฌ ์ค์ฝํ๊ฐ ์์ต๋๋ค.
+ * `oauth2_scheme`๋ฅผ ์ฌ์ฉํ๋ ์์กด์ฑ์ด ์์ต๋๋ค.
+ * `SecurityScopes` ํ์
์ `security_scopes` ๋งค๊ฐ๋ณ์๊ฐ ์์ต๋๋ค:
+ * ์ด `security_scopes` ๋งค๊ฐ๋ณ์๋ ์์์ ์ ์ธ๋ ๋ชจ๋ ์ค์ฝํ๋ฅผ ๋ด์ `list`์ธ `scopes` ์์ฑ์ ๊ฐ์ง๋ฏ๋ก:
+ * *๊ฒฝ๋ก ์ฒ๋ฆฌ* `read_own_items`์ ๊ฒฝ์ฐ `security_scopes.scopes`์๋ `["me", "items"]`๊ฐ ๋ค์ด๊ฐ๋๋ค.
+ * *๊ฒฝ๋ก ์ฒ๋ฆฌ* `read_users_me`์ ๊ฒฝ์ฐ `security_scopes.scopes`์๋ `["me"]`๊ฐ ๋ค์ด๊ฐ๋๋ค. ์ด๋ ์์กด์ฑ `get_current_active_user`์์ ์ ์ธ๋๊ธฐ ๋๋ฌธ์
๋๋ค.
+ * *๊ฒฝ๋ก ์ฒ๋ฆฌ* `read_system_status`์ ๊ฒฝ์ฐ `security_scopes.scopes`์๋ `[]`(์์)๊ฐ ๋ค์ด๊ฐ๋๋ค. `scopes`๊ฐ ์๋ `Security`๋ฅผ ์ ์ธํ์ง ์์๊ณ , ๊ทธ ์์กด์ฑ์ธ `get_current_user`๋ `scopes`๋ฅผ ์ ์ธํ์ง ์์๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+/// tip | ํ
+
+์ฌ๊ธฐ์ ์ค์ํ โ๋ง๋ฒ ๊ฐ์โ ์ ์ `get_current_user`๊ฐ ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ง๋ค ๊ฒ์ฌํด์ผ ํ๋ `scopes` ๋ชฉ๋ก์ด ๋ฌ๋ผ์ง๋ค๋ ๊ฒ์
๋๋ค.
+
+์ด๋ ํน์ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๋ํ ์์กด์ฑ ํธ๋ฆฌ์์, ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๊ฐ ์์กด์ฑ์ ์ ์ธ๋ `scopes`์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค.
+
+///
+
+## `SecurityScopes`์ ๋ํ ์ถ๊ฐ ์ค๋ช
{ #more-details-about-securityscopes }
+
+`SecurityScopes`๋ ์ด๋ ์ง์ ์์๋ , ๊ทธ๋ฆฌ๊ณ ์ฌ๋ฌ ๊ณณ์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, โ๋ฃจํธโ ์์กด์ฑ์๋ง ์์ด์ผ ํ๋ ๊ฒ์ ์๋๋๋ค.
+
+`SecurityScopes`๋ **ํด๋น ํน์ ** *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ **ํด๋น ํน์ ** ์์กด์ฑ ํธ๋ฆฌ์ ๋ํด, ํ์ฌ `Security` ์์กด์ฑ๊ณผ ๋ชจ๋ dependant์ ์ ์ธ๋ ๋ณด์ ์ค์ฝํ๋ฅผ ํญ์ ๊ฐ๊ณ ์์ต๋๋ค.
+
+`SecurityScopes`์๋ dependant๊ฐ ์ ์ธํ ๋ชจ๋ ์ค์ฝํ๊ฐ ํฌํจ๋๋ฏ๋ก, ์ค์์ ์์กด์ฑ ํจ์์์ ํ ํฐ์ด ํ์ํ ์ค์ฝํ๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ๊ฒ์ฆํ ๋ค์, ์๋ก ๋ค๋ฅธ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์์ ์๋ก ๋ค๋ฅธ ์ค์ฝํ ์๊ตฌ์ฌํญ์ ์ ์ธํ ์ ์์ต๋๋ค.
+
+์ด๋ค์ ๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ง๋ค ๋
๋ฆฝ์ ์ผ๋ก ๊ฒ์ฌ๋ฉ๋๋ค.
+
+## ํ์ธํ๊ธฐ { #check-it }
+
+API ๋ฌธ์๋ฅผ ์ด๋ฉด, ์ธ์ฆํ๊ณ ์ธ๊ฐํ ์ค์ฝํ๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
+
+<img src="/img/tutorial/security/image11.png">
+
+์ด๋ค ์ค์ฝํ๋ ์ ํํ์ง ์์ผ๋ฉด โ์ธ์ฆโ์ ๋์ง๋ง, `/users/me/` ๋๋ `/users/me/items/`์ ์ ๊ทผํ๋ ค๊ณ ํ๋ฉด ๊ถํ์ด ์ถฉ๋ถํ์ง ์๋ค๋ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค. `/status/`์๋ ์ฌ์ ํ ์ ๊ทผํ ์ ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ค์ฝํ `me`๋ ์ ํํ์ง๋ง ์ค์ฝํ `items`๋ ์ ํํ์ง ์์๋ค๋ฉด, `/users/me/`์๋ ์ ๊ทผํ ์ ์์ง๋ง `/users/me/items/`์๋ ์ ๊ทผํ ์ ์์ต๋๋ค.
+
+์ด๋ ์ฌ์ฉ์๊ฐ ์ ํ๋ฆฌ์ผ์ด์
์ ์ผ๋ง๋ ๋ง์ ๊ถํ์ ๋ถ์ฌํ๋์ง์ ๋ฐ๋ผ, ์ 3์ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ฌ์ฉ์๋ก๋ถํฐ ์ ๊ณต๋ฐ์ ํ ํฐ์ผ๋ก ์ด *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ค ์ค ํ๋์ ์ ๊ทผํ๋ ค๊ณ ํ ๋ ๋ฐ์ํ๋ ์ํฉ๊ณผ ๊ฐ์ต๋๋ค.
+
+## ์ 3์ ํตํฉ์ ๋ํด { #about-third-party-integrations }
+
+์ด ์์ ์์๋ OAuth2 โpasswordโ ํ๋ก์ฐ๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
+
+์ด๋ ๋ณดํต ์์ฒด ํ๋ก ํธ์๋๊ฐ ์๋ ์ฐ๋ฆฌ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ก๊ทธ์ธํ ๋ ์ ํฉํฉ๋๋ค.
+
+์ฐ๋ฆฌ๊ฐ ์ด๋ฅผ ํต์ ํ๋ฏ๋ก `username`๊ณผ `password`๋ฅผ ๋ฐ๋ ๊ฒ์ ์ ๋ขฐํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+ํ์ง๋ง ๋ค๋ฅธ ์ฌ๋๋ค์ด ์ฐ๊ฒฐํ OAuth2 ์ ํ๋ฆฌ์ผ์ด์
(์ฆ, Facebook, Google, GitHub ๋ฑ๊ณผ ๋๋ฑํ ์ธ์ฆ ์ ๊ณต์๋ฅผ ๋ง๋ค๊ณ ์๋ค๋ฉด)์ ๊ตฌ์ถํ๋ค๋ฉด, ๋ค๋ฅธ ํ๋ก์ฐ ์ค ํ๋๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
+
+๊ฐ์ฅ ํํ ๊ฒ์ implicit ํ๋ก์ฐ์
๋๋ค.
+
+๊ฐ์ฅ ์์ ํ ๊ฒ์ code ํ๋ก์ฐ์ด์ง๋ง, ๋ ๋ง์ ๋จ๊ณ๊ฐ ํ์ํด ๊ตฌํ์ด ๋ ๋ณต์กํฉ๋๋ค. ๋ณต์กํ๊ธฐ ๋๋ฌธ์ ๋ง์ ์ ๊ณต์๋ implicit ํ๋ก์ฐ๋ฅผ ๊ถ์ฅํ๊ฒ ๋ฉ๋๋ค.
+
+/// note | ์ฐธ๊ณ
+
+์ธ์ฆ ์ ๊ณต์๋ง๋ค ์์ ๋ค์ ๋ธ๋๋์ ์ผ๋ถ๋ก ๋ง๋ค๊ธฐ ์ํด, ๊ฐ ํ๋ก์ฐ๋ฅผ ์๋ก ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ์ด๋ฆ ๋ถ์ด๋ ๊ฒฝ์ฐ๊ฐ ํํฉ๋๋ค.
+
+ํ์ง๋ง ๊ฒฐ๊ตญ, ๋์ผํ OAuth2 ํ์ค์ ๊ตฌํํ๊ณ ์๋ ๊ฒ์
๋๋ค.
+
+///
+
+**FastAPI**๋ `fastapi.security.oauth2`์ ์ด๋ฌํ ๋ชจ๋ OAuth2 ์ธ์ฆ ํ๋ก์ฐ๋ฅผ ์ํ ์ ํธ๋ฆฌํฐ๋ฅผ ํฌํจํฉ๋๋ค.
+
+## ๋ฐ์ฝ๋ ์ดํฐ `dependencies`์์์ `Security` { #security-in-decorator-dependencies }
+
+[๊ฒฝ๋ก ์ฒ๋ฆฌ ๋ฐ์ฝ๋ ์ดํฐ์ ์์กด์ฑ](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}์์ ์ค๋ช
ํ ๊ฒ์ฒ๋ผ ๋ฐ์ฝ๋ ์ดํฐ์ `dependencies` ๋งค๊ฐ๋ณ์์ `Depends`์ `list`๋ฅผ ์ ์ํ ์ ์๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก, ๊ฑฐ๊ธฐ์์ `scopes`์ ํจ๊ป `Security`๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
--- /dev/null
+# ์ค์ ๊ณผ ํ๊ฒฝ ๋ณ์ { #settings-and-environment-variables }
+
+๋ง์ ๊ฒฝ์ฐ ์ ํ๋ฆฌ์ผ์ด์
์๋ ์ธ๋ถ ์ค์ ์ด๋ ๊ตฌ์ฑ(์: secret key, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๊ฒฉ ์ฆ๋ช
, ์ด๋ฉ์ผ ์๋น์ค ์๊ฒฉ ์ฆ๋ช
๋ฑ)์ด ํ์ํ ์ ์์ต๋๋ค.
+
+์ด๋ฌํ ์ค์ ๋๋ถ๋ถ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค URL์ฒ๋ผ ๋ณ๋ ๊ฐ๋ฅ(๋ณ๊ฒฝ๋ ์ ์์)ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ ์ค์ ์ secret์ฒ๋ผ ๋ฏผ๊ฐํ ์ ์์ต๋๋ค.
+
+์ด ๋๋ฌธ์ ๋ณดํต ์ ํ๋ฆฌ์ผ์ด์
์ด ์ฝ์ด๋ค์ด๋ ํ๊ฒฝ ๋ณ์๋ก ์ด๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์
๋๋ค.
+
+/// tip | ํ
+
+ํ๊ฒฝ ๋ณ์๋ฅผ ์ดํดํ๋ ค๋ฉด [ํ๊ฒฝ ๋ณ์](../environment-variables.md){.internal-link target=_blank}๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+///
+
+## ํ์
๊ณผ ๊ฒ์ฆ { #types-and-validation }
+
+์ด ํ๊ฒฝ ๋ณ์๋ค์ Python ์ธ๋ถ์ ์์ผ๋ฉฐ ๋ค๋ฅธ ํ๋ก๊ทธ๋จ ๋ฐ ์์คํ
์ ๋๋จธ์ง ๋ถ๋ถ(๊ทธ๋ฆฌ๊ณ Linux, Windows, macOS ๊ฐ์ ์๋ก ๋ค๋ฅธ ์ด์์ฒด์ ์๋)๊ณผ ํธํ๋์ด์ผ ํ๋ฏ๋ก, ํ
์คํธ ๋ฌธ์์ด๋ง ๋ค๋ฃฐ ์ ์์ต๋๋ค.
+
+์ฆ, Python์์ ํ๊ฒฝ ๋ณ์๋ก๋ถํฐ ์ฝ์ด์จ ์ด๋ค ๊ฐ์ด๋ `str`์ด ๋๋ฉฐ, ๋ค๋ฅธ ํ์
์ผ๋ก์ ๋ณํ์ด๋ ๊ฒ์ฆ์ ์ฝ๋์์ ์ํํด์ผ ํฉ๋๋ค.
+
+## Pydantic `Settings` { #pydantic-settings }
+
+๋คํํ Pydantic์ <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/" class="external-link" target="_blank">Pydantic: Settings management</a>๋ฅผ ํตํด ํ๊ฒฝ ๋ณ์์์ ์ค๋ ์ด๋ฌํ ์ค์ ์ ์ฒ๋ฆฌํ ์ ์๋ ํ๋ฅญํ ์ ํธ๋ฆฌํฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
+
+### `pydantic-settings` ์ค์นํ๊ธฐ { #install-pydantic-settings }
+
+๋จผ์ [๊ฐ์ ํ๊ฒฝ](../virtual-environments.md){.internal-link target=_blank}์ ๋ง๋ค๊ณ ํ์ฑํํ ๋ค์, `pydantic-settings` ํจํค์ง๋ฅผ ์ค์นํ์ธ์:
+
+<div class="termy">
+
+```console
+$ pip install pydantic-settings
+---> 100%
+```
+
+</div>
+
+๋๋ ๋ค์์ฒ๋ผ `all` extras๋ฅผ ์ค์นํ๋ฉด ํจ๊ป ํฌํจ๋ฉ๋๋ค:
+
+<div class="termy">
+
+```console
+$ pip install "fastapi[all]"
+---> 100%
+```
+
+</div>
+
+### `Settings` ๊ฐ์ฒด ๋ง๋ค๊ธฐ { #create-the-settings-object }
+
+Pydantic์์ `BaseSettings`๋ฅผ importํ๊ณ , Pydantic ๋ชจ๋ธ๊ณผ ๋งค์ฐ ๋น์ทํ๊ฒ ์๋ธํด๋์ค๋ฅผ ๋ง๋์ธ์.
+
+Pydantic ๋ชจ๋ธ๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก, ํ์
์ด๋
ธํ
์ด์
(๊ทธ๋ฆฌ๊ณ ํ์ํ๋ค๋ฉด ๊ธฐ๋ณธ๊ฐ)๊ณผ ํจ๊ป ํด๋์ค ์์ฑ์ ์ ์ธํฉ๋๋ค.
+
+๋ค์ํ ๋ฐ์ดํฐ ํ์
, `Field()`๋ก ์ถ๊ฐ ๊ฒ์ฆ ๋ฑ Pydantic ๋ชจ๋ธ์์ ์ฌ์ฉํ๋ ๋์ผํ ๊ฒ์ฆ ๊ธฐ๋ฅ๊ณผ ๋๊ตฌ๋ฅผ ๋ชจ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
+
+/// tip | ํ
+
+๋น ๋ฅด๊ฒ ๋ณต์ฌ/๋ถ์ฌ๋ฃ๊ธฐํ ์์๊ฐ ํ์ํ๋ค๋ฉด, ์ด ์์๋ ์ฌ์ฉํ์ง ๋ง๊ณ ์๋์ ๋ง์ง๋ง ์์๋ฅผ ์ฌ์ฉํ์ธ์.
+
+///
+
+๊ทธ ๋ค์, ํด๋น `Settings` ํด๋์ค์ ์ธ์คํด์ค(์ฌ๊ธฐ์๋ `settings` ๊ฐ์ฒด)๋ฅผ ์์ฑํ๋ฉด Pydantic์ด ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์๊ณ ํ๊ฒฝ ๋ณ์๋ฅผ ์ฝ์ต๋๋ค. ๋ฐ๋ผ์ ๋๋ฌธ์ ๋ณ์ `APP_NAME`๋ `app_name` ์์ฑ์ ๋ํด ์ฝํ๋๋ค.
+
+์ดํ ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๊ณ ๊ฒ์ฆํฉ๋๋ค. ๊ทธ๋์ ๊ทธ `settings` ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ๋๋ ์ ์ธํ ํ์
์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ฒ ๋ฉ๋๋ค(์: `items_per_user`๋ `int`๊ฐ ๋ฉ๋๋ค).
+
+### `settings` ์ฌ์ฉํ๊ธฐ { #use-the-settings }
+
+์ด์ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ `settings` ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
+
+### ์๋ฒ ์คํํ๊ธฐ { #run-the-server }
+
+๋ค์์ผ๋ก ํ๊ฒฝ ๋ณ์๋ฅผ ํตํด ๊ตฌ์ฑ์ ์ ๋ฌํ๋ฉด์ ์๋ฒ๋ฅผ ์คํํฉ๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์์ฒ๋ผ `ADMIN_EMAIL`๊ณผ `APP_NAME`์ ์ค์ ํ ์ ์์ต๋๋ค:
+
+<div class="termy">
+
+```console
+$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+```
+
+</div>
+
+/// tip | ํ
+
+ํ๋์ ๋ช
๋ น์ ์ฌ๋ฌ env var๋ฅผ ์ค์ ํ๋ ค๋ฉด ๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถํ๊ณ , ๋ชจ๋ ๋ช
๋ น ์์ ๋์ธ์.
+
+///
+
+๊ทธ๋ฌ๋ฉด `admin_email` ์ค์ ์ `"deadpool@example.com"`์ผ๋ก ์ค์ ๋ฉ๋๋ค.
+
+`app_name`์ `"ChimichangApp"`์ด ๋ฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ `items_per_user`๋ ๊ธฐ๋ณธ๊ฐ `50`์ ์ ์งํฉ๋๋ค.
+
+## ๋ค๋ฅธ ๋ชจ๋์ ์ค์ { #settings-in-another-module }
+
+[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}์์ ๋ณธ ๊ฒ์ฒ๋ผ, ์ค์ ์ ๋ค๋ฅธ ๋ชจ๋ ํ์ผ์ ๋ฃ์ ์๋ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด `config.py` ํ์ผ์ ๋ค์์ฒ๋ผ ๋ง๋ค ์ ์์ต๋๋ค:
+
+{* ../../docs_src/settings/app01_py39/config.py *}
+
+๊ทธ๋ฆฌ๊ณ `main.py` ํ์ผ์์ ์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค:
+
+{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
+
+/// tip | ํ
+
+[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}์์ ๋ณธ ๊ฒ์ฒ๋ผ `__init__.py` ํ์ผ๋ ํ์ํฉ๋๋ค.
+
+///
+
+## ์์กด์ฑ์์ ์ค์ ์ฌ์ฉํ๊ธฐ { #settings-in-a-dependency }
+
+์ด๋ค ๊ฒฝ์ฐ์๋ ์ด๋์๋ ์ฌ์ฉ๋๋ ์ ์ญ `settings` ๊ฐ์ฒด๋ฅผ ๋๋ ๋์ , ์์กด์ฑ์์ ์ค์ ์ ์ ๊ณตํ๋ ๊ฒ์ด ์ ์ฉํ ์ ์์ต๋๋ค.
+
+์ด๋ ํนํ ํ
์คํธ ์ค์ ์ ์ฉํ ์ ์๋๋ฐ, ์ฌ์ฉ์ ์ ์ ์ค์ ์ผ๋ก ์์กด์ฑ์ overrideํ๊ธฐ๊ฐ ๋งค์ฐ ์ฝ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+### config ํ์ผ { #the-config-file }
+
+์ด์ ์์์์ ์ด์ด์, `config.py` ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
+
+์ด์ ๋ ๊ธฐ๋ณธ ์ธ์คํด์ค `settings = Settings()`๋ฅผ ์์ฑํ์ง ์๋๋ค๋ ์ ์ ์ ์ํ์ธ์.
+
+### ๋ฉ์ธ ์ฑ ํ์ผ { #the-main-app-file }
+
+์ด์ ์๋ก์ด `config.Settings()`๋ฅผ ๋ฐํํ๋ ์์กด์ฑ์ ์์ฑํฉ๋๋ค.
+
+{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
+
+/// tip | ํ
+
+`@lru_cache`๋ ์กฐ๊ธ ๋ค์ ๋ค๋ฃน๋๋ค.
+
+์ง๊ธ์ `get_settings()`๊ฐ ์ผ๋ฐ ํจ์๋ผ๊ณ ๊ฐ์ ํด๋ ๋ฉ๋๋ค.
+
+///
+
+๊ทธ ๋ค์ *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์์ ์ด๋ฅผ ์์กด์ฑ์ผ๋ก ์๊ตฌํ๊ณ , ํ์ํ ์ด๋์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
+
+### ์ค์ ๊ณผ ํ
์คํธ { #settings-and-testing }
+
+๊ทธ ๋ค์, `get_settings`์ ๋ํ ์์กด์ฑ override๋ฅผ ๋ง๋ค์ด ํ
์คํธ ์ค์ ๋ค๋ฅธ ์ค์ ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ๊ธฐ๊ฐ ๋งค์ฐ ์ฌ์์ง๋๋ค:
+
+{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
+
+์์กด์ฑ override์์๋ ์ `Settings` ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ `admin_email`์ ์ ๊ฐ์ ์ค์ ํ๊ณ , ๊ทธ ์ ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
+
+๊ทธ ๋ค์ ๊ทธ๊ฒ์ด ์ฌ์ฉ๋๋์ง ํ
์คํธํ ์ ์์ต๋๋ค.
+
+## `.env` ํ์ผ ์ฝ๊ธฐ { #reading-a-env-file }
+
+๋ง์ด ๋ฐ๋ ์ ์๋ ์ค์ ์ด ๋ง๊ณ , ์๋ก ๋ค๋ฅธ ํ๊ฒฝ์์ ์ฌ์ฉํ๋ค๋ฉด, ์ด๋ฅผ ํ์ผ์ ๋ฃ์ด ํ๊ฒฝ ๋ณ์์ธ ๊ฒ์ฒ๋ผ ์ฝ๋ ๊ฒ์ด ์ ์ฉํ ์ ์์ต๋๋ค.
+
+์ด ๊ดํ์ ์ถฉ๋ถํ ํํด์ ์ด๋ฆ๋ ์๋๋ฐ, ์ด๋ฌํ ํ๊ฒฝ ๋ณ์๋ค์ ๋ณดํต `.env` ํ์ผ์ ๋๋ฉฐ, ๊ทธ ํ์ผ์ "dotenv"๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
+
+/// tip | ํ
+
+์ (`.`)์ผ๋ก ์์ํ๋ ํ์ผ์ Linux, macOS ๊ฐ์ Unix ๊ณ์ด ์์คํ
์์ ์จ๊น ํ์ผ์
๋๋ค.
+
+ํ์ง๋ง dotenv ํ์ผ์ด ๊ผญ ๊ทธ ์ ํํ ํ์ผ๋ช
์ ๊ฐ์ ธ์ผ ํ๋ ๊ฒ์ ์๋๋๋ค.
+
+///
+
+Pydantic์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด ์ด๋ฐ ์ ํ์ ํ์ผ์์ ์ฝ๋ ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค. ์์ธํ ๋ด์ฉ์ <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings: Dotenv (.env) support</a>๋ฅผ ์ฐธ๊ณ ํ์ธ์.
+
+/// tip | ํ
+
+์ด๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด `pip install python-dotenv`๊ฐ ํ์ํฉ๋๋ค.
+
+///
+
+### `.env` ํ์ผ { #the-env-file }
+
+๋ค์๊ณผ ๊ฐ์ `.env` ํ์ผ์ ๋ ์ ์์ต๋๋ค:
+
+```bash
+ADMIN_EMAIL="deadpool@example.com"
+APP_NAME="ChimichangApp"
+```
+
+### `.env`์์ ์ค์ ์ฝ๊ธฐ { #read-settings-from-env }
+
+๊ทธ๋ฆฌ๊ณ `config.py`๋ฅผ ๋ค์์ฒ๋ผ ์
๋ฐ์ดํธํฉ๋๋ค:
+
+{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
+
+/// tip | ํ
+
+`model_config` ์์ฑ์ Pydantic ์ค์ ์ ์ํ ๊ฒ์
๋๋ค. ์์ธํ ๋ด์ฉ์ <a href="https://docs.pydantic.dev/latest/concepts/config/" class="external-link" target="_blank">Pydantic: Concepts: Configuration</a>์ ์ฐธ๊ณ ํ์ธ์.
+
+///
+
+์ฌ๊ธฐ์๋ Pydantic `Settings` ํด๋์ค ์์ config `env_file`์ ์ ์ํ๊ณ , ์ฌ์ฉํ๋ ค๋ dotenv ํ์ผ์ ํ์ผ๋ช
์ ๊ฐ์ผ๋ก ์ค์ ํฉ๋๋ค.
+
+### `lru_cache`๋ก `Settings`๋ฅผ ํ ๋ฒ๋ง ์์ฑํ๊ธฐ { #creating-the-settings-only-once-with-lru-cache }
+
+๋์คํฌ์์ ํ์ผ์ ์ฝ๋ ๊ฒ์ ๋ณดํต ๋น์ฉ์ด ํฐ(๋๋ฆฐ) ์์
์ด๋ฏ๋ก, ๊ฐ ์์ฒญ๋ง๋ค ์ฝ๊ธฐ๋ณด๋ค๋ ํ ๋ฒ๋ง ์ํํ๊ณ ๋์ผํ settings ๊ฐ์ฒด๋ฅผ ์ฌ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
+
+ํ์ง๋ง ๋งค๋ฒ ๋ค์์ ์ํํ๋ฉด:
+
+```Python
+Settings()
+```
+
+์ `Settings` ๊ฐ์ฒด๊ฐ ์์ฑ๋๊ณ , ์์ฑ ์์ ์ `.env` ํ์ผ์ ๋ค์ ์ฝ๊ฒ ๋ฉ๋๋ค.
+
+์์กด์ฑ ํจ์๊ฐ ๋จ์ํ ๋ค์๊ณผ ๊ฐ๋ค๋ฉด:
+
+```Python
+def get_settings():
+ return Settings()
+```
+
+์์ฒญ๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ฒ ๋๊ณ , ์์ฒญ๋ง๋ค `.env` ํ์ผ์ ์ฝ๊ฒ ๋ฉ๋๋ค. โ ๏ธ
+
+ํ์ง๋ง ์์ `@lru_cache` ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฏ๋ก, `Settings` ๊ฐ์ฒด๋ ์ต์ด ํธ์ถ ์ ๋ฑ ํ ๋ฒ๋ง ์์ฑ๋ฉ๋๋ค. โ๏ธ
+
+{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
+
+๊ทธ ๋ค์ ์์ฒญ๋ค์์ ์์กด์ฑ์ผ๋ก `get_settings()`๊ฐ ๋ค์ ํธ์ถ๋ ๋๋ง๋ค, `get_settings()`์ ๋ด๋ถ ์ฝ๋๋ฅผ ์คํํด์ ์ `Settings` ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋์ , ์ฒซ ํธ์ถ์์ ๋ฐํ๋ ๋์ผํ ๊ฐ์ฒด๋ฅผ ๊ณ์ ๋ฐํํฉ๋๋ค.
+
+#### `lru_cache` Technical Details { #lru-cache-technical-details }
+
+`@lru_cache`๋ ๋ฐ์ฝ๋ ์ด์
ํ ํจ์๊ฐ ๋งค๋ฒ ๋ค์ ๊ณ์ฐํ๋ ๋์ , ์ฒซ ๋ฒ์งธ์ ๋ฐํ๋ ๋์ผํ ๊ฐ์ ๋ฐํํ๋๋ก ํจ์๋ฅผ ์์ ํฉ๋๋ค(์ฆ, ๋งค๋ฒ ํจ์ ์ฝ๋๋ฅผ ์คํํ์ง ์์ต๋๋ค).
+
+๋ฐ๋ผ์ ์๋์ ํจ์๋ ์ธ์ ์กฐํฉ๋ง๋ค ํ ๋ฒ์ฉ ์คํ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ๊ฐ์ ์ธ์ ์กฐํฉ์ ๋ํด ๋ฐํ๋ ๊ฐ์, ํจ์๊ฐ ์ ํํ ๊ฐ์ ์ธ์ ์กฐํฉ์ผ๋ก ํธ์ถ๋ ๋๋ง๋ค ๋ฐ๋ณตํด์ ์ฌ์ฉ๋ฉ๋๋ค.
+
+์๋ฅผ ๋ค์ด ๋ค์ ํจ์๊ฐ ์๋ค๋ฉด:
+
+```Python
+@lru_cache
+def say_hi(name: str, salutation: str = "Ms."):
+ return f"Hello {salutation} {name}"
+```
+
+ํ๋ก๊ทธ๋จ์ ๋ค์๊ณผ ๊ฐ์ด ์คํ๋ ์ ์์ต๋๋ค:
+
+```mermaid
+sequenceDiagram
+
+participant code as Code
+participant function as say_hi()
+participant execute as Execute function
+
+ rect rgba(0, 255, 0, .1)
+ code ->> function: say_hi(name="Camila")
+ function ->> execute: execute function code
+ execute ->> code: return the result
+ end
+
+ rect rgba(0, 255, 255, .1)
+ code ->> function: say_hi(name="Camila")
+ function ->> code: return stored result
+ end
+
+ rect rgba(0, 255, 0, .1)
+ code ->> function: say_hi(name="Rick")
+ function ->> execute: execute function code
+ execute ->> code: return the result
+ end
+
+ rect rgba(0, 255, 0, .1)
+ code ->> function: say_hi(name="Rick", salutation="Mr.")
+ function ->> execute: execute function code
+ execute ->> code: return the result
+ end
+
+ rect rgba(0, 255, 255, .1)
+ code ->> function: say_hi(name="Rick")
+ function ->> code: return stored result
+ end
+
+ rect rgba(0, 255, 255, .1)
+ code ->> function: say_hi(name="Camila")
+ function ->> code: return stored result
+ end
+```
+
+์ฐ๋ฆฌ์ ์์กด์ฑ `get_settings()`์ ๊ฒฝ์ฐ, ํจ์๊ฐ ์ด๋ค ์ธ์๋ ๋ฐ์ง ์์ผ๋ฏ๋ก ํญ์ ๊ฐ์ ๊ฐ์ ๋ฐํํฉ๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด ๊ฑฐ์ ์ ์ญ ๋ณ์์ฒ๋ผ ๋์ํฉ๋๋ค. ํ์ง๋ง ์์กด์ฑ ํจ์๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ํ
์คํธ๋ฅผ ์ํด ์ฝ๊ฒ overrideํ ์ ์์ต๋๋ค.
+
+`@lru_cache`๋ Python ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ `functools`์ ํฌํจ๋์ด ์์ผ๋ฉฐ, ์์ธํ ๋ด์ฉ์ <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">`@lru_cache`์ ๋ํ Python ๋ฌธ์</a>์์ ํ์ธํ ์ ์์ต๋๋ค.
+
+## ์ ๋ฆฌ { #recap }
+
+Pydantic Settings๋ฅผ ์ฌ์ฉํ๋ฉด Pydantic ๋ชจ๋ธ์ ๋ชจ๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ํ์ฉํด ์ ํ๋ฆฌ์ผ์ด์
์ ์ค์ ๋๋ ๊ตฌ์ฑ์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
+
+* ์์กด์ฑ์ ์ฌ์ฉํ๋ฉด ํ
์คํธ๋ฅผ ๋จ์ํํ ์ ์์ต๋๋ค.
+* `.env` ํ์ผ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+* `@lru_cache`๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ ์์ฒญ๋ง๋ค dotenv ํ์ผ์ ๋ฐ๋ณตํด์ ์ฝ๋ ๊ฒ์ ํผํ๋ฉด์๋, ํ
์คํธ ์ค์๋ ์ด๋ฅผ overrideํ ์ ์์ต๋๋ค.
--- /dev/null
+# ๋์, ์๊ฐ, ๋น๊ต { #alternatives-inspiration-and-comparisons }
+
+**FastAPI**์ ์๊ฐ์ ์ค ๊ฒ๋ค, ๋์๊ณผ์ ๋น๊ต, ๊ทธ๋ฆฌ๊ณ ๊ทธ๋ก๋ถํฐ ๋ฌด์์ ๋ฐฐ์ ๋์ง์ ๋ํ ๋ด์ฉ์
๋๋ค.
+
+## ์๊ฐ { #intro }
+
+๋ค๋ฅธ ์ฌ๋๋ค์ ์ด์ ์์
์ด ์์๋ค๋ฉด **FastAPI**๋ ์กด์ฌํ์ง ์์์ ๊ฒ์
๋๋ค.
+
+๊ทธ ์ ์ ๋ง๋ค์ด์ง ๋ง์ ๋๊ตฌ๋ค์ด **FastAPI**์ ํ์์ ์๊ฐ์ ์ฃผ์์ต๋๋ค.
+
+์ ๋ ์ฌ๋ฌ ํด ๋์ ์๋ก์ด framework๋ฅผ ๋ง๋๋ ๊ฒ์ ํผํ๊ณ ์์์ต๋๋ค. ๋จผ์ **FastAPI**๊ฐ ๋ค๋ฃจ๋ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ฌ๋ฌ ์๋ก ๋ค๋ฅธ framework, plug-in, ๋๊ตฌ๋ฅผ ์ฌ์ฉํด ํด๊ฒฐํด ๋ณด๋ ค๊ณ ํ์ต๋๋ค.
+
+ํ์ง๋ง ์ด๋ ์์ ์๋, ์ด์ ๋๊ตฌ๋ค์ ๊ฐ์ฅ ์ข์ ์์ด๋์ด๋ฅผ ๊ฐ์ ธ์ ๊ฐ๋ฅํ ์ต์ ์ ๋ฐฉ์์ผ๋ก ์กฐํฉํ๊ณ , ์ด์ ์๋ ์กด์ฌํ์ง ์์๋ ์ธ์ด ๊ธฐ๋ฅ(Python 3.6+ type hints)์ ํ์ฉํด ์ด ๋ชจ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๋ฌด์ธ๊ฐ๋ฅผ ๋ง๋๋ ๊ฒ ์ธ์๋ ๋ค๋ฅธ ์ ํ์ง๊ฐ ์์์ต๋๋ค.
+
+## ์ด์ ๋๊ตฌ๋ค { #previous-tools }
+
+### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a> { #django }
+
+๊ฐ์ฅ ์ธ๊ธฐ ์๋ Python framework์ด๋ฉฐ ๋๋ฆฌ ์ ๋ขฐ๋ฐ๊ณ ์์ต๋๋ค. Instagram ๊ฐ์ ์์คํ
์ ๋ง๋๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
+
+์๋์ ์ผ๋ก ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค(์: MySQL ๋๋ PostgreSQL)์ ๊ฐํ๊ฒ ๊ฒฐํฉ๋์ด ์์ด์, NoSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค(์: Couchbase, MongoDB, Cassandra ๋ฑ)๋ฅผ ์ฃผ ์ ์ฅ ์์ง์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์ ๊ทธ๋ฆฌ ์ฝ์ง ์์ต๋๋ค.
+
+๋ฐฑ์๋์์ HTML์ ์์ฑํ๊ธฐ ์ํด ๋ง๋ค์ด์ก์ง, ํ๋์ ์ธ ํ๋ฐํธ์๋(์: React, Vue.js, Angular)๋ ๋ค๋ฅธ ์์คํ
(์: <abbr title="Internet of Things - ์ฌ๋ฌผ ์ธํฐ๋ท">IoT</abbr> ๊ธฐ๊ธฐ)์์ ์ฌ์ฉ๋๋ API๋ฅผ ๋ง๋ค๊ธฐ ์ํด ์ค๊ณ๋ ๊ฒ์ ์๋๋๋ค.
+
+### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> { #django-rest-framework }
+
+Django REST framework๋ Django๋ฅผ ๊ธฐ๋ฐ์ผ๋ก Web API๋ฅผ ๊ตฌ์ถํ๊ธฐ ์ํ ์ ์ฐํ toolkit์ผ๋ก ๋ง๋ค์ด์ก๊ณ , Django์ API ๊ธฐ๋ฅ์ ๊ฐ์ ํ๊ธฐ ์ํ ๋ชฉ์ ์ด์์ต๋๋ค.
+
+Mozilla, Red Hat, Eventbrite๋ฅผ ํฌํจํด ๋ง์ ํ์ฌ์์ ์ฌ์ฉํฉ๋๋ค.
+
+**์๋ API ๋ฌธ์ํ**์ ์ด๊ธฐ ์ฌ๋ก ์ค ํ๋์๊ณ , ์ด๊ฒ์ด ํนํ **FastAPI**๋ฅผ "์ฐพ๊ฒ ๋" ์ฒซ ์์ด๋์ด ์ค ํ๋์์ต๋๋ค.
+
+/// note | ์ฐธ๊ณ
+
+Django REST Framework๋ Tom Christie๊ฐ ๋ง๋ค์์ต๋๋ค. **FastAPI**์ ๊ธฐ๋ฐ์ด ๋๋ Starlette์ Uvicorn์ ๋ง๋ ์ฌ๋๊ณผ ๋์ผํฉ๋๋ค.
+
+///
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+์๋ API ๋ฌธ์ํ ์น ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๊ธฐ.
+
+///
+
+### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a> { #flask }
+
+Flask๋ "microframework"๋ก, Django์ ๊ธฐ๋ณธ์ผ๋ก ํฌํจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํตํฉ์ด๋ ์ฌ๋ฌ ๊ธฐ๋ฅ๋ค์ ํฌํจํ์ง ์์ต๋๋ค.
+
+์ด ๋จ์ํจ๊ณผ ์ ์ฐ์ฑ ๋๋ถ์ NoSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฃผ ๋ฐ์ดํฐ ์ ์ฅ ์์คํ
์ผ๋ก ์ฌ์ฉํ๋ ๊ฐ์ ์์
์ด ๊ฐ๋ฅํฉ๋๋ค.
+
+๋งค์ฐ ๋จ์ํ๊ธฐ ๋๋ฌธ์ ๋น๊ต์ ์ง๊ด์ ์ผ๋ก ๋ฐฐ์ธ ์ ์์ง๋ง, ๋ฌธ์๊ฐ ์ด๋ค ์ง์ ์์๋ ๋ค์ ๊ธฐ์ ์ ์ผ๋ก ๊น์ด์ง๊ธฐ๋ ํฉ๋๋ค.
+
+๋ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค, ์ฌ์ฉ์ ๊ด๋ฆฌ, ํน์ Django์ ๋ฏธ๋ฆฌ ๊ตฌ์ถ๋์ด ์๋ ๋ค์ํ ๊ธฐ๋ฅ๋ค์ด ๊ผญ ํ์ํ์ง ์์ ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์
์๋ ํํ ์ฌ์ฉ๋ฉ๋๋ค. ๋ฌผ๋ก ์ด๋ฐ ๊ธฐ๋ฅ๋ค ์ค ๋ค์๋ plug-in์ผ๋ก ์ถ๊ฐํ ์ ์์ต๋๋ค.
+
+์ด๋ฐ ๊ตฌ์ฑ์์์ ๋ถ๋ฆฌ์, ํ์ํ ๊ฒ๋ง ์ ํํ ๋ง๋ถ์ฌ ํ์ฅํ ์ ์๋ "microframework"๋ผ๋ ์ ์ ์ ๊ฐ ์ ์งํ๊ณ ์ถ์๋ ํต์ฌ ํน์ฑ์ด์์ต๋๋ค.
+
+Flask์ ๋จ์ํจ์ ๊ณ ๋ คํ๋ฉด API๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ ์ ๋ง๋ ๊ฒ์ฒ๋ผ ๋ณด์์ต๋๋ค. ๋ค์์ผ๋ก ์ฐพ๊ณ ์ ํ๋ ๊ฒ์ Flask์ฉ "Django REST Framework"์์ต๋๋ค.
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+micro-framework๊ฐ ๋๊ธฐ. ํ์ํ ๋๊ตฌ์ ๊ตฌ์ฑ์์๋ฅผ ์ฝ๊ฒ ์กฐํฉํ ์ ์๋๋ก ํ๊ธฐ.
+
+๋จ์ํ๊ณ ์ฌ์ฉํ๊ธฐ ์ฌ์ด routing ์์คํ
์ ๊ฐ๊ธฐ.
+
+///
+
+### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a> { #requests }
+
+**FastAPI**๋ ์ค์ ๋ก **Requests**์ ๋์์ด ์๋๋๋ค. ๋์ ๋ฒ์๋ ๋งค์ฐ ๋ค๋ฆ
๋๋ค.
+
+์ค์ ๋ก FastAPI ์ ํ๋ฆฌ์ผ์ด์
*๋ด๋ถ์์* Requests๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ํํฉ๋๋ค.
+
+๊ทธ๋ผ์๋ FastAPI๋ Requests๋ก๋ถํฐ ๊ฝค ๋ง์ ์๊ฐ์ ์ป์์ต๋๋ค.
+
+**Requests**๋ (ํด๋ผ์ด์ธํธ๋ก์) API์ *์ํธ์์ฉ*ํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ณ , **FastAPI**๋ (์๋ฒ๋ก์) API๋ฅผ *๊ตฌ์ถ*ํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค.
+
+๋๋ต ๋งํ๋ฉด ์๋ก ๋ฐ๋ํธ์ ์์ผ๋ฉฐ, ์๋ก๋ฅผ ๋ณด์ํฉ๋๋ค.
+
+Requests๋ ๋งค์ฐ ๋จ์ํ๊ณ ์ง๊ด์ ์ธ ์ค๊ณ๋ฅผ ๊ฐ์ก๊ณ , ํฉ๋ฆฌ์ ์ธ ๊ธฐ๋ณธ๊ฐ์ ๋ฐํ์ผ๋ก ์ฌ์ฉํ๊ธฐ๊ฐ ์์ฃผ ์ฝ์ต๋๋ค. ๋์์ ๋งค์ฐ ๊ฐ๋ ฅํ๊ณ ์ปค์คํฐ๋ง์ด์ง๋ ๊ฐ๋ฅํฉ๋๋ค.
+
+๊ทธ๋์ ๊ณต์ ์น์ฌ์ดํธ์์ ๋งํ๋ฏ์ด:
+
+> Requests is one of the most downloaded Python packages of all time
+
+์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋งค์ฐ ๊ฐ๋จํฉ๋๋ค. ์๋ฅผ ๋ค์ด `GET` ์์ฒญ์ ํ๋ ค๋ฉด ๋ค์์ฒ๋ผ ์์ฑํฉ๋๋ค:
+
+```Python
+response = requests.get("http://example.com/some/url")
+```
+
+์ด์ ๋์ํ๋ FastAPI์ API *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ผ ์ ์์ต๋๋ค:
+
+```Python hl_lines="1"
+@app.get("/some/url")
+def read_url():
+ return {"message": "Hello World"}
+```
+
+`requests.get(...)`์ `@app.get(...)`์ ์ ์ฌ์ฑ์ ํ์ธํด ๋ณด์ธ์.
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+* ๋จ์ํ๊ณ ์ง๊ด์ ์ธ API๋ฅผ ๊ฐ๊ธฐ.
+* HTTP method ์ด๋ฆ(operations)์ ์ง์ , ์ง๊ด์ ์ด๊ณ ๋ช
ํํ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ๊ธฐ.
+* ํฉ๋ฆฌ์ ์ธ ๊ธฐ๋ณธ๊ฐ์ ์ ๊ณตํ๋, ๊ฐ๋ ฅํ ์ปค์คํฐ๋ง์ด์ง์ ๊ฐ๋ฅํ๊ฒ ํ๊ธฐ.
+
+///
+
+### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a> { #swagger-openapi }
+
+์ ๊ฐ Django REST Framework์์ ๊ฐ์ฅ ์ํ๋ ์ฃผ์ ๊ธฐ๋ฅ์ ์๋ API ๋ฌธ์ํ์์ต๋๋ค.
+
+๊ทธ ํ JSON(๋๋ JSON์ ํ์ฅ์ธ YAML)์ ์ฌ์ฉํด API๋ฅผ ๋ฌธ์ํํ๋ ํ์ค์ธ Swagger๊ฐ ์๋ค๋ ๊ฒ์ ์๊ฒ ๋์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ Swagger API๋ฅผ ์ํ ์น ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ ์ด๋ฏธ ๋ง๋ค์ด์ ธ ์์์ต๋๋ค. ๊ทธ๋์ API์ ๋ํ Swagger ๋ฌธ์๋ฅผ ์์ฑํ ์ ์๋ค๋ฉด, ์ด ์น ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ์๋์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค.
+
+์ด๋ ์์ ์ Swagger๋ Linux Foundation์ผ๋ก ๋์ด๊ฐ OpenAPI๋ก ์ด๋ฆ์ด ๋ฐ๋์์ต๋๋ค.
+
+๊ทธ๋์ 2.0 ๋ฒ์ ์ ์ด์ผ๊ธฐํ ๋๋ "Swagger"๋ผ๊ณ ๋งํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๊ณ , 3+ ๋ฒ์ ์ "OpenAPI"๋ผ๊ณ ๋งํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์
๋๋ค.
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+์ปค์คํ
schema ๋์ , API ์ฌ์์ ์ํ ์ด๋ฆฐ ํ์ค์ ์ฑํํ๊ณ ์ฌ์ฉํ๊ธฐ.
+
+๋ํ ํ์ค ๊ธฐ๋ฐ์ ์ฌ์ฉ์ ์ธํฐํ์ด์ค ๋๊ตฌ๋ฅผ ํตํฉํ๊ธฐ:
+
+* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>
+* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>
+
+์ด ๋ ๊ฐ์ง๋ ๊ฝค ๋์ค์ ์ด๊ณ ์์ ์ ์ด๊ธฐ ๋๋ฌธ์ ์ ํ๋์์ต๋๋ค. ํ์ง๋ง ๊ฐ๋จํ ๊ฒ์ํด๋ณด๋ฉด OpenAPI๋ฅผ ์ํ ๋์ UI๊ฐ ์์ญ ๊ฐ์ง๋ ์๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค(**FastAPI**์ ํจ๊ป ์ฌ์ฉํ ์ ์์ต๋๋ค).
+
+///
+
+### Flask REST framework๋ค { #flask-rest-frameworks }
+
+Flask REST framework๋ ์ฌ๋ฌ ๊ฐ๊ฐ ์์ง๋ง, ์๊ฐ์ ๋ค์ฌ ์กฐ์ฌํด ๋ณธ ๊ฒฐ๊ณผ, ์๋น์๊ฐ ์ค๋จ๋์๊ฑฐ๋ ๋ฐฉ์น๋์ด ์์๊ณ , ํด๊ฒฐ๋์ง ์์ ์ฌ๋ฌ ์ด์ ๋๋ฌธ์ ์ ํฉํ์ง ์์ ๊ฒฝ์ฐ๊ฐ ๋ง์์ต๋๋ค.
+
+### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
+
+API ์์คํ
์ ํ์ํ ์ฃผ์ ๊ธฐ๋ฅ ์ค ํ๋๋ ๋ฐ์ดํฐ "<abbr title="also called marshalling, conversion - ๋ง์ฌ๋ง, ๋ณํ์ด๋ผ๊ณ ๋ ํฉ๋๋ค">serialization</abbr>"์
๋๋ค. ์ด๋ ์ฝ๋(Python)์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ๋คํธ์ํฌ๋ก ์ ์กํ ์ ์๋ ํํ๋ก ๋ณํํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์๋ฅผ ๋ค์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๊ฐ์ฒด๋ฅผ JSON ๊ฐ์ฒด๋ก ๋ณํํ๊ฑฐ๋, `datetime` ๊ฐ์ฒด๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํ๋ ๋ฑ์ ์์
์
๋๋ค.
+
+API์ ๋ ํ๋ ํฌ๊ฒ ํ์ํ ๊ธฐ๋ฅ์ ๋ฐ์ดํฐ ๊ฒ์ฆ์
๋๋ค. ํน์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์ ํจํ์ง ํ์ธํ๋ ๊ฒ์
๋๋ค. ์๋ฅผ ๋ค์ด ์ด๋ค ํ๋๊ฐ `int`์ธ์ง, ์์์ ๋ฌธ์์ด์ด ์๋์ง ํ์ธํ๋ ์์
๋๋ค. ์ด๋ ํนํ ๋ค์ด์ค๋ ๋ฐ์ดํฐ์ ์ ์ฉํฉ๋๋ค.
+
+๋ฐ์ดํฐ ๊ฒ์ฆ ์์คํ
์ด ์๋ค๋ฉด, ๋ชจ๋ ๊ฒ์ฌ๋ฅผ ์ฝ๋์์ ์๋์ผ๋ก ํด์ผ ํฉ๋๋ค.
+
+์ด๋ฐ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํ๊ธฐ ์ํด Marshmallow๊ฐ ๋ง๋ค์ด์ก์ต๋๋ค. ํ๋ฅญํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ฉฐ, ์ ๋ ์ด์ ์ ๋ง์ด ์ฌ์ฉํ์ต๋๋ค.
+
+ํ์ง๋ง Python type hints๊ฐ ์กด์ฌํ๊ธฐ ์ ์ ๋ง๋ค์ด์ก์ต๋๋ค. ๊ทธ๋์ ๊ฐ <abbr title="the definition of how data should be formed - ๋ฐ์ดํฐ๊ฐ ์ด๋ป๊ฒ ๊ตฌ์ฑ๋์ด์ผ ํ๋์ง์ ๋ํ ์ ์">schema</abbr>๋ฅผ ์ ์ํ๋ ค๋ฉด Marshmallow๊ฐ ์ ๊ณตํ๋ ํน์ ์ ํธ๋ฆฌํฐ์ ํด๋์ค๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+๋ฐ์ดํฐ ํ์
๊ณผ ๊ฒ์ฆ์ ์ ๊ณตํ๋ "schema"๋ฅผ ์ฝ๋๋ก ์ ์ํ๊ณ , ์ด๋ฅผ ์๋์ผ๋ก ํ์ฉํ๊ธฐ.
+
+///
+
+### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
+
+API์ ํ์ํ ๋ ๋ค๋ฅธ ํฐ ๊ธฐ๋ฅ์ ๋ค์ด์ค๋ ์์ฒญ์์ ๋ฐ์ดํฐ๋ฅผ <abbr title="reading and converting to Python data - ์ฝ์ด์ Python ๋ฐ์ดํฐ๋ก ๋ณํํ๊ธฐ">parsing</abbr>ํ๋ ๊ฒ์
๋๋ค.
+
+Webargs๋ Flask๋ฅผ ํฌํจํ ์ฌ๋ฌ framework ์์์ ์ด๋ฅผ ์ ๊ณตํ๊ธฐ ์ํด ๋ง๋ค์ด์ง ๋๊ตฌ์
๋๋ค.
+
+๋ด๋ถ์ ์ผ๋ก Marshmallow๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ ๊ฒ์ฆ์ ์ํํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ ๊ฐ๋ฐ์๋ค์ด ๋ง๋ค์์ต๋๋ค.
+
+์์ฃผ ํ๋ฅญํ ๋๊ตฌ์ด๋ฉฐ, ์ ๋ **FastAPI**๋ฅผ ๋ง๋ค๊ธฐ ์ ์ ๋ง์ด ์ฌ์ฉํ์ต๋๋ค.
+
+/// info | ์ ๋ณด
+
+Webargs๋ Marshmallow์ ๊ฐ์ ๊ฐ๋ฐ์๋ค์ด ๋ง๋ค์์ต๋๋ค.
+
+///
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+๋ค์ด์ค๋ ์์ฒญ ๋ฐ์ดํฐ์ ์๋ ๊ฒ์ฆ์ ๊ฐ๊ธฐ.
+
+///
+
+### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a> { #apispec }
+
+Marshmallow์ Webargs๋ plug-in ํํ๋ก ๊ฒ์ฆ, parsing, serialization์ ์ ๊ณตํฉ๋๋ค.
+
+ํ์ง๋ง ๋ฌธ์ํ๋ ์ฌ์ ํ ๋ถ์กฑํ์ต๋๋ค. ๊ทธ๋์ APISpec์ด ๋ง๋ค์ด์ก์ต๋๋ค.
+
+์ด๋ ์ฌ๋ฌ framework๋ฅผ ์ํ plug-in์ด๋ฉฐ(Starlette์ฉ plug-in๋ ์์ต๋๋ค).
+
+์๋ ๋ฐฉ์์, ๊ฐ route๋ฅผ ์ฒ๋ฆฌํ๋ ํจ์์ docstring ์์ YAML ํ์์ผ๋ก schema ์ ์๋ฅผ ์์ฑํ๊ณ ,
+
+๊ทธ๋ก๋ถํฐ OpenAPI schema๋ฅผ ์์ฑํฉ๋๋ค.
+
+Flask, Starlette, Responder ๋ฑ์์ ์ด๋ฐ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค.
+
+ํ์ง๋ง ๋ค์, Python ๋ฌธ์์ด ๋ด๋ถ(ํฐ YAML)์์ micro-syntax๋ฅผ ๋ค๋ฃจ์ด์ผ ํ๋ค๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
+
+์๋ํฐ๊ฐ ์ด๋ฅผ ํฌ๊ฒ ๋์์ฃผ์ง ๋ชปํฉ๋๋ค. ๋ํ ํ๋ผ๋ฏธํฐ๋ Marshmallow schema๋ฅผ ์์ ํด๋๊ณ YAML docstring๋ ๊ฐ์ด ์์ ํ๋ ๊ฒ์ ์์ด๋ฒ๋ฆฌ๋ฉด, ์์ฑ๋ schema๋ ์ค๋๋ ์ํ๊ฐ ๋ฉ๋๋ค.
+
+/// info | ์ ๋ณด
+
+APISpec์ Marshmallow์ ๊ฐ์ ๊ฐ๋ฐ์๋ค์ด ๋ง๋ค์์ต๋๋ค.
+
+///
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+API๋ฅผ ์ํ ์ด๋ฆฐ ํ์ค์ธ OpenAPI๋ฅผ ์ง์ํ๊ธฐ.
+
+///
+
+### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a> { #flask-apispec }
+
+Flask plug-in์ผ๋ก, Webargs, Marshmallow, APISpec์ ๋ฌถ์ด์ค๋๋ค.
+
+Webargs์ Marshmallow์ ์ ๋ณด๋ฅผ ์ฌ์ฉํด APISpec์ผ๋ก OpenAPI schema๋ฅผ ์๋ ์์ฑํฉ๋๋ค.
+
+ํ๋ฅญํ ๋๊ตฌ์ธ๋ฐ๋ ๊ณผ์ํ๊ฐ๋์ด ์์ต๋๋ค. ๋ค๋ฅธ ๋ง์ Flask plug-in๋ณด๋ค ํจ์ฌ ๋ ์ ๋ช
ํด์ ธ์ผ ํฉ๋๋ค. ๋ฌธ์๊ฐ ๋๋ฌด ๊ฐ๊ฒฐํ๊ณ ์ถ์์ ์ด๋ผ์ ๊ทธ๋ด ์๋ ์์ต๋๋ค.
+
+์ด ๋๊ตฌ๋ Python docstring ๋ด๋ถ์ YAML(๋ ๋ค๋ฅธ ๋ฌธ๋ฒ)์ ์์ฑํด์ผ ํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.
+
+Flask + Flask-apispec + Marshmallow + Webargs ์กฐํฉ์ **FastAPI**๋ฅผ ๋ง๋ค๊ธฐ ์ ๊น์ง ์ ๊ฐ ๊ฐ์ฅ ์ข์ํ๋ ๋ฐฑ์๋ stack์ด์์ต๋๋ค.
+
+์ด๋ฅผ ์ฌ์ฉํ๋ฉด์ ์ฌ๋ฌ Flask full-stack generator๊ฐ ๋ง๋ค์ด์ก์ต๋๋ค. ์ด๊ฒ๋ค์ด ์ง๊ธ๊น์ง ์ (๊ทธ๋ฆฌ๊ณ ์ฌ๋ฌ ์ธ๋ถ ํ)๊ฐ ์ฌ์ฉํด ์จ ์ฃผ์ stack์
๋๋ค:
+
+* <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a>
+* <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a>
+* <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a>
+
+๊ทธ๋ฆฌ๊ณ ์ด ๋์ผํ full-stack generator๋ค์ด [**FastAPI** Project Generators](project-generation.md){.internal-link target=_blank}์ ๊ธฐ๋ฐ์ด ๋์์ต๋๋ค.
+
+/// info | ์ ๋ณด
+
+Flask-apispec์ Marshmallow์ ๊ฐ์ ๊ฐ๋ฐ์๋ค์ด ๋ง๋ค์์ต๋๋ค.
+
+///
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+serialization๊ณผ validation์ ์ ์ํ๋ ๋์ผํ ์ฝ๋๋ก๋ถํฐ OpenAPI schema๋ฅผ ์๋ ์์ฑํ๊ธฐ.
+
+///
+
+### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (๊ทธ๋ฆฌ๊ณ <a href="https://angular.io/" class="external-link" target="_blank">Angular</a>) { #nestjs-and-angular }
+
+์ด๊ฑด Python๋ ์๋๋๋ค. NestJS๋ Angular์์ ์๊ฐ์ ๋ฐ์ JavaScript(TypeScript) NodeJS framework์
๋๋ค.
+
+Flask-apispec์ผ๋ก ํ ์ ์๋ ๊ฒ๊ณผ ์ด๋ ์ ๋ ๋น์ทํ ๊ฒ์ ๋ฌ์ฑํฉ๋๋ค.
+
+Angular 2์์ ์๊ฐ์ ๋ฐ์ ์์กด์ฑ ์ฃผ์
์์คํ
์ด ํตํฉ๋์ด ์์ต๋๋ค. ์ ๊ฐ ์๋ ๋ค๋ฅธ ์์กด์ฑ ์ฃผ์
์์คํ
๋ค์ฒ๋ผ "injectable"์ ์ฌ์ ์ ๋ฑ๋กํด์ผ ํ๋ฏ๋ก, ์ฅํฉํจ๊ณผ ์ฝ๋ ๋ฐ๋ณต์ด ๋์ด๋ฉ๋๋ค.
+
+ํ๋ผ๋ฏธํฐ๊ฐ TypeScript ํ์
(Python type hints์ ์ ์ฌํจ)์ผ๋ก ์ค๋ช
๋๊ธฐ ๋๋ฌธ์ ์๋ํฐ ์ง์์ ๊ฝค ์ข์ต๋๋ค.
+
+ํ์ง๋ง TypeScript ๋ฐ์ดํฐ๋ JavaScript๋ก ์ปดํ์ผ๋ ๋ค์๋ ๋ณด์กด๋์ง ์๊ธฐ ๋๋ฌธ์, ํ์
์ ์์กดํด ๊ฒ์ฆ, serialization, ๋ฌธ์ํ๋ฅผ ๋์์ ์ ์ํ ์ ์์ต๋๋ค. ์ด ์ ๊ณผ ์ผ๋ถ ์ค๊ณ ๊ฒฐ์ ๋๋ฌธ์, ๊ฒ์ฆ/serialization/์๋ schema ์์ฑ์ ํ๋ ค๋ฉด ์ฌ๋ฌ ๊ณณ์ decorator๋ฅผ ์ถ๊ฐํด์ผ ํ๋ฉฐ, ๊ฒฐ๊ณผ์ ์ผ๋ก ๋งค์ฐ ์ฅํฉํด์ง๋๋ค.
+
+์ค์ฒฉ ๋ชจ๋ธ์ ์ ์ฒ๋ฆฌํ์ง ๋ชปํฉ๋๋ค. ์ฆ, ์์ฒญ์ JSON body๊ฐ ๋ด๋ถ ํ๋๋ฅผ ๊ฐ์ง JSON ๊ฐ์ฒด์ด๊ณ ๊ทธ ๋ด๋ถ ํ๋๋ค์ด ๋ค์ ์ค์ฒฉ๋ JSON ๊ฐ์ฒด์ธ ๊ฒฝ์ฐ, ์ ๋๋ก ๋ฌธ์ํํ๊ณ ๊ฒ์ฆํ ์ ์์ต๋๋ค.
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+Python ํ์
์ ์ฌ์ฉํด ๋ฐ์ด๋ ์๋ํฐ ์ง์์ ์ ๊ณตํ๊ธฐ.
+
+๊ฐ๋ ฅํ ์์กด์ฑ ์ฃผ์
์์คํ
์ ๊ฐ์ถ๊ธฐ. ์ฝ๋ ๋ฐ๋ณต์ ์ต์ํํ๋ ๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ.
+
+///
+
+### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a> { #sanic }
+
+`asyncio` ๊ธฐ๋ฐ์ ๋งค์ฐ ๋น ๋ฅธ Python framework ์ค ์ด๊ธฐ ์ฌ๋ก์์ต๋๋ค. Flask์ ๋งค์ฐ ์ ์ฌํ๊ฒ ๋ง๋ค์ด์ก์ต๋๋ค.
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+๊ธฐ๋ณธ Python `asyncio` ๋ฃจํ ๋์ <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a>๋ฅผ ์ฌ์ฉํ์ต๋๋ค. ์ด๊ฒ์ด ๋งค์ฐ ๋น ๋ฅด๊ฒ ๋ง๋ ์์ธ์
๋๋ค.
+
+์ด๋ Uvicorn๊ณผ Starlette์ ๋ช
ํํ ์๊ฐ์ ์ฃผ์๊ณ , ํ์ฌ ๊ณต๊ฐ benchmark์์๋ ์ด ๋์ด Sanic๋ณด๋ค ๋ ๋น ๋ฆ
๋๋ค.
+
+///
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+๋ฏธ์น ์ฑ๋ฅ์ ๋ผ ์ ์๋ ๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ.
+
+๊ทธ๋์ **FastAPI**๋ Starlette๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค. Starlette๋ ์ฌ์ฉ ๊ฐ๋ฅํ framework ์ค ๊ฐ์ฅ ๋น ๋ฅด๊ธฐ ๋๋ฌธ์
๋๋ค(์๋ํํฐ benchmark๋ก ํ
์คํธ๋จ).
+
+///
+
+### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a> { #falcon }
+
+Falcon์ ๋ ๋ค๋ฅธ ๊ณ ์ฑ๋ฅ Python framework๋ก, ์ต์ํ์ผ๋ก ์ค๊ณ๋์๊ณ Hug ๊ฐ์ ๋ค๋ฅธ framework์ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋๋ก ๋ง๋ค์ด์ก์ต๋๋ค.
+
+ํจ์๊ฐ ๋ ๊ฐ์ ํ๋ผ๋ฏธํฐ(ํ๋๋ "request", ํ๋๋ "response")๋ฅผ ๋ฐ๋๋ก ์ค๊ณ๋์ด ์์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ request์์ ์ผ๋ถ๋ฅผ "์ฝ๊ณ ", response์ ์ผ๋ถ๋ฅผ "์์ฑ"ํฉ๋๋ค. ์ด ์ค๊ณ ๋๋ฌธ์, ํ์ค Python type hints๋ฅผ ํจ์ ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉํด ์์ฒญ ํ๋ผ๋ฏธํฐ์ body๋ฅผ ์ ์ธํ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค.
+
+๋ฐ๋ผ์ ๋ฐ์ดํฐ ๊ฒ์ฆ, serialization, ๋ฌธ์ํ๋ ์๋์ผ๋ก ๋์ง ์๊ณ ์ฝ๋๋ก ํด์ผ ํฉ๋๋ค. ๋๋ Hug์ฒ๋ผ Falcon ์์ framework๋ฅผ ์น์ด ๊ตฌํํด์ผ ํฉ๋๋ค. request ๊ฐ์ฒด ํ๋์ response ๊ฐ์ฒด ํ๋๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋ Falcon์ ์ค๊ณ์์ ์๊ฐ์ ๋ฐ์ ๋ค๋ฅธ framework์์๋ ๊ฐ์ ๊ตฌ๋ถ์ด ๋ํ๋ฉ๋๋ค.
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+ํ๋ฅญํ ์ฑ๋ฅ์ ์ป๋ ๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ.
+
+Hug(= Falcon ๊ธฐ๋ฐ)๊ณผ ํจ๊ป, ํจ์์์ `response` ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ธํ๋๋ก **FastAPI**์ ์๊ฐ์ ์ฃผ์์ต๋๋ค.
+
+๋ค๋ง FastAPI์์๋ ์ ํ ์ฌํญ์ด๋ฉฐ, ์ฃผ๋ก ํค๋, ์ฟ ํค, ๊ทธ๋ฆฌ๊ณ ๋์ฒด status code๋ฅผ ์ค์ ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
+
+///
+
+### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a> { #molten }
+
+**FastAPI**๋ฅผ ๋ง๋ค๊ธฐ ์์ํ ์ด๊ธฐ ๋จ๊ณ์์ Molten์ ์๊ฒ ๋์๊ณ , ๊ฝค ๋น์ทํ ์์ด๋์ด๋ฅผ ๊ฐ๊ณ ์์์ต๋๋ค:
+
+* Python type hints ๊ธฐ๋ฐ
+* ์ด ํ์
์ผ๋ก๋ถํฐ ๊ฒ์ฆ๊ณผ ๋ฌธ์ํ ์์ฑ
+* ์์กด์ฑ ์ฃผ์
์์คํ
+
+Pydantic ๊ฐ์ ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ ๊ฒ์ฆ/serialization/๋ฌธ์ํ๋ฅผ ํ์ง ์๊ณ ์์ฒด ๊ตฌํ์ ์ฌ์ฉํฉ๋๋ค. ๊ทธ๋์ ์ด๋ฐ ๋ฐ์ดํฐ ํ์
์ ์๋ฅผ ์ฝ๊ฒ ์ฌ์ฌ์ฉํ๊ธฐ๋ ์ด๋ ต์ต๋๋ค.
+
+์กฐ๊ธ ๋ ์ฅํฉํ ์ค์ ์ด ํ์ํฉ๋๋ค. ๋ํ WSGI(ASGI๊ฐ ์๋๋ผ) ๊ธฐ๋ฐ์ด๋ฏ๋ก, Uvicorn, Starlette, Sanic ๊ฐ์ ๋๊ตฌ๊ฐ ์ ๊ณตํ๋ ๊ณ ์ฑ๋ฅ์ ํ์ฉํ๋๋ก ์ค๊ณ๋์ง ์์์ต๋๋ค.
+
+์์กด์ฑ ์ฃผ์
์์คํ
์ ์์กด์ฑ์ ์ฌ์ ์ ๋ฑ๋กํด์ผ ํ๊ณ , ์ ์ธ๋ ํ์
์ ๊ธฐ๋ฐ์ผ๋ก ์์กด์ฑ์ ํด๊ฒฐํฉ๋๋ค. ๋ฐ๋ผ์ ํน์ ํ์
์ ์ ๊ณตํ๋ "component"๋ฅผ ๋ ๊ฐ ์ด์ ์ ์ธํ ์ ์์ต๋๋ค.
+
+Route๋ ํ ๊ณณ์์ ์ ์ธํ๊ณ , ๋ค๋ฅธ ๊ณณ์ ์ ์ธ๋ ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค(์๋ํฌ์ธํธ๋ฅผ ์ฒ๋ฆฌํ๋ ํจ์ ๋ฐ๋ก ์์ ๋ ์ ์๋ decorator๋ฅผ ์ฌ์ฉํ๋ ๋์ ). ์ด๋ Flask(๋ฐ Starlette)๋ณด๋ค๋ Django ๋ฐฉ์์ ๊ฐ๊น์ต๋๋ค. ์ฝ๋์์ ์๋์ ์ผ๋ก ๊ฐํ๊ฒ ๊ฒฐํฉ๋ ๊ฒ๋ค์ ๋ถ๋ฆฌํด ๋์ต๋๋ค.
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+๋ชจ๋ธ ์์ฑ์ "default" ๊ฐ์ผ๋ก ๋ฐ์ดํฐ ํ์
์ ๋ํ ์ถ๊ฐ ๊ฒ์ฆ์ ์ ์ํ๊ธฐ. ์ด๋ ์๋ํฐ ์ง์์ ๊ฐ์ ํ๋ฉฐ, ์ด์ ์๋ Pydantic์ ์์์ต๋๋ค.
+
+์ด๊ฒ์ ์ค์ ๋ก Pydantic์ ์ผ๋ถ๋ฅผ ์
๋ฐ์ดํธํ์ฌ ๊ฐ์ ๊ฒ์ฆ ์ ์ธ ์คํ์ผ์ ์ง์ํ๋๋ก ํ๋ ๋ฐ ์๊ฐ์ ์ฃผ์์ต๋๋ค(์ด ๊ธฐ๋ฅ์ ์ด์ Pydantic์ ์ด๋ฏธ ํฌํจ๋์ด ์์ต๋๋ค).
+
+///
+
+### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a> { #hug }
+
+Hug๋ Python type hints๋ฅผ ์ฌ์ฉํด API ํ๋ผ๋ฏธํฐ ํ์
์ ์ ์ธํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ด๊ธฐ framework ์ค ํ๋์์ต๋๋ค. ์ด๋ ๋ค๋ฅธ ๋๊ตฌ๋ค๋ ๊ฐ์ ๋ฐฉ์์ ํ๋๋ก ์๊ฐ์ ์ค ํ๋ฅญํ ์์ด๋์ด์์ต๋๋ค.
+
+ํ์ค Python ํ์
๋์ ์ปค์คํ
ํ์
์ ์ ์ธ์ ์ฌ์ฉํ์ง๋ง, ์ฌ์ ํ ํฐ ์ง์ ์ด์์ต๋๋ค.
+
+๋ํ ์ ์ฒด API๋ฅผ JSON์ผ๋ก ์ ์ธํ๋ ์ปค์คํ
schema๋ฅผ ์์ฑํ ์ด๊ธฐ framework ์ค ํ๋์์ต๋๋ค.
+
+OpenAPI๋ JSON Schema ๊ฐ์ ํ์ค์ ๊ธฐ๋ฐ์ผ๋ก ํ์ง ์์๊ธฐ ๋๋ฌธ์ Swagger UI ๊ฐ์ ๋ค๋ฅธ ๋๊ตฌ์ ํตํฉํ๋ ๊ฒ์ ์ง๊ด์ ์ด์ง ์์์ต๋๋ค. ํ์ง๋ง ์ญ์ ๋งค์ฐ ํ์ ์ ์ธ ์์ด๋์ด์์ต๋๋ค.
+
+ํฅ๋ฏธ๋กญ๊ณ ํ์น ์์ ๊ธฐ๋ฅ์ด ํ๋ ์์ต๋๋ค. ๊ฐ์ framework๋ก API๋ฟ ์๋๋ผ CLI๋ ๋ง๋ค ์ ์์ต๋๋ค.
+
+๋๊ธฐ์ Python ์น framework์ ์ด์ ํ์ค(WSGI) ๊ธฐ๋ฐ์ด์ด์ Websockets์ ๋ค๋ฅธ ๊ฒ๋ค์ ์ฒ๋ฆฌํ ์๋ ์์ง๋ง, ์ฑ๋ฅ์ ์ฌ์ ํ ๋์ต๋๋ค.
+
+/// info | ์ ๋ณด
+
+Hug๋ Timothy Crosley๊ฐ ๋ง๋ค์์ต๋๋ค. Python ํ์ผ์์ import๋ฅผ ์๋์ผ๋ก ์ ๋ ฌํ๋ ํ๋ฅญํ ๋๊ตฌ์ธ <a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a>์ ์ ์์์ด๊ธฐ๋ ํฉ๋๋ค.
+
+///
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ์์ด๋์ด๋ค
+
+Hug๋ APIStar์ ์ผ๋ถ์ ์๊ฐ์ ์ฃผ์๊ณ , ์ ๋ APIStar์ ํจ๊ป Hug๋ฅผ ๊ฐ์ฅ ์ ๋งํ ๋๊ตฌ ์ค ํ๋๋ก ๋ณด์์ต๋๋ค.
+
+Hug๋ Python type hints๋ก ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ธํ๊ณ , API๋ฅผ ์ ์ํ๋ schema๋ฅผ ์๋์ผ๋ก ์์ฑํ๋๋ก **FastAPI**์ ์๊ฐ์ ์ฃผ์์ต๋๋ค.
+
+Hug๋ ํค๋์ ์ฟ ํค๋ฅผ ์ค์ ํ๊ธฐ ์ํด ํจ์์ `response` ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ธํ๋๋ก **FastAPI**์ ์๊ฐ์ ์ฃผ์์ต๋๋ค.
+
+///
+
+### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5) { #apistar-0-5 }
+
+**FastAPI**๋ฅผ ๋ง๋ค๊ธฐ๋ก ๊ฒฐ์ ํ๊ธฐ ์ง์ ์ **APIStar** ์๋ฒ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค. ์ฐพ๊ณ ์๋ ๊ฑฐ์ ๋ชจ๋ ๊ฒ์ ๊ฐ์ถ๊ณ ์์๊ณ ์ค๊ณ๋ ํ๋ฅญํ์ต๋๋ค.
+
+NestJS์ Molten๋ณด๋ค ์์, Python type hints๋ฅผ ์ฌ์ฉํด ํ๋ผ๋ฏธํฐ์ ์์ฒญ์ ์ ์ธํ๋ framework ๊ตฌํ์ ์ ๊ฐ ์ฒ์ ๋ณธ ์ฌ๋ก๋ค ์ค ํ๋์์ต๋๋ค. Hug์ ๊ฑฐ์ ๊ฐ์ ์๊ธฐ์ ๋ฐ๊ฒฌํ์ต๋๋ค. ํ์ง๋ง APIStar๋ OpenAPI ํ์ค์ ์ฌ์ฉํ์ต๋๋ค.
+
+์ฌ๋ฌ ์์น์์ ๋์ผํ type hints๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋ ๋ฐ์ดํฐ ๊ฒ์ฆ, ๋ฐ์ดํฐ serialization, OpenAPI schema ์์ฑ์ ์ ๊ณตํ์ต๋๋ค.
+
+Body schema ์ ์๋ Pydantic์ฒ๋ผ ๋์ผํ Python type hints๋ฅผ ์ฌ์ฉํ์ง๋ ์์๊ณ Marshmallow์ ์กฐ๊ธ ๋ ๋น์ทํด์ ์๋ํฐ ์ง์์ ๊ทธ๋งํผ ์ข์ง ์์์ง๋ง, ๊ทธ๋๋ APIStar๋ ๋น์ ์ฌ์ฉํ ์ ์๋ ์ต์ ์ ์ ํ์ง์์ต๋๋ค.
+
+๋น์ ์ต๊ณ ์ ์ฑ๋ฅ benchmark๋ฅผ ๊ฐ์ก์ต๋๋ค(Starlette์ ์ํด์๋ง ์ถ์๋จ).
+
+์ฒ์์๋ ์๋ API ๋ฌธ์ํ ์น UI๊ฐ ์์์ง๋ง, Swagger UI๋ฅผ ์ถ๊ฐํ ์ ์๋ค๋ ๊ฒ์ ์๊ณ ์์์ต๋๋ค.
+
+์์กด์ฑ ์ฃผ์
์์คํ
๋ ์์์ต๋๋ค. ์์์ ์ธ๊ธํ ๋ค๋ฅธ ๋๊ตฌ๋ค์ฒ๋ผ component์ ์ฌ์ ๋ฑ๋ก์ด ํ์ํ์ง๋ง, ์ฌ์ ํ ํ๋ฅญํ ๊ธฐ๋ฅ์ด์์ต๋๋ค.
+
+๋ณด์ ํตํฉ์ด ์์ด์ ์ ์ฒด ํ๋ก์ ํธ์์ ์ฌ์ฉํด ๋ณผ ์๋ ์์์ต๋๋ค. ๊ทธ๋์ Flask-apispec ๊ธฐ๋ฐ full-stack generator๋ก ๊ฐ์ถ๊ณ ์๋ ๋ชจ๋ ๊ธฐ๋ฅ์ ๋์ฒดํ ์ ์์์ต๋๋ค. ๊ทธ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ pull request๋ฅผ ๋ง๋๋ ๊ฒ์ด ์ ๋ฐฑ๋ก๊ทธ์ ์์์ต๋๋ค.
+
+ํ์ง๋ง ์ดํ ํ๋ก์ ํธ์ ์ด์ ์ด ๋ฐ๋์์ต๋๋ค.
+
+๋ ์ด์ API web framework๊ฐ ์๋๊ฒ ๋์๋๋ฐ, ์ ์์๊ฐ Starlette์ ์ง์คํด์ผ ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+์ด์ APIStar๋ web framework๊ฐ ์๋๋ผ OpenAPI ์ฌ์์ ๊ฒ์ฆํ๊ธฐ ์ํ ๋๊ตฌ ๋ชจ์์
๋๋ค.
+
+/// info | ์ ๋ณด
+
+APIStar๋ Tom Christie๊ฐ ๋ง๋ค์์ต๋๋ค. ๋ค์์ ๋ง๋ ์ฌ๋๊ณผ ๋์ผํฉ๋๋ค:
+
+* Django REST Framework
+* Starlette(**FastAPI**์ ๊ธฐ๋ฐ)
+* Uvicorn(Starlette์ **FastAPI**์์ ์ฌ์ฉ)
+
+///
+
+/// check | **FastAPI**์ ์๊ฐ์ ์ค ๊ฒ
+
+์กด์ฌํ๊ฒ ๋ง๋ค๊ธฐ.
+
+๋์ผํ Python ํ์
์ผ๋ก ์ฌ๋ฌ ๊ฐ์ง(๋ฐ์ดํฐ ๊ฒ์ฆ, serialization, ๋ฌธ์ํ)๋ฅผ ์ ์ธํ๋ฉด์ ๋์์ ๋ฐ์ด๋ ์๋ํฐ ์ง์์ ์ ๊ณตํ๋ค๋ ์์ด๋์ด๋ ์ ๊ฐ ๋งค์ฐ ํ๋ฅญํ๋ค๊ณ ์๊ฐํ์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ค๋ซ๋์ ๋น์ทํ framework๋ฅผ ์ฐพ์ ์ฌ๋ฌ ๋์์ ํ
์คํธํ ๋์, APIStar๊ฐ ๊ทธ๋ ์ด์ฉ ๊ฐ๋ฅํ ์ต์ ์ ์ ํ์ง์์ต๋๋ค.
+
+๊ทธ ํ APIStar ์๋ฒ๊ฐ ๋๋ ์กด์ฌํ์ง ์๊ฒ ๋๊ณ Starlette๊ฐ ๋ง๋ค์ด์ก๋๋ฐ, ์ด๋ ๊ทธ๋ฐ ์์คํ
์ ์ํ ๋ ์๋กญ๊ณ ๋ ๋์ ๊ธฐ๋ฐ์ด์์ต๋๋ค. ์ด๊ฒ์ด **FastAPI**๋ฅผ ๋ง๋ค๊ฒ ๋ ์ต์ข
์๊ฐ์ด์์ต๋๋ค.
+
+์ ๋ **FastAPI**๋ฅผ APIStar์ "์ ์ ์ ํ๊ณ์"๋ก ์๊ฐํฉ๋๋ค. ๋์์, ์ด ๋ชจ๋ ์ด์ ๋๊ตฌ๋ค์์ ๋ฐฐ์ด ๊ฒ๋ค์ ๋ฐํ์ผ๋ก ๊ธฐ๋ฅ, typing ์์คํ
, ๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ๋ถ๋ถ๋ค์ ๊ฐ์ ํ๊ณ ํ์ฅํ์ต๋๋ค.
+
+///
+
+## **FastAPI**๊ฐ ์ฌ์ฉํ๋ ๊ฒ { #used-by-fastapi }
+
+### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> { #pydantic }
+
+Pydantic์ Python type hints๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ ๊ฒ์ฆ, serialization, ๋ฌธ์ํ(JSON Schema ์ฌ์ฉ)๋ฅผ ์ ์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค.
+
+๊ทธ ๋๋ถ์ ๋งค์ฐ ์ง๊ด์ ์
๋๋ค.
+
+Marshmallow์ ๋น๊ตํ ์ ์์ต๋๋ค. ๋ค๋ง benchmark์์ Marshmallow๋ณด๋ค ๋น ๋ฆ
๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋์ผํ Python type hints๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฏ๋ก ์๋ํฐ ์ง์๋ ํ๋ฅญํฉ๋๋ค.
+
+/// check | **FastAPI**๊ฐ ์ด๋ฅผ ์ฌ์ฉํ๋ ๋ชฉ์
+
+๋ชจ๋ ๋ฐ์ดํฐ ๊ฒ์ฆ, ๋ฐ์ดํฐ serialization, ์๋ ๋ชจ๋ธ ๋ฌธ์ํ(JSON Schema ๊ธฐ๋ฐ)๋ฅผ ์ฒ๋ฆฌํ๊ธฐ.
+
+๊ทธ ๋ค์ **FastAPI**๋ ๊ทธ JSON Schema ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ OpenAPI์ ํฌํจ์ํค๋ฉฐ, ๊ทธ ์ธ์๋ ์ฌ๋ฌ ์์
์ ์ํํฉ๋๋ค.
+
+///
+
+### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
+
+Starlette๋ ๊ฒฝ๋ <abbr title="The new standard for building asynchronous Python web applications - ๋น๋๊ธฐ Python ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๊ธฐ ์ํ ์๋ก์ด ํ์ค">ASGI</abbr> framework/toolkit์ผ๋ก, ๊ณ ์ฑ๋ฅ asyncio ์๋น์ค๋ฅผ ๋ง๋ค๊ธฐ์ ์ด์์ ์
๋๋ค.
+
+๋งค์ฐ ๋จ์ํ๊ณ ์ง๊ด์ ์
๋๋ค. ์ฝ๊ฒ ํ์ฅํ ์ ์๋๋ก ์ค๊ณ๋์๊ณ , ๋ชจ๋์ component๋ฅผ ๊ฐ์ต๋๋ค.
+
+๋ค์์ด ํฌํจ๋ฉ๋๋ค:
+
+* ์ ๋ง ์ธ์์ ์ธ ์ฑ๋ฅ.
+* WebSocket ์ง์.
+* ํ๋ก์ธ์ค ๋ด ๋ฐฑ๊ทธ๋ผ์ด๋ ์์
.
+* ์์ ๋ฐ ์ข
๋ฃ ์ด๋ฒคํธ.
+* HTTPX ๊ธฐ๋ฐ์ ํ
์คํธ ํด๋ผ์ด์ธํธ.
+* CORS, GZip, Static Files, Streaming responses.
+* ์ธ์
๋ฐ ์ฟ ํค ์ง์.
+* 100% ํ
์คํธ ์ปค๋ฒ๋ฆฌ์ง.
+* 100% ํ์
์ฃผ์์ด ๋ฌ๋ฆฐ ์ฝ๋๋ฒ ์ด์ค.
+* ์์์ ํ์ ์์กด์ฑ.
+
+Starlette๋ ํ์ฌ ํ
์คํธ๋ Python framework ์ค ๊ฐ์ฅ ๋น ๋ฆ
๋๋ค. ๋จ, framework๊ฐ ์๋๋ผ ์๋ฒ์ธ Uvicorn์ด ๋ ๋น ๋ฆ
๋๋ค.
+
+Starlette๋ ์น microframework์ ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ ๋ชจ๋ ์ ๊ณตํฉ๋๋ค.
+
+ํ์ง๋ง ์๋ ๋ฐ์ดํฐ ๊ฒ์ฆ, serialization, ๋ฌธ์ํ๋ ์ ๊ณตํ์ง ์์ต๋๋ค.
+
+๊ทธ๊ฒ์ด **FastAPI**๊ฐ ์์ ์ถ๊ฐํ๋ ํต์ฌ ์ค ํ๋์ด๋ฉฐ, ๋ชจ๋ Python type hints(Pydantic ์ฌ์ฉ)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค. ์ฌ๊ธฐ์ ๋ํด ์์กด์ฑ ์ฃผ์
์์คํ
, ๋ณด์ ์ ํธ๋ฆฌํฐ, OpenAPI schema ์์ฑ ๋ฑ๋ ํฌํจ๋ฉ๋๋ค.
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+ASGI๋ Django ์ฝ์ด ํ ๋ฉค๋ฒ๋ค์ด ๊ฐ๋ฐ ์ค์ธ ์๋ก์ด "ํ์ค"์
๋๋ค. ์์ง "Python ํ์ค"(PEP)์ ์๋์ง๋ง, ๊ทธ ๋ฐฉํฅ์ผ๋ก ์งํ ์ค์
๋๋ค.
+
+๊ทธ๋ผ์๋ ์ด๋ฏธ ์ฌ๋ฌ ๋๊ตฌ์์ "ํ์ค"์ผ๋ก ์ฌ์ฉ๋๊ณ ์์ต๋๋ค. ์ด๋ ์ํธ์ด์ฉ์ฑ์ ํฌ๊ฒ ๊ฐ์ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด Uvicorn์ ๋ค๋ฅธ ASGI ์๋ฒ(์: Daphne ๋๋ Hypercorn)๋ก ๊ต์ฒดํ ์๋ ์๊ณ , `python-socketio` ๊ฐ์ ASGI ํธํ ๋๊ตฌ๋ฅผ ์ถ๊ฐํ ์๋ ์์ต๋๋ค.
+
+///
+
+/// check | **FastAPI**๊ฐ ์ด๋ฅผ ์ฌ์ฉํ๋ ๋ชฉ์
+
+ํต์ฌ ์น ๋ถ๋ถ์ ๋ชจ๋ ์ฒ๋ฆฌํ๊ธฐ. ๊ทธ ์์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ.
+
+`FastAPI` ํด๋์ค ์์ฒด๋ `Starlette` ํด๋์ค๋ฅผ ์ง์ ์์ํฉ๋๋ค.
+
+๋ฐ๋ผ์ Starlette๋ก ํ ์ ์๋ ๋ชจ๋ ๊ฒ์ ๊ธฐ๋ณธ์ ์ผ๋ก **FastAPI**๋ก๋ ์ง์ ํ ์ ์์ต๋๋ค. ์ฆ, **FastAPI**๋ ์ฌ์ค์ Starlette์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ๋ํ ๊ฒ์
๋๋ค.
+
+///
+
+### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a> { #uvicorn }
+
+Uvicorn์ uvloop๊ณผ httptools๋ก ๊ตฌ์ถ๋ ์ด๊ณ ์ ASGI ์๋ฒ์
๋๋ค.
+
+web framework๊ฐ ์๋๋ผ ์๋ฒ์
๋๋ค. ์๋ฅผ ๋ค์ด ๊ฒฝ๋ก ๊ธฐ๋ฐ routing์ ์ํ ๋๊ตฌ๋ ์ ๊ณตํ์ง ์์ต๋๋ค. ๊ทธ๋ฐ ๊ฒ์ Starlette(๋๋ **FastAPI**) ๊ฐ์ framework๊ฐ ์์์ ์ ๊ณตํฉ๋๋ค.
+
+Starlette์ **FastAPI**์์ ๊ถ์ฅํ๋ ์๋ฒ์
๋๋ค.
+
+/// check | **FastAPI**๊ฐ ์ด๋ฅผ ๊ถ์ฅํ๋ ๋ฐฉ์
+
+**FastAPI** ์ ํ๋ฆฌ์ผ์ด์
์ ์คํํ๊ธฐ ์ํ ์ฃผ์ ์น ์๋ฒ.
+
+๋ํ `--workers` ์ปค๋งจ๋๋ผ์ธ ์ต์
์ ์ฌ์ฉํ๋ฉด ๋น๋๊ธฐ ๋ฉํฐํ๋ก์ธ์ค ์๋ฒ๋ก ์คํํ ์๋ ์์ต๋๋ค.
+
+์์ธํ ๋ด์ฉ์ [๋ฐฐํฌ](deployment/index.md){.internal-link target=_blank} ์น์
์ ํ์ธํ์ธ์.
+
+///
+
+## ๋ฒค์น๋งํฌ์ ์๋ { #benchmarks-and-speed }
+
+Uvicorn, Starlette, FastAPI ์ฌ์ด์ ์ฐจ์ด๋ฅผ ์ดํดํ๊ณ ๋น๊ตํ๋ ค๋ฉด [๋ฒค์น๋งํฌ](benchmarks.md){.internal-link target=_blank} ์น์
์ ํ์ธํ์ธ์.
--- /dev/null
+# ๋ฐฐํฌ ๊ฐ๋
{ #deployments-concepts }
+
+**FastAPI** ์ ํ๋ฆฌ์ผ์ด์
(์ฌ์ค ์ด๋ค ์ข
๋ฅ์ ์น API๋ )์ ๋ฐฐํฌํ ๋๋, ์ฌ๋ฌ๋ถ์ด ์ ๊ฒฝ ์จ์ผ ํ ์ฌ๋ฌ ๊ฐ๋
์ด ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๊ฐ๋
๋ค์ ํ์ฉํ๋ฉด **์ ํ๋ฆฌ์ผ์ด์
์ ๋ฐฐํฌํ๊ธฐ ์ํ ๊ฐ์ฅ ์ ์ ํ ๋ฐฉ๋ฒ**์ ์ฐพ์ ์ ์์ต๋๋ค.
+
+์ค์ํ ๊ฐ๋
๋ช ๊ฐ์ง๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* ๋ณด์ - HTTPS
+* ์์ ์ ์คํ
+* ์ฌ์์
+* ๋ณต์ (์คํ ์ค์ธ ํ๋ก์ธ์ค ์)
+* ๋ฉ๋ชจ๋ฆฌ
+* ์์ ์ ์ฌ์ ๋จ๊ณ
+
+์ด๊ฒ๋ค์ด **๋ฐฐํฌ**์ ์ด๋ค ์ํฅ์ ์ฃผ๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+๊ฒฐ๊ตญ ์ต์ข
๋ชฉํ๋ **API ํด๋ผ์ด์ธํธ์ ์๋น์ค๋ฅผ ์ ๊ณต**ํ ๋ **๋ณด์**์ ๋ณด์ฅํ๊ณ , **์ค๋จ์ ํผํ๋ฉฐ**, **์ปดํจํ
๋ฆฌ์์ค**(์: ์๊ฒฉ ์๋ฒ/๊ฐ์ ๋จธ์ )๋ฅผ ๊ฐ๋ฅํ ํ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค. ๐
+
+์ฌ๊ธฐ์ ์ด **๊ฐ๋
๋ค**์ ์กฐ๊ธ ๋ ์ค๋ช
ํ๊ฒ ์ต๋๋ค. ๊ทธ๋ฌ๋ฉด ์๋ก ๋งค์ฐ ๋ค๋ฅธ ํ๊ฒฝ, ์ฌ์ง์ด ์์ง ์กด์ฌํ์ง ์๋ **๋ฏธ๋**์ ํ๊ฒฝ์์๋ API๋ฅผ ์ด๋ป๊ฒ ๋ฐฐํฌํ ์ง ๊ฒฐ์ ํ๋ ๋ฐ ํ์ํ **์ง๊ด**์ ์ป์ ์ ์์ ๊ฒ์
๋๋ค.
+
+์ด ๊ฐ๋
๋ค์ ๊ณ ๋ คํ๋ฉด, ์ฌ๋ฌ๋ถ์ **์์ ์ API**๋ฅผ ๋ฐฐํฌํ๊ธฐ ์ํ ์ต์ ์ ๋ฐฉ๋ฒ์ **ํ๊ฐํ๊ณ ์ค๊ณ**ํ ์ ์์ต๋๋ค.
+
+๋ค์ ์ฅ๋ค์์๋ FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฐฐํฌํ๊ธฐ ์ํ ๋ **๊ตฌ์ฒด์ ์ธ ๋ ์ํผ**๋ฅผ ์ ๊ณตํ๊ฒ ์ต๋๋ค.
+
+ํ์ง๋ง ์ง๊ธ์, ์ด ์ค์ํ **๊ฐ๋
์ ์์ด๋์ด**๋ค์ ํ์ธํด ๋ด
์๋ค. ์ด ๊ฐ๋
๋ค์ ๋ค๋ฅธ ์ด๋ค ์ข
๋ฅ์ ์น API์๋ ๋์ผํ๊ฒ ์ ์ฉ๋ฉ๋๋ค. ๐ก
+
+## ๋ณด์ - HTTPS { #security-https }
+
+[์ด์ HTTPS ์ฅ](https.md){.internal-link target=_blank}์์ HTTPS๊ฐ API์ ์ํธํ๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ์์ ๋ํด ๋ฐฐ์ ์ต๋๋ค.
+
+๋ํ HTTPS๋ ์ผ๋ฐ์ ์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์
์๋ฒ ๋ฐ๊นฅ์ **์ธ๋ถ** ์ปดํฌ๋ํธ์ธ **TLS Termination Proxy**๊ฐ ์ ๊ณตํ๋ค๋ ๊ฒ๋ ํ์ธํ์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ **HTTPS ์ธ์ฆ์ ๊ฐฑ์ **์ ๋ด๋นํ๋ ๋ฌด์ธ๊ฐ๊ฐ ํ์ํฉ๋๋ค. ๊ฐ์ ์ปดํฌ๋ํธ๊ฐ ๊ทธ ์ญํ ์ ํ ์๋ ์๊ณ , ๋ค๋ฅธ ๋ฌด์ธ๊ฐ๊ฐ ๋ด๋นํ ์๋ ์์ต๋๋ค.
+
+### HTTPS๋ฅผ ์ํ ๋๊ตฌ ์์ { #example-tools-for-https }
+
+TLS Termination Proxy๋ก ์ฌ์ฉํ ์ ์๋ ๋๊ตฌ๋ ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* Traefik
+ * ์ธ์ฆ์ ๊ฐฑ์ ์ ์๋์ผ๋ก ์ฒ๋ฆฌ โจ
+* Caddy
+ * ์ธ์ฆ์ ๊ฐฑ์ ์ ์๋์ผ๋ก ์ฒ๋ฆฌ โจ
+* Nginx
+ * ์ธ์ฆ์ ๊ฐฑ์ ์ ์ํด Certbot ๊ฐ์ ์ธ๋ถ ์ปดํฌ๋ํธ ์ฌ์ฉ
+* HAProxy
+ * ์ธ์ฆ์ ๊ฐฑ์ ์ ์ํด Certbot ๊ฐ์ ์ธ๋ถ ์ปดํฌ๋ํธ ์ฌ์ฉ
+* Nginx ๊ฐ์ Ingress Controller๋ฅผ ์ฌ์ฉํ๋ Kubernetes
+ * ์ธ์ฆ์ ๊ฐฑ์ ์ ์ํด cert-manager ๊ฐ์ ์ธ๋ถ ์ปดํฌ๋ํธ ์ฌ์ฉ
+* ํด๋ผ์ฐ๋ ์ ๊ณต์๊ฐ ์๋น์ค ์ผ๋ถ๋ก ๋ด๋ถ์ ์ผ๋ก ์ฒ๋ฆฌ(์๋๋ฅผ ์ฝ์ด๋ณด์ธ์ ๐)
+
+๋ ๋ค๋ฅธ ์ ํ์ง๋ HTTPS ์ค์ ์ ํฌํจํด ๋ ๋ง์ ์ผ์ ๋์ ํด์ฃผ๋ **ํด๋ผ์ฐ๋ ์๋น์ค**๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค. ์ ์ฝ์ด ์๊ฑฐ๋ ๋น์ฉ์ด ๋ ๋ค ์๋ ์์ต๋๋ค. ํ์ง๋ง ๊ทธ ๊ฒฝ์ฐ์๋ TLS Termination Proxy๋ฅผ ์ง์ ์ค์ ํ ํ์๊ฐ ์์ต๋๋ค.
+
+๋ค์ ์ฅ์์ ๊ตฌ์ฒด์ ์ธ ์์๋ฅผ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
+
+---
+
+๋ค์์ผ๋ก ๊ณ ๋ คํ ๊ฐ๋
๋ค์ ์ค์ ๋ก ์ฌ๋ฌ๋ถ์ API๋ฅผ ์คํํ๋ ํ๋ก๊ทธ๋จ(์: Uvicorn)๊ณผ ๊ด๋ จ๋ ๋ด์ฉ์
๋๋ค.
+
+## ํ๋ก๊ทธ๋จ๊ณผ ํ๋ก์ธ์ค { #program-and-process }
+
+์คํ ์ค์ธ "**ํ๋ก์ธ์ค**"์ ๋ํด ๋ง์ด ์ด์ผ๊ธฐํ๊ฒ ๋ ํ
๋ฐ, ์ด ๋ง์ด ๋ฌด์์ ์๋ฏธํ๋์ง, ๊ทธ๋ฆฌ๊ณ "**ํ๋ก๊ทธ๋จ**"์ด๋ผ๋ ๋จ์ด์ ๋ฌด์์ด ๋ค๋ฅธ์ง ๋ช
ํํ ํด๋๋ ๊ฒ์ด ์ ์ฉํฉ๋๋ค.
+
+### ํ๋ก๊ทธ๋จ์ด๋ { #what-is-a-program }
+
+**ํ๋ก๊ทธ๋จ**์ด๋ผ๋ ๋จ์ด๋ ๋ณดํต ์ฌ๋ฌ ๊ฐ์ง๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค:
+
+* ์ฌ๋ฌ๋ถ์ด ์์ฑํ๋ **์ฝ๋**, ์ฆ **Python ํ์ผ**๋ค
+* ์ด์์ฒด์ ์์ **์คํ**ํ ์ ์๋ **ํ์ผ**, ์: `python`, `python.exe`, `uvicorn`
+* ์ด์์ฒด์ ์์ **์คํ ์ค**์ธ ํน์ ํ๋ก๊ทธ๋จ์ผ๋ก, CPU๋ฅผ ์ฌ์ฉํ๊ณ ๋ฉ๋ชจ๋ฆฌ์ ๋ด์ฉ์ ์ ์ฅํฉ๋๋ค. ์ด๊ฒ์ **ํ๋ก์ธ์ค**๋ผ๊ณ ๋ ํฉ๋๋ค.
+
+### ํ๋ก์ธ์ค๋ { #what-is-a-process }
+
+**ํ๋ก์ธ์ค**๋ผ๋ ๋จ์ด๋ ๋ณดํต ๋ ๊ตฌ์ฒด์ ์ผ๋ก, ์ด์์ฒด์ ์์ ์คํ ์ค์ธ ๊ฒ(์ ๋ง์ง๋ง ํญ๋ชฉ์ฒ๋ผ)๋ง์ ๊ฐ๋ฆฌํค๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค:
+
+* ์ด์์ฒด์ ์์ **์คํ ์ค**์ธ ํน์ ํ๋ก๊ทธ๋จ
+ * ํ์ผ์ด๋ ์ฝ๋๋ฅผ ์๋ฏธํ๋ ๊ฒ์ด ์๋๋ผ, ์ด์์ฒด์ ๊ฐ **์ค์ ๋ก ์คํ**ํ๊ณ ๊ด๋ฆฌํ๋ ๋์์ **๊ตฌ์ฒด์ ์ผ๋ก** ์๋ฏธํฉ๋๋ค.
+* ์ด๋ค ํ๋ก๊ทธ๋จ์ด๋ ์ด๋ค ์ฝ๋๋ , **์คํ**๋ ๋๋ง ๋ฌด์ธ๊ฐ๋ฅผ **ํ ์ ์์ต๋๋ค**. ์ฆ, **ํ๋ก์ธ์ค๊ฐ ์คํ ์ค**์ผ ๋์
๋๋ค.
+* ํ๋ก์ธ์ค๋ ์ฌ๋ฌ๋ถ์ด, ํน์ ์ด์์ฒด์ ๊ฐ **์ข
๋ฃ**(๋๋ โkillโ)ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ฉด ์คํ์ด ๋ฉ์ถ๊ณ , ๋ ์ด์ **์๋ฌด๊ฒ๋ ํ ์ ์์ต๋๋ค**.
+* ์ปดํจํฐ์์ ์คํ ์ค์ธ ๊ฐ ์ ํ๋ฆฌ์ผ์ด์
๋ค์๋ ํ๋ก์ธ์ค๊ฐ ์์ต๋๋ค. ์คํ ์ค์ธ ํ๋ก๊ทธ๋จ, ๊ฐ ์ฐฝ ๋ฑ๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ปดํจํฐ๊ฐ ์ผ์ ธ ์๋ ๋์ ๋ณดํต ๋ง์ ํ๋ก์ธ์ค๊ฐ **๋์์** ์คํ๋ฉ๋๋ค.
+* **๊ฐ์ ํ๋ก๊ทธ๋จ**์ **์ฌ๋ฌ ํ๋ก์ธ์ค**๊ฐ ๋์์ ์คํ๋ ์๋ ์์ต๋๋ค.
+
+์ด์์ฒด์ ์ โ์์
๊ด๋ฆฌ์(task manager)โ๋ โ์์คํ
๋ชจ๋ํฐ(system monitor)โ(๋๋ ๋น์ทํ ๋๊ตฌ)๋ฅผ ํ์ธํด ๋ณด๋ฉด, ์ด๋ฐ ํ๋ก์ธ์ค๊ฐ ๋ง์ด ์คํ ์ค์ธ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
+
+๋ ์๋ฅผ ๋ค์ด, ๊ฐ์ ๋ธ๋ผ์ฐ์ ํ๋ก๊ทธ๋จ(Firefox, Chrome, Edge ๋ฑ)์ ์คํํ๋ ํ๋ก์ธ์ค๊ฐ ์ฌ๋ฌ ๊ฐ ์๋ ๊ฒ๋ ๋ณด์ผ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค. ๋ณดํต ํญ๋ง๋ค ํ๋์ ํ๋ก์ธ์ค๋ฅผ ์คํํ๊ณ , ๊ทธ ์ธ์๋ ์ถ๊ฐ ํ๋ก์ธ์ค ๋ช ๊ฐ๊ฐ ๋ ์์ต๋๋ค.
+
+<img class="shadow" src="/img/deployment/concepts/image01.png">
+
+---
+
+์ด์ **ํ๋ก์ธ์ค**์ **ํ๋ก๊ทธ๋จ**์ ์ฐจ์ด๋ฅผ ์์์ผ๋, ๋ฐฐํฌ์ ๋ํ ์ด์ผ๊ธฐ๋ฅผ ๊ณ์ํด ๋ณด๊ฒ ์ต๋๋ค.
+
+## ์์ ์ ์คํ { #running-on-startup }
+
+๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์น API๋ฅผ ๋ง๋ค๋ฉด, ํด๋ผ์ด์ธํธ๊ฐ ์ธ์ ๋ ์ ๊ทผํ ์ ์๋๋ก **ํญ์ ์คํ**๋๊ณ ์ค๋จ๋์ง ์๊ธฐ๋ฅผ ์ํฉ๋๋ค. ๋ฌผ๋ก ํน์ ์ํฉ์์๋ง ์คํํ๊ณ ์ถ์ ํน๋ณํ ์ด์ ๊ฐ ์์ ์๋ ์์ง๋ง, ๋๋ถ๋ถ์ ์ง์์ ์ผ๋ก ์คํ๋๋ฉฐ **์ฌ์ฉ ๊ฐ๋ฅ**ํ ์ํ์ด๊ธฐ๋ฅผ ์ํฉ๋๋ค.
+
+### ์๊ฒฉ ์๋ฒ์์ { #in-a-remote-server }
+
+์๊ฒฉ ์๋ฒ(ํด๋ผ์ฐ๋ ์๋ฒ, ๊ฐ์ ๋จธ์ ๋ฑ)๋ฅผ ์ค์ ํ ๋, ๊ฐ์ฅ ๋จ์ํ ๋ฐฉ๋ฒ์ ๋ก์ปฌ ๊ฐ๋ฐ ๋์ฒ๋ผ ์๋์ผ๋ก `fastapi run`(Uvicorn์ ์ฌ์ฉํฉ๋๋ค)์ด๋ ๋น์ทํ ๋ช
๋ น์ ์คํํ๋ ๊ฒ์
๋๋ค.
+
+์ด ๋ฐฉ์์ ๋์ํ๊ณ , **๊ฐ๋ฐ ์ค์๋** ์ ์ฉํฉ๋๋ค.
+
+ํ์ง๋ง ์๋ฒ์ ๋ํ ์ฐ๊ฒฐ์ด ๋๊ธฐ๋ฉด, ์คํ ์ค์ธ **ํ๋ก์ธ์ค**๋ ์๋ง ์ข
๋ฃ๋ ๊ฒ์
๋๋ค.
+
+๋ ์๋ฒ๊ฐ ์ฌ์์๋๋ฉด(์: ์
๋ฐ์ดํธ ์ดํ, ํน์ ํด๋ผ์ฐ๋ ์ ๊ณต์์ ๋ง์ด๊ทธ๋ ์ด์
์ดํ) ์ฌ๋ฌ๋ถ์ ์๋ง **์์์ฐจ๋ฆฌ์ง ๋ชปํ ** ๊ฒ๋๋ค. ๊ทธ ๊ฒฐ๊ณผ, ํ๋ก์ธ์ค๋ฅผ ์๋์ผ๋ก ๋ค์ ์์ํด์ผ ํ๋ค๋ ์ฌ์ค๋ ๋ชจ๋ฅด๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ฉด API๋ ๊ทธ๋ฅ ์ฃฝ์ ์ํ๋ก ๋จ์ต๋๋ค. ๐ฑ
+
+### ์์ ์ ์๋ ์คํ { #run-automatically-on-startup }
+
+์ผ๋ฐ์ ์ผ๋ก ์๋ฒ ํ๋ก๊ทธ๋จ(์: Uvicorn)์ ์๋ฒ๊ฐ ์์๋ ๋ ์๋์ผ๋ก ์์๋๊ณ , **์ฌ๋์ ๊ฐ์
** ์์ด๋ FastAPI ์ฑ์ ์คํํ๋ ํ๋ก์ธ์ค๊ฐ ํญ์ ์คํ ์ค์ด๋๋ก(์: FastAPI ์ฑ์ ์คํํ๋ Uvicorn) ๊ตฌ์ฑํ๊ณ ์ถ์ ๊ฒ์
๋๋ค.
+
+### ๋ณ๋์ ํ๋ก๊ทธ๋จ { #separate-program }
+
+์ด๋ฅผ ์ํด ๋ณดํต ์ ํ๋ฆฌ์ผ์ด์
์ด ์์ ์ ์คํ๋๋๋ก ๋ณด์ฅํ๋ **๋ณ๋์ ํ๋ก๊ทธ๋จ**์ ๋ก๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ ๊ฒฝ์ฐ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ ์ ํ๋ฆฌ์ผ์ด์
๋ ํจ๊ป ์คํ๋๋๋ก ๋ณด์ฅํฉ๋๋ค.
+
+### ์์ ์ ์คํ์ ์ํ ๋๊ตฌ ์์ { #example-tools-to-run-at-startup }
+
+์ด ์ญํ ์ ํ ์ ์๋ ๋๊ตฌ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* Docker
+* Kubernetes
+* Docker Compose
+* Swarm Mode์ Docker
+* Systemd
+* Supervisor
+* ํด๋ผ์ฐ๋ ์ ๊ณต์๊ฐ ์๋น์ค ์ผ๋ถ๋ก ๋ด๋ถ์ ์ผ๋ก ์ฒ๋ฆฌ
+* ๊ธฐํ...
+
+๋ค์ ์ฅ์์ ๋ ๊ตฌ์ฒด์ ์ธ ์์๋ฅผ ์ ๊ณตํ๊ฒ ์ต๋๋ค.
+
+## ์ฌ์์ { #restarts }
+
+์ ํ๋ฆฌ์ผ์ด์
์ด ์์ ์ ์คํ๋๋๋ก ๋ณด์ฅํ๋ ๊ฒ๊ณผ ๋น์ทํ๊ฒ, ์ฅ์ ๊ฐ ๋ฐ์ํ์ ๋ **์ฌ์์**๋๋๋ก ๋ณด์ฅํ๊ณ ์ถ์ ๊ฒ์
๋๋ค.
+
+### ์ฐ๋ฆฌ๋ ์ค์ํฉ๋๋ค { #we-make-mistakes }
+
+์ฌ๋์ ์ธ์ ๋ **์ค์**ํฉ๋๋ค. ์ํํธ์จ์ด์๋ ๊ฑฐ์ *ํญ์* ์ฌ๊ธฐ์ ๊ธฐ์ ์จ์ **๋ฒ๊ทธ**๊ฐ ์์ต๋๋ค. ๐
+
+๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฐ์๋ ๋ฒ๊ทธ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์๋ก์ด ๊ธฐ๋ฅ์ ๊ตฌํํ๋ฉด์ ์ฝ๋๋ฅผ ๊ณ์ ๊ฐ์ ํฉ๋๋ค(์๋ก์ด ๋ฒ๊ทธ๋ ์ถ๊ฐํ ์ ์๊ฒ ์ฃ ๐
).
+
+### ์์ ์ค๋ฅ๋ ์๋์ผ๋ก ์ฒ๋ฆฌ๋จ { #small-errors-automatically-handled }
+
+FastAPI๋ก ์น API๋ฅผ ๋ง๋ค ๋ ์ฝ๋์ ์ค๋ฅ๊ฐ ์์ผ๋ฉด, FastAPI๋ ๋ณดํต ๊ทธ ์ค๋ฅ๋ฅผ ๋ฐ์์ํจ ๋จ์ผ ์์ฒญ ์์๋ง ๋ฌธ์ ๋ฅผ ๊ฐ๋ก๋๋ค. ๐ก
+
+ํด๋ผ์ด์ธํธ๋ ํด๋น ์์ฒญ์ ๋ํด **500 Internal Server Error**๋ฅผ ๋ฐ์ง๋ง, ์ ํ๋ฆฌ์ผ์ด์
์ ์์ ํ ํฌ๋์ํ์ง ์๊ณ ๋ค์ ์์ฒญ๋ถํฐ๋ ๊ณ์ ๋์ํฉ๋๋ค.
+
+### ๋ ํฐ ์ค๋ฅ - ํฌ๋์ { #bigger-errors-crashes }
+
+๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ , ์ฐ๋ฆฌ๊ฐ ์์ฑํ ์ฝ๋๊ฐ **์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์
์ ํฌ๋์**์์ผ Uvicorn๊ณผ Python ์์ฒด๊ฐ ์ข
๋ฃ๋๋ ๊ฒฝ์ฐ๊ฐ ์์ ์ ์์ต๋๋ค. ๐ฅ
+
+๊ทธ๋๋ ํ ๊ตฐ๋ฐ ์ค๋ฅ ๋๋ฌธ์ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ฃฝ์ ์ฑ๋ก ๋จ์ ์๊ธฐ๋ฅผ ๋ฐ๋ผ์ง๋ ์์ ๊ฒ์
๋๋ค. ๋ง๊ฐ์ง ๊ฒฝ๋ก ์ฒ๋ฆฌ๋ฅผ ์ ์ธํ ๋๋จธ์ง *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ผ๋ **๊ณ์ ์คํ**๋๊ธฐ๋ฅผ ์ํ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
+
+### ํฌ๋์ ํ ์ฌ์์ { #restart-after-crash }
+
+ํ์ง๋ง ์คํ ์ค์ธ **ํ๋ก์ธ์ค**๊ฐ ํฌ๋์ํ๋ ์ ๋ง ์ฌ๊ฐํ ์ค๋ฅ์ ๊ฒฝ์ฐ์๋, ์ ์ด๋ ๋ช ๋ฒ์ ํ๋ก์ธ์ค๋ฅผ **์ฌ์์**ํ๋๋ก ๋ด๋นํ๋ ์ธ๋ถ ์ปดํฌ๋ํธ๊ฐ ํ์ํฉ๋๋ค...
+
+/// tip | ํ
+
+...๋ค๋ง ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด๊ฐ **์ฆ์ ๊ณ์ ํฌ๋์**ํ๋ค๋ฉด, ๋ฌดํํ ์ฌ์์ํ๋ ๊ฒ์ ์๋ง ์๋ฏธ๊ฐ ์์ ๊ฒ์
๋๋ค. ๊ทธ๋ฐ ๊ฒฝ์ฐ์๋ ๊ฐ๋ฐ ์ค์, ๋๋ ์ต์ํ ๋ฐฐํฌ ์งํ์ ์์์ฐจ๋ฆด ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
+
+๊ทธ๋ฌ๋ ์ฌ๊ธฐ์๋, ํน์ ํ ๊ฒฝ์ฐ์๋ง ์ ์ฒด๊ฐ ํฌ๋์ํ ์ ์๊ณ **๋ฏธ๋**์๋ ๊ทธ๋ด ์ ์์ผ๋ฉฐ, ๊ทธ๋๋ ์ฌ์์ํ๋ ๊ฒ์ด ์๋ฏธ ์๋ ์ฃผ์ ์ฌ๋ก์ ์ง์คํด ๋ด
์๋ค.
+
+///
+
+์ ํ๋ฆฌ์ผ์ด์
์ ์ฌ์์ํ๋ ์ญํ ์ **์ธ๋ถ ์ปดํฌ๋ํธ**๊ฐ ๋งก๋ ํธ์ด ๋ณดํต ์ข์ต๋๋ค. ๊ทธ ์์ ์๋ Uvicorn๊ณผ Python์ ํฌํจํ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ด๋ฏธ ํฌ๋์ํ๊ธฐ ๋๋ฌธ์, ๊ฐ์ ์ฑ์ ๊ฐ์ ์ฝ๋ ์์์ ์ด๋ฅผ ํด๊ฒฐํ ๋ฐฉ๋ฒ์ด ์๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+### ์๋ ์ฌ์์์ ์ํ ๋๊ตฌ ์์ { #example-tools-to-restart-automatically }
+
+๋๋ถ๋ถ์ ๊ฒฝ์ฐ **์์ ์ ์คํ**์ ์ฌ์ฉํ ๋๊ตฌ๊ฐ ์๋ **์ฌ์์**๋ ํจ๊ป ์ฒ๋ฆฌํฉ๋๋ค.
+
+์๋ฅผ ๋ค์ด ๋ค์์ด ๊ฐ๋ฅํฉ๋๋ค:
+
+* Docker
+* Kubernetes
+* Docker Compose
+* Swarm Mode์ Docker
+* Systemd
+* Supervisor
+* ํด๋ผ์ฐ๋ ์ ๊ณต์๊ฐ ์๋น์ค ์ผ๋ถ๋ก ๋ด๋ถ์ ์ผ๋ก ์ฒ๋ฆฌ
+* ๊ธฐํ...
+
+## ๋ณต์ - ํ๋ก์ธ์ค์ ๋ฉ๋ชจ๋ฆฌ { #replication-processes-and-memory }
+
+FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ Uvicorn์ ์คํํ๋ `fastapi` ๋ช
๋ น ๊ฐ์ ์๋ฒ ํ๋ก๊ทธ๋จ์ ์ฌ์ฉํ๋ฉด, **ํ๋์ ํ๋ก์ธ์ค**๋ก ์คํํ๋๋ผ๋ ์ฌ๋ฌ ํด๋ผ์ด์ธํธ๋ฅผ ๋์์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ๋ง์ ๊ฒฝ์ฐ, ์ฌ๋ฌ ์์ปค ํ๋ก์ธ์ค๋ฅผ ๋์์ ์คํํ๊ณ ์ถ์ ๊ฒ์
๋๋ค.
+
+### ์ฌ๋ฌ ํ๋ก์ธ์ค - ์์ปค { #multiple-processes-workers }
+
+๋จ์ผ ํ๋ก์ธ์ค๊ฐ ์ฒ๋ฆฌํ ์ ์๋ ๊ฒ๋ณด๋ค ํด๋ผ์ด์ธํธ๊ฐ ๋ ๋ง๊ณ (์: ๊ฐ์ ๋จธ์ ์ด ๊ทธ๋ฆฌ ํฌ์ง ์์ ๋), ์๋ฒ CPU์ **์ฌ๋ฌ ์ฝ์ด**๊ฐ ์๋ค๋ฉด, ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์
์ ์คํํ๋ **์ฌ๋ฌ ํ๋ก์ธ์ค**๋ฅผ ๋์์ ๋์ฐ๊ณ ์์ฒญ์ ๋ถ์ฐ์ํฌ ์ ์์ต๋๋ค.
+
+๊ฐ์ API ํ๋ก๊ทธ๋จ์ **์ฌ๋ฌ ํ๋ก์ธ์ค**๋ก ์คํํ ๋, ์ด ํ๋ก์ธ์ค๋ค์ ๋ณดํต **workers**๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
+
+### ์์ปค ํ๋ก์ธ์ค์ ํฌํธ { #worker-processes-and-ports }
+
+[HTTPS์ ๋ํ ๋ฌธ์](https.md){.internal-link target=_blank}์์, ์๋ฒ์์ ํ๋์ ํฌํธ์ IP ์ฃผ์ ์กฐํฉ์๋ ํ๋์ ํ๋ก์ธ์ค๋ง ๋ฆฌ์ค๋ํ ์ ์๋ค๋ ๊ฒ์ ๊ธฐ์ตํ์๋์?
+
+์ด๊ฒ์ ์ฌ์ ํ ์ฌ์ค์
๋๋ค.
+
+๋ฐ๋ผ์ **์ฌ๋ฌ ํ๋ก์ธ์ค**๋ฅผ ๋์์ ์คํํ๋ ค๋ฉด, ๋จผ์ **ํฌํธ์์ ๋ฆฌ์ค๋ํ๋ ๋จ์ผ ํ๋ก์ธ์ค**๊ฐ ์์ด์ผ ํ๊ณ , ๊ทธ ํ๋ก์ธ์ค๊ฐ ์ด๋ค ๋ฐฉ์์ผ๋ก๋ ๊ฐ ์์ปค ํ๋ก์ธ์ค๋ก ํต์ ์ ์ ๋ฌํด์ผ ํฉ๋๋ค.
+
+### ํ๋ก์ธ์ค๋น ๋ฉ๋ชจ๋ฆฌ { #memory-per-process }
+
+์ด์ ํ๋ก๊ทธ๋จ์ด ๋ฉ๋ชจ๋ฆฌ์ ๋ฌด์ธ๊ฐ๋ฅผ ๋ก๋ํ๋ค๊ณ ํด๋ด
์๋ค. ์๋ฅผ ๋ค์ด ๋จธ์ ๋ฌ๋ ๋ชจ๋ธ์ ๋ณ์์ ์ฌ๋ฆฌ๊ฑฐ๋ ํฐ ํ์ผ ๋ด์ฉ์ ๋ณ์์ ์ฌ๋ฆฌ๋ ๊ฒฝ์ฐ์
๋๋ค. ์ด๋ฐ ๊ฒ๋ค์ ์๋ฒ์ **๋ฉ๋ชจ๋ฆฌ(RAM)**๋ฅผ ์ด๋ ์ ๋ ์ฌ์ฉํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ฌ๋ฌ ํ๋ก์ธ์ค๋ ๋ณดํต **๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ณต์ ํ์ง ์์ต๋๋ค**. ์ฆ, ๊ฐ ์คํ ์ค์ธ ํ๋ก์ธ์ค๋ ์์ฒด ๋ณ์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ต๋๋ค. ์ฝ๋์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค๋ฉด, **๊ฐ ํ๋ก์ธ์ค**๊ฐ ๊ทธ๋งํผ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.
+
+### ์๋ฒ ๋ฉ๋ชจ๋ฆฌ { #server-memory }
+
+์๋ฅผ ๋ค์ด ์ฝ๋๊ฐ ํฌ๊ธฐ **1 GB**์ ๋จธ์ ๋ฌ๋ ๋ชจ๋ธ์ ๋ก๋ํ๋ค๊ณ ํด๋ด
์๋ค. API๋ฅผ ํ๋ก์ธ์ค ํ๋๋ก ์คํํ๋ฉด RAM์ ์ต์ 1GB ์ฌ์ฉํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ **4๊ฐ ํ๋ก์ธ์ค**(์์ปค 4๊ฐ)๋ฅผ ์์ํ๋ฉด ๊ฐ๊ฐ 1GB RAM์ ์ฌ์ฉํฉ๋๋ค. ์ฆ ์ด **4 GB RAM**์ ์ฌ์ฉํฉ๋๋ค.
+
+๊ทธ๋ฐ๋ฐ ์๊ฒฉ ์๋ฒ๋ ๊ฐ์ ๋จธ์ ์ RAM์ด 3GB๋ฟ์ด๋ผ๋ฉด, 4GB๋ฅผ ๋๊ฒ ๋ก๋ํ๋ ค๊ณ ํ ๋ ๋ฌธ์ ๊ฐ ์๊น๋๋ค. ๐จ
+
+### ์ฌ๋ฌ ํ๋ก์ธ์ค - ์์ { #multiple-processes-an-example }
+
+์ด ์์์์๋ **Manager Process**๊ฐ ๋ ๊ฐ์ **Worker Processes**๋ฅผ ์์ํ๊ณ ์ ์ดํฉ๋๋ค.
+
+์ด Manager Process๋ ์๋ง IP์ **ํฌํธ**์์ ๋ฆฌ์ค๋ํ๋ ์ญํ ์ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ํต์ ์ ์์ปค ํ๋ก์ธ์ค๋ก ์ ๋ฌํฉ๋๋ค.
+
+์์ปค ํ๋ก์ธ์ค๋ค์ด ์ค์ ๋ก ์ ํ๋ฆฌ์ผ์ด์
์ ์คํํ๋ฉฐ, **์์ฒญ**์ ๋ฐ์ **์๋ต**์ ๋ฐํํ๋ ์ฃผ์ ์ฐ์ฐ์ ์ํํ๊ณ , RAM์ ๋ณ์๋ก ๋ก๋ํ ๋ชจ๋ ๋ด์ฉ์ ๋ด์ต๋๋ค.
+
+<img src="/img/deployment/concepts/process-ram.drawio.svg">
+
+๊ทธ๋ฆฌ๊ณ ๋ฌผ๋ก ๊ฐ์ ๋จธ์ ์๋ ์ ํ๋ฆฌ์ผ์ด์
์ธ์๋ **๋ค๋ฅธ ํ๋ก์ธ์ค**๋ค์ด ์คํ ์ค์ผ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
+
+ํฅ๋ฏธ๋ก์ด ์ ์ ๊ฐ ํ๋ก์ธ์ค์ **CPU ์ฌ์ฉ๋ฅ **์ ์๊ฐ์ ๋ฐ๋ผ ํฌ๊ฒ **๋ณ๋**ํ ์ ์์ง๋ง, **๋ฉ๋ชจ๋ฆฌ(RAM)**๋ ๋ณดํต ๋์ฒด๋ก **์์ ์ **์ผ๋ก ์ ์ง๋๋ค๋ ๊ฒ์
๋๋ค.
+
+๋งค๋ฒ ๋น์ทํ ์์ ์ฐ์ฐ์ ์ํํ๋ API์ด๊ณ ํด๋ผ์ด์ธํธ๊ฐ ๋ง๋ค๋ฉด, **CPU ์ฌ์ฉ๋ฅ **๋ (๊ธ๊ฒฉํ ์ค๋ฅด๋ด๋ฆฌ๊ธฐ๋ณด๋ค๋) *์์ ์ ์ผ* ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
+
+### ๋ณต์ ๋๊ตฌ์ ์ ๋ต ์์ { #examples-of-replication-tools-and-strategies }
+
+์ด๋ฅผ ๋ฌ์ฑํ๋ ์ ๊ทผ ๋ฐฉ์์ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ ์ ์์ผ๋ฉฐ, ๋ค์ ์ฅ๋ค์์ Docker์ ์ปจํ
์ด๋๋ฅผ ์ค๋ช
ํ ๋ ๊ตฌ์ฒด์ ์ธ ์ ๋ต์ ๋ ์๋ ค๋๋ฆฌ๊ฒ ์ต๋๋ค.
+
+๊ณ ๋ คํด์ผ ํ ์ฃผ์ ์ ์ฝ์ **๊ณต๊ฐ IP**์ **ํฌํธ**๋ฅผ ์ฒ๋ฆฌํ๋ **๋จ์ผ** ์ปดํฌ๋ํธ๊ฐ ์์ด์ผ ํ๋ค๋ ์ ์
๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ปดํฌ๋ํธ๋ ๋ณต์ ๋ **ํ๋ก์ธ์ค/์์ปค**๋ก ํต์ ์ **์ ๋ฌ**ํ ๋ฐฉ๋ฒ์ด ์์ด์ผ ํฉ๋๋ค.
+
+๊ฐ๋ฅํ ์กฐํฉ๊ณผ ์ ๋ต ๋ช ๊ฐ์ง๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* `--workers` ์ต์
์ ์ฌ์ฉํ **Uvicorn**
+ * ํ๋์ Uvicorn **ํ๋ก์ธ์ค ๋งค๋์ **๊ฐ **IP**์ **ํฌํธ**์์ ๋ฆฌ์ค๋ํ๊ณ , **์ฌ๋ฌ Uvicorn ์์ปค ํ๋ก์ธ์ค**๋ฅผ ์์ํฉ๋๋ค.
+* **Kubernetes** ๋ฐ ๊ธฐํ ๋ถ์ฐ **์ปจํ
์ด๋ ์์คํ
**
+ * **Kubernetes** ๋ ์ด์ด์ ๋ฌด์ธ๊ฐ๊ฐ **IP**์ **ํฌํธ**์์ ๋ฆฌ์ค๋ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ **์ฌ๋ฌ ์ปจํ
์ด๋**๋ฅผ ๋์ด ๋ณต์ ํ๋ฉฐ, ๊ฐ ์ปจํ
์ด๋์๋ **ํ๋์ Uvicorn ํ๋ก์ธ์ค**๊ฐ ์คํ๋ฉ๋๋ค.
+* ์ด๋ฅผ ๋์ ์ฒ๋ฆฌํด์ฃผ๋ **ํด๋ผ์ฐ๋ ์๋น์ค**
+ * ํด๋ผ์ฐ๋ ์๋น์ค๊ฐ **๋ณต์ ๋ฅผ ๋์ ์ฒ๋ฆฌ**ํด์ค ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค. ์คํํ **ํ๋ก์ธ์ค**๋ ์ฌ์ฉํ **์ปจํ
์ด๋ ์ด๋ฏธ์ง**๋ฅผ ์ ์ํ๊ฒ ํด์ค ์๋ ์์ง๋ง, ์ด๋ค ๊ฒฝ์ฐ๋ ๋๊ฐ **๋จ์ผ Uvicorn ํ๋ก์ธ์ค**๋ฅผ ๊ธฐ์ค์ผ๋ก ํ๊ณ , ํด๋ผ์ฐ๋ ์๋น์ค๊ฐ ์ด๋ฅผ ๋ณต์ ํ๋ ์ญํ ์ ๋งก์ต๋๋ค.
+
+/// tip | ํ
+
+**์ปจํ
์ด๋**, Docker, Kubernetes์ ๋ํ ์ผ๋ถ ๋ด์ฉ์ด ์์ง์ ์ ์ดํด๋์ง ์์๋ ๊ด์ฐฎ์ต๋๋ค.
+
+๋ค์ ์ฅ์์ ์ปจํ
์ด๋ ์ด๋ฏธ์ง, Docker, Kubernetes ๋ฑ์ ๋ ์ค๋ช
ํ๊ฒ ์ต๋๋ค: [์ปจํ
์ด๋์์ FastAPI - Docker](docker.md){.internal-link target=_blank}.
+
+///
+
+## ์์ ์ ์ฌ์ ๋จ๊ณ { #previous-steps-before-starting }
+
+์ ํ๋ฆฌ์ผ์ด์
์ **์์ํ๊ธฐ ์ ์** ์ด๋ค ๋จ๊ณ๋ฅผ ์ํํ๊ณ ์ถ์ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด **๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์
**์ ์คํํ๊ณ ์ถ์ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ๋๋ถ๋ถ์ ๊ฒฝ์ฐ, ์ด๋ฐ ๋จ๊ณ๋ **ํ ๋ฒ๋ง** ์ํํ๊ณ ์ถ์ ๊ฒ์
๋๋ค.
+
+๊ทธ๋์ ์ ํ๋ฆฌ์ผ์ด์
์ ์์ํ๊ธฐ ์ ์ ๊ทธ **์ฌ์ ๋จ๊ณ**๋ฅผ ์ํํ **๋จ์ผ ํ๋ก์ธ์ค**๋ฅผ ๋๊ณ ์ถ์ ๊ฒ์
๋๋ค.
+
+๋ํ ์ดํ์ ์ ํ๋ฆฌ์ผ์ด์
์์ฒด๋ฅผ **์ฌ๋ฌ ํ๋ก์ธ์ค**(์ฌ๋ฌ ์์ปค)๋ก ์์ํ๋๋ผ๋, ์ฌ์ ๋จ๊ณ๋ฅผ ์ํํ๋ ํ๋ก์ธ์ค๋ *๋ฐ๋์* ํ๋๋ง ์คํ๋๋๋ก ํด์ผ ํฉ๋๋ค. ๋ง์ฝ ์ฌ์ ๋จ๊ณ๋ฅผ **์ฌ๋ฌ ํ๋ก์ธ์ค**๊ฐ ์ํํ๋ฉด, **๋ณ๋ ฌ๋ก** ์คํํ๋ฉด์ ์์
์ด **์ค๋ณต**๋ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์
์ฒ๋ผ ๋ฏผ๊ฐํ ์์
์ด๋ผ๋ฉด ์๋ก ์ถฉ๋์ ์ผ์ผํฌ ์ ์์ต๋๋ค.
+
+๋ฌผ๋ก ์ฌ์ ๋จ๊ณ๋ฅผ ์ฌ๋ฌ ๋ฒ ์คํํด๋ ๋ฌธ์ ๊ฐ ์๋ ๊ฒฝ์ฐ๋ ์์ต๋๋ค. ๊ทธ๋ฐ ๊ฒฝ์ฐ์๋ ์ฒ๋ฆฌํ๊ธฐ๊ฐ ํจ์ฌ ์ฝ์ต๋๋ค.
+
+/// tip | ํ
+
+๋ํ ์ค์ ์ ๋ฐ๋ผ, ์ด๋ค ๊ฒฝ์ฐ์๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์์ํ๊ธฐ ์ ์ **์ฌ์ ๋จ๊ณ๊ฐ ์ ํ ํ์ ์์** ์๋ ์๋ค๋ ์ ์ ๊ธฐ์ตํ์ธ์.
+
+๊ทธ๋ฐ ๊ฒฝ์ฐ์๋ ์ด๋ฐ ๊ฒ๋ค์ ์ ํ ๊ฑฑ์ ํ ํ์๊ฐ ์์ต๋๋ค. ๐คท
+
+///
+
+### ์ฌ์ ๋จ๊ณ ์ ๋ต ์์ { #examples-of-previous-steps-strategies }
+
+์ด๋ ์ฌ๋ฌ๋ถ์ด **์์คํ
์ ๋ฐฐํฌํ๋ ๋ฐฉ์**์ ํฌ๊ฒ ์ข์ฐ๋๋ฉฐ, ํ๋ก๊ทธ๋จ์ ์์ํ๋ ๋ฐฉ์, ์ฌ์์ ์ฒ๋ฆฌ ๋ฐฉ์ ๋ฑ๊ณผ๋ ์ฐ๊ฒฐ๋์ด ์์ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
+
+๊ฐ๋ฅํ ์์ด๋์ด๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* ์ฑ ์ปจํ
์ด๋๋ณด๋ค ๋จผ์ ์คํ๋๋ Kubernetes์ โInit Containerโ
+* ์ฌ์ ๋จ๊ณ๋ฅผ ์คํํ ๋ค์ ์ ํ๋ฆฌ์ผ์ด์
์ ์์ํ๋ bash ์คํฌ๋ฆฝํธ
+ * ์ด bash ์คํฌ๋ฆฝํธ๋ฅผ ์์/์ฌ์์ํ๊ณ , ์ค๋ฅ๋ฅผ ๊ฐ์งํ๋ ๋ฑ์ ๋ฐฉ๋ฒ๋ ์ฌ์ ํ ํ์ํฉ๋๋ค.
+
+/// tip | ํ
+
+์ปจํ
์ด๋๋ก ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ๋ ๊ตฌ์ฒด์ ์ธ ์์๋ ๋ค์ ์ฅ์์ ์ ๊ณตํ๊ฒ ์ต๋๋ค: [์ปจํ
์ด๋์์ FastAPI - Docker](docker.md){.internal-link target=_blank}.
+
+///
+
+## ๋ฆฌ์์ค ํ์ฉ { #resource-utilization }
+
+์๋ฒ๋ ์ฌ๋ฌ๋ถ์ด ํ๋ก๊ทธ๋จ์ผ๋ก ์๋นํ๊ฑฐ๋ **ํ์ฉ(utilize)**ํ ์ ์๋ **๋ฆฌ์์ค**์
๋๋ค. CPU์ ๊ณ์ฐ ์๊ฐ๊ณผ ์ฌ์ฉ ๊ฐ๋ฅํ RAM ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ํ์ ์
๋๋ค.
+
+์์คํ
๋ฆฌ์์ค๋ฅผ ์ผ๋ง๋ ์๋น/ํ์ฉํ๊ณ ์ถ์ผ์ ๊ฐ์? โ๋ง์ง ์๊ฒโ๋ผ๊ณ ์๊ฐํ๊ธฐ ์ฝ์ง๋ง, ์ค์ ๋ก๋ **ํฌ๋์ํ์ง ์๋ ์ ์์ ๊ฐ๋ฅํ ํ ๋ง์ด** ์ฌ์ฉํ๊ณ ์ถ์ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
+
+์๋ฒ 3๋๋ฅผ ๋น์ฉ์ ๋ด๊ณ ์ฐ๊ณ ์๋๋ฐ RAM๊ณผ CPU๋ฅผ ์กฐ๊ธ๋ง ์ฌ์ฉํ๋ค๋ฉด, ์๋ง **๋์ ๋ญ๋น**ํ๊ณ ๐ธ, **์๋ฒ ์ ๋ ฅ๋ ๋ญ๋น**ํ๊ณ ๐, ๊ธฐํ ๋ฑ๋ฑ์ด ๋ ์ ์์ต๋๋ค.
+
+๊ทธ ๊ฒฝ์ฐ์๋ ์๋ฒ๋ฅผ 2๋๋ง ๋๊ณ , ๊ฐ ์๋ฒ์ ๋ฆฌ์์ค(CPU, ๋ฉ๋ชจ๋ฆฌ, ๋์คํฌ, ๋คํธ์ํฌ ๋์ญํญ ๋ฑ)๋ฅผ ๋ ๋์ ๋น์จ๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ๋์ ์ ์์ต๋๋ค.
+
+๋ฐ๋๋ก ์๋ฒ 2๋๋ฅผ ๋๊ณ CPU์ RAM์ **100%** ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, ์ด๋ ์์ ์ ํ๋ก์ธ์ค ํ๋๊ฐ ๋ ๋ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์์ฒญํ๊ฒ ๋๊ณ , ์๋ฒ๋ ๋์คํฌ๋ฅผ โ๋ฉ๋ชจ๋ฆฌโ์ฒ๋ผ ์ฌ์ฉํด์ผ ํ ์๋ ์์ต๋๋ค(์์ฒ ๋ฐฐ ๋๋ฆด ์ ์์ต๋๋ค). ๋๋ ์ฌ์ง์ด **ํฌ๋์**ํ ์๋ ์์ต๋๋ค. ํน์ ์ด๋ค ํ๋ก์ธ์ค๊ฐ ๊ณ์ฐ์ ํด์ผ ํ๋๋ฐ CPU๊ฐ ๋ค์ ๋น์์ง ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ ์๋ ์์ต๋๋ค.
+
+์ด ๊ฒฝ์ฐ์๋ **์๋ฒ ํ ๋๋ฅผ ์ถ๊ฐ**๋ก ํ๋ณดํ๊ณ ์ผ๋ถ ํ๋ก์ธ์ค๋ฅผ ๊ทธ์ชฝ์์ ์คํํด, ๋ชจ๋๊ฐ **์ถฉ๋ถํ RAM๊ณผ CPU ์๊ฐ**์ ๊ฐ๋๋ก ํ๋ ํธ์ด ๋ ๋ซ์ต๋๋ค.
+
+๋ ์ด๋ค ์ด์ ๋ก API ์ฌ์ฉ๋์ด **๊ธ์ฆ(spike)**ํ ๊ฐ๋ฅ์ฑ๋ ์์ต๋๋ค. ๋ฐ์ด๋ด์ด ๋์๊ฑฐ๋, ๋ค๋ฅธ ์๋น์ค๋ ๋ด์ด ์ฌ์ฉํ๊ธฐ ์์ํ์ ์๋ ์์ต๋๋ค. ๊ทธ๋ฐ ๊ฒฝ์ฐ๋ฅผ ๋๋นํด ์ถ๊ฐ ๋ฆฌ์์ค๋ฅผ ํ๋ณดํด๋๊ณ ์ถ์ ์ ์์ต๋๋ค.
+
+๋ฆฌ์์ค ํ์ฉ๋ฅ ๋ชฉํ๋ก **์์์ ์์น**๋ฅผ ์ ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด **50%์์ 90% ์ฌ์ด**์ฒ๋ผ์. ์์ ์, ์ด๋ฐ ๊ฒ๋ค์ด ๋ฐฐํฌ๋ฅผ ์กฐ์ ํ ๋ ์ธก์ ํ๊ณ ํ๋ํ๋ ์ฃผ์ ์งํ๊ฐ ๋ ๊ฐ๋ฅ์ฑ์ด ํฌ๋ค๋ ๊ฒ์
๋๋ค.
+
+`htop` ๊ฐ์ ๊ฐ๋จํ ๋๊ตฌ๋ก ์๋ฒ์ CPU์ RAM ์ฌ์ฉ๋, ๋๋ ๊ฐ ํ๋ก์ธ์ค๋ณ ์ฌ์ฉ๋์ ๋ณผ ์ ์์ต๋๋ค. ํน์ ์๋ฒ ์ฌ๋ฌ ๋์ ๋ถ์ฐ๋ ์๋ ์๋ ๋ ๋ณต์กํ ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
+
+## ์์ฝ { #recap }
+
+์ฌ๊ธฐ๊น์ง ์ ํ๋ฆฌ์ผ์ด์
๋ฐฐํฌ ๋ฐฉ์์ ๊ฒฐ์ ํ ๋ ์ผ๋์ ๋์ด์ผ ํ ์ฃผ์ ๊ฐ๋
๋ค์ ์ฝ์์ต๋๋ค:
+
+* ๋ณด์ - HTTPS
+* ์์ ์ ์คํ
+* ์ฌ์์
+* ๋ณต์ (์คํ ์ค์ธ ํ๋ก์ธ์ค ์)
+* ๋ฉ๋ชจ๋ฆฌ
+* ์์ ์ ์ฌ์ ๋จ๊ณ
+
+์ด ์์ด๋์ด๋ค์ ์ดํดํ๊ณ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์๋ฉด, ๋ฐฐํฌ๋ฅผ ๊ตฌ์ฑํ๊ณ ์กฐ์ ํ ๋ ํ์ํ ์ง๊ด์ ์ป๋ ๋ฐ ๋์์ด ๋ ๊ฒ์
๋๋ค. ๐ค
+
+๋ค์ ์น์
์์๋ ๋ฐ๋ผ ํ ์ ์๋ ๊ฐ๋ฅํ ์ ๋ต์ ๋ ๊ตฌ์ฒด์ ์ธ ์์๋ฅผ ์ ๊ณตํ๊ฒ ์ต๋๋ค. ๐
--- /dev/null
+# FastAPI Cloud { #fastapi-cloud }
+
+**ํ ๋ฒ์ ๋ช
๋ น**์ผ๋ก FastAPI ์ฑ์ <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>์ ๋ฐฐํฌํ ์ ์์ต๋๋ค. ์์ง์ด๋ผ๋ฉด ๋๊ธฐ์ ๋ช
๋จ์ ๋ฑ๋กํด ๋ณด์ธ์. ๐
+
+## ๋ก๊ทธ์ธํ๊ธฐ { #login }
+
+๋จผ์ **FastAPI Cloud** ๊ณ์ ์ด ์ด๋ฏธ ์๋์ง ํ์ธํ์ธ์(๋๊ธฐ์ ๋ช
๋จ์์ ์ด๋ํด ๋๋ ธ์ ๊ฑฐ์์ ๐).
+
+๊ทธ๋ค์ ๋ก๊ทธ์ธํฉ๋๋ค:
+
+<div class="termy">
+
+```console
+$ fastapi login
+
+You are logged in to FastAPI Cloud ๐
+```
+
+</div>
+
+## ๋ฐฐํฌํ๊ธฐ { #deploy }
+
+์ด์ **ํ ๋ฒ์ ๋ช
๋ น**์ผ๋ก ์ฑ์ ๋ฐฐํฌํฉ๋๋ค:
+
+<div class="termy">
+
+```console
+$ fastapi deploy
+
+Deploying to FastAPI Cloud...
+
+โ
Deployment successful!
+
+๐ Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
+```
+
+</div>
+
+์ด๊ฒ ์ ๋ถ์
๋๋ค! ์ด์ ํด๋น URL์์ ์ฑ์ ์ ๊ทผํ ์ ์์ต๋๋ค. โจ
+
+## FastAPI Cloud ์๊ฐ { #about-fastapi-cloud }
+
+**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>**๋ **FastAPI**๋ฅผ ๋ง๋ ๋์ผํ ์ ์์ ํ์ด ๊ตฌ์ถํ์ต๋๋ค.
+
+์ต์ํ์ ๋
ธ๋ ฅ์ผ๋ก API๋ฅผ **๊ตฌ์ถ**, **๋ฐฐํฌ**, **์ ๊ทผ**ํ๋ ๊ณผ์ ์ ๊ฐ์ํํฉ๋๋ค.
+
+FastAPI๋ก ์ฑ์ ๋ง๋ค ๋์ ๋์ผํ **๊ฐ๋ฐ์ ๊ฒฝํ**์, ํด๋ผ์ฐ๋์ **๋ฐฐํฌ**ํ ๋๋ ์ ๊ณตํฉ๋๋ค. ๐
+
+๋ํ ์ฑ์ ๋ฐฐํฌํ ๋ ๋ณดํต ํ์ํ ๋๋ถ๋ถ์ ๊ฒ๋ค๋ ์ฒ๋ฆฌํด ์ค๋๋ค. ์๋ฅผ ๋ค๋ฉด:
+
+* HTTPS
+* ์์ฒญ์ ๊ธฐ๋ฐ์ผ๋ก ์๋ ์ค์ผ์ผ๋งํ๋ ๋ณต์ (Replication)
+* ๋ฑ
+
+FastAPI Cloud๋ *FastAPI and friends* ์คํ ์์ค ํ๋ก์ ํธ์ ์ฃผ์ ์คํฐ์์ด์ ์๊ธ ์ง์ ์ ๊ณต์์
๋๋ค. โจ
+
+## ๋ค๋ฅธ ํด๋ผ์ฐ๋ ์ ๊ณต์
์ฒด์ ๋ฐฐํฌํ๊ธฐ { #deploy-to-other-cloud-providers }
+
+FastAPI๋ ์คํ ์์ค์ด๋ฉฐ ํ์ค์ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค. ์ํ๋ ์ด๋ค ํด๋ผ์ฐ๋ ์ ๊ณต์
์ฒด์๋ FastAPI ์ฑ์ ๋ฐฐํฌํ ์ ์์ต๋๋ค.
+
+ํด๋น ํด๋ผ์ฐ๋ ์ ๊ณต์
์ฒด์ ๊ฐ์ด๋๋ฅผ ๋ฐ๋ผ FastAPI ์ฑ์ ๋ฐฐํฌํ์ธ์. ๐ค
+
+## ์์ฒด ์๋ฒ์ ๋ฐฐํฌํ๊ธฐ { #deploy-your-own-server }
+
+๋ํ ์ด **Deployment** ๊ฐ์ด๋์์ ์ดํ์ ๋ชจ๋ ์ธ๋ถ์ฌํญ์ ์๋ ค๋๋ฆด ๊ฑฐ์์. ๊ทธ๋์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง, ๋ฌด์์ด ํ์ํ๋ฉฐ, ๋ณธ์ธ์ ์๋ฒ๋ฅผ ํฌํจํด ์ง์ FastAPI ์ฑ์ ์ด๋ป๊ฒ ๋ฐฐํฌํ๋์ง๊น์ง ์ดํดํ ์ ์๊ฒ ๋ ๊ฒ์
๋๋ค. ๐ค
--- /dev/null
+# HTTPS ์์๋ณด๊ธฐ { #about-https }
+
+HTTPS๋ ๊ทธ๋ฅ โ์ผ์ ธ ์๊ฑฐ๋โ ์๋๋ฉด โ๊บผ์ ธ ์๋โ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๊ธฐ ์ฝ์ต๋๋ค.
+
+ํ์ง๋ง ์ค์ ๋ก๋ ํจ์ฌ ๋ ๋ณต์กํฉ๋๋ค.
+
+/// tip | ํ
+
+๋ฐ์๊ฑฐ๋ ๋ณ๋ก ์ ๊ฒฝ ์ฐ๊ณ ์ถ์ง ์๋ค๋ฉด, ๋ค์ ์น์
์์ ๋ค์ํ ๊ธฐ๋ฒ์ผ๋ก ๋ชจ๋ ๊ฒ์ ์ค์ ํ๋ ๋จ๊ณ๋ณ ์๋ด๋ฅผ ๊ณ์ ๋ณด์ธ์.
+
+///
+
+์๋น์ ๊ด์ ์์ **HTTPS์ ๊ธฐ๋ณธ์ ๋ฐฐ์ฐ๋ ค๋ฉด** <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>๋ฅผ ํ์ธํ์ธ์.
+
+์ด์ **๊ฐ๋ฐ์ ๊ด์ **์์ HTTPS๋ฅผ ์๊ฐํ ๋ ์ผ๋์ ๋์ด์ผ ํ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค:
+
+* HTTPS๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด, **์๋ฒ**๊ฐ **์ 3์**๊ฐ ๋ฐ๊ธํ **"์ธ์ฆ์(certificates)"**๋ฅผ **๋ณด์ **ํด์ผ ํฉ๋๋ค.
+ * ์ด ์ธ์ฆ์๋ ์ค์ ๋ก ์ 3์๊ฐ โ์์ฑโํด ์ฃผ๋ ๊ฒ์ด๊ณ , ์๋ฒ๊ฐ ๋ง๋๋ ๊ฒ์ด ์๋๋ผ ์ 3์๋ก๋ถํฐ **๋ฐ๊ธ/ํ๋**ํ๋ ๊ฒ์
๋๋ค.
+* ์ธ์ฆ์์๋ **์ ํจ ๊ธฐ๊ฐ**์ด ์์ต๋๋ค.
+ * ์ฆ, **๋ง๋ฃ**๋ฉ๋๋ค.
+ * ๊ทธ๋ฆฌ๊ณ ๋๋ฉด ์ 3์๋ก๋ถํฐ ๋ค์ **๊ฐฑ์ **ํด์ **์ฌ๋ฐ๊ธ/์ฌํ๋**ํด์ผ ํฉ๋๋ค.
+* ์ฐ๊ฒฐ์ ์ํธํ๋ **TCP ๋ ๋ฒจ**์์ ์ผ์ด๋ฉ๋๋ค.
+ * ์ด๋ **HTTP๋ณด๋ค ํ ๊ณ์ธต ์๋**์
๋๋ค.
+ * ๋ฐ๋ผ์ **์ธ์ฆ์์ ์ํธํ** ์ฒ๋ฆฌ๋ **HTTP ์ด์ **์ ์ํ๋ฉ๋๋ค.
+* **TCP๋ "๋๋ฉ์ธ"์ ๋ชจ๋ฆ
๋๋ค.** IP ์ฃผ์๋ง ์๋๋ค.
+ * ์ด๋ค **ํน์ ๋๋ฉ์ธ**์ ์์ฒญํ๋์ง์ ๋ํ ์ ๋ณด๋ **HTTP ๋ฐ์ดํฐ**์ ๋ค์ด ์์ต๋๋ค.
+* **HTTPS ์ธ์ฆ์**๋ ํน์ **๋๋ฉ์ธ**์ โ์ธ์ฆโํ์ง๋ง, ํ๋กํ ์ฝ๊ณผ ์ํธํ๋ TCP ๋ ๋ฒจ์์ ์ผ์ด๋๋ฉฐ, ์ด๋ค ๋๋ฉ์ธ์ ๋ค๋ฃจ๋์ง **์๊ธฐ ์ ์** ์ฒ๋ฆฌ๋ฉ๋๋ค.
+* **๊ธฐ๋ณธ์ ์ผ๋ก** ์ด๋ IP ์ฃผ์ ํ๋๋น **HTTPS ์ธ์ฆ์ ํ๋๋ง** ๋ ์ ์๋ค๋ ๋ป์
๋๋ค.
+ * ์๋ฒ๊ฐ ์๋ฌด๋ฆฌ ํฌ๋ , ๊ทธ ์์ ์ฌ๋ฆฐ ๊ฐ ์ ํ๋ฆฌ์ผ์ด์
์ด ์๋ฌด๋ฆฌ ์๋ ์๊ด์์ต๋๋ค.
+ * ํ์ง๋ง ์ด์ ๋ํ **ํด๊ฒฐ์ฑ
**์ด ์์ต๋๋ค.
+* **TLS** ํ๋กํ ์ฝ(HTTP ์ด์ , TCP ๋ ๋ฒจ์์ ์ํธํ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒ)์ ๋ํ **ํ์ฅ** ์ค์ **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication - ์๋ฒ ์ด๋ฆ ํ์">SNI</abbr></a>**๋ผ๋ ๊ฒ์ด ์์ต๋๋ค.
+ * ์ด SNI ํ์ฅ์ ์ฌ์ฉํ๋ฉด, ๋จ์ผ ์๋ฒ(**๋จ์ผ IP ์ฃผ์**)์์ **์ฌ๋ฌ HTTPS ์ธ์ฆ์**๋ฅผ ์ฌ์ฉํ๊ณ **์ฌ๋ฌ HTTPS ๋๋ฉ์ธ/์ ํ๋ฆฌ์ผ์ด์
**์ ์ ๊ณตํ ์ ์์ต๋๋ค.
+ * ์ด๋ฅผ ์ํด์๋ ์๋ฒ์์ **๊ณต๊ฐ IP ์ฃผ์**๋ก ๋ฆฌ์ค๋ํ๋ **ํ๋์** ์ปดํฌ๋ํธ(ํ๋ก๊ทธ๋จ)๊ฐ ์๋ฒ์ ์๋ **๋ชจ๋ HTTPS ์ธ์ฆ์**์ ์ ๊ทผํ ์ ์์ด์ผ ํฉ๋๋ค.
+* ๋ณด์ ์ฐ๊ฒฐ์ ์ป์ **์ดํ์๋**, ํต์ ํ๋กํ ์ฝ ์์ฒด๋ **์ฌ์ ํ HTTP**์
๋๋ค.
+ * **HTTP ํ๋กํ ์ฝ**๋ก ์ ์ก๋๋๋ผ๋, ๋ด์ฉ์ **์ํธํ**๋์ด ์์ต๋๋ค.
+
+์ผ๋ฐ์ ์ผ๋ก ์๋ฒ(๋จธ์ , ํธ์คํธ ๋ฑ)์๋ **ํ๋ก๊ทธ๋จ/HTTP ์๋ฒ ํ๋**๋ฅผ ์คํํด **HTTPS ๊ด๋ จ ๋ถ๋ถ ์ ์ฒด**๋ฅผ ๊ด๋ฆฌํ๊ฒ ํฉ๋๋ค: **์ํธํ๋ HTTPS ์์ฒญ**์ ๋ฐ๊ณ , ๋ณตํธํ๋ **HTTP ์์ฒญ**์ ๊ฐ์ ์๋ฒ์์ ์คํ ์ค์ธ ์ค์ HTTP ์ ํ๋ฆฌ์ผ์ด์
(์ด ๊ฒฝ์ฐ **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
)์ผ๋ก ์ ๋ฌํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์
์ **HTTP ์๋ต**์ ๋ฐ์ ์ ์ ํ **HTTPS ์ธ์ฆ์**๋ก **์ํธํ**ํ ๋ค **HTTPS**๋ก ํด๋ผ์ด์ธํธ์ ๋ค์ ๋ณด๋ด๋ ์ญํ ์
๋๋ค. ์ด๋ฐ ์๋ฒ๋ฅผ ํํ **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>**๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
+
+TLS Termination Proxy๋ก ์ฌ์ฉํ ์ ์๋ ์ต์
์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* Traefik (์ธ์ฆ์ ๊ฐฑ์ ๋ ์ฒ๋ฆฌ ๊ฐ๋ฅ)
+* Caddy (์ธ์ฆ์ ๊ฐฑ์ ๋ ์ฒ๋ฆฌ ๊ฐ๋ฅ)
+* Nginx
+* HAProxy
+
+## Let's Encrypt { #lets-encrypt }
+
+Let's Encrypt ์ด์ ์๋ ์ด๋ฌํ **HTTPS ์ธ์ฆ์**๊ฐ ์ ๋ขฐํ ์ ์๋ ์ 3์์ ์ํด ํ๋งค๋์์ต๋๋ค.
+
+์ธ์ฆ์๋ฅผ ํ๋ํ๋ ๊ณผ์ ์ ๋ฒ๊ฑฐ๋กญ๊ณ , ๊ฝค ๋ง์ ์๋ฅ ์์
์ด ํ์ํ์ผ๋ฉฐ, ์ธ์ฆ์๋ ์๋นํ ๋น์์ต๋๋ค.
+
+ํ์ง๋ง ๊ทธ ํ **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>**๊ฐ ๋ง๋ค์ด์ก์ต๋๋ค.
+
+์ด๋ Linux Foundation์ ํ๋ก์ ํธ์
๋๋ค. ํ์ค ์ํธํ์ ๋ณด์์ ๋ชจ๋ ์ฌ์ฉํ๋ **HTTPS ์ธ์ฆ์**๋ฅผ **๋ฌด๋ฃ๋ก**, ์๋ํ๋ ๋ฐฉ์์ผ๋ก ์ ๊ณตํฉ๋๋ค. ์ด ์ธ์ฆ์๋ค์ ์๋ช
์ด ์งง๊ณ (์ฝ 3๊ฐ์) ๊ทธ๋์ ์ ํจ ๊ธฐ๊ฐ์ด ์งง์ ๋งํผ **์ค์ ๋ก ๋ณด์์ด ๋ ์ข์์ง๊ธฐ๋** ํฉ๋๋ค.
+
+๋๋ฉ์ธ์ ์์ ํ๊ฒ ๊ฒ์ฆ๋๋ฉฐ ์ธ์ฆ์๋ ์๋์ผ๋ก ์์ฑ๋ฉ๋๋ค. ๋ํ ์ด๋ก ์ธํด ์ธ์ฆ์ ๊ฐฑ์ ๋ ์๋ํํ ์ ์์ต๋๋ค.
+
+๋ชฉํ๋ ์ธ์ฆ์์ ๋ฐ๊ธ๊ณผ ๊ฐฑ์ ์ ์๋ํํ์ฌ **๋ฌด๋ฃ๋ก, ์๊ตฌํ, ์์ ํ HTTPS**๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ํ๋ ๊ฒ์
๋๋ค.
+
+## ๊ฐ๋ฐ์๋ฅผ ์ํ HTTPS { #https-for-developers }
+
+๊ฐ๋ฐ์์๊ฒ ์ค์ํ ๊ฐ๋
๋ค์ ์ค์ฌ์ผ๋ก, HTTPS API๊ฐ ๋จ๊ณ๋ณ๋ก ์ด๋ป๊ฒ ๋ณด์ผ ์ ์๋์ง ์์๋ฅผ ๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
+
+### ๋๋ฉ์ธ ์ด๋ฆ { #domain-name }
+
+์๋ง๋ ์์์ **๋๋ฉ์ธ ์ด๋ฆ**์ **ํ๋**ํ๋ ๊ฒ์ผ ๊ฒ๋๋ค. ๊ทธ ๋ค์ DNS ์๋ฒ(์๋ง ๊ฐ์ ํด๋ผ์ฐ๋ ์ ๊ณต์
์ฒด)์์ ์ด๋ฅผ ์ค์ ํฉ๋๋ค.
+
+๋๊ฐ ํด๋ผ์ฐ๋ ์๋ฒ(๊ฐ์ ๋จธ์ ) ๊ฐ์ ๊ฒ์ ์ฌ์ฉํ๊ฒ ๋๊ณ , ๊ฑฐ๊ธฐ์๋ <abbr title="That doesn't change - ๋ณํ์ง ์์">fixed</abbr> **๊ณต๊ฐ IP ์ฃผ์**๊ฐ ์์ต๋๋ค.
+
+DNS ์๋ฒ(๋ค)์์ **๋๋ฉ์ธ**์ด ์๋ฒ์ **๊ณต๊ฐ IP ์ฃผ์**๋ฅผ ๊ฐ๋ฆฌํค๋๋ก ๋ ์ฝ๋(โ`A record`โ)๋ฅผ ์ค์ ํฉ๋๋ค.
+
+๋ณดํต์ ์ฒ์ ํ ๋ฒ, ๋ชจ๋ ๊ฒ์ ์ค์ ํ ๋๋ง ์ด ์์
์ ํฉ๋๋ค.
+
+/// tip | ํ
+
+๋๋ฉ์ธ ์ด๋ฆ ๋ถ๋ถ์ HTTPS๋ณด๋ค ํจ์ฌ ์ด์ ๋จ๊ณ์ง๋ง, ๋ชจ๋ ๊ฒ์ด ๋๋ฉ์ธ๊ณผ IP ์ฃผ์์ ์์กดํ๋ฏ๋ก ์ฌ๊ธฐ์ ์ธ๊ธํ ๊ฐ์น๊ฐ ์์ต๋๋ค.
+
+///
+
+### DNS { #dns }
+
+์ด์ ์ค์ HTTPS ๋ถ๋ถ์ ์ง์คํด ๋ณด๊ฒ ์ต๋๋ค.
+
+๋จผ์ ๋ธ๋ผ์ฐ์ ๋ **DNS ์๋ฒ**์ ์ง์ํ์ฌ, ์ฌ๊ธฐ์๋ `someapp.example.com`์ด๋ผ๋ **๋๋ฉ์ธ์ ๋ํ IP**๊ฐ ๋ฌด์์ธ์ง ํ์ธํฉ๋๋ค.
+
+DNS ์๋ฒ๋ ๋ธ๋ผ์ฐ์ ์๊ฒ ํน์ **IP ์ฃผ์**๋ฅผ ์ฌ์ฉํ๋ผ๊ณ ์๋ ค์ค๋๋ค. ์ด๋ DNS ์๋ฒ์ ์ค์ ํด ๋, ์๋ฒ๊ฐ ์ฌ์ฉํ๋ ๊ณต๊ฐ IP ์ฃผ์์
๋๋ค.
+
+<img src="/img/deployment/https/https01.drawio.svg">
+
+### TLS ํธ๋์
ฐ์ดํฌ ์์ { #tls-handshake-start }
+
+๊ทธ ๋ค์ ๋ธ๋ผ์ฐ์ ๋ **ํฌํธ 443**(HTTPS ํฌํธ)์์ ํด๋น IP ์ฃผ์์ ํต์ ํฉ๋๋ค.
+
+ํต์ ์ ์ฒซ ๋ถ๋ถ์ ํด๋ผ์ด์ธํธ์ ์๋ฒ ์ฌ์ด์ ์ฐ๊ฒฐ์ ์ค์ ํ๊ณ , ์ฌ์ฉํ ์ํธํ ํค ๋ฑ์ ๊ฒฐ์ ํ๋ ๊ณผ์ ์
๋๋ค.
+
+<img src="/img/deployment/https/https02.drawio.svg">
+
+ํด๋ผ์ด์ธํธ์ ์๋ฒ๊ฐ TLS ์ฐ๊ฒฐ์ ์ค์ ํ๊ธฐ ์ํด ์ํธ์์ฉํ๋ ์ด ๊ณผ์ ์ **TLS ํธ๋์
ฐ์ดํฌ**๋ผ๊ณ ํฉ๋๋ค.
+
+### SNI ํ์ฅ์ ์ฌ์ฉํ๋ TLS { #tls-with-sni-extension }
+
+์๋ฒ์์๋ ํน์ **IP ์ฃผ์**์ ํน์ **ํฌํธ**์์ **ํ๋์ ํ๋ก์ธ์ค๋ง** ๋ฆฌ์ค๋ํ ์ ์์ต๋๋ค. ๊ฐ์ IP ์ฃผ์์์ ๋ค๋ฅธ ํฌํธ๋ก ๋ฆฌ์ค๋ํ๋ ํ๋ก์ธ์ค๋ ์์ ์ ์์ง๋ง, IP ์ฃผ์์ ํฌํธ ์กฐํฉ๋ง๋ค ํ๋๋ง ๊ฐ๋ฅํฉ๋๋ค.
+
+TLS(HTTPS)๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํน์ ํฌํธ `443`์ ์ฌ์ฉํฉ๋๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ๊ฐ ํ์ํ ํฌํธ๋ ์ด๊ฒ์
๋๋ค.
+
+์ด ํฌํธ์์ ํ๋์ ํ๋ก์ธ์ค๋ง ๋ฆฌ์ค๋ํ ์ ์์ผ๋ฏ๋ก, ๊ทธ ์ญํ ์ ํ๋ ํ๋ก์ธ์ค๋ **TLS Termination Proxy**๊ฐ ๋ฉ๋๋ค.
+
+TLS Termination Proxy๋ ํ๋ ์ด์์ **TLS ์ธ์ฆ์**(HTTPS ์ธ์ฆ์)์ ์ ๊ทผํ ์ ์์ต๋๋ค.
+
+์์์ ์ค๋ช
ํ **SNI ํ์ฅ**์ ์ฌ์ฉํด, TLS Termination Proxy๋ ์ด ์ฐ๊ฒฐ์ ์ฌ์ฉํ ์ ์๋ TLS(HTTPS) ์ธ์ฆ์๋ค ์ค์์ ํด๋ผ์ด์ธํธ๊ฐ ๊ธฐ๋ํ๋ ๋๋ฉ์ธ๊ณผ ์ผ์นํ๋ ๊ฒ์ ํ์ธํด ์ ํํฉ๋๋ค.
+
+์ด ๊ฒฝ์ฐ์๋ `someapp.example.com`์ ๋ํ ์ธ์ฆ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+<img src="/img/deployment/https/https03.drawio.svg">
+
+ํด๋ผ์ด์ธํธ๋ ์ด๋ฏธ ํด๋น TLS ์ธ์ฆ์๋ฅผ ์์ฑํ ์ฃผ์ฒด(์ฌ๊ธฐ์๋ Let's Encrypt์ด์ง๋ง, ์ด๋ ๋ค์์ ๋ค์ ๋ณด๊ฒ ์ต๋๋ค)๋ฅผ **์ ๋ขฐ**ํ๋ฏ๋ก, ์ธ์ฆ์๊ฐ ์ ํจํ์ง **๊ฒ์ฆ**ํ ์ ์์ต๋๋ค.
+
+๊ทธ ๋ค์ ์ธ์ฆ์๋ฅผ ์ฌ์ฉํด ํด๋ผ์ด์ธํธ์ TLS Termination Proxy๋ ๋๋จธ์ง **TCP ํต์ **์ ์ด๋ป๊ฒ **์ํธํํ ์ง ๊ฒฐ์ **ํฉ๋๋ค. ์ด๋ก์จ **TLS ํธ๋์
ฐ์ดํฌ** ๋จ๊ณ๊ฐ ์๋ฃ๋ฉ๋๋ค.
+
+์ดํ ํด๋ผ์ด์ธํธ์ ์๋ฒ๋ TLS๊ฐ ์ ๊ณตํ๋ **์ํธํ๋ TCP ์ฐ๊ฒฐ**์ ๊ฐ๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ฐ๊ฒฐ์ ์ฌ์ฉํด ์ค์ **HTTP ํต์ **์ ์์ํ ์ ์์ต๋๋ค.
+
+์ด๊ฒ์ด ๋ฐ๋ก **HTTPS**์
๋๋ค. ์์(์ํธํ๋์ง ์์) TCP ์ฐ๊ฒฐ ๋์ **์์ ํ TLS ์ฐ๊ฒฐ** ์์์ **HTTP**๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
+
+/// tip | ํ
+
+ํต์ ์ ์ํธํ๋ HTTP ๋ ๋ฒจ์ด ์๋๋ผ **TCP ๋ ๋ฒจ**์์ ์ผ์ด๋๋ค๋ ์ ์ ์ฃผ์ํ์ธ์.
+
+///
+
+### HTTPS ์์ฒญ { #https-request }
+
+์ด์ ํด๋ผ์ด์ธํธ์ ์๋ฒ(๊ตฌ์ฒด์ ์ผ๋ก๋ ๋ธ๋ผ์ฐ์ ์ TLS Termination Proxy)๊ฐ **์ํธํ๋ TCP ์ฐ๊ฒฐ**์ ๊ฐ๊ฒ ๋์์ผ๋ **HTTP ํต์ **์ ์์ํ ์ ์์ต๋๋ค.
+
+๋ฐ๋ผ์ ํด๋ผ์ด์ธํธ๋ **HTTPS ์์ฒญ**์ ๋ณด๋
๋๋ค. ์ด๋ ์ํธํ๋ TLS ์ฐ๊ฒฐ์ ํตํด ์ ๋ฌ๋๋ HTTP ์์ฒญ์ผ ๋ฟ์
๋๋ค.
+
+<img src="/img/deployment/https/https04.drawio.svg">
+
+### ์์ฒญ ๋ณตํธํ { #decrypt-the-request }
+
+TLS Termination Proxy๋ ํฉ์๋ ์ํธํ๋ฅผ ์ฌ์ฉํด **์์ฒญ์ ๋ณตํธํ**ํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์
์ ์คํ ์ค์ธ ํ๋ก์ธ์ค(์: FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ์คํํ๋ Uvicorn ํ๋ก์ธ์ค)์ **์ผ๋ฐ(๋ณตํธํ๋) HTTP ์์ฒญ**์ ์ ๋ฌํฉ๋๋ค.
+
+<img src="/img/deployment/https/https05.drawio.svg">
+
+### HTTP ์๋ต { #http-response }
+
+์ ํ๋ฆฌ์ผ์ด์
์ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ **์ผ๋ฐ(์ํธํ๋์ง ์์) HTTP ์๋ต**์ TLS Termination Proxy๋ก ๋ณด๋
๋๋ค.
+
+<img src="/img/deployment/https/https06.drawio.svg">
+
+### HTTPS ์๋ต { #https-response }
+
+๊ทธ ๋ค์ TLS Termination Proxy๋ ์ด์ ์ ํฉ์ํ ์ํธํ( `someapp.example.com` ์ธ์ฆ์๋ก ์์๋ ๊ฒ)๋ฅผ ์ฌ์ฉํด **์๋ต์ ์ํธํ**ํ๊ณ , ๋ธ๋ผ์ฐ์ ๋ก ๋ค์ ๋ณด๋
๋๋ค.
+
+์ดํ ๋ธ๋ผ์ฐ์ ๋ ์๋ต์ด ์ ํจํ์ง, ์ฌ๋ฐ๋ฅธ ์ํธํ ํค๋ก ์ํธํ๋์๋์ง ๋ฑ์ ํ์ธํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ **์๋ต์ ๋ณตํธํ**ํ๊ณ ์ฒ๋ฆฌํฉ๋๋ค.
+
+<img src="/img/deployment/https/https07.drawio.svg">
+
+ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ )๋ ์์ **HTTPS ์ธ์ฆ์**๋ก ํฉ์ํ ์ํธํ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฏ๋ก, ํด๋น ์๋ต์ด ์ฌ๋ฐ๋ฅธ ์๋ฒ์์ ์๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
+
+### ์ฌ๋ฌ ์ ํ๋ฆฌ์ผ์ด์
{ #multiple-applications }
+
+๊ฐ์ ์๋ฒ(๋๋ ์ฌ๋ฌ ์๋ฒ)์๋ ์๋ฅผ ๋ค์ด ๋ค๋ฅธ API ํ๋ก๊ทธ๋จ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ฒ๋ผ **์ฌ๋ฌ ์ ํ๋ฆฌ์ผ์ด์
**์ด ์์ ์ ์์ต๋๋ค.
+
+ํน์ IP์ ํฌํธ ์กฐํฉ์ ํ๋์ ํ๋ก์ธ์ค๋ง ์ฒ๋ฆฌํ ์ ์์ง๋ง(์์์์๋ TLS Termination Proxy), ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์
/ํ๋ก์ธ์ค๋ **๊ณต๊ฐ IP์ ํฌํธ ์กฐํฉ**์ ๋์ผํ๊ฒ ์ฐ๋ ค๊ณ ๋ง ํ์ง ์๋๋ค๋ฉด ์๋ฒ์์ ํจ๊ป ์คํ๋ ์ ์์ต๋๋ค.
+
+<img src="/img/deployment/https/https08.drawio.svg">
+
+์ด๋ ๊ฒ ํ๋ฉด TLS Termination Proxy๊ฐ **์ฌ๋ฌ ๋๋ฉ์ธ**์ ๋ํ HTTPS์ ์ธ์ฆ์๋ฅผ **์ฌ๋ฌ ์ ํ๋ฆฌ์ผ์ด์
**์ ๋ํด ์ฒ๋ฆฌํ๊ณ , ๊ฐ ๊ฒฝ์ฐ์ ๋ง๋ ์ ํ๋ฆฌ์ผ์ด์
์ผ๋ก ์์ฒญ์ ์ ๋ฌํ ์ ์์ต๋๋ค.
+
+### ์ธ์ฆ์ ๊ฐฑ์ { #certificate-renewal }
+
+๋ฏธ๋์ ์ด๋ ์์ ์๋ ๊ฐ ์ธ์ฆ์๊ฐ **๋ง๋ฃ**๋ฉ๋๋ค(ํ๋ ํ ์ฝ 3๊ฐ์).
+
+๊ทธ ๋ค์์๋ ๋ ๋ค๋ฅธ ํ๋ก๊ทธ๋จ(๊ฒฝ์ฐ์ ๋ฐ๋ผ ๋ณ๋ ํ๋ก๊ทธ๋จ์ผ ์๋ ์๊ณ , ๊ฒฝ์ฐ์ ๋ฐ๋ผ ๊ฐ์ TLS Termination Proxy์ผ ์๋ ์์ต๋๋ค)์ด Let's Encrypt์ ํต์ ํ์ฌ ์ธ์ฆ์๋ฅผ ๊ฐฑ์ ํฉ๋๋ค.
+
+<img src="/img/deployment/https/https.drawio.svg">
+
+**TLS ์ธ์ฆ์**๋ IP ์ฃผ์๊ฐ ์๋๋ผ **๋๋ฉ์ธ ์ด๋ฆ**๊ณผ **์ฐ๊ฒฐ**๋์ด ์์ต๋๋ค.
+
+๋ฐ๋ผ์ ์ธ์ฆ์๋ฅผ ๊ฐฑ์ ํ๋ ค๋ฉด, ๊ฐฑ์ ํ๋ก๊ทธ๋จ์ด ๊ถํ ๊ธฐ๊ด(Let's Encrypt)์๊ฒ ํด๋น ๋๋ฉ์ธ์ ์ค์ ๋ก **โ์์ โํ๊ณ ์ ์ดํ๊ณ ์์**์ **์ฆ๋ช
**ํด์ผ ํฉ๋๋ค.
+
+์ด๋ฅผ ์ํด, ๊ทธ๋ฆฌ๊ณ ๋ค์ํ ์ ํ๋ฆฌ์ผ์ด์
์๊ตฌ๋ฅผ ์์ฉํ๊ธฐ ์ํด ์ฌ๋ฌ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ๋๋ฆฌ ์ฐ์ด๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* **์ผ๋ถ DNS ๋ ์ฝ๋ ์์ **.
+ * ์ด๋ฅผ ์ํด์๋ ๊ฐฑ์ ํ๋ก๊ทธ๋จ์ด DNS ์ ๊ณต์
์ฒด์ API๋ฅผ ์ง์ํด์ผ ํ๋ฏ๋ก, ์ฌ์ฉํ๋ DNS ์ ๊ณต์
์ฒด์ ๋ฐ๋ผ ๊ฐ๋ฅํ ์๋, ์๋ ์๋ ์์ต๋๋ค.
+* ๋๋ฉ์ธ๊ณผ ์ฐ๊ฒฐ๋ ๊ณต๊ฐ IP ์ฃผ์์์ **์๋ฒ๋ก ์คํ**(์ ์ด๋ ์ธ์ฆ์ ๋ฐ๊ธ ๊ณผ์ ๋์).
+ * ์์์ ๋งํ๋ฏ ํน์ IP์ ํฌํธ์์๋ ํ๋์ ํ๋ก์ธ์ค๋ง ๋ฆฌ์ค๋ํ ์ ์์ต๋๋ค.
+ * ์ด๊ฒ์ด ๋์ผํ TLS Termination Proxy๊ฐ ์ธ์ฆ์ ๊ฐฑ์ ๊ณผ์ ๊น์ง ์ฒ๋ฆฌํ ๋ ๋งค์ฐ ์ ์ฉํ ์ด์ ์ค ํ๋์
๋๋ค.
+ * ๊ทธ๋ ์ง ์์ผ๋ฉด TLS Termination Proxy๋ฅผ ์ ์ ์ค์งํ๊ณ , ๊ฐฑ์ ํ๋ก๊ทธ๋จ์ ์์ํด ์ธ์ฆ์๋ฅผ ํ๋ํ ๋ค์, TLS Termination Proxy์ ์ธ์ฆ์๋ฅผ ์ค์ ํ๊ณ , ๋ค์ TLS Termination Proxy๋ฅผ ์ฌ์์ํด์ผ ํ ์๋ ์์ต๋๋ค. ์ด๋ TLS Termination Proxy๊ฐ ๊บผ์ ธ ์๋ ๋์ ์ฑ(๋ค)์ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก ์ด์์ ์ด์ง ์์ต๋๋ค.
+
+์ฑ์ ๊ณ์ ์ ๊ณตํ๋ฉด์ ์ด ๊ฐฑ์ ๊ณผ์ ์ ์ฒ๋ฆฌํ ์ ์๋ ๊ฒ์, ์ ํ๋ฆฌ์ผ์ด์
์๋ฒ(์: Uvicorn)์์ TLS ์ธ์ฆ์๋ฅผ ์ง์ ์ฐ๋ ๋์ TLS Termination Proxy๋ก HTTPS๋ฅผ ์ฒ๋ฆฌํ๋ **๋ณ๋์ ์์คํ
**์ ๋๊ณ ์ถ์ด์ง๋ ์ฃผ์ ์ด์ ์ค ํ๋์
๋๋ค.
+
+## ํ๋ก์ ์ ๋ฌ ํค๋ { #proxy-forwarded-headers }
+
+ํ๋ก์๋ฅผ ์ฌ์ฉํด HTTPS๋ฅผ ์ฒ๋ฆฌํ ๋, **์ ํ๋ฆฌ์ผ์ด์
์๋ฒ**(์: FastAPI CLI๋ฅผ ํตํ Uvicorn)๋ HTTPS ๊ณผ์ ์ ๋ํด ์๋ฌด๊ฒ๋ ์์ง ๋ชปํ๊ณ **TLS Termination Proxy**์๋ ์ผ๋ฐ HTTP๋ก ํต์ ํฉ๋๋ค.
+
+์ด **ํ๋ก์**๋ ๋ณดํต ์์ฒญ์ **์ ํ๋ฆฌ์ผ์ด์
์๋ฒ**์ ์ ๋ฌํ๊ธฐ ์ ์, ์์ฒญ์ด ํ๋ก์์ ์ํด **์ ๋ฌ(forwarded)**๋๊ณ ์์์ ์ ํ๋ฆฌ์ผ์ด์
์๋ฒ๊ฐ ์ ์ ์๋๋ก ์ผ๋ถ HTTP ํค๋๋ฅผ ์ฆ์์์ ์ค์ ํฉ๋๋ค.
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+ํ๋ก์ ํค๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
+* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
+* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
+
+///
+
+๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ **์ ํ๋ฆฌ์ผ์ด์
์๋ฒ**๋ ์์ ์ด ์ ๋ขฐํ ์ ์๋ **ํ๋ก์** ๋ค์ ์๋ค๋ ๊ฒ์ ๋ชจ๋ฅด๋ฏ๋ก, ๊ธฐ๋ณธ์ ์ผ๋ก๋ ๊ทธ ํค๋๋ค์ ์ ๋ขฐํ์ง ์์ต๋๋ค.
+
+ํ์ง๋ง **์ ํ๋ฆฌ์ผ์ด์
์๋ฒ**๊ฐ **ํ๋ก์**๊ฐ ๋ณด๋ธ *forwarded* ํค๋๋ฅผ ์ ๋ขฐํ๋๋ก ์ค์ ํ ์ ์์ต๋๋ค. FastAPI CLI๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, *CLI Option* `--forwarded-allow-ips`๋ฅผ ์ฌ์ฉํด ์ด๋ค IP์์ ์จ *forwarded* ํค๋๋ฅผ ์ ๋ขฐํ ์ง ์ง์ ํ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด **์ ํ๋ฆฌ์ผ์ด์
์๋ฒ**๊ฐ ์ ๋ขฐํ๋ **ํ๋ก์**๋ก๋ถํฐ๋ง ํต์ ์ ๋ฐ๋๋ค๋ฉด, `--forwarded-allow-ips="*"`๋ก ์ค์ ํด ๋ค์ด์ค๋ ๋ชจ๋ IP๋ฅผ ์ ๋ขฐํ๊ฒ ํ ์ ์์ต๋๋ค. ์ด์ฐจํผ **ํ๋ก์**๊ฐ ์ฌ์ฉํ๋ IP์์๋ง ์์ฒญ์ ๋ฐ๊ฒ ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์
์ ์์ ์ด ์ฌ์ฉํ๋ ๊ณต๊ฐ URL์ด ๋ฌด์์ธ์ง, HTTPS๋ฅผ ์ฌ์ฉํ๋์ง, ๋๋ฉ์ธ์ด ๋ฌด์์ธ์ง ๋ฑ์ ์ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด ๋ฆฌ๋ค์ด๋ ํธ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌํ๋ ๋ฐ ์ ์ฉํฉ๋๋ค.
+
+/// tip | ํ
+
+์ด์ ๋ํด์๋ [ํ๋ก์ ๋ค์์ ์คํํ๊ธฐ - ํ๋ก์ ์ ๋ฌ ํค๋ ํ์ฑํ](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank} ๋ฌธ์์์ ๋ ์์๋ณผ ์ ์์ต๋๋ค.
+
+///
+
+## ์์ฝ { #recap }
+
+**HTTPS**๋ ๋งค์ฐ ์ค์ํ๋ฉฐ, ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์๋นํ **ํต์ฌ์ **์
๋๋ค. ๊ฐ๋ฐ์๊ฐ HTTPS์ ๊ด๋ จํด ํด์ผ ํ๋ ๋
ธ๋ ฅ์ ๋๋ถ๋ถ์ ๊ฒฐ๊ตญ **์ด ๊ฐ๋
๋ค์ ์ดํด**ํ๊ณ ๊ทธ๊ฒ๋ค์ด ์ด๋ป๊ฒ ๋์ํ๋์ง ํ์
ํ๋ ๊ฒ์
๋๋ค.
+
+ํ์ง๋ง **๊ฐ๋ฐ์๋ฅผ ์ํ HTTPS**์ ๊ธฐ๋ณธ ์ ๋ณด๋ฅผ ์๊ณ ๋๋ฉด, ์ฌ๋ฌ ๋๊ตฌ๋ฅผ ์ฝ๊ฒ ์กฐํฉํ๊ณ ์ค์ ํ์ฌ ๋ชจ๋ ๊ฒ์ ๊ฐ๋จํ๊ฒ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
+
+๋ค์ ์ฅ๋ค์์๋ **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
์ ์ํ **HTTPS** ์ค์ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๊ตฌ์ฒด์ ์ธ ์์๋ก ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค. ๐
--- /dev/null
+# ์๋ฒ๋ฅผ ์๋์ผ๋ก ์คํํ๊ธฐ { #run-a-server-manually }
+
+## `fastapi run` ๋ช
๋ น ์ฌ์ฉํ๊ธฐ { #use-the-fastapi-run-command }
+
+์์ฝํ๋ฉด, `fastapi run`์ ์ฌ์ฉํด FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ์๋น์คํ์ธ์:
+
+<div class="termy">
+
+```console
+$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
+
+ <span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server ๐
+
+ Searching for package file structure from directories
+ with <font color="#3465A4">__init__.py</font> files
+ Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
+
+ <span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> ๐ main.py
+
+ <span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
+ the following code:
+
+ <u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
+
+ <span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
+
+ <span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
+ <span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
+
+ Logs:
+
+ <span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>2306215</b></font><b>]</b>
+ <span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
+ <span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
+ <span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C
+ to quit<b>)</b>
+```
+
+</div>
+
+๋๋ถ๋ถ์ ๊ฒฝ์ฐ์๋ ์ด๊ฒ์ผ๋ก ๋์ํฉ๋๋ค. ๐
+
+์๋ฅผ ๋ค์ด ์ด ๋ช
๋ น์ ์ปจํ
์ด๋๋ ์๋ฒ ๋ฑ์์ **FastAPI** ์ฑ์ ์์ํ ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+## ASGI ์๋ฒ { #asgi-servers }
+
+์ด์ ์กฐ๊ธ ๋ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+FastAPI๋ <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr>๋ผ๊ณ ๋ถ๋ฆฌ๋, Python ์น ํ๋ ์์ํฌ์ ์๋ฒ๋ฅผ ๋ง๋ค๊ธฐ ์ํ ํ์ค์ ์ฌ์ฉํฉ๋๋ค. FastAPI๋ ASGI ์น ํ๋ ์์ํฌ์
๋๋ค.
+
+์๊ฒฉ ์๋ฒ ๋จธ์ ์์ **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
(๋๋ ๋ค๋ฅธ ASGI ์ ํ๋ฆฌ์ผ์ด์
)์ ์คํํ๊ธฐ ์ํด ํ์ํ ํต์ฌ ์์๋ **Uvicorn** ๊ฐ์ ASGI ์๋ฒ ํ๋ก๊ทธ๋จ์
๋๋ค. `fastapi` ๋ช
๋ น์๋ ๊ธฐ๋ณธ์ผ๋ก ์ด๊ฒ์ด ํฌํจ๋์ด ์์ต๋๋ค.
+
+๋ค์์ ํฌํจํด ์ฌ๋ฌ ๋์์ด ์์ต๋๋ค:
+
+* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>: ๊ณ ์ฑ๋ฅ ASGI ์๋ฒ.
+* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a>: HTTP/2 ๋ฐ Trio ๋ฑ ์ฌ๋ฌ ๊ธฐ๋ฅ๊ณผ ํธํ๋๋ ASGI ์๋ฒ.
+* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: Django Channels๋ฅผ ์ํด ๋ง๋ค์ด์ง ASGI ์๋ฒ.
+* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a>: Python ์ ํ๋ฆฌ์ผ์ด์
์ ์ํ Rust HTTP ์๋ฒ.
+* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a>: NGINX Unit์ ๊ฐ๋ณ๊ณ ๋ค์ฉ๋๋ก ์ฌ์ฉํ ์ ์๋ ์น ์ ํ๋ฆฌ์ผ์ด์
๋ฐํ์์
๋๋ค.
+
+## ์๋ฒ ๋จธ์ ๊ณผ ์๋ฒ ํ๋ก๊ทธ๋จ { #server-machine-and-server-program }
+
+์ด๋ฆ์ ๊ดํด ๊ธฐ์ตํด ๋ ์์ ๋ํ
์ผ์ด ์์ต๋๋ค. ๐ก
+
+"**server**"๋ผ๋ ๋จ์ด๋ ๋ณดํต ์๊ฒฉ/ํด๋ผ์ฐ๋ ์ปดํจํฐ(๋ฌผ๋ฆฌ ๋๋ ๊ฐ์ ๋จธ์ )์, ๊ทธ ๋จธ์ ์์ ์คํ ์ค์ธ ํ๋ก๊ทธ๋จ(์: Uvicorn) ๋ ๋ค๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
+
+์ผ๋ฐ์ ์ผ๋ก "server"๋ฅผ ์ฝ์ ๋, ์ด ๋ ๊ฐ์ง ์ค ํ๋๋ฅผ ์๋ฏธํ ์ ์๋ค๋ ์ ์ ๊ธฐ์ตํ์ธ์.
+
+์๊ฒฉ ๋จธ์ ์ ๊ฐ๋ฆฌํฌ ๋๋ **server**๋ผ๊ณ ๋ถ๋ฅด๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด์ง๋ง, **machine**, **VM**(virtual machine), **node**๋ผ๊ณ ๋ถ๋ฅด๊ธฐ๋ ํฉ๋๋ค. ์ด๊ฒ๋ค์ ๋ณดํต Linux๋ฅผ ์คํํ๋ ์๊ฒฉ ๋จธ์ ์ ํ ํํ๋ฅผ ๋ปํ๋ฉฐ, ๊ทธ๊ณณ์์ ํ๋ก๊ทธ๋จ์ ์คํํฉ๋๋ค.
+
+## ์๋ฒ ํ๋ก๊ทธ๋จ ์ค์นํ๊ธฐ { #install-the-server-program }
+
+FastAPI๋ฅผ ์ค์นํ๋ฉด ํ๋ก๋์
์๋ฒ์ธ Uvicorn์ด ํจ๊ป ์ค์น๋๋ฉฐ, `fastapi run` ๋ช
๋ น์ผ๋ก ์์ํ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ASGI ์๋ฒ๋ฅผ ์๋์ผ๋ก ์ค์นํ ์๋ ์์ต๋๋ค.
+
+[๊ฐ์ ํ๊ฒฝ](../virtual-environments.md){.internal-link target=_blank}์ ๋ง๋ค๊ณ ํ์ฑํํ ๋ค์, ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์
์ ์ค์นํ์ธ์.
+
+์๋ฅผ ๋ค์ด Uvicorn์ ์ค์นํ๋ ค๋ฉด:
+
+<div class="termy">
+
+```console
+$ pip install "uvicorn[standard]"
+
+---> 100%
+```
+
+</div>
+
+๋ค๋ฅธ ์ด๋ค ASGI ์๋ฒ ํ๋ก๊ทธ๋จ๋ ๋น์ทํ ๊ณผ์ ์ด ์ ์ฉ๋ฉ๋๋ค.
+
+/// tip | ํ
+
+`standard`๋ฅผ ์ถ๊ฐํ๋ฉด Uvicorn์ด ๊ถ์ฅ๋๋ ์ถ๊ฐ ์์กด์ฑ ๋ช ๊ฐ์ง๋ฅผ ์ค์นํ๊ณ ์ฌ์ฉํฉ๋๋ค.
+
+์ฌ๊ธฐ์๋ `asyncio`๋ฅผ ๊ณ ์ฑ๋ฅ์ผ๋ก ๋์ฒดํ ์ ์๋ ๋๋กญ์ธ ๋์ฒด์ฌ์ธ `uvloop`๊ฐ ํฌํจ๋๋ฉฐ, ํฐ ๋์์ฑ ์ฑ๋ฅ ํฅ์์ ์ ๊ณตํฉ๋๋ค.
+
+`pip install "fastapi[standard]"` ๊ฐ์ ๋ฐฉ์์ผ๋ก FastAPI๋ฅผ ์ค์นํ๋ฉด `uvicorn[standard]`๋ ํจ๊ป ์ค์น๋ฉ๋๋ค.
+
+///
+
+## ์๋ฒ ํ๋ก๊ทธ๋จ ์คํํ๊ธฐ { #run-the-server-program }
+
+ASGI ์๋ฒ๋ฅผ ์๋์ผ๋ก ์ค์นํ๋ค๋ฉด, ๋ณดํต FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ์ํฌํธํ๊ธฐ ์ํด ํน๋ณํ ํ์์ import string์ ์ ๋ฌํด์ผ ํฉ๋๋ค:
+
+<div class="termy">
+
+```console
+$ uvicorn main:app --host 0.0.0.0 --port 80
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
+```
+
+</div>
+
+/// note | ์ฐธ๊ณ
+
+`uvicorn main:app` ๋ช
๋ น์ ๋ค์์ ๊ฐ๋ฆฌํต๋๋ค:
+
+* `main`: ํ์ผ `main.py`(Python "module").
+* `app`: `main.py` ์์์ `app = FastAPI()` ๋ผ์ธ์ผ๋ก ์์ฑ๋ ๊ฐ์ฒด.
+
+์ด๋ ๋ค์๊ณผ ๋์ผํฉ๋๋ค:
+
+```Python
+from main import app
+```
+
+///
+
+๊ฐ ASGI ์๋ฒ ํ๋ก๊ทธ๋จ์ ๋์๋ ๋น์ทํ ๋ช
๋ น์ ๊ฐ๊ณ ์์ผ๋ฉฐ, ์์ธํ ๋ด์ฉ์ ๊ฐ์์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
+
+/// warning | ๊ฒฝ๊ณ
+
+Uvicorn๊ณผ ๋ค๋ฅธ ์๋ฒ๋ ๊ฐ๋ฐ ์ค์ ์ ์ฉํ `--reload` ์ต์
์ ์ง์ํฉ๋๋ค.
+
+`--reload` ์ต์
์ ํจ์ฌ ๋ ๋ง์ ๋ฆฌ์์ค๋ฅผ ์๋นํ๊ณ , ๋ ๋ถ์์ ํฉ๋๋ค.
+
+**๊ฐ๋ฐ** ์ค์๋ ํฐ ๋์์ด ๋์ง๋ง, **ํ๋ก๋์
**์์๋ ์ฌ์ฉํ์ง **๋ง์์ผ** ํฉ๋๋ค.
+
+///
+
+## ๋ฐฐํฌ ๊ฐ๋
{ #deployment-concepts }
+
+์ด ์์ ๋ค์ ์๋ฒ ํ๋ก๊ทธ๋จ(์: Uvicorn)์ ์คํํ์ฌ **๋จ์ผ ํ๋ก์ธ์ค**๋ฅผ ์์ํ๊ณ , ์ฌ์ ์ ์ ํ ํฌํธ(์: `80`)์์ ๋ชจ๋ IP(`0.0.0.0`)๋ก ๋ค์ด์ค๋ ์์ฒญ์ ๋ฐ๋๋ก ํฉ๋๋ค.
+
+์ด๊ฒ์ด ๊ธฐ๋ณธ ์์ด๋์ด์
๋๋ค. ํ์ง๋ง ๋ณดํต์ ๋ค์๊ณผ ๊ฐ์ ์ถ๊ฐ ์ฌํญ๋ค๋ ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค:
+
+* ๋ณด์ - HTTPS
+* ์์ ์ ์๋ ์คํ
+* ์ฌ์์
+* ๋ณต์ (์คํ ์ค์ธ ํ๋ก์ธ์ค ์)
+* ๋ฉ๋ชจ๋ฆฌ
+* ์์ ์ ์ ํ ๋จ๊ณ
+
+๋ค์ ์ฅ๋ค์์ ์ด ๊ฐ๊ฐ์ ๊ฐ๋
์ ์ด๋ป๊ฒ ์๊ฐํด์ผ ํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ ์ ๋ต์ ๊ตฌ์ฒด์ ์ธ ์์๋ฅผ ๋ ์๋ ค๋๋ฆฌ๊ฒ ์ต๋๋ค. ๐
--- /dev/null
+# ์ด์ 403 ์ธ์ฆ ์ค๋ฅ ์ํ ์ฝ๋ ์ฌ์ฉํ๊ธฐ { #use-old-403-authentication-error-status-codes }
+
+FastAPI ๋ฒ์ `0.122.0` ์ด์ ์๋, ํตํฉ ๋ณด์ ์ ํธ๋ฆฌํฐ๊ฐ ์ธ์ฆ ์คํจ ํ ํด๋ผ์ด์ธํธ์ ์ค๋ฅ๋ฅผ ๋ฐํํ ๋ HTTP ์ํ ์ฝ๋ `403 Forbidden`์ ์ฌ์ฉํ์ต๋๋ค.
+
+FastAPI ๋ฒ์ `0.122.0`๋ถํฐ๋ ๋ ์ ์ ํ HTTP ์ํ ์ฝ๋ `401 Unauthorized`๋ฅผ ์ฌ์ฉํ๋ฉฐ, HTTP ๋ช
์ธ์ธ <a href="https://datatracker.ietf.org/doc/html/rfc7235#section-3.1" class="external-link" target="_blank">RFC 7235</a>, <a href="https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized" class="external-link" target="_blank">RFC 9110</a>๋ฅผ ๋ฐ๋ผ ์๋ต์ ํฉ๋ฆฌ์ ์ธ `WWW-Authenticate` ํค๋๋ฅผ ๋ฐํํฉ๋๋ค.
+
+ํ์ง๋ง ์ด๋ค ์ด์ ๋ก๋ ํด๋ผ์ด์ธํธ๊ฐ ์ด์ ๋์์ ์์กดํ๊ณ ์๋ค๋ฉด, ๋ณด์ ํด๋์ค์์ `make_not_authenticated_error` ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ์ฌ ์ด์ ๋์์ผ๋ก ๋๋๋ฆด ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด, ๊ธฐ๋ณธ๊ฐ์ธ `401 Unauthorized` ์ค๋ฅ ๋์ `403 Forbidden` ์ค๋ฅ๋ฅผ ๋ฐํํ๋ `HTTPBearer`์ ์๋ธํด๋์ค๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค:
+
+{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *}
+
+/// tip | ํ
+
+ํจ์๋ ์์ธ๋ฅผ `raise`ํ๋ ๊ฒ์ด ์๋๋ผ ์์ธ ์ธ์คํด์ค๋ฅผ `return`ํ๋ค๋ ์ ์ ์ ์ํ์ธ์. ์์ธ๋ฅผ ๋ฐ์์ํค๋(`raise`) ์์
์ ๋ด๋ถ ์ฝ๋์ ๋๋จธ์ง ๋ถ๋ถ์์ ์ํ๋ฉ๋๋ค.
+
+///
--- /dev/null
+# ์ปค์คํ
Docs UI ์ ์ ์์
(์์ฒด ํธ์คํ
) { #custom-docs-ui-static-assets-self-hosting }
+
+API ๋ฌธ์๋ **Swagger UI**์ **ReDoc**์ ์ฌ์ฉํ๋ฉฐ, ๊ฐ๊ฐ JavaScript์ CSS ํ์ผ์ด ํ์ํฉ๋๋ค.
+
+๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฌํ ํ์ผ์ <abbr title="Content Delivery Network - ์ฝํ
์ธ ์ ์ก ๋คํธ์ํฌ: ์ผ๋ฐ์ ์ผ๋ก ์ฌ๋ฌ ์๋ฒ๋ก ๊ตฌ์ฑ๋์ด JavaScript์ CSS ๊ฐ์ ์ ์ ํ์ผ์ ์ ๊ณตํ๋ ์๋น์ค์
๋๋ค. ๋ณดํต ํด๋ผ์ด์ธํธ์ ๋ ๊ฐ๊น์ด ์๋ฒ์์ ํ์ผ์ ์ ๊ณตํด ์ฑ๋ฅ์ ํฅ์์ํค๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.">CDN</abbr>์์ ์ ๊ณต๋ฉ๋๋ค.
+
+ํ์ง๋ง ์ด๋ฅผ ์ปค์คํฐ๋ง์ด์งํ ์ ์์ผ๋ฉฐ, ํน์ CDN์ ์ง์ ํ๊ฑฐ๋ ํ์ผ์ ์ง์ ์ ๊ณตํ ์๋ ์์ต๋๋ค.
+
+## JavaScript์ CSS์ฉ ์ปค์คํ
CDN { #custom-cdn-for-javascript-and-css }
+
+์๋ฅผ ๋ค์ด ๋ค๋ฅธ <abbr title="Content Delivery Network">CDN</abbr>์ ์ฌ์ฉํ๊ณ ์ถ๋ค๊ณ ํด๋ด
์๋ค. ์๋ฅผ ๋ค๋ฉด `https://unpkg.com/`์ ์ฌ์ฉํ๋ ค๋ ๊ฒฝ์ฐ์
๋๋ค.
+
+์ด๋ ์๋ฅผ ๋ค์ด ํน์ ๊ตญ๊ฐ์์ ์ผ๋ถ URL์ ์ ํํ๋ ๊ฒฝ์ฐ์ ์ ์ฉํ ์ ์์ต๋๋ค.
+
+### ์๋ ๋ฌธ์ ๋นํ์ฑํํ๊ธฐ { #disable-the-automatic-docs }
+
+์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ ์๋ ๋ฌธ์๋ฅผ ๋นํ์ฑํํ๋ ๊ฒ์
๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ ๋ฌธ์๋ ๊ธฐ๋ณธ CDN์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+๋นํ์ฑํํ๋ ค๋ฉด `FastAPI` ์ฑ์ ์์ฑํ ๋ ํด๋น URL์ `None`์ผ๋ก ์ค์ ํ์ธ์:
+
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
+
+### ์ปค์คํ
๋ฌธ์ ํฌํจํ๊ธฐ { #include-the-custom-docs }
+
+์ด์ ์ปค์คํ
๋ฌธ์๋ฅผ ์ํ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
+
+FastAPI ๋ด๋ถ ํจ์๋ฅผ ์ฌ์ฌ์ฉํด ๋ฌธ์์ฉ HTML ํ์ด์ง๋ฅผ ์์ฑํ๊ณ , ํ์ํ ์ธ์๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค:
+
+* `openapi_url`: ๋ฌธ์ HTML ํ์ด์ง๊ฐ API์ OpenAPI ์คํค๋ง๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ URL์
๋๋ค. ์ฌ๊ธฐ์๋ `app.openapi_url` ์์ฑ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+* `title`: API์ ์ ๋ชฉ์
๋๋ค.
+* `oauth2_redirect_url`: ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํ๋ ค๋ฉด ์ฌ๊ธฐ์ `app.swagger_ui_oauth2_redirect_url`์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+* `swagger_js_url`: Swagger UI ๋ฌธ์์ HTML์ด **JavaScript** ํ์ผ์ ๊ฐ์ ธ์ฌ ์ ์๋ URL์
๋๋ค. ์ปค์คํ
CDN URL์
๋๋ค.
+* `swagger_css_url`: Swagger UI ๋ฌธ์์ HTML์ด **CSS** ํ์ผ์ ๊ฐ์ ธ์ฌ ์ ์๋ URL์
๋๋ค. ์ปค์คํ
CDN URL์
๋๋ค.
+
+ReDoc๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค...
+
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
+
+/// tip | ํ
+
+`swagger_ui_redirect`์ ๋ํ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ OAuth2๋ฅผ ์ฌ์ฉํ ๋ ๋์์ด ๋๋ ํฌํผ์
๋๋ค.
+
+API๋ฅผ OAuth2 provider์ ํตํฉํ๋ฉด ์ธ์ฆ์ ์ํํ ๋ค ํ๋ํ ์๊ฒฉ ์ฆ๋ช
์ผ๋ก API ๋ฌธ์๋ก ๋ค์ ๋์์ฌ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ค์ OAuth2 ์ธ์ฆ์ ์ฌ์ฉํด API์ ์ํธ์์ฉํ ์ ์์ต๋๋ค.
+
+Swagger UI๊ฐ ์ด ๊ณผ์ ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ฒ๋ฆฌํด ์ฃผ์ง๋ง, ์ด๋ฅผ ์ํด ์ด "redirect" ํฌํผ๊ฐ ํ์ํฉ๋๋ค.
+
+///
+
+### ํ
์คํธ์ฉ *๊ฒฝ๋ก ์ฒ๋ฆฌ* ๋ง๋ค๊ธฐ { #create-a-path-operation-to-test-it }
+
+์ด์ ๋ชจ๋ ๊ฒ์ด ์ ๋๋ก ๋์ํ๋์ง ํ
์คํธํ ์ ์๋๋ก *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ํ๋ ๋ง๋์ธ์:
+
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
+
+### ํ
์คํธํ๊ธฐ { #test-it }
+
+์ด์ <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>์์ ๋ฌธ์์ ์ ์ํ ๋ค ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๋ฉด, ์ CDN์์ ์์
์ ๋ถ๋ฌ์ค๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
+
+## ๋ฌธ์์ฉ JavaScript์ CSS ์์ฒด ํธ์คํ
ํ๊ธฐ { #self-hosting-javascript-and-css-for-docs }
+
+JavaScript์ CSS๋ฅผ ์์ฒด ํธ์คํ
ํ๋ ๊ฒ์ ์๋ฅผ ๋ค์ด, ์คํ๋ผ์ธ ์ํ์ด๊ฑฐ๋ ์ธ๋ถ ์ธํฐ๋ท์ ์ ๊ทผํ ์ ์๋ ํ๊ฒฝ, ๋๋ ๋ก์ปฌ ๋คํธ์ํฌ์์๋ ์ฑ์ด ๊ณ์ ๋์ํด์ผ ํ ๋ ์ ์ฉํ ์ ์์ต๋๋ค.
+
+์ฌ๊ธฐ์๋ ๋์ผํ FastAPI ์ฑ์์ ํด๋น ํ์ผ์ ์ง์ ์ ๊ณตํ๊ณ , ๋ฌธ์๊ฐ ์ด๋ฅผ ์ฌ์ฉํ๋๋ก ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ด
๋๋ค.
+
+### ํ๋ก์ ํธ ํ์ผ ๊ตฌ์กฐ { #project-file-structure }
+
+ํ๋ก์ ํธ ํ์ผ ๊ตฌ์กฐ๊ฐ ๋ค์๊ณผ ๊ฐ๋ค๊ณ ํด๋ด
์๋ค:
+
+```
+.
+โโโ app
+โ โโโ __init__.py
+โ โโโ main.py
+```
+
+์ด์ ํด๋น ์ ์ ํ์ผ์ ์ ์ฅํ ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ง๋์ธ์.
+
+์ ํ์ผ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค:
+
+```
+.
+โโโ app
+โย ย โโโ __init__.py
+โย ย โโโ main.py
+โโโ static/
+```
+
+### ํ์ผ ๋ค์ด๋ก๋ํ๊ธฐ { #download-the-files }
+
+๋ฌธ์์ ํ์ํ ์ ์ ํ์ผ์ ๋ค์ด๋ก๋ํด์ `static/` ๋๋ ํฐ๋ฆฌ์ ๋ฃ์ผ์ธ์.
+
+๊ฐ ๋งํฌ๋ฅผ ์ฐํด๋ฆญํ ๋ค "๋งํฌ๋ฅผ ๋ค๋ฅธ ์ด๋ฆ์ผ๋ก ์ ์ฅ..."๊ณผ ๋น์ทํ ์ต์
์ ์ ํํ๋ฉด ๋ ๊ฒ์
๋๋ค.
+
+**Swagger UI**๋ ๋ค์ ํ์ผ์ ์ฌ์ฉํฉ๋๋ค:
+
+* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a>
+* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a>
+
+**ReDoc**์ ๋ค์ ํ์ผ์ ์ฌ์ฉํฉ๋๋ค:
+
+* <a href="https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a>
+
+์ดํ ํ์ผ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค:
+
+```
+.
+โโโ app
+โย ย โโโ __init__.py
+โย ย โโโ main.py
+โโโ static
+ โโโ redoc.standalone.js
+ โโโ swagger-ui-bundle.js
+ โโโ swagger-ui.css
+```
+
+### ์ ์ ํ์ผ ์ ๊ณตํ๊ธฐ { #serve-the-static-files }
+
+* `StaticFiles`๋ฅผ importํฉ๋๋ค.
+* ํน์ ๊ฒฝ๋ก์ `StaticFiles()` ์ธ์คํด์ค๋ฅผ "๋ง์ดํธ(mount)"ํฉ๋๋ค.
+
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
+
+### ์ ์ ํ์ผ ํ
์คํธํ๊ธฐ { #test-the-static-files }
+
+์ ํ๋ฆฌ์ผ์ด์
์ ์์ํ๊ณ <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>๋ก ์ด๋ํ์ธ์.
+
+**ReDoc**์ฉ ๋งค์ฐ ๊ธด JavaScript ํ์ผ์ด ๋ณด์ผ ๊ฒ์
๋๋ค.
+
+์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ด ์์ํ ์ ์์ต๋๋ค:
+
+```JavaScript
+/*! For license information please see redoc.standalone.js.LICENSE.txt */
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
+...
+```
+
+์ด๋ ์ฑ์์ ์ ์ ํ์ผ์ ์ ๊ณตํ ์ ์๊ณ , ๋ฌธ์์ฉ ์ ์ ํ์ผ์ ์ฌ๋ฐ๋ฅธ ์์น์ ๋ฐฐ์นํ๋ค๋ ๊ฒ์ ํ์ธํด ์ค๋๋ค.
+
+์ด์ ๋ฌธ์๊ฐ ์ด ์ ์ ํ์ผ์ ์ฌ์ฉํ๋๋ก ์ฑ์ ์ค์ ํ ์ ์์ต๋๋ค.
+
+### ์ ์ ํ์ผ์ ์ํ ์๋ ๋ฌธ์ ๋นํ์ฑํํ๊ธฐ { #disable-the-automatic-docs-for-static-files }
+
+์ปค์คํ
CDN์ ์ฌ์ฉํ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ฒซ ๋จ๊ณ๋ ์๋ ๋ฌธ์๋ฅผ ๋นํ์ฑํํ๋ ๊ฒ์
๋๋ค. ์๋ ๋ฌธ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก CDN์ ์ฌ์ฉํฉ๋๋ค.
+
+๋นํ์ฑํํ๋ ค๋ฉด `FastAPI` ์ฑ์ ์์ฑํ ๋ ํด๋น URL์ `None`์ผ๋ก ์ค์ ํ์ธ์:
+
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
+
+### ์ ์ ํ์ผ์ ์ํ ์ปค์คํ
๋ฌธ์ ํฌํจํ๊ธฐ { #include-the-custom-docs-for-static-files }
+
+๊ทธ๋ฆฌ๊ณ ์ปค์คํ
CDN์ ์ฌ์ฉํ ๋์ ๋์ผํ ๋ฐฉ์์ผ๋ก, ์ด์ ์ปค์คํ
๋ฌธ์๋ฅผ ์ํ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
+
+๋ค์ ํ ๋ฒ, FastAPI ๋ด๋ถ ํจ์๋ฅผ ์ฌ์ฌ์ฉํด ๋ฌธ์์ฉ HTML ํ์ด์ง๋ฅผ ์์ฑํ๊ณ , ํ์ํ ์ธ์๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค:
+
+* `openapi_url`: ๋ฌธ์ HTML ํ์ด์ง๊ฐ API์ OpenAPI ์คํค๋ง๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ URL์
๋๋ค. ์ฌ๊ธฐ์๋ `app.openapi_url` ์์ฑ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+* `title`: API์ ์ ๋ชฉ์
๋๋ค.
+* `oauth2_redirect_url`: ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํ๋ ค๋ฉด ์ฌ๊ธฐ์ `app.swagger_ui_oauth2_redirect_url`์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+* `swagger_js_url`: Swagger UI ๋ฌธ์์ HTML์ด **JavaScript** ํ์ผ์ ๊ฐ์ ธ์ฌ ์ ์๋ URL์
๋๋ค. **์ด์ ๋ ์ฌ๋ฌ๋ถ์ ์ฑ์ด ์ง์ ์ ๊ณตํ๋ ํ์ผ์
๋๋ค**.
+* `swagger_css_url`: Swagger UI ๋ฌธ์์ HTML์ด **CSS** ํ์ผ์ ๊ฐ์ ธ์ฌ ์ ์๋ URL์
๋๋ค. **์ด์ ๋ ์ฌ๋ฌ๋ถ์ ์ฑ์ด ์ง์ ์ ๊ณตํ๋ ํ์ผ์
๋๋ค**.
+
+ReDoc๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค...
+
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
+
+/// tip | ํ
+
+`swagger_ui_redirect`์ ๋ํ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ OAuth2๋ฅผ ์ฌ์ฉํ ๋ ๋์์ด ๋๋ ํฌํผ์
๋๋ค.
+
+API๋ฅผ OAuth2 provider์ ํตํฉํ๋ฉด ์ธ์ฆ์ ์ํํ ๋ค ํ๋ํ ์๊ฒฉ ์ฆ๋ช
์ผ๋ก API ๋ฌธ์๋ก ๋ค์ ๋์์ฌ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ค์ OAuth2 ์ธ์ฆ์ ์ฌ์ฉํด API์ ์ํธ์์ฉํ ์ ์์ต๋๋ค.
+
+Swagger UI๊ฐ ์ด ๊ณผ์ ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ฒ๋ฆฌํด ์ฃผ์ง๋ง, ์ด๋ฅผ ์ํด ์ด "redirect" ํฌํผ๊ฐ ํ์ํฉ๋๋ค.
+
+///
+
+### ์ ์ ํ์ผ ํ
์คํธ์ฉ *๊ฒฝ๋ก ์ฒ๋ฆฌ* ๋ง๋ค๊ธฐ { #create-a-path-operation-to-test-static-files }
+
+์ด์ ๋ชจ๋ ๊ฒ์ด ์ ๋๋ก ๋์ํ๋์ง ํ
์คํธํ ์ ์๋๋ก *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ํ๋ ๋ง๋์ธ์:
+
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
+
+### ์ ์ ํ์ผ UI ํ
์คํธํ๊ธฐ { #test-static-files-ui }
+
+์ด์ WiFi ์ฐ๊ฒฐ์ ๋๊ณ <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>์์ ๋ฌธ์์ ์ ์ํ ๋ค ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํด ๋ณด์ธ์.
+
+์ธํฐ๋ท์ด ์์ด๋ API ๋ฌธ์๋ฅผ ๋ณด๊ณ , API์ ์ํธ์์ฉํ ์ ์์ ๊ฒ์
๋๋ค.
--- /dev/null
+# ์ปค์คํ
Request ๋ฐ APIRoute ํด๋์ค { #custom-request-and-apiroute-class }
+
+์ผ๋ถ ๊ฒฝ์ฐ์๋ `Request`์ `APIRoute` ํด๋์ค์์ ์ฌ์ฉ๋๋ ๋ก์ง์ ์ค๋ฒ๋ผ์ด๋ํ๊ณ ์ถ์ ์ ์์ต๋๋ค.
+
+ํนํ, ์ด๋ middleware์ ์๋ ๋ก์ง์ ์ข์ ๋์์ด ๋ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด, ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฒ๋ฆฌ๋๊ธฐ ์ ์ ์์ฒญ ๋ฐ๋๋ฅผ ์ฝ๊ฑฐ๋ ์กฐ์ํ๊ณ ์ถ์ ๋๊ฐ ๊ทธ๋ ์ต๋๋ค.
+
+/// danger | ์ํ
+
+์ด ๊ธฐ๋ฅ์ "๊ณ ๊ธ" ๊ธฐ๋ฅ์
๋๋ค.
+
+**FastAPI**๋ฅผ ์ด์ ๋ง ์์ํ๋ค๋ฉด ์ด ์น์
์ ๊ฑด๋๋ฐ๋ ๊ฒ์ด ์ข์ต๋๋ค.
+
+///
+
+## ์ฌ์ฉ ์ฌ๋ก { #use-cases }
+
+์ฌ์ฉ ์ฌ๋ก์๋ ๋ค์์ด ํฌํจ๋ฉ๋๋ค:
+
+* JSON์ด ์๋ ์์ฒญ ๋ฐ๋๋ฅผ JSON์ผ๋ก ๋ณํํ๊ธฐ(์: <a href="https://msgpack.org/index.html" class="external-link" target="_blank">`msgpack`</a>).
+* gzip์ผ๋ก ์์ถ๋ ์์ฒญ ๋ฐ๋ ์์ถ ํด์ ํ๊ธฐ.
+* ๋ชจ๋ ์์ฒญ ๋ฐ๋๋ฅผ ์๋์ผ๋ก ๋ก๊น
ํ๊ธฐ.
+
+## ์ปค์คํ
์์ฒญ ๋ฐ๋ ์ธ์ฝ๋ฉ ์ฒ๋ฆฌํ๊ธฐ { #handling-custom-request-body-encodings }
+
+์ปค์คํ
`Request` ์๋ธํด๋์ค๋ฅผ ์ฌ์ฉํด gzip ์์ฒญ์ ์์ถ์ ํด์ ํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๊ทธ ์ปค์คํ
์์ฒญ ํด๋์ค๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ `APIRoute` ์๋ธํด๋์ค๋ ํจ๊ป ๋ณด๊ฒ ์ต๋๋ค.
+
+### ์ปค์คํ
`GzipRequest` ํด๋์ค ๋ง๋ค๊ธฐ { #create-a-custom-gziprequest-class }
+
+/// tip | ํ
+
+์ด ์์๋ ๋์ ๋ฐฉ์ ์์ฐ์ ์ํ ์ฅ๋๊ฐ ์์ ์
๋๋ค. Gzip ์ง์์ด ํ์ํ๋ค๋ฉด ์ ๊ณต๋๋ [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+///
+
+๋จผ์ , `GzipRequest` ํด๋์ค๋ฅผ ๋ง๋ญ๋๋ค. ์ด ํด๋์ค๋ `Request.body()` ๋ฉ์๋๋ฅผ ๋ฎ์ด์จ์, ์ ์ ํ ํค๋๊ฐ ์๋ ๊ฒฝ์ฐ ๋ฐ๋๋ฅผ ์์ถ ํด์ ํฉ๋๋ค.
+
+ํค๋์ `gzip`์ด ์์ผ๋ฉด ๋ฐ๋๋ฅผ ์์ถ ํด์ ํ๋ ค๊ณ ์๋ํ์ง ์์ต๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด ๋์ผํ route ํด๋์ค๊ฐ gzip์ผ๋ก ์์ถ๋ ์์ฒญ๊ณผ ์์ถ๋์ง ์์ ์์ฒญ์ ๋ชจ๋ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
+
+### ์ปค์คํ
`GzipRoute` ํด๋์ค ๋ง๋ค๊ธฐ { #create-a-custom-gziproute-class }
+
+๋ค์์ผ๋ก, `GzipRequest`๋ฅผ ํ์ฉํ๋ `fastapi.routing.APIRoute`์ ์ปค์คํ
์๋ธํด๋์ค๋ฅผ ๋ง๋ญ๋๋ค.
+
+์ด๋ฒ์๋ `APIRoute.get_route_handler()` ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํฉ๋๋ค.
+
+์ด ๋ฉ์๋๋ ํจ์๋ฅผ ๋ฐํํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ํจ์๊ฐ ์์ฒญ์ ๋ฐ์ ์๋ต์ ๋ฐํํฉ๋๋ค.
+
+์ฌ๊ธฐ์๋ ์๋ณธ ์์ฒญ์ผ๋ก๋ถํฐ `GzipRequest`๋ฅผ ๋ง๋ค๊ธฐ ์ํด ์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+`Request`์๋ `request.scope` ์์ฑ์ด ์๋๋ฐ, ์ด๋ ์์ฒญ๊ณผ ๊ด๋ จ๋ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๋ด๊ณ ์๋ Python `dict`์
๋๋ค.
+
+`Request`์๋ ๋ํ `request.receive`๊ฐ ์๋๋ฐ, ์ด๋ ์์ฒญ์ ๋ฐ๋๋ฅผ "๋ฐ๊ธฐ(receive)" ์ํ ํจ์์
๋๋ค.
+
+`scope` `dict`์ `receive` ํจ์๋ ๋ชจ๋ ASGI ๋ช
์ธ์ ์ผ๋ถ์
๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ด ๋ ๊ฐ์ง, `scope`์ `receive`๊ฐ ์๋ก์ด `Request` ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ๋ฐ ํ์ํ ๊ฒ๋ค์
๋๋ค.
+
+`Request`์ ๋ํด ๋ ์์๋ณด๋ ค๋ฉด <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">Starlette์ Requests ๋ฌธ์</a>๋ฅผ ํ์ธํ์ธ์.
+
+///
+
+`GzipRequest.get_route_handler`๊ฐ ๋ฐํํ๋ ํจ์๊ฐ ๋ค๋ฅด๊ฒ ํ๋ ์ ์ผํ ๊ฒ์ `Request`๋ฅผ `GzipRequest`๋ก ๋ณํํ๋ ๊ฒ์
๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด, ์ฐ๋ฆฌ์ `GzipRequest`๊ฐ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ก ์ ๋ฌํ๊ธฐ ์ ์(ํ์ํ๋ค๋ฉด) ๋ฐ์ดํฐ์ ์์ถ ํด์ ๋ฅผ ๋ด๋นํ๊ฒ ๋ฉ๋๋ค.
+
+๊ทธ ์ดํ์ ๋ชจ๋ ์ฒ๋ฆฌ ๋ก์ง์ ๋์ผํฉ๋๋ค.
+
+ํ์ง๋ง `GzipRequest.body`์์ ๋ณ๊ฒฝ์ ํ๊ธฐ ๋๋ฌธ์, ํ์ํ ๋ **FastAPI**๊ฐ ๋ก๋ํ๋ ์์ ์ ์์ฒญ ๋ฐ๋๋ ์๋์ผ๋ก ์์ถ ํด์ ๋ฉ๋๋ค.
+
+## ์์ธ ํธ๋ค๋ฌ์์ ์์ฒญ ๋ฐ๋ ์ ๊ทผํ๊ธฐ { #accessing-the-request-body-in-an-exception-handler }
+
+/// tip | ํ
+
+๊ฐ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด `RequestValidationError`์ ๋ํ ์ปค์คํ
ํธ๋ค๋ฌ์์ `body`๋ฅผ ์ฌ์ฉํ๋ ํธ์ด ์๋ง ํจ์ฌ ๋ ์ฝ์ต๋๋ค([์ค๋ฅ ์ฒ๋ฆฌํ๊ธฐ](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}).
+
+ํ์ง๋ง ์ด ์์๋ ์ฌ์ ํ ์ ํจํ๋ฉฐ, ๋ด๋ถ ์ปดํฌ๋ํธ์ ์ํธ์์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค.
+
+///
+
+๊ฐ์ ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํด ์์ธ ํธ๋ค๋ฌ์์ ์์ฒญ ๋ฐ๋์ ์ ๊ทผํ ์๋ ์์ต๋๋ค.
+
+ํ์ํ ๊ฒ์ `try`/`except` ๋ธ๋ก ์์์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๊ฒ๋ฟ์
๋๋ค:
+
+{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
+
+์์ธ๊ฐ ๋ฐ์ํ๋๋ผ๋ `Request` ์ธ์คํด์ค๋ ์ฌ์ ํ ์ค์ฝํ ์์ ๋จ์ ์์ผ๋ฏ๋ก, ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ ๋ ์์ฒญ ๋ฐ๋๋ฅผ ์ฝ๊ณ ํ์ฉํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
+
+## ๋ผ์ฐํฐ์์์ ์ปค์คํ
`APIRoute` ํด๋์ค { #custom-apiroute-class-in-a-router }
+
+`APIRouter`์ `route_class` ํ๋ผ๋ฏธํฐ๋ฅผ ์ค์ ํ ์๋ ์์ต๋๋ค:
+
+{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
+
+์ด ์์์์๋ `router` ์๋์ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ค์ด ์ปค์คํ
`TimedRoute` ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ฉฐ, ์๋ต์ ์์ฑํ๋ ๋ฐ ๊ฑธ๋ฆฐ ์๊ฐ์ ๋ด์ ์ถ๊ฐ `X-Response-Time` ํค๋๊ฐ ์๋ต์ ํฌํจ๋ฉ๋๋ค:
+
+{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
--- /dev/null
+# OpenAPI ํ์ฅํ๊ธฐ { #extending-openapi }
+
+์์ฑ๋ OpenAPI ์คํค๋ง๋ฅผ ์์ ํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
+
+์ด ์น์
์์ ๊ทธ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+## ์ผ๋ฐ์ ์ธ ๊ณผ์ { #the-normal-process }
+
+์ผ๋ฐ์ ์ธ(๊ธฐ๋ณธ) ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
+
+`FastAPI` ์ ํ๋ฆฌ์ผ์ด์
(์ธ์คํด์ค)์๋ OpenAPI ์คํค๋ง๋ฅผ ๋ฐํํด์ผ ํ๋ `.openapi()` ๋ฉ์๋๊ฐ ์์ต๋๋ค.
+
+์ ํ๋ฆฌ์ผ์ด์
๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ณผ์ ์์ `/openapi.json`(๋๋ `openapi_url`์ ์ค์ ํ ๊ฒฝ๋ก)์ฉ *๊ฒฝ๋ก ์ฒ๋ฆฌ*๊ฐ ๋ฑ๋ก๋ฉ๋๋ค.
+
+์ด ๊ฒฝ๋ก ์ฒ๋ฆฌ๋ ์ ํ๋ฆฌ์ผ์ด์
์ `.openapi()` ๋ฉ์๋ ๊ฒฐ๊ณผ๋ฅผ JSON ์๋ต์ผ๋ก ๋ฐํํ ๋ฟ์
๋๋ค.
+
+๊ธฐ๋ณธ์ ์ผ๋ก `.openapi()` ๋ฉ์๋๋ ํ๋กํผํฐ `.openapi_schema`์ ๋ด์ฉ์ด ์๋์ง ํ์ธํ๊ณ , ์์ผ๋ฉด ๊ทธ ๋ด์ฉ์ ๋ฐํํฉ๋๋ค.
+
+์์ผ๋ฉด `fastapi.openapi.utils.get_openapi`์ ์๋ ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ์ฌ์ฉํด ์์ฑํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ `get_openapi()` ํจ์๋ ๋ค์์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ต๋๋ค:
+
+* `title`: ๋ฌธ์์ ํ์๋๋ OpenAPI ์ ๋ชฉ.
+* `version`: API ๋ฒ์ . ์: `2.5.0`.
+* `openapi_version`: ์ฌ์ฉ๋๋ OpenAPI ์คํ ๋ฒ์ . ๊ธฐ๋ณธ๊ฐ์ ์ต์ ์ธ `3.1.0`.
+* `summary`: API์ ๋ํ ์งง์ ์์ฝ.
+* `description`: API ์ค๋ช
. markdown์ ํฌํจํ ์ ์์ผ๋ฉฐ ๋ฌธ์์ ํ์๋ฉ๋๋ค.
+* `routes`: ๋ผ์ฐํธ ๋ชฉ๋ก. ๊ฐ๊ฐ ๋ฑ๋ก๋ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์
๋๋ค. `app.routes`์์ ๊ฐ์ ธ์ต๋๋ค.
+
+/// info | ์ ๋ณด
+
+`summary` ํ๋ผ๋ฏธํฐ๋ OpenAPI 3.1.0 ์ด์์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, FastAPI 0.99.0 ์ด์์์ ์ง์๋ฉ๋๋ค.
+
+///
+
+## ๊ธฐ๋ณธ๊ฐ ๋ฎ์ด์ฐ๊ธฐ { #overriding-the-defaults }
+
+์ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก, ๋์ผํ ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ์ฌ์ฉํด OpenAPI ์คํค๋ง๋ฅผ ์์ฑํ๊ณ ํ์ํ ๊ฐ ๋ถ๋ถ์ ๋ฎ์ด์ธ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด, <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">์ปค์คํ
๋ก๊ณ ๋ฅผ ํฌํจํ๊ธฐ ์ํ ReDoc์ OpenAPI ํ์ฅ</a>์ ์ถ๊ฐํด ๋ณด๊ฒ ์ต๋๋ค.
+
+### ์ผ๋ฐ์ ์ธ **FastAPI** { #normal-fastapi }
+
+๋จผ์ , ํ์์ฒ๋ผ **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
์ ๋ชจ๋ ์์ฑํฉ๋๋ค:
+
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
+
+### OpenAPI ์คํค๋ง ์์ฑํ๊ธฐ { #generate-the-openapi-schema }
+
+๊ทธ๋ค์ `custom_openapi()` ํจ์ ์์์, ๋์ผํ ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ์ฌ์ฉํด OpenAPI ์คํค๋ง๋ฅผ ์์ฑํฉ๋๋ค:
+
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
+
+### OpenAPI ์คํค๋ง ์์ ํ๊ธฐ { #modify-the-openapi-schema }
+
+์ด์ OpenAPI ์คํค๋ง์ `info` "object"์ ์ปค์คํ
`x-logo`๋ฅผ ์ถ๊ฐํ์ฌ ReDoc ํ์ฅ์ ๋ํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
+
+### OpenAPI ์คํค๋ง ์บ์ํ๊ธฐ { #cache-the-openapi-schema }
+
+์์ฑํ ์คํค๋ง๋ฅผ ์ ์ฅํ๊ธฐ ์ํ "cache"๋ก `.openapi_schema` ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ฉ์๊ฐ API ๋ฌธ์๋ฅผ ์ด ๋๋ง๋ค ์ ํ๋ฆฌ์ผ์ด์
์ด ์คํค๋ง๋ฅผ ๋งค๋ฒ ์์ฑํ์ง ์์๋ ๋ฉ๋๋ค.
+
+์คํค๋ง๋ ํ ๋ฒ๋ง ์์ฑ๋๊ณ , ์ดํ ์์ฒญ์์๋ ๊ฐ์ ์บ์๋ ์คํค๋ง๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
+
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
+
+### ๋ฉ์๋ ์ค๋ฒ๋ผ์ด๋ํ๊ธฐ { #override-the-method }
+
+์ด์ `.openapi()` ๋ฉ์๋๋ฅผ ์ ํจ์๋ก ๊ต์ฒดํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
+
+### ํ์ธํ๊ธฐ { #check-it }
+
+<a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>๋ก ์ด๋ํ๋ฉด ์ปค์คํ
๋ก๊ณ (์ด ์์์์๋ **FastAPI** ๋ก๊ณ )๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/extending-openapi/image01.png">
--- /dev/null
+# ์ผ๋ฐ - ์ฌ์ฉ ๋ฐฉ๋ฒ - ๋ ์ํผ { #general-how-to-recipes }
+
+์ผ๋ฐ์ ์ด๊ฑฐ๋ ์์ฃผ ๋์ค๋ ์ง๋ฌธ์ ๋ํด, ๋ฌธ์์ ๋ค๋ฅธ ์์น๋ก ์๋ดํ๋ ๋ช ๊ฐ์ง ํฌ์ธํฐ๋ฅผ ์๊ฐํฉ๋๋ค.
+
+## ๋ฐ์ดํฐ ํํฐ๋ง - ๋ณด์ { #filter-data-security }
+
+๋ฐํํ๋ฉด ์ ๋๋ ๋ฐ์ดํฐ๋ฅผ ๊ณผ๋ํ๊ฒ ๋ฐํํ์ง ์๋๋ก ํ๋ ค๋ฉด, [ํํ ๋ฆฌ์ผ - ์๋ต ๋ชจ๋ธ - ๋ฐํ ํ์
](../tutorial/response-model.md){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+## ๋ฌธ์ํ ํ๊ทธ - OpenAPI { #documentation-tags-openapi }
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ํ๊ทธ๋ฅผ ์ถ๊ฐํ๊ณ , ๋ฌธ์ UI์์ ์ด๋ฅผ ๊ทธ๋ฃนํํ๋ ค๋ฉด [ํํ ๋ฆฌ์ผ - ๊ฒฝ๋ก ์ฒ๋ฆฌ ๊ตฌ์ฑ - ํ๊ทธ](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+## ๋ฌธ์ํ ์์ฝ ๋ฐ ์ค๋ช
- OpenAPI { #documentation-summary-and-description-openapi }
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ์์ฝ๊ณผ ์ค๋ช
์ ์ถ๊ฐํ๊ณ , ๋ฌธ์ UI์ ํ์ํ๋ ค๋ฉด [ํํ ๋ฆฌ์ผ - ๊ฒฝ๋ก ์ฒ๋ฆฌ ๊ตฌ์ฑ - ์์ฝ ๋ฐ ์ค๋ช
](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+## ๋ฌธ์ํ ์๋ต ์ค๋ช
- OpenAPI { #documentation-response-description-openapi }
+
+๋ฌธ์ UI์ ํ์๋๋ ์๋ต์ ์ค๋ช
์ ์ ์ํ๋ ค๋ฉด [ํํ ๋ฆฌ์ผ - ๊ฒฝ๋ก ์ฒ๋ฆฌ ๊ตฌ์ฑ - ์๋ต ์ค๋ช
](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+## ๋ฌธ์ํ *๊ฒฝ๋ก ์ฒ๋ฆฌ* ์ง์ ์ค๋จํ๊ธฐ - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ์ง์ ์ค๋จ(deprecate)์ผ๋ก ํ์ํ๊ณ , ๋ฌธ์ UI์ ๋ณด์ฌ์ฃผ๋ ค๋ฉด [ํํ ๋ฆฌ์ผ - ๊ฒฝ๋ก ์ฒ๋ฆฌ ๊ตฌ์ฑ - ์ง์ ์ค๋จ](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+## ์ด๋ค ๋ฐ์ดํฐ๋ JSON ํธํ์ผ๋ก ๋ณํํ๊ธฐ { #convert-any-data-to-json-compatible }
+
+์ด๋ค ๋ฐ์ดํฐ๋ JSON ํธํ ํ์์ผ๋ก ๋ณํํ๋ ค๋ฉด [ํํ ๋ฆฌ์ผ - JSON ํธํ ์ธ์ฝ๋](../tutorial/encoder.md){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+## OpenAPI ๋ฉํ๋ฐ์ดํฐ - ๋ฌธ์ { #openapi-metadata-docs }
+
+๋ผ์ด์ ์ค, ๋ฒ์ , ์ฐ๋ฝ์ฒ ๋ฑ์ ์ ๋ณด๋ฅผ ํฌํจํด OpenAPI ์คํค๋ง์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๋ ค๋ฉด [ํํ ๋ฆฌ์ผ - ๋ฉํ๋ฐ์ดํฐ์ ๋ฌธ์ URL](../tutorial/metadata.md){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+## OpenAPI ์ฌ์ฉ์ ์ ์ URL { #openapi-custom-url }
+
+OpenAPI URL์ ์ปค์คํฐ๋ง์ด์ฆ(๋๋ ์ ๊ฑฐ)ํ๋ ค๋ฉด [ํํ ๋ฆฌ์ผ - ๋ฉํ๋ฐ์ดํฐ์ ๋ฌธ์ URL](../tutorial/metadata.md#openapi-url){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+## OpenAPI ๋ฌธ์ URL { #openapi-docs-urls }
+
+์๋์ผ๋ก ์์ฑ๋๋ ๋ฌธ์ ์ฌ์ฉ์ ์ธํฐํ์ด์ค์์ ์ฌ์ฉํ๋ URL์ ์
๋ฐ์ดํธํ๋ ค๋ฉด [ํํ ๋ฆฌ์ผ - ๋ฉํ๋ฐ์ดํฐ์ ๋ฌธ์ URL](../tutorial/metadata.md#docs-urls){.internal-link target=_blank} ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์ธ์.
--- /dev/null
+# GraphQL { #graphql }
+
+**FastAPI**๋ **ASGI** ํ์ค์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฏ๋ก, ASGI์๋ ํธํ๋๋ ์ด๋ค **GraphQL** ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋งค์ฐ ์ฝ๊ฒ ํตํฉํ ์ ์์ต๋๋ค.
+
+๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ผ๋ฐ FastAPI **๊ฒฝ๋ก ์ฒ๋ฆฌ**์ GraphQL์ ํจ๊ป ์กฐํฉํ ์ ์์ต๋๋ค.
+
+/// tip | ํ
+
+**GraphQL**์ ๋ช ๊ฐ์ง ๋งค์ฐ ํน์ ํ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ํด๊ฒฐํฉ๋๋ค.
+
+์ผ๋ฐ์ ์ธ **web API**์ ๋น๊ตํ์ ๋ **์ฅ์ **๊ณผ **๋จ์ **์ด ์์ต๋๋ค.
+
+์ฌ๋ฌ๋ถ์ ์ฌ์ฉ ์ฌ๋ก์์ **์ด์ **์ด **๋จ์ **์ ์์ํ๋์ง ๊ผญ ํ๊ฐํด ๋ณด์ธ์. ๐ค
+
+///
+
+## GraphQL ๋ผ์ด๋ธ๋ฌ๋ฆฌ { #graphql-libraries }
+
+๋ค์์ **ASGI** ์ง์์ด ์๋ **GraphQL** ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์
๋๋ค. **FastAPI**์ ํจ๊ป ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+* <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a> ๐
+ * <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">FastAPI์ฉ ๋ฌธ์</a> ์ ๊ณต
+* <a href="https://ariadnegraphql.org/" class="external-link" target="_blank">Ariadne</a>
+ * <a href="https://ariadnegraphql.org/docs/fastapi-integration" class="external-link" target="_blank">FastAPI์ฉ ๋ฌธ์</a> ์ ๊ณต
+* <a href="https://tartiflette.io/" class="external-link" target="_blank">Tartiflette</a>
+ * ASGI ํตํฉ์ ์ ๊ณตํ๊ธฐ ์ํด <a href="https://tartiflette.github.io/tartiflette-asgi/" class="external-link" target="_blank">Tartiflette ASGI</a> ์ฌ์ฉ
+* <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>
+ * <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a> ์ฌ์ฉ
+
+## Strawberry๋ก GraphQL ์ฌ์ฉํ๊ธฐ { #graphql-with-strawberry }
+
+**GraphQL**๋ก ์์
ํด์ผ ํ๊ฑฐ๋ ์์
ํ๊ณ ์ถ๋ค๋ฉด, <a href="https://strawberry.rocks/" class="external-link" target="_blank">**Strawberry**</a>๋ฅผ **๊ถ์ฅ**ํฉ๋๋ค. **FastAPI**์ ์ค๊ณ์ ๊ฐ์ฅ ๊ฐ๊น๊ณ , ๋ชจ๋ ๊ฒ์ด **type annotations**์ ๊ธฐ๋ฐํด ์๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+์ฌ์ฉ ์ฌ๋ก์ ๋ฐ๋ผ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ํธํ ์๋ ์์ง๋ง, ์ ๊ฒ ๋ฌป๋๋ค๋ฉด ์๋ง **Strawberry**๋ฅผ ๋จผ์ ์๋ํด ๋ณด๋ผ๊ณ ์ ์ํ ๊ฒ์
๋๋ค.
+
+๋ค์์ Strawberry๋ฅผ FastAPI์ ํตํฉํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๊ฐ๋จํ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์
๋๋ค:
+
+{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
+
+<a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry ๋ฌธ์</a>์์ Strawberry์ ๋ํด ๋ ์์๋ณผ ์ ์์ต๋๋ค.
+
+๋ํ <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">FastAPI์์ Strawberry ์ฌ์ฉ</a>์ ๋ํ ๋ฌธ์๋ ํ์ธํด ๋ณด์ธ์.
+
+## Starlette์ ์์ `GraphQLApp` { #older-graphqlapp-from-starlette }
+
+์ด์ ๋ฒ์ ์ Starlette์๋ <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>๊ณผ ํตํฉํ๊ธฐ ์ํ `GraphQLApp` ํด๋์ค๊ฐ ํฌํจ๋์ด ์์์ต๋๋ค.
+
+์ด๊ฒ์ Starlette์์ deprecated ๋์์ง๋ง, ์ด๋ฅผ ์ฌ์ฉํ๋ ์ฝ๋๊ฐ ์๋ค๋ฉด ๊ฐ์ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ๋ค๋ฃจ๊ณ **๊ฑฐ์ ๋์ผํ ์ธํฐํ์ด์ค**๋ฅผ ๊ฐ์ง <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>๋ก ์ฝ๊ฒ **๋ง์ด๊ทธ๋ ์ด์
**ํ ์ ์์ต๋๋ค.
+
+/// tip | ํ
+
+GraphQL์ด ํ์ํ๋ค๋ฉด, ์ปค์คํ
ํด๋์ค์ ํ์
๋์ type annotations์ ๊ธฐ๋ฐํ <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a>๋ฅผ ์ฌ์ ํ ํ์ธํด ๋ณด์๊ธธ ๊ถ์ฅํฉ๋๋ค.
+
+///
+
+## ๋ ์์๋ณด๊ธฐ { #learn-more }
+
+<a href="https://graphql.org/" class="external-link" target="_blank">๊ณต์ GraphQL ๋ฌธ์</a>์์ **GraphQL**์ ๋ํด ๋ ์์๋ณผ ์ ์์ต๋๋ค.
+
+๋ํ ์์์ ์ค๋ช
ํ ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํด์๋ ํด๋น ๋งํฌ์์ ๋ ์์ธํ ์ฝ์ด๋ณผ ์ ์์ต๋๋ค.
--- /dev/null
+# How To - ๋ ์ํผ { #how-to-recipes }
+
+์ฌ๊ธฐ์์๋ **์ฌ๋ฌ ์ฃผ์ **์ ๋ํ ๋ค์ํ ๋ ์ํผ(โhow toโ ๊ฐ์ด๋)๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
+
+๋๋ถ๋ถ์ ์์ด๋์ด๋ ์ด๋ ์ ๋ **์๋ก ๋
๋ฆฝ์ **์ด๋ฉฐ, ๋๋ถ๋ถ์ ๊ฒฝ์ฐ **์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ**์ ์ง์ ์ ์ฉ๋๋ ๊ฒฝ์ฐ์๋ง ํ์ตํ๋ฉด ๋ฉ๋๋ค.
+
+ํ๋ก์ ํธ์ ํฅ๋ฏธ๋กญ๊ณ ์ ์ฉํด ๋ณด์ด๋ ๊ฒ์ด ์๋ค๋ฉด ํ์ธํด ๋ณด์ธ์. ๊ทธ๋ ์ง ์๋ค๋ฉด ์๋ง ๊ฑด๋๋ฐ์ด๋ ๋ฉ๋๋ค.
+
+/// tip | ํ
+
+**FastAPI๋ฅผ ๊ตฌ์กฐ์ ์ผ๋ก ํ์ต**ํ๊ณ ์ถ๋ค๋ฉด(๊ถ์ฅ), ๋์ [ํํ ๋ฆฌ์ผ - ์ฌ์ฉ์ ๊ฐ์ด๋](../tutorial/index.md){.internal-link target=_blank}๋ฅผ ์ฅ๋ณ๋ก ์ฝ์ด๋ณด์ธ์.
+
+///
--- /dev/null
+# Pydantic v1์์ Pydantic v2๋ก ๋ง์ด๊ทธ๋ ์ด์
ํ๊ธฐ { #migrate-from-pydantic-v1-to-pydantic-v2 }
+
+์ค๋๋ FastAPI ์ฑ์ด ์๋ค๋ฉด Pydantic ๋ฒ์ 1์ ์ฌ์ฉํ๊ณ ์์ ์ ์์ต๋๋ค.
+
+FastAPI 0.100.0 ๋ฒ์ ์ Pydantic v1 ๋๋ v2 ์ค ํ๋๋ฅผ ์ง์ํ์ต๋๋ค. ์ค์น๋์ด ์๋ ์ชฝ์ ์ฌ์ฉํ์ต๋๋ค.
+
+FastAPI 0.119.0 ๋ฒ์ ์์๋ v2๋ก์ ๋ง์ด๊ทธ๋ ์ด์
์ ์ฝ๊ฒ ํ๊ธฐ ์ํด, Pydantic v2 ๋ด๋ถ์์ Pydantic v1์(`pydantic.v1`๋ก) ๋ถ๋ถ์ ์ผ๋ก ์ง์ํ๊ธฐ ์์ํ์ต๋๋ค.
+
+FastAPI 0.126.0 ๋ฒ์ ์์๋ Pydantic v1 ์ง์์ ์ค๋จํ์ง๋ง, `pydantic.v1`์ ์ ์ ๋์ ๊ณ์ ์ง์ํ์ต๋๋ค.
+
+/// warning | ๊ฒฝ๊ณ
+
+Pydantic ํ์ **Python 3.14**๋ถํฐ ์ต์ Python ๋ฒ์ ์์ Pydantic v1 ์ง์์ ์ค๋จํ์ต๋๋ค.
+
+์ฌ๊ธฐ์๋ `pydantic.v1`๋ ํฌํจ๋๋ฉฐ, Python 3.14 ์ด์์์๋ ๋ ์ด์ ์ง์๋์ง ์์ต๋๋ค.
+
+Python์ ์ต์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด Pydantic v2๋ฅผ ์ฌ์ฉํ๊ณ ์๋์ง ํ์ธํด์ผ ํฉ๋๋ค.
+
+///
+
+Pydantic v1์ ์ฌ์ฉํ๋ ์ค๋๋ FastAPI ์ฑ์ด ์๋ค๋ฉด, ์ฌ๊ธฐ์๋ ์ด๋ฅผ Pydantic v2๋ก ๋ง์ด๊ทธ๋ ์ด์
ํ๋ ๋ฐฉ๋ฒ๊ณผ ์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์
์ ๋๋ **FastAPI 0.119.0์ ๊ธฐ๋ฅ**์ ์๊ฐํ๊ฒ ์ต๋๋ค.
+
+## ๊ณต์ ๊ฐ์ด๋ { #official-guide }
+
+Pydantic์๋ v1์์ v2๋ก์ ๊ณต์ <a href="https://docs.pydantic.dev/latest/migration/" class="external-link" target="_blank">Migration Guide</a>๊ฐ ์์ต๋๋ค.
+
+์ฌ๊ธฐ์๋ ๋ฌด์์ด ๋ฐ๋์๋์ง, ๊ฒ์ฆ์ด ์ด์ ์ด๋ป๊ฒ ๋ ์ ํํ๊ณ ์๊ฒฉํด์ก๋์ง, ๊ฐ๋ฅํ ์ฃผ์์ฌํญ ๋ฑ๋ ํฌํจ๋์ด ์์ต๋๋ค.
+
+๋ณ๊ฒฝ๋ ๋ด์ฉ์ ๋ ์ ์ดํดํ๊ธฐ ์ํด ์ฝ์ด๋ณด๋ฉด ์ข์ต๋๋ค.
+
+## ํ
์คํธ { #tests }
+
+์ฑ์ ๋ํ [tests](../tutorial/testing.md){.internal-link target=_blank}๊ฐ ์๋์ง ํ์ธํ๊ณ , ์ง์์ ํตํฉ(CI)์์ ํ
์คํธ๋ฅผ ์คํํ์ธ์.
+
+์ด๋ ๊ฒ ํ๋ฉด ์
๊ทธ๋ ์ด๋๋ฅผ ์งํํ๋ฉด์๋ ๋ชจ๋ ๊ฒ์ด ๊ธฐ๋ํ ๋๋ก ๊ณ์ ๋์ํ๋์ง ํ์ธํ ์ ์์ต๋๋ค.
+
+## `bump-pydantic` { #bump-pydantic }
+
+๋ง์ ๊ฒฝ์ฐ, ์ปค์คํฐ๋ง์ด์ง ์์ด ์ผ๋ฐ์ ์ธ Pydantic ๋ชจ๋ธ์ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด Pydantic v1์์ Pydantic v2๋ก์ ๋ง์ด๊ทธ๋ ์ด์
๊ณผ์ ๋๋ถ๋ถ์ ์๋ํํ ์ ์์ต๋๋ค.
+
+๊ฐ์ Pydantic ํ์ด ์ ๊ณตํ๋ <a href="https://github.com/pydantic/bump-pydantic" class="external-link" target="_blank">`bump-pydantic`</a>๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+์ด ๋๊ตฌ๋ ๋ณ๊ฒฝํด์ผ ํ๋ ์ฝ๋์ ๋๋ถ๋ถ์ ์๋์ผ๋ก ๋ฐ๊พธ๋ ๋ฐ ๋์์ ์ค๋๋ค.
+
+๊ทธ ๋ค์ ํ
์คํธ๋ฅผ ์คํํด์ ๋ชจ๋ ๊ฒ์ด ๋์ํ๋์ง ํ์ธํ๋ฉด ๋ฉ๋๋ค. ์ ๋๋ค๋ฉด ๋์
๋๋ค. ๐
+
+## v2 ์์ Pydantic v1 { #pydantic-v1-in-v2 }
+
+Pydantic v2๋ Pydantic v1์ ๋ชจ๋ ๊ฒ์ ์๋ธ๋ชจ๋ `pydantic.v1`๋ก ํฌํจํฉ๋๋ค. ํ์ง๋ง ์ด๋ Python 3.13๋ณด๋ค ๋์ ๋ฒ์ ์์๋ ๋ ์ด์ ์ง์๋์ง ์์ต๋๋ค.
+
+์ฆ, Pydantic v2์ ์ต์ ๋ฒ์ ์ ์ค์นํ ๋ค, ์ด ์๋ธ๋ชจ๋์์ ์์ Pydantic v1 ๊ตฌ์ฑ ์์๋ฅผ importํ์ฌ ์์ Pydantic v1์ ์ค์นํ ๊ฒ์ฒ๋ผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
+
+### v2 ์์ Pydantic v1์ ๋ํ FastAPI ์ง์ { #fastapi-support-for-pydantic-v1-in-v2 }
+
+FastAPI 0.119.0๋ถํฐ๋ v2๋ก์ ๋ง์ด๊ทธ๋ ์ด์
์ ์ฝ๊ฒ ํ๊ธฐ ์ํด, Pydantic v2 ๋ด๋ถ์ Pydantic v1์ ๋ํด์๋ ๋ถ๋ถ์ ์ธ ์ง์์ด ์์ต๋๋ค.
+
+๋ฐ๋ผ์ Pydantic์ ์ต์ v2๋ก ์
๊ทธ๋ ์ด๋ํ๊ณ , import๋ฅผ `pydantic.v1` ์๋ธ๋ชจ๋์ ์ฌ์ฉํ๋๋ก ๋ฐ๊พธ๋ฉด, ๋ง์ ๊ฒฝ์ฐ ๊ทธ๋๋ก ๋์ํฉ๋๋ค.
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
+
+/// warning | ๊ฒฝ๊ณ
+
+Pydantic ํ์ด Python 3.14๋ถํฐ ์ต์ Python ๋ฒ์ ์์ Pydantic v1์ ๋ ์ด์ ์ง์ํ์ง ์์ผ๋ฏ๋ก, `pydantic.v1`์ ์ฌ์ฉํ๋ ๊ฒ ์ญ์ Python 3.14 ์ด์์์๋ ์ง์๋์ง ์๋๋ค๋ ์ ์ ์ผ๋์ ๋์ธ์.
+
+///
+
+### ๊ฐ์ ์ฑ์์ Pydantic v1๊ณผ v2 ํจ๊ป ์ฌ์ฉํ๊ธฐ { #pydantic-v1-and-v2-on-the-same-app }
+
+Pydantic์์๋ Pydantic v2 ๋ชจ๋ธ์ ํ๋๋ฅผ Pydantic v1 ๋ชจ๋ธ๋ก ์ ์ํ๊ฑฐ๋ ๊ทธ ๋ฐ๋๋ก ํ๋ ๊ฒ์ **์ง์ํ์ง ์์ต๋๋ค**.
+
+```mermaid
+graph TB
+ subgraph "โ Not Supported"
+ direction TB
+ subgraph V2["Pydantic v2 Model"]
+ V1Field["Pydantic v1 Model"]
+ end
+ subgraph V1["Pydantic v1 Model"]
+ V2Field["Pydantic v2 Model"]
+ end
+ end
+
+ style V2 fill:#f9fff3
+ style V1 fill:#fff6f0
+ style V1Field fill:#fff6f0
+ style V2Field fill:#f9fff3
+```
+
+...ํ์ง๋ง ๊ฐ์ ์ฑ์์ Pydantic v1๊ณผ v2๋ฅผ ์ฌ์ฉํ๋, ๋ชจ๋ธ์ ๋ถ๋ฆฌํด์ ๋ ์๋ ์์ต๋๋ค.
+
+```mermaid
+graph TB
+ subgraph "โ
Supported"
+ direction TB
+ subgraph V2["Pydantic v2 Model"]
+ V2Field["Pydantic v2 Model"]
+ end
+ subgraph V1["Pydantic v1 Model"]
+ V1Field["Pydantic v1 Model"]
+ end
+ end
+
+ style V2 fill:#f9fff3
+ style V1 fill:#fff6f0
+ style V1Field fill:#fff6f0
+ style V2Field fill:#f9fff3
+```
+
+์ด๋ค ๊ฒฝ์ฐ์๋ FastAPI ์ฑ์ ๊ฐ์ **๊ฒฝ๋ก ์ฒ๋ฆฌ**์์ Pydantic v1๊ณผ v2 ๋ชจ๋ธ์ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ๋ ๊ฐ๋ฅํฉ๋๋ค:
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
+
+์ ์์ ์์ ์
๋ ฅ ๋ชจ๋ธ์ Pydantic v1 ๋ชจ๋ธ์ด๊ณ , ์ถ๋ ฅ ๋ชจ๋ธ(`response_model=ItemV2`๋ก ์ ์๋จ)์ Pydantic v2 ๋ชจ๋ธ์
๋๋ค.
+
+### Pydantic v1 ํ๋ผ๋ฏธํฐ { #pydantic-v1-parameters }
+
+Pydantic v1 ๋ชจ๋ธ๊ณผ ํจ๊ป `Body`, `Query`, `Form` ๋ฑ ํ๋ผ๋ฏธํฐ์ฉ FastAPI ์ ์ฉ ๋๊ตฌ ์ผ๋ถ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค๋ฉด, Pydantic v2๋ก์ ๋ง์ด๊ทธ๋ ์ด์
์ ๋ง์น ๋๊น์ง `fastapi.temp_pydantic_v1_params`์์ importํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
+
+### ๋จ๊ณ์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์
ํ๊ธฐ { #migrate-in-steps }
+
+/// tip | ํ
+
+๋จผ์ `bump-pydantic`๋ก ์๋ํด ๋ณด์ธ์. ํ
์คํธ๊ฐ ํต๊ณผํ๊ณ ์ ๋์ํ๋ค๋ฉด, ํ ๋ฒ์ ๋ช
๋ น์ผ๋ก ๋์
๋๋ค. โจ
+
+///
+
+`bump-pydantic`๊ฐ ์ฌ๋ฌ๋ถ์ ์ฌ์ฉ ์ฌ๋ก์ ๋ง์ง ์๋๋ค๋ฉด, ๊ฐ์ ์ฑ์์ Pydantic v1๊ณผ v2 ๋ชจ๋ธ์ ๋ชจ๋ ์ง์ํ๋ ๊ธฐ๋ฅ์ ์ด์ฉํด Pydantic v2๋ก ์ ์ง์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์
ํ ์ ์์ต๋๋ค.
+
+๋จผ์ Pydantic์ ์ต์ v2๋ก ์
๊ทธ๋ ์ด๋ํ๊ณ , ๋ชจ๋ ๋ชจ๋ธ์ import๋ฅผ `pydantic.v1`์ ์ฌ์ฉํ๋๋ก ๋ฐ๊ฟ ์ ์์ต๋๋ค.
+
+๊ทธ ๋ค์ Pydantic v1์์ v2๋ก ๋ชจ๋ธ์ ๊ทธ๋ฃน ๋จ์๋ก, ์ ์ง์ ์ธ ๋จ๊ณ๋ก ๋ง์ด๊ทธ๋ ์ด์
์ ์์ํ๋ฉด ๋ฉ๋๋ค. ๐ถ
--- /dev/null
+# ์
๋ ฅ๊ณผ ์ถ๋ ฅ์ ๋ํด OpenAPI ์คํค๋ง๋ฅผ ๋ถ๋ฆฌํ ์ง ์ฌ๋ถ { #separate-openapi-schemas-for-input-and-output-or-not }
+
+**Pydantic v2**๊ฐ ๋ฆด๋ฆฌ์ค๋ ์ดํ, ์์ฑ๋๋ OpenAPI๋ ์ด์ ๋ณด๋ค ์กฐ๊ธ ๋ ์ ํํ๊ณ **์ฌ๋ฐ๋ฅด๊ฒ** ๋ง๋ค์ด์ง๋๋ค. ๐
+
+์ค์ ๋ก ์ด๋ค ๊ฒฝ์ฐ์๋, ๊ฐ์ Pydantic ๋ชจ๋ธ์ ๋ํด OpenAPI ์์ **๋ ๊ฐ์ JSON Schema**๊ฐ ์๊ธฐ๊ธฐ๋ ํฉ๋๋ค. **๊ธฐ๋ณธ๊ฐ(default value)**์ด ์๋์ง ์ฌ๋ถ์ ๋ฐ๋ผ, ์
๋ ฅ์ฉ๊ณผ ์ถ๋ ฅ์ฉ์ผ๋ก ๋๋ฉ๋๋ค.
+
+์ด๊ฒ์ด ์ด๋ป๊ฒ ๋์ํ๋์ง, ๊ทธ๋ฆฌ๊ณ ํ์ํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ณ๊ฒฝํ ์ ์๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
+
+## ์
๋ ฅ๊ณผ ์ถ๋ ฅ์ ์ํ Pydantic ๋ชจ๋ธ { #pydantic-models-for-input-and-output }
+
+์๋ฅผ ๋ค์ด, ๋ค์์ฒ๋ผ ๊ธฐ๋ณธ๊ฐ์ด ์๋ Pydantic ๋ชจ๋ธ์ด ์๋ค๊ณ ํด๋ณด๊ฒ ์ต๋๋ค:
+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
+
+### ์
๋ ฅ์ฉ ๋ชจ๋ธ { #model-for-input }
+
+์ด ๋ชจ๋ธ์ ๋ค์์ฒ๋ผ ์
๋ ฅ์ผ๋ก ์ฌ์ฉํ๋ฉด:
+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
+
+...`description` ํ๋๋ **ํ์๊ฐ ์๋๋๋ค**. `None`์ด๋ผ๋ ๊ธฐ๋ณธ๊ฐ์ด ์๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+### ๋ฌธ์์์์ ์
๋ ฅ ๋ชจ๋ธ { #input-model-in-docs }
+
+๋ฌธ์์์ `description` ํ๋์ **๋นจ๊ฐ ๋ณํ**๊ฐ ์๊ณ , ํ์๋ก ํ์๋์ง ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค:
+
+<div class="screenshot">
+<img src="/img/tutorial/separate-openapi-schemas/image01.png">
+</div>
+
+### ์ถ๋ ฅ์ฉ ๋ชจ๋ธ { #model-for-output }
+
+ํ์ง๋ง ๊ฐ์ ๋ชจ๋ธ์ ๋ค์์ฒ๋ผ ์ถ๋ ฅ์ผ๋ก ์ฌ์ฉํ๋ฉด:
+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
+
+...`description`์ ๊ธฐ๋ณธ๊ฐ์ด ์๊ธฐ ๋๋ฌธ์, ๊ทธ ํ๋์ ๋ํด **์๋ฌด๊ฒ๋ ๋ฐํํ์ง ์๋๋ผ๋** ์ฌ์ ํ ๊ทธ **๊ธฐ๋ณธ๊ฐ**์ด ๋ค์ด๊ฐ๊ฒ ๋ฉ๋๋ค.
+
+### ์ถ๋ ฅ ์๋ต ๋ฐ์ดํฐ์ฉ ๋ชจ๋ธ { #model-for-output-response-data }
+
+๋ฌธ์์์ ์ง์ ๋์์์ผ ์๋ต์ ํ์ธํด ๋ณด๋ฉด, ์ฝ๋๊ฐ `description` ํ๋ ์ค ํ๋์ ์๋ฌด๊ฒ๋ ์ถ๊ฐํ์ง ์์๋๋ผ๋ JSON ์๋ต์๋ ๊ธฐ๋ณธ๊ฐ(`null`)์ด ํฌํจ๋์ด ์์ต๋๋ค:
+
+<div class="screenshot">
+<img src="/img/tutorial/separate-openapi-schemas/image02.png">
+</div>
+
+์ด๋ ํด๋น ํ๋๊ฐ **ํญ์ ๊ฐ์ ๊ฐ์ง๋ค๋ ๊ฒ**์ ์๋ฏธํฉ๋๋ค. ๋ค๋ง ๊ทธ ๊ฐ์ด ๋๋ก๋ `None`(JSON์์๋ `null`)์ผ ์ ์์ต๋๋ค.
+
+์ฆ, API๋ฅผ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ๋ ๊ฐ์ด ์กด์ฌํ๋์ง ์ฌ๋ถ๋ฅผ ํ์ธํ ํ์๊ฐ ์๊ณ , **ํ๋๊ฐ ํญ์ ์กด์ฌํ๋ค๊ณ ๊ฐ์ **ํ ์ ์์ต๋๋ค. ๋ค๋ง ์ด๋ค ๊ฒฝ์ฐ์๋ ๊ธฐ๋ณธ๊ฐ `None`์ด ๋ค์ด๊ฐ๋๋ค.
+
+์ด๋ฅผ OpenAPI์์ ํํํ๋ ๋ฐฉ๋ฒ์, ๊ทธ ํ๋๋ฅผ **required**๋ก ํ์ํ๋ ๊ฒ์
๋๋ค. ํญ์ ์กด์ฌํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+์ด ๋๋ฌธ์, ํ๋์ ๋ชจ๋ธ์ด๋ผ๋ **์
๋ ฅ์ฉ์ธ์ง ์ถ๋ ฅ์ฉ์ธ์ง**์ ๋ฐ๋ผ JSON Schema๊ฐ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค:
+
+* **์
๋ ฅ**์์๋ `description`์ด **ํ์๊ฐ ์๋**
+* **์ถ๋ ฅ**์์๋ **ํ์์** (๊ทธ๋ฆฌ๊ณ ๊ฐ์ `None`์ผ ์๋ ์์ผ๋ฉฐ, JSON ์ฉ์ด๋ก๋ `null`)
+
+### ๋ฌธ์์์์ ์ถ๋ ฅ์ฉ ๋ชจ๋ธ { #model-for-output-in-docs }
+
+๋ฌธ์์์ ์ถ๋ ฅ ๋ชจ๋ธ์ ํ์ธํด ๋ณด๋ฉด, `name`๊ณผ `description` **๋ ๋ค** **๋นจ๊ฐ ๋ณํ**๋ก **ํ์**๋ก ํ์๋์ด ์์ต๋๋ค:
+
+<div class="screenshot">
+<img src="/img/tutorial/separate-openapi-schemas/image03.png">
+</div>
+
+### ๋ฌธ์์์์ ์
๋ ฅ๊ณผ ์ถ๋ ฅ ๋ชจ๋ธ { #model-for-input-and-output-in-docs }
+
+๋ OpenAPI์์ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ Schemas(JSON Schemas)๋ฅผ ํ์ธํด ๋ณด๋ฉด, `Item-Input` ํ๋์ `Item-Output` ํ๋, ์ด๋ ๊ฒ ๋ ๊ฐ๊ฐ ์๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
+
+`Item-Input`์์๋ `description`์ด **ํ์๊ฐ ์๋๋ฉฐ**, ๋นจ๊ฐ ๋ณํ๊ฐ ์์ต๋๋ค.
+
+ํ์ง๋ง `Item-Output`์์๋ `description`์ด **ํ์์ด๋ฉฐ**, ๋นจ๊ฐ ๋ณํ๊ฐ ์์ต๋๋ค.
+
+<div class="screenshot">
+<img src="/img/tutorial/separate-openapi-schemas/image04.png">
+</div>
+
+**Pydantic v2**์ ์ด ๊ธฐ๋ฅ ๋๋ถ์ API ๋ฌธ์๋ ๋ **์ ๋ฐ**ํด์ง๊ณ , ์๋ ์์ฑ๋ ํด๋ผ์ด์ธํธ์ SDK๊ฐ ์๋ค๋ฉด ๊ทธ๊ฒ๋ค๋ ๋ ์ ๋ฐํด์ ธ์ ๋ ๋์ **developer experience**์ ์ผ๊ด์ฑ์ ์ ๊ณตํ ์ ์์ต๋๋ค. ๐
+
+## ์คํค๋ง๋ฅผ ๋ถ๋ฆฌํ์ง ์๊ธฐ { #do-not-separate-schemas }
+
+์ด์ ์ด๋ค ๊ฒฝ์ฐ์๋ **์
๋ ฅ๊ณผ ์ถ๋ ฅ์ ๋ํด ๊ฐ์ ์คํค๋ง๋ฅผ ์ฌ์ฉ**ํ๊ณ ์ถ์ ์๋ ์์ต๋๋ค.
+
+๊ฐ์ฅ ๋ํ์ ์ธ ๊ฒฝ์ฐ๋, ์ด๋ฏธ ์๋ ์์ฑ๋ ํด๋ผ์ด์ธํธ ์ฝ๋/SDK๊ฐ ์๊ณ , ์์ง์ ๊ทธ ์๋ ์์ฑ๋ ํด๋ผ์ด์ธํธ ์ฝ๋/SDK๋ค์ ์ ๋ถ ์
๋ฐ์ดํธํ๊ณ ์ถ์ง ์์ ๊ฒฝ์ฐ์
๋๋ค. ์ธ์ ๊ฐ๋ ์
๋ฐ์ดํธํด์ผ ํ ๊ฐ๋ฅ์ฑ์ด ๋์ง๋ง, ์ง๊ธ ๋น์ฅ์ ์๋ ์๋ ์์ต๋๋ค.
+
+๊ทธ๋ฐ ๊ฒฝ์ฐ์๋, **FastAPI**์์ `separate_input_output_schemas=False` ํ๋ผ๋ฏธํฐ๋ก ์ด ๊ธฐ๋ฅ์ ๋นํ์ฑํํ ์ ์์ต๋๋ค.
+
+/// info | ์ ๋ณด
+
+`separate_input_output_schemas` ์ง์์ FastAPI `0.102.0`์ ์ถ๊ฐ๋์์ต๋๋ค. ๐ค
+
+///
+
+{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
+
+### ๋ฌธ์์์ ์
๋ ฅ๊ณผ ์ถ๋ ฅ ๋ชจ๋ธ์ ๊ฐ์ ์คํค๋ง ์ฌ์ฉ { #same-schema-for-input-and-output-models-in-docs }
+
+์ด์ ๋ชจ๋ธ์ ๋ํด ์
๋ ฅ๊ณผ ์ถ๋ ฅ ๋ชจ๋์ ์ฌ์ฉ๋๋ ๋จ์ผ ์คํค๋ง(์ค์ง `Item`๋ง)๊ฐ ์์ฑ๋๋ฉฐ, `description`์ **ํ์๊ฐ ์๋ ๊ฒ**์ผ๋ก ํ์๋ฉ๋๋ค:
+
+<div class="screenshot">
+<img src="/img/tutorial/separate-openapi-schemas/image05.png">
+</div>
--- /dev/null
+# ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์คํธํ๊ธฐ { #testing-a-database }
+
+๋ฐ์ดํฐ๋ฒ ์ด์ค, SQL, SQLModel์ ๋ํด์๋ <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel ๋ฌธ์</a>์์ ํ์ตํ ์ ์์ต๋๋ค. ๐ค
+
+<a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">FastAPI์์ SQLModel์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋ฏธ๋ ํํ ๋ฆฌ์ผ</a>๋ ์์ต๋๋ค. โจ
+
+ํด๋น ํํ ๋ฆฌ์ผ์๋ <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">SQL ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์คํธ</a>์ ๋ํ ์น์
๋ ํฌํจ๋์ด ์์ต๋๋ค. ๐
--- /dev/null
+# ๋ ํฐ ์ ํ๋ฆฌ์ผ์ด์
- ์ฌ๋ฌ ํ์ผ { #bigger-applications-multiple-files }
+
+์ ํ๋ฆฌ์ผ์ด์
์ด๋ ์น API๋ฅผ ๋ง๋ค ๋, ๋ชจ๋ ๊ฒ์ ํ๋์ ํ์ผ์ ๋ด์ ์ ์๋ ๊ฒฝ์ฐ๋ ๋๋ญ
๋๋ค.
+
+**FastAPI**๋ ๋ชจ๋ ์ ์ฐ์ฑ์ ์ ์งํ๋ฉด์๋ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์กฐํํ ์ ์๊ฒ ํด์ฃผ๋ ํธ๋ฆฌํ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
+
+/// info | ์ ๋ณด
+
+Flask๋ฅผ ์ฌ์ฉํด ๋ณด์
จ๋ค๋ฉด, ์ด๋ Flask์ Blueprints์ ํด๋นํ๋ ๊ฐ๋
์
๋๋ค.
+
+///
+
+## ์์ ํ์ผ ๊ตฌ์กฐ { #an-example-file-structure }
+
+๋ค์๊ณผ ๊ฐ์ ํ์ผ ๊ตฌ์กฐ๊ฐ ์๋ค๊ณ ํด๋ด
์๋ค:
+
+```
+.
+โโโ app
+โย ย โโโ __init__.py
+โย ย โโโ main.py
+โย ย โโโ dependencies.py
+โย ย โโโ routers
+โย ย โ โโโ __init__.py
+โย ย โ โโโ items.py
+โย ย โ โโโ users.py
+โย ย โโโ internal
+โย ย โโโ __init__.py
+โย ย โโโ admin.py
+```
+
+/// tip | ํ
+
+`__init__.py` ํ์ผ์ด ์ฌ๋ฌ ๊ฐ ์์ต๋๋ค: ๊ฐ ๋๋ ํฐ๋ฆฌ ๋๋ ํ์ ๋๋ ํฐ๋ฆฌ์ ํ๋์ฉ ์์ต๋๋ค.
+
+์ด ํ์ผ๋ค์ด ํ ํ์ผ์ ์ฝ๋๋ฅผ ๋ค๋ฅธ ํ์ผ๋ก importํ ์ ์๊ฒ ํด์ค๋๋ค.
+
+์๋ฅผ ๋ค์ด `app/main.py`์๋ ๋ค์๊ณผ ๊ฐ์ ์ค์ด ์์ ์ ์์ต๋๋ค:
+
+```
+from app.routers import items
+```
+
+///
+
+* `app` ๋๋ ํฐ๋ฆฌ์๋ ๋ชจ๋ ๊ฒ์ด ๋ค์ด ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋น์ด ์๋ ํ์ผ `app/__init__.py`๊ฐ ์์ด "Python package"(โPython modulesโ์ ๋ชจ์)์ธ `app`์ด ๋ฉ๋๋ค.
+* `app/main.py` ํ์ผ์ด ์์ต๋๋ค. Python package(`__init__.py` ํ์ผ์ด ์๋ ๋๋ ํฐ๋ฆฌ) ์์ ์์ผ๋ฏ๋ก, ์ด package์ "module"์
๋๋ค: `app.main`.
+* `app/dependencies.py` ํ์ผ๋ ์์ต๋๋ค. `app/main.py`์ ๋ง์ฐฌ๊ฐ์ง๋ก "module"์
๋๋ค: `app.dependencies`.
+* `app/routers/` ํ์ ๋๋ ํฐ๋ฆฌ๊ฐ ์๊ณ , ์ฌ๊ธฐ์ ๋ `__init__.py` ํ์ผ์ด ์์ผ๋ฏ๋ก "Python subpackage"์
๋๋ค: `app.routers`.
+* `app/routers/items.py` ํ์ผ์ `app/routers/` package ์์ ์์ผ๋ฏ๋ก, submodule์
๋๋ค: `app.routers.items`.
+* `app/routers/users.py`๋ ๋์ผํ๊ฒ ๋ ๋ค๋ฅธ submodule์
๋๋ค: `app.routers.users`.
+* `app/internal/` ํ์ ๋๋ ํฐ๋ฆฌ๋ ์๊ณ ์ฌ๊ธฐ์ `__init__.py`๊ฐ ์์ผ๋ฏ๋ก ๋ ๋ค๋ฅธ "Python subpackage"์
๋๋ค: `app.internal`.
+* ๊ทธ๋ฆฌ๊ณ `app/internal/admin.py` ํ์ผ์ ๋ ๋ค๋ฅธ submodule์
๋๋ค: `app.internal.admin`.
+
+<img src="/img/tutorial/bigger-applications/package.drawio.svg">
+
+๊ฐ์ ํ์ผ ๊ตฌ์กฐ์ ์ฃผ์์ ์ถ๊ฐํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+```bash
+.
+โโโ app # "app" is a Python package
+โย ย โโโ __init__.py # this file makes "app" a "Python package"
+โย ย โโโ main.py # "main" module, e.g. import app.main
+โย ย โโโ dependencies.py # "dependencies" module, e.g. import app.dependencies
+โย ย โโโ routers # "routers" is a "Python subpackage"
+โย ย โ โโโ __init__.py # makes "routers" a "Python subpackage"
+โย ย โ โโโ items.py # "items" submodule, e.g. import app.routers.items
+โย ย โ โโโ users.py # "users" submodule, e.g. import app.routers.users
+โย ย โโโ internal # "internal" is a "Python subpackage"
+โย ย โโโ __init__.py # makes "internal" a "Python subpackage"
+โย ย โโโ admin.py # "admin" submodule, e.g. import app.internal.admin
+```
+
+## `APIRouter` { #apirouter }
+
+์ฌ์ฉ์๋ง ์ฒ๋ฆฌํ๋ ์ ์ฉ ํ์ผ์ด `/app/routers/users.py`์ submodule์ด๋ผ๊ณ ํด๋ด
์๋ค.
+
+์ฝ๋๋ฅผ ์ ๋ฆฌํ๊ธฐ ์ํด ์ฌ์ฉ์์ ๊ด๋ จ๋ *path operations*๋ฅผ ๋๋จธ์ง ์ฝ๋์ ๋ถ๋ฆฌํด ๋๊ณ ์ถ์ ๊ฒ์
๋๋ค.
+
+ํ์ง๋ง ์ด๊ฒ์ ์ฌ์ ํ ๊ฐ์ **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
/์น API์ ์ผ๋ถ์
๋๋ค(๊ฐ์ "Python Package"์ ์ผ๋ถ์
๋๋ค).
+
+`APIRouter`๋ฅผ ์ฌ์ฉํด ํด๋น ๋ชจ๋์ *path operations*๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
+
+### `APIRouter` importํ๊ธฐ { #import-apirouter }
+
+`FastAPI` ํด๋์ค์ ๋์ผํ ๋ฐฉ์์ผ๋ก importํ๊ณ "instance"๋ฅผ ์์ฑํฉ๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
+
+### `APIRouter`๋ก *path operations* ๋ง๋ค๊ธฐ { #path-operations-with-apirouter }
+
+๊ทธ ๋ค์ ์ด๋ฅผ ์ฌ์ฉํด *path operations*๋ฅผ ์ ์ธํฉ๋๋ค.
+
+`FastAPI` ํด๋์ค๋ฅผ ์ฌ์ฉํ ๋์ ๋์ผํ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํฉ๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
+
+`APIRouter`๋ "๋ฏธ๋ `FastAPI`" ํด๋์ค๋ผ๊ณ ์๊ฐํ ์ ์์ต๋๋ค.
+
+๋์ผํ ์ต์
๋ค์ด ๋ชจ๋ ์ง์๋ฉ๋๋ค.
+
+๋์ผํ `parameters`, `responses`, `dependencies`, `tags` ๋ฑ๋ฑ.
+
+/// tip | ํ
+
+์ด ์์์์๋ ๋ณ์ ์ด๋ฆ์ด `router`์ด์ง๋ง, ์ํ๋ ์ด๋ฆ์ผ๋ก ์ง์ด๋ ๋ฉ๋๋ค.
+
+///
+
+์ด์ ์ด `APIRouter`๋ฅผ ๋ฉ์ธ `FastAPI` ์ฑ์ ํฌํจ(include)ํ ๊ฒ์ด์ง๋ง, ๋จผ์ dependencies์ ๋ค๋ฅธ `APIRouter` ํ๋๋ฅผ ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
+
+## Dependencies { #dependencies }
+
+์ ํ๋ฆฌ์ผ์ด์
์ ์ฌ๋ฌ ์์น์์ ์ฌ์ฉ๋๋ dependencies๊ฐ ์ผ๋ถ ํ์ํ๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
+
+๊ทธ๋์ ์ด๋ฅผ ๋ณ๋์ `dependencies` ๋ชจ๋(`app/dependencies.py`)์ ๋ก๋๋ค.
+
+์ด์ ๊ฐ๋จํ dependency๋ฅผ ์ฌ์ฉํด ์ปค์คํ
`X-Token` ํค๋๋ฅผ ์ฝ์ด ๋ณด๊ฒ ์ต๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
+
+/// tip | ํ
+
+์ด ์์๋ฅผ ๋จ์ํํ๊ธฐ ์ํด ์์๋ก ๋ง๋ ํค๋๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
+
+ํ์ง๋ง ์ค์ ์ํฉ์์๋ ํตํฉ๋ [Security ์ ํธ๋ฆฌํฐ](security/index.md){.internal-link target=_blank}๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
+
+///
+
+## `APIRouter`๊ฐ ์๋ ๋ ๋ค๋ฅธ ๋ชจ๋ { #another-module-with-apirouter }
+
+์ ํ๋ฆฌ์ผ์ด์
์ "items"๋ฅผ ์ฒ๋ฆฌํ๋ ์ ์ฉ endpoint๋ค๋ `app/routers/items.py` ๋ชจ๋์ ์๋ค๊ณ ํด๋ด
์๋ค.
+
+์ฌ๊ธฐ์๋ ๋ค์์ ๋ํ *path operations*๊ฐ ์์ต๋๋ค:
+
+* `/items/`
+* `/items/{item_id}`
+
+๊ตฌ์กฐ๋ `app/routers/users.py`์ ์์ ํ ๋์ผํฉ๋๋ค.
+
+ํ์ง๋ง ์ฐ๋ฆฌ๋ ์กฐ๊ธ ๋ ๋๋ํ๊ฒ, ์ฝ๋๋ฅผ ์ฝ๊ฐ ๋จ์ํํ๊ณ ์ถ์ต๋๋ค.
+
+์ด ๋ชจ๋์ ๋ชจ๋ *path operations*์๋ ๋ค์์ด ๋์ผํ๊ฒ ์ ์ฉ๋ฉ๋๋ค:
+
+* ๊ฒฝ๋ก `prefix`: `/items`.
+* `tags`: (ํ๊ทธ ํ๋: `items`).
+* ์ถ๊ฐ `responses`.
+* `dependencies`: ๋ชจ๋ ์ฐ๋ฆฌ๊ฐ ๋ง๋ `X-Token` dependency๊ฐ ํ์ํฉ๋๋ค.
+
+๋ฐ๋ผ์ ๊ฐ *path operation*๋ง๋ค ๋งค๋ฒ ๋ชจ๋ ์ถ๊ฐํ๋ ๋์ , `APIRouter`์ ํ ๋ฒ์ ์ถ๊ฐํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
+
+๊ฐ *path operation*์ ๊ฒฝ๋ก๋ ๋ค์์ฒ๋ผ `/`๋ก ์์ํด์ผ ํ๋ฏ๋ก:
+
+```Python hl_lines="1"
+@router.get("/{item_id}")
+async def read_item(item_id: str):
+ ...
+```
+
+...prefix์๋ ๋ง์ง๋ง `/`๊ฐ ํฌํจ๋๋ฉด ์ ๋ฉ๋๋ค.
+
+๋ฐ๋ผ์ ์ด ๊ฒฝ์ฐ prefix๋ `/items`์
๋๋ค.
+
+๋ํ ์ด router์ ํฌํจ๋ ๋ชจ๋ *path operations*์ ์ ์ฉ๋ `tags` ๋ชฉ๋ก๊ณผ ์ถ๊ฐ `responses`๋ ๋ฃ์ ์ ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ router์ ๋ชจ๋ *path operations*์ ์ถ๊ฐ๋ `dependencies` ๋ชฉ๋ก๋ ์ถ๊ฐํ ์ ์์ผ๋ฉฐ, ํด๋น ๊ฒฝ๋ก๋ค๋ก ๋ค์ด์ค๋ ๊ฐ ์์ฒญ๋ง๋ค ์คํ/ํด๊ฒฐ๋ฉ๋๋ค.
+
+/// tip | ํ
+
+[*path operation decorator์ dependencies*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}์ ๋ง์ฐฌ๊ฐ์ง๋ก, *path operation function*์ ์ด๋ค ๊ฐ๋ ์ ๋ฌ๋์ง ์์ต๋๋ค.
+
+///
+
+์ต์ข
์ ์ผ๋ก item ๊ฒฝ๋ก๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* `/items/`
+* `/items/{item_id}`
+
+...์๋ํ ๊ทธ๋๋ก์
๋๋ค.
+
+* ๋จ์ผ ๋ฌธ์์ด `"items"`๋ฅผ ํฌํจํ๋ ํ๊ทธ ๋ชฉ๋ก์ผ๋ก ํ์๋ฉ๋๋ค.
+ * ์ด "tags"๋ ์๋ ๋ํํ ๋ฌธ์ ์์คํ
(OpenAPI ์ฌ์ฉ)์ ํนํ ์ ์ฉํฉ๋๋ค.
+* ๋ชจ๋ ๋ฏธ๋ฆฌ ์ ์๋ `responses`๋ฅผ ํฌํจํฉ๋๋ค.
+* ์ด ๋ชจ๋ *path operations*๋ ์คํ๋๊ธฐ ์ ์ `dependencies` ๋ชฉ๋ก์ด ํ๊ฐ/์คํ๋ฉ๋๋ค.
+ * ํน์ *path operation*์ dependencies๋ฅผ ์ถ๊ฐ๋ก ์ ์ธํ๋ฉด **๊ทธ๊ฒ๋ค๋ ์คํ๋ฉ๋๋ค**.
+ * router dependencies๊ฐ ๋จผ์ ์คํ๋๊ณ , ๊ทธ ๋ค์์ [decorator์ `dependencies`](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, ๊ทธ๋ฆฌ๊ณ ์ผ๋ฐ ํ๋ผ๋ฏธํฐ dependencies๊ฐ ์คํ๋ฉ๋๋ค.
+ * [`scopes`๊ฐ ์๋ `Security` dependencies](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}๋ ์ถ๊ฐํ ์ ์์ต๋๋ค.
+
+/// tip | ํ
+
+`APIRouter`์ `dependencies`๋ฅผ ๋๋ ๊ฒ์ ์๋ฅผ ๋ค์ด ์ ์ฒด *path operations* ๊ทธ๋ฃน์ ์ธ์ฆ์ ์๊ตฌํ ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ฐ ๊ฒฝ๋ก ์ฒ๋ฆฌ์ ๊ฐ๋ณ์ ์ผ๋ก dependencies๋ฅผ ์ถ๊ฐํ์ง ์์๋ ๋ฉ๋๋ค.
+
+///
+
+/// check | ํ์ธ
+
+`prefix`, `tags`, `responses`, `dependencies` ํ๋ผ๋ฏธํฐ๋ (๋ค๋ฅธ ๋ง์ ๊ฒฝ์ฐ์ ๋ง์ฐฌ๊ฐ์ง๋ก) ์ฝ๋ ์ค๋ณต์ ํผํ๋๋ก ๋์์ฃผ๋ **FastAPI**์ ๊ธฐ๋ฅ์
๋๋ค.
+
+///
+
+### dependencies importํ๊ธฐ { #import-the-dependencies }
+
+์ด ์ฝ๋๋ ๋ชจ๋ `app.routers.items`, ํ์ผ `app/routers/items.py`์ ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ dependency ํจ์๋ ๋ชจ๋ `app.dependencies`, ํ์ผ `app/dependencies.py`์์ ๊ฐ์ ธ์์ผ ํฉ๋๋ค.
+
+๊ทธ๋์ dependencies์ ๋ํด `..`๋ฅผ ์ฌ์ฉํ๋ ์๋ import๋ฅผ ์ฌ์ฉํฉ๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
+
+#### ์๋ import๊ฐ ๋์ํ๋ ๋ฐฉ์ { #how-relative-imports-work }
+
+/// tip | ํ
+
+import๊ฐ ๋์ํ๋ ๋ฐฉ์์ ์๋ฒฝํ ์๊ณ ์๋ค๋ฉด, ์๋ ๋ค์ ์น์
์ผ๋ก ๋์ด๊ฐ์ธ์.
+
+///
+
+๋ค์๊ณผ ๊ฐ์ด ์ ํ๋ `.`๋ฅผ ์ฐ๋ฉด:
+
+```Python
+from .dependencies import get_token_header
+```
+
+์๋ฏธ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* ์ด ๋ชจ๋(ํ์ผ `app/routers/items.py`)์ด ์ํ ๊ฐ์ package(๋๋ ํฐ๋ฆฌ `app/routers/`)์์ ์์ํด์...
+* `dependencies` ๋ชจ๋(๊ฐ์์ ํ์ผ `app/routers/dependencies.py`)์ ์ฐพ๊ณ ...
+* ๊ทธ ์์์ ํจ์ `get_token_header`๋ฅผ importํฉ๋๋ค.
+
+ํ์ง๋ง ๊ทธ ํ์ผ์ ์กด์ฌํ์ง ์์ต๋๋ค. dependencies๋ `app/dependencies.py` ํ์ผ์ ์์ต๋๋ค.
+
+์ฐ๋ฆฌ ์ฑ/ํ์ผ ๊ตฌ์กฐ๋ฅผ ๋ค์ ๋ ์ฌ๋ ค ๋ณด์ธ์:
+
+<img src="/img/tutorial/bigger-applications/package.drawio.svg">
+
+---
+
+๋ค์์ฒ๋ผ ์ ๋ ๊ฐ `..`๋ฅผ ์ฐ๋ฉด:
+
+```Python
+from ..dependencies import get_token_header
+```
+
+์๋ฏธ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* ์ด ๋ชจ๋(ํ์ผ `app/routers/items.py`)์ด ์ํ ๊ฐ์ package(๋๋ ํฐ๋ฆฌ `app/routers/`)์์ ์์ํด์...
+* ์์ package(๋๋ ํฐ๋ฆฌ `app/`)๋ก ์ฌ๋ผ๊ฐ๊ณ ...
+* ๊ทธ ์์์ `dependencies` ๋ชจ๋(ํ์ผ `app/dependencies.py`)์ ์ฐพ๊ณ ...
+* ๊ทธ ์์์ ํจ์ `get_token_header`๋ฅผ importํฉ๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด ์ ๋๋ก ๋์ํฉ๋๋ค! ๐
+
+---
+
+๊ฐ์ ๋ฐฉ์์ผ๋ก ์ ์ธ ๊ฐ `...`๋ฅผ ์ฌ์ฉํ๋ค๋ฉด:
+
+```Python
+from ...dependencies import get_token_header
+```
+
+์๋ฏธ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* ์ด ๋ชจ๋(ํ์ผ `app/routers/items.py`)์ด ์ํ ๊ฐ์ package(๋๋ ํฐ๋ฆฌ `app/routers/`)์์ ์์ํด์...
+* ์์ package(๋๋ ํฐ๋ฆฌ `app/`)๋ก ์ฌ๋ผ๊ฐ๊ณ ...
+* ๊ทธ package์ ์์๋ก ๋ ์ฌ๋ผ๊ฐ๋๋ฐ(์์ package๊ฐ ์์ต๋๋ค, `app`์ด ์ต์์์
๋๋ค ๐ฑ)...
+* ๊ทธ ์์์ `dependencies` ๋ชจ๋(ํ์ผ `app/dependencies.py`)์ ์ฐพ๊ณ ...
+* ๊ทธ ์์์ ํจ์ `get_token_header`๋ฅผ importํฉ๋๋ค.
+
+์ด๋ `app/` ์์ชฝ์ ์ด๋ค package(์์ ์ `__init__.py` ํ์ผ ๋ฑ์ ๊ฐ์ง)์ ๋ํ ์ฐธ์กฐ๊ฐ ๋ฉ๋๋ค. ํ์ง๋ง ์ฐ๋ฆฌ๋ ๊ทธ๋ฐ ๊ฒ์ด ์์ต๋๋ค. ๊ทธ๋์ ์ด ์์์์๋ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค. ๐จ
+
+์ด์ ์ด๋ป๊ฒ ๋์ํ๋์ง ์์์ผ๋, ์ฑ์ด ์ผ๋ง๋ ๋ณต์กํ๋ ์๋ import๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๐ค
+
+### ์ปค์คํ
`tags`, `responses`, `dependencies` ์ถ๊ฐํ๊ธฐ { #add-some-custom-tags-responses-and-dependencies }
+
+`APIRouter`์ ์ด๋ฏธ prefix `/items`์ `tags=["items"]`๋ฅผ ์ถ๊ฐํ๊ธฐ ๋๋ฌธ์ ๊ฐ *path operation*์ ์ด๋ฅผ ์ถ๊ฐํ์ง ์์ต๋๋ค.
+
+ํ์ง๋ง ํน์ *path operation*์๋ง ์ ์ฉ๋ _์ถ๊ฐ_ `tags`๋ฅผ ๋ํ ์๋ ์๊ณ , ๊ทธ *path operation* ์ ์ฉ์ ์ถ๊ฐ `responses`๋ ๋ฃ์ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
+
+/// tip | ํ
+
+์ด ๋ง์ง๋ง ๊ฒฝ๋ก ์ฒ๋ฆฌ๋ `["items", "custom"]` ํ๊ทธ ์กฐํฉ์ ๊ฐ๊ฒ ๋ฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋ฌธ์์๋ `404`์ฉ ์๋ต๊ณผ `403`์ฉ ์๋ต, ๋ ๊ฐ์ง ๋ชจ๋๊ฐ ํ์๋ฉ๋๋ค.
+
+///
+
+## ๋ฉ์ธ `FastAPI` { #the-main-fastapi }
+
+์ด์ `app/main.py` ๋ชจ๋์ ๋ด
์๋ค.
+
+์ฌ๊ธฐ์์ `FastAPI` ํด๋์ค๋ฅผ importํ๊ณ ์ฌ์ฉํฉ๋๋ค.
+
+์ด ํ์ผ์ ๋ชจ๋ ๊ฒ์ ํ๋๋ก ์ฎ๋ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฉ์ธ ํ์ผ์ด ๋ ๊ฒ์
๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋๋ถ๋ถ์ ๋ก์ง์ด ๊ฐ์์ ํน์ ๋ชจ๋๋ก ๋ถ๋ฆฌ๋์ด ์์ผ๋ฏ๋ก, ๋ฉ์ธ ํ์ผ์ ๊ฝค ๋จ์ํด์ง๋๋ค.
+
+### `FastAPI` importํ๊ธฐ { #import-fastapi }
+
+ํ์์ฒ๋ผ `FastAPI` ํด๋์ค๋ฅผ importํ๊ณ ์์ฑํฉ๋๋ค.
+
+๋ํ ๊ฐ `APIRouter`์ dependencies์ ๊ฒฐํฉ๋ [global dependencies](dependencies/global-dependencies.md){.internal-link target=_blank}๋ ์ ์ธํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
+
+### `APIRouter` importํ๊ธฐ { #import-the-apirouter }
+
+์ด์ `APIRouter`๊ฐ ์๋ ๋ค๋ฅธ submodule๋ค์ importํฉ๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
+
+`app/routers/users.py`์ `app/routers/items.py` ํ์ผ์ ๊ฐ์ Python package `app`์ ์ํ submodule๋ค์ด๋ฏ๋ก, ์ ํ๋ `.`๋ฅผ ์ฌ์ฉํด "์๋ import"๋ก ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
+
+### import๊ฐ ๋์ํ๋ ๋ฐฉ์ { #how-the-importing-works }
+
+๋ค์ ๊ตฌ๋ฌธ์:
+
+```Python
+from .routers import items, users
+```
+
+์๋ฏธ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
+
+* ์ด ๋ชจ๋(ํ์ผ `app/main.py`)์ด ์ํ ๊ฐ์ package(๋๋ ํฐ๋ฆฌ `app/`)์์ ์์ํด์...
+* subpackage `routers`(๋๋ ํฐ๋ฆฌ `app/routers/`)๋ฅผ ์ฐพ๊ณ ...
+* ๊ทธ ์์์ submodule `items`(ํ์ผ `app/routers/items.py`)์ `users`(ํ์ผ `app/routers/users.py`)๋ฅผ importํฉ๋๋ค...
+
+`items` ๋ชจ๋์๋ `router` ๋ณ์(`items.router`)๊ฐ ์์ต๋๋ค. ์ด๋ `app/routers/items.py` ํ์ผ์์ ๋ง๋ ๊ฒ๊ณผ ๋์ผํ๋ฉฐ `APIRouter` ๊ฐ์ฒด์
๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ `users` ๋ชจ๋๋ ๊ฐ์ ๋ฐฉ์์
๋๋ค.
+
+๋ค์์ฒ๋ผ importํ ์๋ ์์ต๋๋ค:
+
+```Python
+from app.routers import items, users
+```
+
+/// info | ์ ๋ณด
+
+์ฒซ ๋ฒ์งธ ๋ฒ์ ์ "์๋ import"์
๋๋ค:
+
+```Python
+from .routers import items, users
+```
+
+๋ ๋ฒ์งธ ๋ฒ์ ์ "์ ๋ import"์
๋๋ค:
+
+```Python
+from app.routers import items, users
+```
+
+Python Packages์ Modules์ ๋ํด ๋ ์์๋ณด๋ ค๋ฉด <a href="https://docs.python.org/3/tutorial/modules.html" class="external-link" target="_blank">Modules์ ๋ํ Python ๊ณต์ ๋ฌธ์</a>๋ฅผ ์ฝ์ด๋ณด์ธ์.
+
+///
+
+### ์ด๋ฆ ์ถฉ๋ ํผํ๊ธฐ { #avoid-name-collisions }
+
+submodule `items`๋ฅผ ์ง์ importํ๊ณ , ๊ทธ ์์ `router` ๋ณ์๋ง importํ์ง๋ ์์ต๋๋ค.
+
+์ด๋ submodule `users`์๋ `router`๋ผ๋ ์ด๋ฆ์ ๋ณ์๊ฐ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+๋ง์ฝ ๋ค์์ฒ๋ผ ์์๋๋ก importํ๋ค๋ฉด:
+
+```Python
+from .routers.items import router
+from .routers.users import router
+```
+
+`users`์ `router`๊ฐ `items`์ `router`๋ฅผ ๋ฎ์ด์จ์ ๋์์ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค.
+
+๋ฐ๋ผ์ ๊ฐ์ ํ์ผ์์ ๋ ๋ค ์ฌ์ฉํ ์ ์๋๋ก submodule๋ค์ ์ง์ importํฉ๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
+
+### `users`์ `items`์ฉ `APIRouter` ํฌํจํ๊ธฐ { #include-the-apirouters-for-users-and-items }
+
+์ด์ submodule `users`์ `items`์ `router`๋ฅผ ํฌํจํด ๋ด
์๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
+
+/// info | ์ ๋ณด
+
+`users.router`๋ `app/routers/users.py` ํ์ผ ์์ `APIRouter`๋ฅผ ๋ด๊ณ ์์ต๋๋ค.
+
+`items.router`๋ `app/routers/items.py` ํ์ผ ์์ `APIRouter`๋ฅผ ๋ด๊ณ ์์ต๋๋ค.
+
+///
+
+`app.include_router()`๋ก ๊ฐ `APIRouter`๋ฅผ ๋ฉ์ธ `FastAPI` ์ ํ๋ฆฌ์ผ์ด์
์ ์ถ๊ฐํ ์ ์์ต๋๋ค.
+
+๊ทธ router์ ๋ชจ๋ route๊ฐ ์ ํ๋ฆฌ์ผ์ด์
์ ์ผ๋ถ๋ก ํฌํจ๋ฉ๋๋ค.
+
+/// note Technical Details | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+๋ด๋ถ์ ์ผ๋ก๋ `APIRouter`์ ์ ์ธ๋ ๊ฐ *path operation*๋ง๋ค *path operation*์ ์ค์ ๋ก ์์ฑํฉ๋๋ค.
+
+์ฆ, ๋ด๋ถ์ ์ผ๋ก๋ ๋ชจ๋ ๊ฒ์ด ๋์ผํ ํ๋์ ์ฑ์ธ ๊ฒ์ฒ๋ผ ๋์ํฉ๋๋ค.
+
+///
+
+/// check | ํ์ธ
+
+router๋ฅผ ํฌํจ(include)ํ ๋ ์ฑ๋ฅ์ ๊ฑฑ์ ํ ํ์๋ ์์ต๋๋ค.
+
+์ด ์์
์ ๋ง์ดํฌ๋ก์ด ๋จ์์ด๋ฉฐ ์์ ์์๋ง ๋ฐ์ํฉ๋๋ค.
+
+๋ฐ๋ผ์ ์ฑ๋ฅ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. โก
+
+///
+
+### ์ปค์คํ
`prefix`, `tags`, `responses`, `dependencies`๋ก `APIRouter` ํฌํจํ๊ธฐ { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
+
+์ด์ ์กฐ์ง์์ `app/internal/admin.py` ํ์ผ์ ๋ฐ์๋ค๊ณ ๊ฐ์ ํด ๋ด
์๋ค.
+
+์ฌ๊ธฐ์๋ ์กฐ์ง์์ ์ฌ๋ฌ ํ๋ก์ ํธ ๊ฐ์ ๊ณต์ ํ๋ ๊ด๋ฆฌ์์ฉ *path operations*๊ฐ ์๋ `APIRouter`๊ฐ ๋ค์ด ์์ต๋๋ค.
+
+์ด ์์์์๋ ๋งค์ฐ ๋จ์ํ๊ฒ ๋ง๋ค๊ฒ ์ต๋๋ค. ํ์ง๋ง ์กฐ์ง ๋ด ๋ค๋ฅธ ํ๋ก์ ํธ์ ๊ณต์ ๋๊ธฐ ๋๋ฌธ์, ์ด๋ฅผ ์์ ํ ์ ์์ด `prefix`, `dependencies`, `tags` ๋ฑ์ `APIRouter`์ ์ง์ ์ถ๊ฐํ ์ ์๋ค๊ณ ํด๋ด
์๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
+
+ํ์ง๋ง `APIRouter`๋ฅผ ํฌํจํ ๋ ์ปค์คํ
`prefix`๋ฅผ ์ง์ ํด ๋ชจ๋ *path operations*๊ฐ `/admin`์ผ๋ก ์์ํ๊ฒ ํ๊ณ , ์ด ํ๋ก์ ํธ์์ ์ด๋ฏธ ๊ฐ์ง `dependencies`๋ก ๋ณดํธํ๊ณ , `tags`์ `responses`๋ ํฌํจํ๊ณ ์ถ์ต๋๋ค.
+
+์๋ `APIRouter`๋ฅผ ์์ ํ์ง ์๊ณ ๋ `app.include_router()`์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฌํด์ ์ด๋ฅผ ์ ์ธํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
+
+์ด๋ ๊ฒ ํ๋ฉด ์๋ `APIRouter`๋ ์์ ๋์ง ์์ผ๋ฏ๋ก, ์กฐ์ง ๋ด ๋ค๋ฅธ ํ๋ก์ ํธ์์๋ ๋์ผํ `app/internal/admin.py` ํ์ผ์ ๊ณ์ ๊ณต์ ํ ์ ์์ต๋๋ค.
+
+๊ฒฐ๊ณผ์ ์ผ๋ก ์ฐ๋ฆฌ ์ฑ์์ `admin` ๋ชจ๋์ ๊ฐ *path operations*๋ ๋ค์์ ๊ฐ๊ฒ ๋ฉ๋๋ค:
+
+* prefix `/admin`.
+* tag `admin`.
+* dependency `get_token_header`.
+* ์๋ต `418`. ๐ต
+
+ํ์ง๋ง ์ด๋ ์ฐ๋ฆฌ ์ฑ์์ ๊ทธ `APIRouter`์๋ง ์ํฅ์ ์ฃผ๋ฉฐ, ์ด๋ฅผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ์ฝ๋์๋ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค.
+
+๋ฐ๋ผ์ ๋ค๋ฅธ ํ๋ก์ ํธ๋ค์ ๊ฐ์ `APIRouter`๋ฅผ ๋ค๋ฅธ ์ธ์ฆ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
+
+### *path operation* ํฌํจํ๊ธฐ { #include-a-path-operation }
+
+*path operations*๋ฅผ `FastAPI` ์ฑ์ ์ง์ ์ถ๊ฐํ ์๋ ์์ต๋๋ค.
+
+์ฌ๊ธฐ์๋ ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด... ๊ทธ๋ฅ ํด๋ด
๋๋ค ๐คท:
+
+{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
+
+๊ทธ๋ฆฌ๊ณ `app.include_router()`๋ก ์ถ๊ฐํ ๋ค๋ฅธ ๋ชจ๋ *path operations*์ ํจ๊ป ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํฉ๋๋ค.
+
+/// info | ์ ๋ณด
+
+**์ฐธ๊ณ **: ์ด๋ ๋งค์ฐ ๊ธฐ์ ์ ์ธ ์ธ๋ถ์ฌํญ์ด๋ผ ์๋ง **๊ทธ๋ฅ ๊ฑด๋๋ฐ์ด๋ ๋ฉ๋๋ค**.
+
+---
+
+`APIRouter`๋ "mount"๋๋ ๊ฒ์ด ์๋๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์
์ ๋๋จธ์ง ๋ถ๋ถ๊ณผ ๊ฒฉ๋ฆฌ๋์ด ์์ง ์์ต๋๋ค.
+
+์ด๋ OpenAPI ์คํค๋ง์ ์ฌ์ฉ์ ์ธํฐํ์ด์ค์ ๊ทธ๋ค์ *path operations*๋ฅผ ํฌํจ์ํค๊ณ ์ถ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+๋๋จธ์ง์ ๋
๋ฆฝ์ ์ผ๋ก ๊ฒฉ๋ฆฌํด "mount"ํ ์ ์์ผ๋ฏ๋ก, *path operations*๋ ์ง์ ํฌํจ๋๋ ๊ฒ์ด ์๋๋ผ "clone"(์ฌ์์ฑ)๋ฉ๋๋ค.
+
+///
+
+## ์๋ API ๋ฌธ์ ํ์ธํ๊ธฐ { #check-the-automatic-api-docs }
+
+์ด์ ์ฑ์ ์คํํ์ธ์:
+
+<div class="termy">
+
+```console
+$ fastapi dev app/main.py
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+```
+
+</div>
+
+๊ทธ๋ฆฌ๊ณ <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>์์ ๋ฌธ์๋ฅผ ์ฌ์ธ์.
+
+์ฌ๋ฐ๋ฅธ ๊ฒฝ๋ก(๋ฐ prefix)์ ์ฌ๋ฐ๋ฅธ ํ๊ทธ๋ฅผ ์ฌ์ฉํด, ๋ชจ๋ submodule์ ๊ฒฝ๋ก๋ฅผ ํฌํจํ ์๋ API ๋ฌธ์๋ฅผ ๋ณผ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/bigger-applications/image01.png">
+
+## ๊ฐ์ router๋ฅผ ๋ค๋ฅธ `prefix`๋ก ์ฌ๋ฌ ๋ฒ ํฌํจํ๊ธฐ { #include-the-same-router-multiple-times-with-different-prefix }
+
+`.include_router()`๋ฅผ ์ฌ์ฉํด *๊ฐ์* router๋ฅผ ์๋ก ๋ค๋ฅธ prefix๋ก ์ฌ๋ฌ ๋ฒ ํฌํจํ ์๋ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด `/api/v1`๊ณผ `/api/latest`์ฒ๋ผ ์๋ก ๋ค๋ฅธ prefix๋ก ๋์ผํ API๋ฅผ ๋
ธ์ถํ ๋ ์ ์ฉํ ์ ์์ต๋๋ค.
+
+์ด๋ ๊ณ ๊ธ ์ฌ์ฉ ๋ฐฉ์์ด๋ผ ์ค์ ๋ก ํ์ํ์ง ์์ ์๋ ์์ง๋ง, ํ์ํ ๋๋ฅผ ์ํด ์ ๊ณต๋ฉ๋๋ค.
+
+## `APIRouter`์ ๋ค๋ฅธ `APIRouter` ํฌํจํ๊ธฐ { #include-an-apirouter-in-another }
+
+`APIRouter`๋ฅผ `FastAPI` ์ ํ๋ฆฌ์ผ์ด์
์ ํฌํจํ ์ ์๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก, ๋ค์์ ์ฌ์ฉํด `APIRouter`๋ฅผ ๋ค๋ฅธ `APIRouter`์ ํฌํจํ ์ ์์ต๋๋ค:
+
+```Python
+router.include_router(other_router)
+```
+
+`FastAPI` ์ฑ์ `router`๋ฅผ ํฌํจํ๊ธฐ ์ ์ ์ํํด์ผ ํ๋ฉฐ, ๊ทธ๋์ผ `other_router`์ *path operations*๋ ํจ๊ป ํฌํจ๋ฉ๋๋ค.
--- /dev/null
+# Body - ์
๋ฐ์ดํธ { #body-updates }
+
+## `PUT`์ผ๋ก ๊ต์ฒด ์
๋ฐ์ดํธํ๊ธฐ { #update-replacing-with-put }
+
+ํญ๋ชฉ์ ์
๋ฐ์ดํธํ๋ ค๋ฉด <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a> ์์
์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+`jsonable_encoder`๋ฅผ ์ฌ์ฉํด ์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ JSON์ผ๋ก ์ ์ฅํ ์ ์๋ ๋ฐ์ดํฐ๋ก ๋ณํํ ์ ์์ต๋๋ค(์: NoSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ ์). ์๋ฅผ ๋ค์ด `datetime`์ `str`๋ก ๋ณํํ๋ ๊ฒฝ์ฐ์
๋๋ค.
+
+{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
+
+`PUT`์ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ **๋์ฒด**ํด์ผ ํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ ๋ฐ ์ฌ์ฉํฉ๋๋ค.
+
+### ๋์ฒด ์ ์ฃผ์์ฌํญ { #warning-about-replacing }
+
+์ฆ, `PUT`์ผ๋ก ํญ๋ชฉ `bar`๋ฅผ ์
๋ฐ์ดํธํ๋ฉด์ ๋ค์๊ณผ ๊ฐ์ body๋ฅผ ๋ณด๋ธ๋ค๋ฉด:
+
+```Python
+{
+ "name": "Barz",
+ "price": 3,
+ "description": None,
+}
+```
+
+์ด๋ฏธ ์ ์ฅ๋ ์์ฑ `"tax": 20.2`๊ฐ ํฌํจ๋์ด ์์ง ์๊ธฐ ๋๋ฌธ์, ์
๋ ฅ ๋ชจ๋ธ์ `"tax": 10.5`๋ผ๋ ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ๋ ๊ทธ โ์๋ก์ดโ `tax` ๊ฐ `10.5`๋ก ์ ์ฅ๋ฉ๋๋ค.
+
+## `PATCH`๋ก ๋ถ๋ถ ์
๋ฐ์ดํธํ๊ธฐ { #partial-updates-with-patch }
+
+<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> ์์
์ ์ฌ์ฉํด ๋ฐ์ดํฐ๋ฅผ *๋ถ๋ถ์ ์ผ๋ก* ์
๋ฐ์ดํธํ ์๋ ์์ต๋๋ค.
+
+์ด๋ ์
๋ฐ์ดํธํ๋ ค๋ ๋ฐ์ดํฐ๋ง ๋ณด๋ด๊ณ , ๋๋จธ์ง๋ ๊ทธ๋๋ก ๋๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
+
+/// note | ์ฐธ๊ณ
+
+`PATCH`๋ `PUT`๋ณด๋ค ๋ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๊ณ ๋ ์๋ ค์ ธ ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋ง์ ํ์ด ๋ถ๋ถ ์
๋ฐ์ดํธ์๋ `PUT`๋ง ์ฌ์ฉํฉ๋๋ค.
+
+์ฌ๋ฌ๋ถ์ ์ํ๋ ๋ฐฉ์์ผ๋ก **์์ ๋กญ๊ฒ** ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, **FastAPI**๋ ์ด๋ค ์ ํ๋ ๊ฐ์ ํ์ง ์์ต๋๋ค.
+
+๋ค๋ง ์ด ๊ฐ์ด๋๋ ์๋๋ ์ฌ์ฉ ๋ฐฉ์์ด ๋๋ต ์ด๋ป๊ฒ ๋๋์ง๋ฅผ ๋ณด์ฌ์ค๋๋ค.
+
+///
+
+### Pydantic์ `exclude_unset` ํ๋ผ๋ฏธํฐ ์ฌ์ฉํ๊ธฐ { #using-pydantics-exclude-unset-parameter }
+
+๋ถ๋ถ ์
๋ฐ์ดํธ๋ฅผ ๋ฐ์ผ๋ ค๋ฉด Pydantic ๋ชจ๋ธ์ `.model_dump()`์์ `exclude_unset` ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋งค์ฐ ์ ์ฉํฉ๋๋ค.
+
+์: `item.model_dump(exclude_unset=True)`.
+
+์ด๋ `item` ๋ชจ๋ธ์ ๋ง๋ค ๋ ์ค์ ๋ก ์ค์ ๋ ๋ฐ์ดํฐ๋ง ํฌํจํ๋ `dict`๋ฅผ ์์ฑํ๊ณ , ๊ธฐ๋ณธ๊ฐ์ ์ ์ธํฉ๋๋ค.
+
+๊ทธ ๋ค์ ์ด๋ฅผ ์ฌ์ฉํด (์์ฒญ์์ ์ ์ก๋์ด) ์ค์ ๋ ๋ฐ์ดํฐ๋ง ํฌํจํ๊ณ ๊ธฐ๋ณธ๊ฐ์ ์๋ตํ `dict`๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค:
+
+{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
+
+### Pydantic์ `update` ํ๋ผ๋ฏธํฐ ์ฌ์ฉํ๊ธฐ { #using-pydantics-update-parameter }
+
+์ด์ `.model_copy()`๋ฅผ ์ฌ์ฉํด ๊ธฐ์กด ๋ชจ๋ธ์ ๋ณต์ฌ๋ณธ์ ๋ง๋ค๊ณ , ์
๋ฐ์ดํธํ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์๋ `dict`๋ฅผ `update` ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.
+
+์: `stored_item_model.model_copy(update=update_data)`:
+
+{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
+
+### ๋ถ๋ถ ์
๋ฐ์ดํธ ์์ฝ { #partial-updates-recap }
+
+์ ๋ฆฌํ๋ฉด, ๋ถ๋ถ ์
๋ฐ์ดํธ๋ฅผ ์ ์ฉํ๋ ค๋ฉด ๋ค์์ ์ํํฉ๋๋ค:
+
+* (์ ํ ์ฌํญ) `PUT` ๋์ `PATCH`๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+* ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํํฉ๋๋ค.
+* ๊ทธ ๋ฐ์ดํฐ๋ฅผ Pydantic ๋ชจ๋ธ์ ๋ฃ์ต๋๋ค.
+* ์
๋ ฅ ๋ชจ๋ธ์์ ๊ธฐ๋ณธ๊ฐ์ด ์ ์ธ๋ `dict`๋ฅผ ์์ฑํฉ๋๋ค(`exclude_unset` ์ฌ์ฉ).
+ * ์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋ธ์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด๋ฏธ ์ ์ฅ๋ ๊ฐ์ ๋ฎ์ด์ฐ์ง ์๊ณ , ์ฌ์ฉ์๊ฐ ์ค์ ๋ก ์ค์ ํ ๊ฐ๋ง ์
๋ฐ์ดํธํ ์ ์์ต๋๋ค.
+* ์ ์ฅ๋ ๋ชจ๋ธ์ ๋ณต์ฌ๋ณธ์ ๋ง๋ค๊ณ , ๋ฐ์ ๋ถ๋ถ ์
๋ฐ์ดํธ๋ก ํด๋น ์์ฑ๋ค์ ๊ฐฑ์ ํฉ๋๋ค(`update` ํ๋ผ๋ฏธํฐ ์ฌ์ฉ).
+* ๋ณต์ฌํ ๋ชจ๋ธ์ DB์ ์ ์ฅํ ์ ์๋ ํํ๋ก ๋ณํํฉ๋๋ค(์: `jsonable_encoder` ์ฌ์ฉ).
+ * ์ด๋ ๋ชจ๋ธ์ `.model_dump()` ๋ฉ์๋๋ฅผ ๋ค์ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๋น์ทํ์ง๋ง, JSON์ผ๋ก ๋ณํ ๊ฐ๋ฅํ ๋ฐ์ดํฐ ํ์
์ผ๋ก ๊ฐ์ด ํ์คํ ๋ณํ๋๋๋ก ๋ณด์ฅํฉ๋๋ค(์: `datetime` โ `str`).
+* ๋ฐ์ดํฐ๋ฅผ DB์ ์ ์ฅํฉ๋๋ค.
+* ์
๋ฐ์ดํธ๋ ๋ชจ๋ธ์ ๋ฐํํฉ๋๋ค.
+
+{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
+
+/// tip | ํ
+
+๋์ผํ ๊ธฐ๋ฒ์ HTTP `PUT` ์์
์์๋ ์ค์ ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ์ฌ๊ธฐ์ ์์๋ ์ด๋ฐ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ํด ๋ง๋ค์ด์ง `PATCH`๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+///
+
+/// note | ์ฐธ๊ณ
+
+์
๋ ฅ ๋ชจ๋ธ์ ์ฌ์ ํ ๊ฒ์ฆ๋๋ค๋ ์ ์ ์ ์ํ์ธ์.
+
+๋ฐ๋ผ์ ๋ชจ๋ ์์ฑ์ ์๋ตํ ์ ์๋ ๋ถ๋ถ ์
๋ฐ์ดํธ๋ฅผ ๋ฐ์ผ๋ ค๋ฉด, ๋ชจ๋ ์์ฑ์ด optional๋ก ํ์๋(๊ธฐ๋ณธ๊ฐ์ ๊ฐ์ง๊ฑฐ๋ `None`์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๊ฐ์ง๋) ๋ชจ๋ธ์ด ํ์ํฉ๋๋ค.
+
+**์
๋ฐ์ดํธ**๋ฅผ ์ํ โ๋ชจ๋ ๊ฐ์ด optional์ธโ ๋ชจ๋ธ๊ณผ, **์์ฑ**์ ์ํ โํ์ ๊ฐ์ด ์๋โ ๋ชจ๋ธ์ ๊ตฌ๋ถํ๋ ค๋ฉด [์ถ๊ฐ ๋ชจ๋ธ](extra-models.md){.internal-link target=_blank}์ ์ค๋ช
๋ ์์ด๋์ด๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+///
--- /dev/null
+# ํ์ ์์กด์ฑ { #sub-dependencies }
+
+**ํ์ ์์กด์ฑ**์ ๊ฐ์ง๋ ์์กด์ฑ์ ๋ง๋ค ์ ์์ต๋๋ค.
+
+ํ์ํ ๋งํผ **๊น๊ฒ** ์ค์ฒฉํ ์๋ ์์ต๋๋ค.
+
+์ด๊ฒ์ ํด๊ฒฐํ๋ ์ผ์ **FastAPI**๊ฐ ์์์ ์ฒ๋ฆฌํฉ๋๋ค.
+
+## ์ฒซ ๋ฒ์งธ ์์กด์ฑ "dependable" { #first-dependency-dependable }
+
+๋ค์๊ณผ ๊ฐ์ด ์ฒซ ๋ฒ์งธ ์์กด์ฑ("dependable")์ ๋ง๋ค ์ ์์ต๋๋ค:
+
+{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
+
+์ด ์์กด์ฑ์ ์ ํ์ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ `q`๋ฅผ `str`๋ก ์ ์ธํ๊ณ , ๊ทธ๋๋ก ๋ฐํํฉ๋๋ค.
+
+๋งค์ฐ ๋จ์ํ ์์(๊ทธ๋ค์ง ์ ์ฉํ์ง ์์)์ด์ง๋ง, ํ์ ์์กด์ฑ์ด ์ด๋ป๊ฒ ๋์ํ๋์ง์ ์ง์คํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
+
+## ๋ ๋ฒ์งธ ์์กด์ฑ "dependable"๊ณผ "dependant" { #second-dependency-dependable-and-dependant }
+
+๊ทธ๋ค์, ๋ ๋ค๋ฅธ ์์กด์ฑ ํจ์("dependable")๋ฅผ ๋ง๋ค ์ ์๋๋ฐ, ์ด ํจ์๋ ๋์์ ์๊ธฐ ์์ ์ ์์กด์ฑ๋ ์ ์ธํฉ๋๋ค(๊ทธ๋์ "dependant"์ด๊ธฐ๋ ํฉ๋๋ค):
+
+{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
+
+์ ์ธ๋ ํ๋ผ๋ฏธํฐ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค:
+
+* ์ด ํจ์ ์์ฒด๊ฐ ์์กด์ฑ("dependable")์ด์ง๋ง, ๋ค๋ฅธ ์์กด์ฑ๋ ํ๋ ์ ์ธํฉ๋๋ค(์ฆ, ๋ค๋ฅธ ๋ฌด์ธ๊ฐ์ "์์กด"ํฉ๋๋ค).
+ * `query_extractor`์ ์์กดํ๋ฉฐ, ๊ทธ ๋ฐํ๊ฐ์ ํ๋ผ๋ฏธํฐ `q`์ ํ ๋นํฉ๋๋ค.
+* ๋ํ ์ ํ์ `last_query` ์ฟ ํค๋ฅผ `str`๋ก ์ ์ธํฉ๋๋ค.
+ * ์ฌ์ฉ์๊ฐ ์ฟผ๋ฆฌ `q`๋ฅผ ์ ๊ณตํ์ง ์์๋ค๋ฉด, ์ด์ ์ ์ฟ ํค์ ์ ์ฅํด ๋ ๋ง์ง๋ง ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+## ์์กด์ฑ ์ฌ์ฉํ๊ธฐ { #use-the-dependency }
+
+๊ทธ๋ค์ ๋ค์๊ณผ ๊ฐ์ด ์์กด์ฑ์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
+
+/// info | ์ ๋ณด
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์์๋ `query_or_cookie_extractor`๋ผ๋ ์์กด์ฑ ํ๋๋ง ์ ์ธํ๊ณ ์๋ค๋ ์ ์ ์ฃผ๋ชฉํ์ธ์.
+
+ํ์ง๋ง **FastAPI**๋ `query_or_cookie_extractor`๋ฅผ ํธ์ถํ๋ ๋์ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฌํ๊ธฐ ์ํด, ๋จผ์ `query_extractor`๋ฅผ ํด๊ฒฐํด์ผ ํ๋ค๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค.
+
+///
+
+```mermaid
+graph TB
+
+query_extractor(["query_extractor"])
+query_or_cookie_extractor(["query_or_cookie_extractor"])
+
+read_query["/items/"]
+
+query_extractor --> query_or_cookie_extractor --> read_query
+```
+
+## ๊ฐ์ ์์กด์ฑ์ ์ฌ๋ฌ ๋ฒ ์ฌ์ฉํ๊ธฐ { #using-the-same-dependency-multiple-times }
+
+๊ฐ์ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์ ๋ํด ์์กด์ฑ ์ค ํ๋๊ฐ ์ฌ๋ฌ ๋ฒ ์ ์ธ๋๋ ๊ฒฝ์ฐ(์: ์ฌ๋ฌ ์์กด์ฑ์ด ๊ณตํต ํ์ ์์กด์ฑ์ ๊ฐ๋ ๊ฒฝ์ฐ), **FastAPI**๋ ๊ทธ ํ์ ์์กด์ฑ์ ์์ฒญ๋น ํ ๋ฒ๋ง ํธ์ถํด์ผ ํ๋ค๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๊ฐ์ ์์ฒญ์ ๋ํด ๋์ผํ ์์กด์ฑ์ ์ฌ๋ฌ ๋ฒ ํธ์ถํ๋ ๋์ , ๋ฐํ๊ฐ์ <abbr title="๊ณ์ฐ/์์ฑ๋ ๊ฐ์ ์ ์ฅํด ๋์๋ค๊ฐ, ๋ค์ ๊ณ์ฐํ์ง ์๊ณ ์ฌ์ฌ์ฉํ๊ธฐ ์ํ ์ ํธ๋ฆฌํฐ/์์คํ
.">"cache"</abbr>์ ์ ์ฅํ๊ณ , ๊ทธ ์์ฒญ์์ ํด๋น ๊ฐ์ด ํ์ํ ๋ชจ๋ "dependants"์ ์ ๋ฌํฉ๋๋ค.
+
+๊ณ ๊ธ ์๋๋ฆฌ์ค๋ก, ๊ฐ์ ์์ฒญ์์ "cached" ๊ฐ์ ์ฐ๋ ๋์ ๋งค ๋จ๊ณ๋ง๋ค(์๋ง๋ ์ฌ๋ฌ ๋ฒ) ์์กด์ฑ์ด ํธ์ถ๋์ด์ผ ํ๋ค๋ ๊ฒ์ ์๊ณ ์๋ค๋ฉด, `Depends`๋ฅผ ์ฌ์ฉํ ๋ `use_cache=False` ํ๋ผ๋ฏธํฐ๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค:
+
+//// tab | Python 3.9+
+
+```Python hl_lines="1"
+async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
+ return {"fresh_value": fresh_value}
+```
+
+////
+
+//// tab | Python 3.9+ ๋น Annotated
+
+/// tip | ํ
+
+๊ฐ๋ฅํ๋ค๋ฉด `Annotated` ๋ฒ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
+
+///
+
+```Python hl_lines="1"
+async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
+ return {"fresh_value": fresh_value}
+```
+
+////
+
+## ์ ๋ฆฌ { #recap }
+
+์ฌ๊ธฐ์ ์ฌ์ฉํ ๊ทธ๋ด๋ฏํ ์ฉ์ด๋ค์ ์ ์ธํ๋ฉด, **Dependency Injection** ์์คํ
์ ๊ฝค ๋จ์ํฉ๋๋ค.
+
+*๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์ ๊ฐ์ ํํ์ ํจ์๋ค์ผ ๋ฟ์
๋๋ค.
+
+ํ์ง๋ง ์ฌ์ ํ ๋งค์ฐ ๊ฐ๋ ฅํ๋ฉฐ, ์์๋ก ๊น๊ฒ ์ค์ฒฉ๋ ์์กด์ฑ "๊ทธ๋ํ"(ํธ๋ฆฌ)๋ฅผ ์ ์ธํ ์ ์์ต๋๋ค.
+
+/// tip | ํ
+
+์ด ๋จ์ํ ์์๋ง ๋ณด๋ฉด ๊ทธ๋ค์ง ์ ์ฉํด ๋ณด์ด์ง ์์ ์๋ ์์ต๋๋ค.
+
+ํ์ง๋ง **๋ณด์**์ ๊ดํ ์ฑํฐ์์ ์ด๊ฒ์ด ์ผ๋ง๋ ์ ์ฉํ์ง ๋ณด๊ฒ ๋ ๊ฒ์
๋๋ค.
+
+๋ํ ์ผ๋ง๋ ๋ง์ ์ฝ๋๋ฅผ ์๊ปด์ฃผ๋์ง๋ ๋ณด๊ฒ ๋ ๊ฒ์
๋๋ค.
+
+///
--- /dev/null
+# ์ค๋ฅ ์ฒ๋ฆฌ { #handling-errors }
+
+API๋ฅผ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ์ ์ค๋ฅ๋ฅผ ์๋ ค์ผ ํ๋ ์ํฉ์ ๋ง์ด ์์ต๋๋ค.
+
+์ด ํด๋ผ์ด์ธํธ๋ ํ๋ก ํธ์๋๊ฐ ์๋ ๋ธ๋ผ์ฐ์ ์ผ ์๋ ์๊ณ , ๋ค๋ฅธ ์ฌ๋์ด ์์ฑํ ์ฝ๋์ผ ์๋ ์๊ณ , IoT ์ฅ์น์ผ ์๋ ์์ต๋๋ค.
+
+ํด๋ผ์ด์ธํธ์ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ์ ์๋ ค์ผ ํ ์๋ ์์ต๋๋ค:
+
+* ํด๋ผ์ด์ธํธ๊ฐ ํด๋น ์์
์ ์ํํ ์ถฉ๋ถํ ๊ถํ์ด ์์ต๋๋ค.
+* ํด๋ผ์ด์ธํธ๊ฐ ํด๋น ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์์ต๋๋ค.
+* ํด๋ผ์ด์ธํธ๊ฐ ์ ๊ทผํ๋ ค๊ณ ํ ํญ๋ชฉ์ด ์กด์ฌํ์ง ์์ต๋๋ค.
+* ๋ฑ๋ฑ.
+
+์ด๋ฐ ๊ฒฝ์ฐ ๋ณดํต **400**๋ฒ๋(400์์ 499) ๋ฒ์์ **HTTP ์ํ ์ฝ๋**๋ฅผ ๋ฐํํฉ๋๋ค.
+
+์ด๋ 200๋ฒ๋ HTTP ์ํ ์ฝ๋(200์์ 299)์ ๋น์ทํฉ๋๋ค. "200" ์ํ ์ฝ๋๋ ์ด๋ค ํํ๋ก๋ ์์ฒญ์ด "์ฑ๊ณต"ํ์์ ์๋ฏธํฉ๋๋ค.
+
+400๋ฒ๋ ์ํ ์ฝ๋๋ ํด๋ผ์ด์ธํธ ์ธก์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์์ ์๋ฏธํฉ๋๋ค.
+
+**"404 Not Found"** ์ค๋ฅ(๊ทธ๋ฆฌ๊ณ ๋๋ด๋ค)๋ ๋ค๋ค ๊ธฐ์ตํ์์ฃ ?
+
+## `HTTPException` ์ฌ์ฉํ๊ธฐ { #use-httpexception }
+
+ํด๋ผ์ด์ธํธ์ ์ค๋ฅ๊ฐ ํฌํจ๋ HTTP ์๋ต์ ๋ฐํํ๋ ค๋ฉด `HTTPException`์ ์ฌ์ฉํฉ๋๋ค.
+
+### `HTTPException` ๊ฐ์ ธ์ค๊ธฐ { #import-httpexception }
+
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
+
+### ์ฝ๋์์ `HTTPException` ๋ฐ์์ํค๊ธฐ { #raise-an-httpexception-in-your-code }
+
+`HTTPException`์ API์ ๊ด๋ จ๋ ์ถ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง ์ผ๋ฐ์ ์ธ Python ์์ธ์
๋๋ค.
+
+Python ์์ธ์ด๋ฏ๋ก `return` ํ๋ ๊ฒ์ด ์๋๋ผ `raise` ํฉ๋๋ค.
+
+์ด๋ ๋ํ, *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์* ๋ด๋ถ์์ ํธ์ถํ๋ ์ ํธ๋ฆฌํฐ ํจ์ ์์์ `HTTPException`์ `raise`ํ๋ฉด, *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์ ๋๋จธ์ง ์ฝ๋๋ ์คํ๋์ง ์๊ณ ์ฆ์ ํด๋น ์์ฒญ์ด ์ข
๋ฃ๋๋ฉฐ `HTTPException`์ HTTP ์ค๋ฅ๊ฐ ํด๋ผ์ด์ธํธ๋ก ์ ์ก๋๋ค๋ ๋ป์
๋๋ค.
+
+๊ฐ์ ๋ฐํํ๋ ๊ฒ๋ณด๋ค ์์ธ๋ฅผ ๋ฐ์์ํค๋ ๊ฒ์ ์ด์ ์ ์์กด์ฑ๊ณผ ๋ณด์์ ๋ํ ์น์
์์ ๋ ๋ถ๋ช
ํด์ง๋๋ค.
+
+์ด ์์์์๋, ํด๋ผ์ด์ธํธ๊ฐ ์กด์ฌํ์ง ์๋ ID๋ก ํญ๋ชฉ์ ์์ฒญํ๋ฉด ์ํ ์ฝ๋ `404`๋ก ์์ธ๋ฅผ ๋ฐ์์ํต๋๋ค:
+
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
+
+### ๊ฒฐ๊ณผ ์๋ต { #the-resulting-response }
+
+ํด๋ผ์ด์ธํธ๊ฐ `http://example.com/items/foo`( `item_id` `"foo"`)๋ฅผ ์์ฒญํ๋ฉด, HTTP ์ํ ์ฝ๋ 200๊ณผ ๋ค์ JSON ์๋ต์ ๋ฐ์ต๋๋ค:
+
+```JSON
+{
+ "item": "The Foo Wrestlers"
+}
+```
+
+ํ์ง๋ง ํด๋ผ์ด์ธํธ๊ฐ `http://example.com/items/bar`(์กด์ฌํ์ง ์๋ `item_id` `"bar"`)๋ฅผ ์์ฒญํ๋ฉด, HTTP ์ํ ์ฝ๋ 404("not found" ์ค๋ฅ)์ ๋ค์ JSON ์๋ต์ ๋ฐ์ต๋๋ค:
+
+```JSON
+{
+ "detail": "Item not found"
+}
+```
+
+/// tip | ํ
+
+`HTTPException`์ ๋ฐ์์ํฌ ๋ `detail` ํ๋ผ๋ฏธํฐ๋ก `str`๋ง ์ ๋ฌํ ์ ์๋ ๊ฒ์ด ์๋๋ผ, JSON์ผ๋ก ๋ณํํ ์ ์๋ ์ด๋ค ๊ฐ์ด๋ ์ ๋ฌํ ์ ์์ต๋๋ค.
+
+`dict`, `list` ๋ฑ์ ์ ๋ฌํ ์ ์์ต๋๋ค.
+
+์ด๋ค์ **FastAPI**๊ฐ ์๋์ผ๋ก ์ฒ๋ฆฌํด JSON์ผ๋ก ๋ณํํฉ๋๋ค.
+
+///
+
+## ์ปค์คํ
ํค๋ ์ถ๊ฐํ๊ธฐ { #add-custom-headers }
+
+HTTP ์ค๋ฅ์ ์ปค์คํ
ํค๋๋ฅผ ์ถ๊ฐํ ์ ์์ผ๋ฉด ์ ์ฉํ ์ํฉ์ด ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ํน์ ๋ณด์ ์ ํ์์ ๊ทธ๋ ์ต๋๋ค.
+
+์๋ง ์ฝ๋์์ ์ง์ ์ฌ์ฉํ ์ผ์ ๊ฑฐ์ ์์ ๊ฒ์
๋๋ค.
+
+ํ์ง๋ง ๊ณ ๊ธ ์๋๋ฆฌ์ค์์ ํ์ํ๋ค๋ฉด ์ปค์คํ
ํค๋๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
+
+## ์ปค์คํ
์์ธ ํธ๋ค๋ฌ ์ค์นํ๊ธฐ { #install-custom-exception-handlers }
+
+<a href="https://www.starlette.dev/exceptions/" class="external-link" target="_blank">Starlette์ ๋์ผํ ์์ธ ์ ํธ๋ฆฌํฐ</a>๋ฅผ ์ฌ์ฉํด ์ปค์คํ
์์ธ ํธ๋ค๋ฌ๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค.
+
+์ฌ๋ฌ๋ถ(๋๋ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ)์ด `raise`ํ ์ ์๋ ์ปค์คํ
์์ธ `UnicornException`์ด ์๋ค๊ณ ๊ฐ์ ํด ๋ด
์๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ด ์์ธ๋ฅผ FastAPI์์ ์ ์ญ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ถ๋ค๊ณ ํด๋ด
์๋ค.
+
+`@app.exception_handler()`๋ก ์ปค์คํ
์์ธ ํธ๋ค๋ฌ๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
+
+์ฌ๊ธฐ์ `/unicorns/yolo`๋ฅผ ์์ฒญํ๋ฉด, *๊ฒฝ๋ก ์ฒ๋ฆฌ*๊ฐ `UnicornException`์ `raise`ํฉ๋๋ค.
+
+ํ์ง๋ง `unicorn_exception_handler`๊ฐ ์ด๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
+
+๋ฐ๋ผ์ HTTP ์ํ ์ฝ๋ `418`๊ณผ ๋ค์ JSON ๋ด์ฉ์ ๊ฐ์ง ๊น๋ํ ์ค๋ฅ๋ฅผ ๋ฐ๊ฒ ๋ฉ๋๋ค:
+
+```JSON
+{"message": "Oops! yolo did something. There goes a rainbow..."}
+```
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+`from starlette.requests import Request`์ `from starlette.responses import JSONResponse`๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
+
+**FastAPI**๋ ๊ฐ๋ฐ์์ ํธ์๋ฅผ ์ํด `starlette.responses`๋ฅผ `fastapi.responses`๋ก๋ ๋์ผํ๊ฒ ์ ๊ณตํฉ๋๋ค. ํ์ง๋ง ์ฌ์ฉ ๊ฐ๋ฅํ ๋๋ถ๋ถ์ ์๋ต์ Starlette์์ ์ง์ ์ต๋๋ค. `Request`๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค.
+
+///
+
+## ๊ธฐ๋ณธ ์์ธ ํธ๋ค๋ฌ ์ค๋ฒ๋ผ์ด๋ํ๊ธฐ { #override-the-default-exception-handlers }
+
+**FastAPI**์๋ ๋ช ๊ฐ์ง ๊ธฐ๋ณธ ์์ธ ํธ๋ค๋ฌ๊ฐ ์์ต๋๋ค.
+
+์ด ํธ๋ค๋ฌ๋ค์ `HTTPException`์ `raise`ํ์ ๋, ๊ทธ๋ฆฌ๊ณ ์์ฒญ์ ์ ํจํ์ง ์์ ๋ฐ์ดํฐ๊ฐ ์์ ๋ ๊ธฐ๋ณธ JSON ์๋ต์ ๋ฐํํ๋ ์ญํ ์ ํฉ๋๋ค.
+
+์ด ์์ธ ํธ๋ค๋ฌ๋ค์ ์ฌ๋ฌ๋ถ์ ๊ฒ์ผ๋ก ์ค๋ฒ๋ผ์ด๋ํ ์ ์์ต๋๋ค.
+
+### ์์ฒญ ๊ฒ์ฆ ์์ธ ์ค๋ฒ๋ผ์ด๋ํ๊ธฐ { #override-request-validation-exceptions }
+
+์์ฒญ์ ์ ํจํ์ง ์์ ๋ฐ์ดํฐ๊ฐ ํฌํจ๋๋ฉด, **FastAPI**๋ ๋ด๋ถ์ ์ผ๋ก `RequestValidationError`๋ฅผ `raise`ํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ์ด์ ๋ํ ๊ธฐ๋ณธ ์์ธ ํธ๋ค๋ฌ๋ ํฌํจ๋์ด ์์ต๋๋ค.
+
+์ด๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ๋ ค๋ฉด `RequestValidationError`๋ฅผ ๊ฐ์ ธ์ค๊ณ , `@app.exception_handler(RequestValidationError)`๋ก ์์ธ ํธ๋ค๋ฌ๋ฅผ ๋ฐ์ฝ๋ ์ดํธํด ์ฌ์ฉํ์ธ์.
+
+์์ธ ํธ๋ค๋ฌ๋ `Request`์ ์์ธ๋ฅผ ๋ฐ์ต๋๋ค.
+
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
+
+์ด์ `/items/foo`๋ก ์ด๋ํ๋ฉด, ๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ณธ JSON ์ค๋ฅ ๋์ :
+
+```JSON
+{
+ "detail": [
+ {
+ "loc": [
+ "path",
+ "item_id"
+ ],
+ "msg": "value is not a valid integer",
+ "type": "type_error.integer"
+ }
+ ]
+}
+```
+
+๋ค์๊ณผ ๊ฐ์ ํ
์คํธ ๋ฒ์ ์ ๋ฐ๊ฒ ๋ฉ๋๋ค:
+
+```
+Validation errors:
+Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
+```
+
+### `HTTPException` ์ค๋ฅ ํธ๋ค๋ฌ ์ค๋ฒ๋ผ์ด๋ํ๊ธฐ { #override-the-httpexception-error-handler }
+
+๊ฐ์ ๋ฐฉ์์ผ๋ก `HTTPException` ํธ๋ค๋ฌ๋ ์ค๋ฒ๋ผ์ด๋ํ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด, ์ด๋ฐ ์ค๋ฅ๋ค์ ๋ํด JSON ๋์ ์ผ๋ฐ ํ
์คํธ ์๋ต์ ๋ฐํํ๊ณ ์ถ์ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
+
+/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+`from starlette.responses import PlainTextResponse`๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
+
+**FastAPI**๋ ๊ฐ๋ฐ์์ ํธ์๋ฅผ ์ํด `starlette.responses`๋ฅผ `fastapi.responses`๋ก๋ ๋์ผํ๊ฒ ์ ๊ณตํฉ๋๋ค. ํ์ง๋ง ์ฌ์ฉ ๊ฐ๋ฅํ ๋๋ถ๋ถ์ ์๋ต์ Starlette์์ ์ง์ ์ต๋๋ค.
+
+///
+
+/// warning | ๊ฒฝ๊ณ
+
+`RequestValidationError`์๋ ๊ฒ์ฆ ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ์ผ ์ด๋ฆ๊ณผ ์ค ์ ๋ณด๊ฐ ํฌํจ๋์ด ์์ด, ์ํ๋ค๋ฉด ๊ด๋ จ ์ ๋ณด์ ํจ๊ป ๋ก๊ทธ์ ํ์ํ ์ ์๋ค๋ ์ ์ ์ ๋
ํ์ธ์.
+
+ํ์ง๋ง ์ด๋ ๋จ์ํ ๋ฌธ์์ด๋ก ๋ณํํด ๊ทธ ์ ๋ณด๋ฅผ ๊ทธ๋๋ก ๋ฐํํ๋ฉด ์์คํ
์ ๋ํ ์ผ๋ถ ์ ๋ณด๋ฅผ ๋์คํ ์ ์๋ค๋ ๋ป์ด๊ธฐ๋ ํฉ๋๋ค. ๊ทธ๋์ ์ฌ๊ธฐ์ ์ฝ๋๋ ๊ฐ ์ค๋ฅ๋ฅผ ๋
๋ฆฝ์ ์ผ๋ก ์ถ์ถํด ๋ณด์ฌ์ค๋๋ค.
+
+///
+
+### `RequestValidationError`์ body ์ฌ์ฉํ๊ธฐ { #use-the-requestvalidationerror-body }
+
+`RequestValidationError`์๋ ์ ํจํ์ง ์์ ๋ฐ์ดํฐ์ ํจ๊ป ๋ฐ์ `body`๊ฐ ํฌํจ๋ฉ๋๋ค.
+
+์ฑ์ ๊ฐ๋ฐํ๋ ๋์ body๋ฅผ ๋ก๊ทธ๋ก ๋จ๊ธฐ๊ณ ๋๋ฒ๊ทธํ๊ฑฐ๋, ์ฌ์ฉ์์๊ฒ ๋ฐํํ๋ ๋ฑ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
+
+์ด์ ๋ค์์ฒ๋ผ ์ ํจํ์ง ์์ item์ ๋ณด๋ด๋ณด์ธ์:
+
+```JSON
+{
+ "title": "towel",
+ "size": "XL"
+}
+```
+
+๋ฐ์ body๋ฅผ ํฌํจํด ๋ฐ์ดํฐ๊ฐ ์ ํจํ์ง ์๋ค๊ณ ์๋ ค์ฃผ๋ ์๋ต์ ๋ฐ๊ฒ ๋ฉ๋๋ค:
+
+```JSON hl_lines="12-15"
+{
+ "detail": [
+ {
+ "loc": [
+ "body",
+ "size"
+ ],
+ "msg": "value is not a valid integer",
+ "type": "type_error.integer"
+ }
+ ],
+ "body": {
+ "title": "towel",
+ "size": "XL"
+ }
+}
+```
+
+#### FastAPI์ `HTTPException` vs Starlette์ `HTTPException` { #fastapis-httpexception-vs-starlettes-httpexception }
+
+**FastAPI**์๋ ์์ฒด `HTTPException`์ด ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ **FastAPI**์ `HTTPException` ์ค๋ฅ ํด๋์ค๋ Starlette์ `HTTPException` ์ค๋ฅ ํด๋์ค๋ฅผ ์์ํฉ๋๋ค.
+
+์ ์ผํ ์ฐจ์ด๋ **FastAPI**์ `HTTPException`์ `detail` ํ๋์ JSON์ผ๋ก ๋ณํ ๊ฐ๋ฅํ ์ด๋ค ๋ฐ์ดํฐ๋ ๋ฐ์ ์ ์๋ ๋ฐ๋ฉด, Starlette์ `HTTPException`์ ๋ฌธ์์ด๋ง ๋ฐ์ ์ ์๋ค๋ ์ ์
๋๋ค.
+
+๋ฐ๋ผ์ ์ฝ๋์์๋ ํ์์ฒ๋ผ **FastAPI**์ `HTTPException`์ ๊ณ์ `raise`ํ๋ฉด ๋ฉ๋๋ค.
+
+ํ์ง๋ง ์์ธ ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํ ๋๋ Starlette์ `HTTPException`์ ๋ํด ๋ฑ๋กํด์ผ ํฉ๋๋ค.
+
+์ด๋ ๊ฒ ํ๋ฉด Starlette ๋ด๋ถ ์ฝ๋์ ์ด๋ค ๋ถ๋ถ, ๋๋ Starlette ํ์ฅ/ํ๋ฌ๊ทธ์ธ์ด Starlette `HTTPException`์ `raise`ํ๋๋ผ๋, ์ฌ๋ฌ๋ถ์ ํธ๋ค๋ฌ๊ฐ ์ด๋ฅผ ์ก์์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
+
+์ด ์์์์๋ ๋์ผํ ์ฝ๋์์ ๋ `HTTPException`์ ๋ชจ๋ ์ฌ์ฉํ ์ ์๋๋ก, Starlette์ ์์ธ๋ฅผ `StarletteHTTPException`์ผ๋ก ์ด๋ฆ์ ๋ฐ๊ฟ๋๋ค:
+
+```Python
+from starlette.exceptions import HTTPException as StarletteHTTPException
+```
+
+### **FastAPI**์ ์์ธ ํธ๋ค๋ฌ ์ฌ์ฌ์ฉํ๊ธฐ { #reuse-fastapis-exception-handlers }
+
+์์ธ๋ฅผ ์ฌ์ฉํ๋ฉด์ **FastAPI**์ ๋์ผํ ๊ธฐ๋ณธ ์์ธ ํธ๋ค๋ฌ๋ ํจ๊ป ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด, `fastapi.exception_handlers`์์ ๊ธฐ๋ณธ ์์ธ ํธ๋ค๋ฌ๋ฅผ ๊ฐ์ ธ์ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค:
+
+{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
+
+์ด ์์์์๋ ๋งค์ฐ ํํ๋ ฅ ์๋ ๋ฉ์์ง๋ก ์ค๋ฅ๋ฅผ ์ถ๋ ฅ๋ง ํ๊ณ ์์ง๋ง, ์์ง๋ ์ดํดํ์
จ์ ๊ฒ๋๋ค. ์์ธ๋ฅผ ์ฌ์ฉํ ๋ค ๊ธฐ๋ณธ ์์ธ ํธ๋ค๋ฌ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
--- /dev/null
+# ๋ณด์ - ์ฒซ ๋จ๊ณ { #security-first-steps }
+
+์ด๋ค ๋๋ฉ์ธ์ **backend** API๊ฐ ์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ๋๋ฉ์ธ์ **frontend**๊ฐ ์๊ฑฐ๋, ๊ฐ์ ๋๋ฉ์ธ์ ๋ค๋ฅธ ๊ฒฝ๋ก์ ์๊ฑฐ๋(๋๋ ๋ชจ๋ฐ์ผ ์ ํ๋ฆฌ์ผ์ด์
์ ์์ ์๋ ์์ต๋๋ค).
+
+๊ทธ๋ฆฌ๊ณ frontend๊ฐ **username**๊ณผ **password**๋ฅผ ์ฌ์ฉํด backend์ ์ธ์ฆํ ์ ์๋ ๋ฐฉ๋ฒ์ด ํ์ํ๋ค๊ณ ํด๋ด
์๋ค.
+
+**FastAPI**์ ํจ๊ป **OAuth2**๋ฅผ ์ฌ์ฉํด์ ์ด๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
+
+ํ์ง๋ง ํ์ํ ์์ ์ ๋ณด ์กฐ๊ฐ๋ค์ ์ฐพ๊ธฐ ์ํด ๊ธธ๊ณ ๊ธด ์ ์ฒด ์คํ์ ์ฝ๋๋ผ ์๊ฐ์ ์ฐ์ง ์๋๋ก ํ๊ฒ ์ต๋๋ค.
+
+๋ณด์์ ์ฒ๋ฆฌํ๊ธฐ ์ํด **FastAPI**๊ฐ ์ ๊ณตํ๋ ๋๊ตฌ๋ค์ ์ฌ์ฉํด ๋ด
์๋ค.
+
+## ์ด๋ป๊ฒ ๋ณด์ด๋์ง { #how-it-looks }
+
+๋จผ์ ์ฝ๋๋ฅผ ๊ทธ๋ฅ ์ฌ์ฉํด์ ์ด๋ป๊ฒ ๋์ํ๋์ง ๋ณด๊ณ , ๊ทธ๋ค์์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋์ง ์ดํดํ๋ฌ ๋ค์ ๋์์ค๊ฒ ์ต๋๋ค.
+
+## `main.py` ๋ง๋ค๊ธฐ { #create-main-py }
+
+์์ ๋ฅผ ํ์ผ `main.py`์ ๋ณต์ฌํ์ธ์:
+
+{* ../../docs_src/security/tutorial001_an_py39.py *}
+
+## ์คํํ๊ธฐ { #run-it }
+
+/// info | ์ ๋ณด
+
+<a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a> ํจํค์ง๋ `pip install "fastapi[standard]"` ๋ช
๋ น์ ์คํํ๋ฉด **FastAPI**์ ํจ๊ป ์๋์ผ๋ก ์ค์น๋ฉ๋๋ค.
+
+ํ์ง๋ง `pip install fastapi` ๋ช
๋ น์ ์ฌ์ฉํ๋ฉด `python-multipart` ํจํค์ง๊ฐ ๊ธฐ๋ณธ์ผ๋ก ํฌํจ๋์ง ์์ต๋๋ค.
+
+์๋์ผ๋ก ์ค์นํ๋ ค๋ฉด, [๊ฐ์ ํ๊ฒฝ](../../virtual-environments.md){.internal-link target=_blank}์ ๋ง๋ค๊ณ ํ์ฑํํ ๋ค์, ์๋๋ก ์ค์นํ์ธ์:
+
+```console
+$ pip install python-multipart
+```
+
+์ด๋ **OAuth2**๊ฐ `username`๊ณผ `password`๋ฅผ ๋ณด๋ด๊ธฐ ์ํด "form data"๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+///
+
+๋ค์์ผ๋ก ์์ ๋ฅผ ์คํํ์ธ์:
+
+<div class="termy">
+
+```console
+$ fastapi dev main.py
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+```
+
+</div>
+
+## ํ์ธํ๊ธฐ { #check-it }
+
+๋ํํ ๋ฌธ์๋ก ์ด๋ํ์ธ์: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
+
+๋ค์๊ณผ ๋น์ทํ ํ๋ฉด์ด ๋ณด์ผ ๊ฒ์
๋๋ค:
+
+<img src="/img/tutorial/security/image01.png">
+
+/// check | Authorize ๋ฒํผ!
+
+๋ฐ์ง์ด๋ ์ "Authorize" ๋ฒํผ์ด ์ด๋ฏธ ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ *๊ฒฝ๋ก ์ฒ๋ฆฌ*์๋ ์ค๋ฅธ์ชฝ ์๋จ์ ํด๋ฆญํ ์ ์๋ ์์ ์๋ฌผ์ ๊ฐ ์์ต๋๋ค.
+
+///
+
+๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ํด๋ฆญํ๋ฉด `username`๊ณผ `password`(๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ ํ์ ํ๋๋ค)๋ฅผ ์
๋ ฅํ ์ ์๋ ์์ ์ธ์ฆ ํผ์ด ๋ํ๋ฉ๋๋ค:
+
+<img src="/img/tutorial/security/image02.png">
+
+/// note | ์ฐธ๊ณ
+
+ํผ์ ๋ฌด์์ ์
๋ ฅํ๋ ์์ง์ ๋์ํ์ง ์์ต๋๋ค. ํ์ง๋ง ๊ณง ์ฌ๊ธฐ๊น์ง ๊ตฌํํ ๊ฒ์
๋๋ค.
+
+///
+
+๋ฌผ๋ก ์ด๊ฒ์ ์ต์ข
์ฌ์ฉ์๋ฅผ ์ํ frontend๋ ์๋์ง๋ง, ๋ชจ๋ API๋ฅผ ๋ํํ์ผ๋ก ๋ฌธ์ํํ๋ ํ๋ฅญํ ์๋ ๋๊ตฌ์
๋๋ค.
+
+frontend ํ(๊ทธ๊ฒ ๋ณธ์ธ์ผ ์๋ ์์ต๋๋ค)์ด ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+์๋ํํฐ ์ ํ๋ฆฌ์ผ์ด์
๊ณผ ์์คํ
์์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋์ผํ ์ ํ๋ฆฌ์ผ์ด์
์ ๋๋ฒ๊ทธํ๊ณ , ํ์ธํ๊ณ , ํ
์คํธํ๊ธฐ ์ํด ๋ณธ์ธ์ด ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
+
+## `password` ํ๋ก์ฐ { #the-password-flow }
+
+์ด์ ์กฐ๊ธ ๋์๊ฐ์ ์ด๊ฒ๋ค์ด ๋ฌด์์ธ์ง ์ดํดํด ๋ด
์๋ค.
+
+`password` "flow"๋ ๋ณด์๊ณผ ์ธ์ฆ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด OAuth2์์ ์ ์ํ ์ฌ๋ฌ ๋ฐฉ์("flows") ์ค ํ๋์
๋๋ค.
+
+OAuth2๋ backend ๋๋ API๊ฐ ์ฌ์ฉ์๋ฅผ ์ธ์ฆํ๋ ์๋ฒ์ ๋
๋ฆฝ์ ์ผ ์ ์๋๋ก ์ค๊ณ๋์์ต๋๋ค.
+
+ํ์ง๋ง ์ด ๊ฒฝ์ฐ์๋ ๊ฐ์ **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
์ด API์ ์ธ์ฆ์ ๋ชจ๋ ์ฒ๋ฆฌํฉ๋๋ค.
+
+๋ฐ๋ผ์, ๋จ์ํ๋ ๊ด์ ์์ ๋ค์ ์ ๋ฆฌํด๋ณด๋ฉด:
+
+* ์ฌ์ฉ์๊ฐ frontend์์ `username`๊ณผ `password`๋ฅผ ์
๋ ฅํ๊ณ `Enter`๋ฅผ ๋๋ฆ
๋๋ค.
+* frontend(์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ์์ ์คํ๋จ)๋ ํด๋น `username`๊ณผ `password`๋ฅผ ์ฐ๋ฆฌ API์ ํน์ URL๋ก ๋ณด๋
๋๋ค(`tokenUrl="token"`๋ก ์ ์ธ๋จ).
+* API๋ `username`๊ณผ `password`๋ฅผ ํ์ธํ๊ณ "token"์ผ๋ก ์๋ตํฉ๋๋ค(์์ง ์๋ฌด๊ฒ๋ ๊ตฌํํ์ง ์์์ต๋๋ค).
+ * "token"์ ๋์ค์ ์ด ์ฌ์ฉ์๋ฅผ ๊ฒ์ฆํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ์ด๋ค ๋ด์ฉ์ด ๋ด๊ธด ๋ฌธ์์ด์ผ ๋ฟ์
๋๋ค.
+ * ๋ณดํต token์ ์ผ์ ์๊ฐ์ด ์ง๋๋ฉด ๋ง๋ฃ๋๋๋ก ์ค์ ํฉ๋๋ค.
+ * ๊ทธ๋์ ์ฌ์ฉ์๋ ๋์ค์ ์ด๋ ์์ ์ ๋ค์ ๋ก๊ทธ์ธํด์ผ ํฉ๋๋ค.
+ * ๊ทธ๋ฆฌ๊ณ token์ด ๋๋๋นํ๋๋ผ๋ ์ํ์ด ๋ ๋ฎ์ต๋๋ค. ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์๊ตฌ์ ์ผ๋ก ํญ์ ๋์ํ๋ ํค์๋ ๋ค๋ฆ
๋๋ค.
+* frontend๋ ๊ทธ token์ ์์๋ก ์ด๋๊ฐ์ ์ ์ฅํฉ๋๋ค.
+* ์ฌ์ฉ์๊ฐ frontend์์ ํด๋ฆญํด์ frontend ์น ์ฑ์ ๋ค๋ฅธ ์น์
์ผ๋ก ์ด๋ํฉ๋๋ค.
+* frontend๋ API์์ ๋ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํฉ๋๋ค.
+ * ํ์ง๋ง ๊ทธ ํน์ endpoint์๋ ์ธ์ฆ์ด ํ์ํฉ๋๋ค.
+ * ๊ทธ๋์ ์ฐ๋ฆฌ API์ ์ธ์ฆํ๊ธฐ ์ํด `Authorization` ํค๋๋ฅผ, ๊ฐ์ `Bearer `์ token์ ๋ํ ํํ๋ก ๋ณด๋
๋๋ค.
+ * token์ `foobar`๊ฐ ๋ค์ด ์๋ค๋ฉด `Authorization` ํค๋์ ๋ด์ฉ์ `Bearer foobar`๊ฐ ๋ฉ๋๋ค.
+
+## **FastAPI**์ `OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer }
+
+**FastAPI**๋ ์ด๋ฐ ๋ณด์ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ํด, ์๋ก ๋ค๋ฅธ ์ถ์ํ ์์ค์์ ์ฌ๋ฌ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
+
+์ด ์์ ์์๋ **OAuth2**์ **Password** ํ๋ก์ฐ์ **Bearer** token์ ์ฌ์ฉํฉ๋๋ค. ์ด๋ฅผ ์ํด `OAuth2PasswordBearer` ํด๋์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+/// info | ์ ๋ณด
+
+"bearer" token๋ง์ด ์ ์ผํ ์ ํ์ง๋ ์๋๋๋ค.
+
+ํ์ง๋ง ์ด ์ฌ์ฉ ์ฌ๋ก์๋ ๊ฐ์ฅ ์ ํฉํ ์ ํ์
๋๋ค.
+
+๋ํ OAuth2 ์ ๋ฌธ๊ฐ๋ก์ ์ ๋ค๋ฅธ ์ต์
์ด ๋ ์ ํฉํ์ง ์ ํํ ์๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด, ๋๋ถ๋ถ์ ์ฌ์ฉ ์ฌ๋ก์๋ ๊ฐ์ฅ ์ ํฉํ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
+
+๊ทธ๋ฐ ๊ฒฝ์ฐ๋ฅผ ์ํด์๋ **FastAPI**๋ ์ด๋ฅผ ๊ตฌ์ฑํ ์ ์๋ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
+
+///
+
+`OAuth2PasswordBearer` ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค ๋ `tokenUrl` ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฌํฉ๋๋ค. ์ด ํ๋ผ๋ฏธํฐ์๋ ํด๋ผ์ด์ธํธ(์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ์์ ์คํ๋๋ frontend)๊ฐ token์ ๋ฐ๊ธฐ ์ํด `username`๊ณผ `password`๋ฅผ ๋ณด๋ผ URL์ด ๋ค์ด ์์ต๋๋ค.
+
+{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
+
+/// tip | ํ
+
+์ฌ๊ธฐ์ `tokenUrl="token"`์ ์์ง ๋ง๋ค์ง ์์ ์๋ URL `token`์ ๊ฐ๋ฆฌํต๋๋ค. ์๋ URL์ด๋ฏ๋ก `./token`๊ณผ ๋์ผํฉ๋๋ค.
+
+์๋ URL์ ์ฌ์ฉํ๋ฏ๋ก, ์๋ฅผ ๋ค์ด API๊ฐ `https://example.com/`์ ์๋ค๋ฉด `https://example.com/token`์ ๊ฐ๋ฆฌํต๋๋ค. ํ์ง๋ง API๊ฐ `https://example.com/api/v1/`์ ์๋ค๋ฉด `https://example.com/api/v1/token`์ ๊ฐ๋ฆฌํต๋๋ค.
+
+์๋ URL์ ์ฌ์ฉํ๋ ๊ฒ์ [ํ๋ก์ ๋ค์์](../../advanced/behind-a-proxy.md){.internal-link target=_blank} ๊ฐ์ ๊ณ ๊ธ ์ฌ์ฉ ์ฌ๋ก์์๋ ์ ํ๋ฆฌ์ผ์ด์
์ด ๊ณ์ ๋์ํ๋๋ก ๋ณด์ฅํ๋ ๋ฐ ์ค์ํฉ๋๋ค.
+
+///
+
+์ด ํ๋ผ๋ฏธํฐ๋ ๊ทธ endpoint / *๊ฒฝ๋ก ์ฒ๋ฆฌ*๋ฅผ ๋ง๋ค์ง๋ ์์ง๋ง, URL `/token`์ด ํด๋ผ์ด์ธํธ๊ฐ token์ ์ป๊ธฐ ์ํด ์ฌ์ฉํด์ผ ํ URL์ด๋ผ๊ณ ์ ์ธํฉ๋๋ค. ์ด ์ ๋ณด๋ OpenAPI์ ์ฌ์ฉ๋๊ณ , ์ด์ด์ ๋ํํ API ๋ฌธ์ ์์คํ
์์๋ ์ฌ์ฉ๋ฉ๋๋ค.
+
+๊ณง ์ค์ ๊ฒฝ๋ก ์ฒ๋ฆฌ๋ฅผ ๋ง๋ค ๊ฒ์
๋๋ค.
+
+/// info | ์ ๋ณด
+
+์๊ฒฉํ "Pythonista"๋ผ๋ฉด `token_url` ๋์ `tokenUrl` ๊ฐ์ ํ๋ผ๋ฏธํฐ ์ด๋ฆ ์คํ์ผ์ด ๋ง์์ ๋ค์ง ์์ ์๋ ์์ต๋๋ค.
+
+์ด๋ OpenAPI ์คํ์์ ์ฌ์ฉํ๋ ์ด๋ฆ๊ณผ ๋์ผํ๊ฒ ๋ง์ถ ๊ฒ์ด๊ธฐ ๋๋ฌธ์
๋๋ค. ๊ทธ๋์ ์ด๋ฐ ๋ณด์ ์คํด์ ๋ํด ๋ ์กฐ์ฌํด์ผ ํ ๋, ๊ทธ๋๋ก ๋ณต์ฌํด์ ๋ถ์ฌ ๋ฃ์ด ๋ ๋ง์ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
+
+///
+
+`oauth2_scheme` ๋ณ์๋ `OAuth2PasswordBearer`์ ์ธ์คํด์ค์ด์ง๋ง, "callable"์ด๊ธฐ๋ ํฉ๋๋ค.
+
+๋ค์์ฒ๋ผ ํธ์ถ๋ ์ ์์ต๋๋ค:
+
+```Python
+oauth2_scheme(some, parameters)
+```
+
+๋ฐ๋ผ์ `Depends`์ ํจ๊ป ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+### ์ฌ์ฉํ๊ธฐ { #use-it }
+
+์ด์ `Depends`๋ก `oauth2_scheme`๋ฅผ ์์กด์ฑ์ ์ ๋ฌํ ์ ์์ต๋๋ค.
+
+{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
+
+์ด ์์กด์ฑ์ `str`์ ์ ๊ณตํ๊ณ , ๊ทธ ๊ฐ์ *๊ฒฝ๋ก ์ฒ๋ฆฌ ํจ์*์ ํ๋ผ๋ฏธํฐ `token`์ ํ ๋น๋ฉ๋๋ค.
+
+**FastAPI**๋ ์ด ์์กด์ฑ์ ์ฌ์ฉํด OpenAPI ์คํค๋ง(๋ฐ ์๋ API ๋ฌธ์)์ "security scheme"๋ฅผ ์ ์ํ ์ ์๋ค๋ ๊ฒ์ ์๊ฒ ๋ฉ๋๋ค.
+
+/// info | ๊ธฐ์ ์ธ๋ถ์ฌํญ
+
+**FastAPI**๋ (์์กด์ฑ์ ์ ์ธ๋) `OAuth2PasswordBearer` ํด๋์ค๋ฅผ ์ฌ์ฉํด OpenAPI์์ ๋ณด์ ์คํด์ ์ ์ํ ์ ์๋ค๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค. ์ด๋ `OAuth2PasswordBearer`๊ฐ `fastapi.security.oauth2.OAuth2`๋ฅผ ์์ํ๊ณ , ์ด๊ฒ์ด ๋ค์ `fastapi.security.base.SecurityBase`๋ฅผ ์์ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
+
+OpenAPI(๋ฐ ์๋ API ๋ฌธ์)์ ํตํฉ๋๋ ๋ชจ๋ ๋ณด์ ์ ํธ๋ฆฌํฐ๋ `SecurityBase`๋ฅผ ์์ํ๋ฉฐ, ๊ทธ๋์ **FastAPI**๊ฐ ์ด๋ฅผ OpenAPI์ ์ด๋ป๊ฒ ํตํฉํ ์ง ์ ์ ์์ต๋๋ค.
+
+///
+
+## ๋ฌด์์ ํ๋์ง { #what-it-does }
+
+์์ฒญ์์ `Authorization` ํค๋๋ฅผ ์ฐพ์, ๊ฐ์ด `Bearer `์ ์ด๋ค token์ด ๋ถ์ ํํ์ธ์ง ํ์ธํ ๋ค, ๊ทธ token์ `str`๋ก ๋ฐํํฉ๋๋ค.
+
+`Authorization` ํค๋๊ฐ ์๊ฑฐ๋, ๊ฐ์ `Bearer ` token์ด ์๋ค๋ฉด, ๊ณง๋ฐ๋ก 401 ์ํ ์ฝ๋ ์ค๋ฅ(`UNAUTHORIZED`)๋ก ์๋ตํฉ๋๋ค.
+
+์ค๋ฅ๋ฅผ ๋ฐํํ๊ธฐ ์ํด token์ด ์กด์ฌํ๋์ง ์ง์ ํ์ธํ ํ์์กฐ์ฐจ ์์ต๋๋ค. ํจ์๊ฐ ์คํ๋์๋ค๋ฉด ๊ทธ token์๋ `str`์ด ๋ค์ด ์๋ค๊ณ ํ์ ํ ์ ์์ต๋๋ค.
+
+๋ํํ ๋ฌธ์์์ ์ด๋ฏธ ์๋ํด ๋ณผ ์ ์์ต๋๋ค:
+
+<img src="/img/tutorial/security/image03.png">
+
+์์ง token์ ์ ํจ์ฑ์ ๊ฒ์ฆํ์ง ์์ง๋ง, ์ด๊ฒ๋ง์ผ๋ก๋ ์์์ ๋ ์
์
๋๋ค.
+
+## ์์ฝ { #recap }
+
+์ฆ, ์ถ๊ฐ๋ก 3~4์ค๋ง์ผ๋ก๋ ์ด๋ฏธ ์์์ ์ธ ํํ์ ๋ณด์์ ๊ฐ์ถ๊ฒ ๋ฉ๋๋ค.
--- /dev/null
+# ๋ณด์ { #security }
+
+๋ณด์, ์ธ์ฆ(authentication), ์ธ๊ฐ(authorization)๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋งค์ฐ ๋ค์ํฉ๋๋ค.
+
+๊ทธ๋ฆฌ๊ณ ๋ณดํต ๋ณต์กํ๊ณ "์ด๋ ค์ด" ์ฃผ์ ์ด๊ธฐ๋ ํฉ๋๋ค.
+
+๋ง์ ํ๋ ์์ํฌ์ ์์คํ
์์ ๋ณด์๊ณผ ์ธ์ฆ๋ง ์ฒ๋ฆฌํ๋ ๋ฐ๋ ํฐ ๋
ธ๋ ฅ๊ณผ ์ฝ๋๊ฐ ํ์ํฉ๋๋ค(๋ง์ ๊ฒฝ์ฐ ์์ฑ๋ ์ ์ฒด ์ฝ๋์ 50% ์ด์์ด ๋ ์๋ ์์ต๋๋ค).
+
+**FastAPI**๋ ๋ชจ๋ ๋ณด์ ๋ช
์ธ๋ฅผ ์ ๋ถ ๊ณต๋ถํ๊ณ ๋ฐฐ์ธ ํ์ ์์ด, ํ์ค์ ์ธ ๋ฐฉ์์ผ๋ก ์ฝ๊ณ ๋น ๋ฅด๊ฒ **๋ณด์(Security)** ์ ๋ค๋ฃฐ ์ ์๋๋ก ์ฌ๋ฌ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
+
+ํ์ง๋ง ๋จผ์ , ๋ช ๊ฐ์ง ์์ ๊ฐ๋
์ ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
+
+## ๊ธํ์ ๊ฐ์? { #in-a-hurry }
+
+์ด ์ฉ์ด๋ค์ ๊ด์ฌ์ด ์๊ณ ์ฌ์ฉ์๋ช
๊ณผ ๋น๋ฐ๋ฒํธ ๊ธฐ๋ฐ ์ธ์ฆ์ ์ฌ์ฉํ ๋ณด์์ *์ง๊ธ ๋น์ฅ* ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด ๋๋ค๋ฉด, ๋ค์ ์ฅ๋ค๋ก ๋์ด๊ฐ์ธ์.
+
+## OAuth2 { #oauth2 }
+
+OAuth2๋ ์ธ์ฆ๊ณผ ์ธ๊ฐ๋ฅผ ์ฒ๋ฆฌํ๋ ์ฌ๋ฌ ๋ฐฉ๋ฒ์ ์ ์ํ๋ ๋ช
์ธ์
๋๋ค.
+
+์๋นํ ๋ฐฉ๋ํ ๋ช
์ธ์ด๋ฉฐ ์ฌ๋ฌ ๋ณต์กํ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ๋ค๋ฃน๋๋ค.
+
+"์ 3์"๋ฅผ ์ฌ์ฉํด ์ธ์ฆํ๋ ๋ฐฉ๋ฒ๋ ํฌํจํฉ๋๋ค.
+
+๋ฐ๋ก `"Facebook, Google, X (Twitter), GitHub๋ก ๋ก๊ทธ์ธ"` ๊ฐ์ ์์คํ
๋ค์ด ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉํ๋ ๋ฐฉ์์
๋๋ค.
+
+### OAuth 1 { #oauth-1 }
+
+OAuth 1๋ ์์๋๋ฐ, ์ด๋ OAuth2์ ๋งค์ฐ ๋ค๋ฅด๊ณ ํต์ ์ ์ํธํํ๋ ๋ฐฉ๋ฒ๊น์ง ์ง์ ๋ช
์ธ์ ํฌํจํ๊ธฐ ๋๋ฌธ์ ๋ ๋ณต์กํ์ต๋๋ค.
+
+์์ฆ์๋ ๊ทธ๋ค์ง ์ธ๊ธฐ ์๊ฑฐ๋ ์ฌ์ฉ๋์ง๋ ์์ต๋๋ค.
+
+OAuth2๋ ํต์ ์ ์ด๋ป๊ฒ ์ํธํํ ์ง๋ ๋ช
์ธํ์ง ์๊ณ , ์ ํ๋ฆฌ์ผ์ด์
์ด HTTPS๋ก ์ ๊ณต๋ ๊ฒ์ ๊ธฐ๋ํฉ๋๋ค.
+
+/// tip | ํ
+
+**๋ฐฐํฌ**์ ๋ํ ์น์
์์ Traefik๊ณผ Let's Encrypt๋ฅผ ์ฌ์ฉํด ๋ฌด๋ฃ๋ก HTTPS๋ฅผ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ๋ณผ ์ ์์ต๋๋ค.
+
+///
+
+## OpenID Connect { #openid-connect }
+
+OpenID Connect๋ **OAuth2**๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ ๋ค๋ฅธ ๋ช
์ธ์
๋๋ค.
+
+OAuth2์์ ๋น๊ต์ ๋ชจํธํ ๋ถ๋ถ์ ์ผ๋ถ ๊ตฌ์ฒดํํ์ฌ ์ํธ ์ด์ฉ์ฑ์ ๋์ด๋ ค๋ ํ์ฅ์
๋๋ค.
+
+์๋ฅผ ๋ค์ด, Google ๋ก๊ทธ์ธ์ OpenID Connect๋ฅผ ์ฌ์ฉํฉ๋๋ค(๋ด๋ถ์ ์ผ๋ก๋ OAuth2๋ฅผ ์ฌ์ฉ).
+
+ํ์ง๋ง Facebook ๋ก๊ทธ์ธ์ OpenID Connect๋ฅผ ์ง์ํ์ง ์์ต๋๋ค. ์์ฒด์ ์ธ ๋ณํ์ OAuth2๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+### OpenID("OpenID Connect"๊ฐ ์๋) { #openid-not-openid-connect }
+
+"OpenID"๋ผ๋ ๋ช
์ธ๋ ์์์ต๋๋ค. ์ด๋ **OpenID Connect**์ ๊ฐ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๊ณ ํ์ง๋ง, OAuth2๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ง ์์์ต๋๋ค.
+
+๋ฐ๋ผ์ ์์ ํ ๋ณ๋์ ์ถ๊ฐ ์์คํ
์ด์์ต๋๋ค.
+
+์์ฆ์๋ ๊ทธ๋ค์ง ์ธ๊ธฐ ์๊ฑฐ๋ ์ฌ์ฉ๋์ง๋ ์์ต๋๋ค.
+
+## OpenAPI { #openapi }
+
+OpenAPI(์ด์ ์๋ Swagger๋ก ์๋ ค์ง)๋ API๋ฅผ ๊ตฌ์ถํ๊ธฐ ์ํ ๊ณต๊ฐ ๋ช
์ธ์
๋๋ค(ํ์ฌ Linux Foundation์ ์ผ๋ถ).
+
+**FastAPI**๋ **OpenAPI**๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค.
+
+์ด ๋๋ถ์ ์ฌ๋ฌ ์๋ ๋ํํ ๋ฌธ์ ์ธํฐํ์ด์ค, ์ฝ๋ ์์ฑ ๋ฑ๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+OpenAPI์๋ ์ฌ๋ฌ ๋ณด์ "scheme"์ ์ ์ํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
+
+์ด๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฌํ ๋ํํ ๋ฌธ์ ์์คํ
์ ํฌํจํด, ํ์ค ๊ธฐ๋ฐ ๋๊ตฌ๋ค์ ๋ชจ๋ ํ์ฉํ ์ ์์ต๋๋ค.
+
+OpenAPI๋ ๋ค์ ๋ณด์ scheme๋ค์ ์ ์ํฉ๋๋ค:
+
+* `apiKey`: ๋ค์์์ ์ ๋ฌ๋ ์ ์๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฉ ํค:
+ * ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ
+ * ํค๋
+ * ์ฟ ํค
+* `http`: ํ์ค HTTP ์ธ์ฆ ์์คํ
, ์:
+ * `bearer`: `Authorization` ํค๋์ `Bearer ` + ํ ํฐ ๊ฐ์ ๋ฃ๋ ๋ฐฉ์. OAuth2์์ ์ ๋ํ์ต๋๋ค.
+ * HTTP Basic ์ธ์ฆ
+ * HTTP Digest ๋ฑ
+* `oauth2`: ๋ณด์์ ์ฒ๋ฆฌํ๋ ๋ชจ๋ OAuth2 ๋ฐฉ์(์ด๋ฅผ "flow"๋ผ๊ณ ๋ถ๋ฆ
๋๋ค).
+ * ์ด flow๋ค ์ค ์ฌ๋ฌ ๊ฐ๋ OAuth 2.0 ์ธ์ฆ ์ ๊ณต์(์: Google, Facebook, X (Twitter), GitHub ๋ฑ)๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ ์ ํฉํฉ๋๋ค:
+ * `implicit`
+ * `clientCredentials`
+ * `authorizationCode`
+ * ํ์ง๋ง ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ง์ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ ๋ฐ ์๋ฒฝํ๊ฒ ์ฌ์ฉํ ์ ์๋ ํน์ "flow"๋ ํ๋ ์์ต๋๋ค:
+ * `password`: ๋ค์ ์ฅ๋ค์์ ์ด์ ๋ํ ์์๋ฅผ ๋ค๋ฃน๋๋ค.
+* `openIdConnect`: OAuth2 ์ธ์ฆ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ํ์(discover)ํ๋ ๋ฐฉ๋ฒ์ ์ ์ํฉ๋๋ค.
+ * ์ด ์๋ ํ์์ OpenID Connect ๋ช
์ธ์์ ์ ์๋ฉ๋๋ค.
+
+
+/// tip | ํ
+
+Google, Facebook, X (Twitter), GitHub ๋ฑ ๋ค๋ฅธ ์ธ์ฆ/์ธ๊ฐ ์ ๊ณต์๋ฅผ ํตํฉํ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ฉฐ ๋น๊ต์ ์ฝ์ต๋๋ค.
+
+๊ฐ์ฅ ๋ณต์กํ ๋ฌธ์ ๋ ๊ทธ๋ฐ ์ธ์ฆ/์ธ๊ฐ ์ ๊ณต์ ์์ฒด๋ฅผ ๊ตฌ์ถํ๋ ๊ฒ์ด์ง๋ง, **FastAPI**๋ ์ด๋ ค์ด ์์
์ ๋์ ์ฒ๋ฆฌํด ์ฃผ๋ฉด์ ์ด๋ฅผ ์ฝ๊ฒ ํ ์ ์๋ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
+
+///
+
+## **FastAPI** ์ ํธ๋ฆฌํฐ { #fastapi-utilities }
+
+FastAPI๋ `fastapi.security` ๋ชจ๋์์ ๊ฐ ๋ณด์ scheme์ ๋ํ ์ฌ๋ฌ ๋๊ตฌ๋ฅผ ์ ๊ณตํ๋ฉฐ, ์ด๋ฌํ ๋ณด์ ๋ฉ์ปค๋์ฆ์ ๋ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ํด์ค๋๋ค.
+
+๋ค์ ์ฅ๋ค์์๋ **FastAPI**๊ฐ ์ ๊ณตํ๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํด API์ ๋ณด์์ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ ๋ณด๊ฒ ๋ ๊ฒ์
๋๋ค.
+
+๋ํ ๋ํํ ๋ฌธ์ ์์คํ
์ ์ด๋ป๊ฒ ์๋์ผ๋ก ํตํฉ๋๋์ง๋ ํ์ธํ๊ฒ ๋ฉ๋๋ค.