From 0383fb3ab912e1e8e1549597afd510f0e857b70f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Jan 2026 17:41:45 +0100 Subject: [PATCH 01/26] =?UTF-8?q?=E2=AC=86=20Bump=20actions/download-artif?= =?UTF-8?q?act=20from=206=20to=207=20(#14526)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy-docs.yml | 2 +- .github/workflows/smokeshow.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 79d7391c4..734fc244d 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -45,7 +45,7 @@ jobs: run: | rm -rf ./site mkdir ./site - - uses: actions/download-artifact@v6 + - uses: actions/download-artifact@v7 with: path: ./site/ pattern: docs-site-* diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml index 1a45174dc..f23b962b7 100644 --- a/.github/workflows/smokeshow.yml +++ b/.github/workflows/smokeshow.yml @@ -28,7 +28,7 @@ jobs: pyproject.toml uv.lock - run: uv sync --locked --no-dev --group github-actions - - uses: actions/download-artifact@v6 + - uses: actions/download-artifact@v7 with: name: coverage-html path: htmlcov diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4d45c20eb..605d7c812 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -112,7 +112,7 @@ jobs: - name: Install Dependencies run: uv sync --locked --no-dev --group tests --extra all - name: Get coverage files - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: pattern: coverage-* path: coverage From 9a76f2fec9043cfa0834a70d8c879a03506e6860 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 16:42:08 +0000 Subject: [PATCH 02/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 52166f4e8..46488d326 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -22,6 +22,7 @@ hide: ### Internal +* โฌ† Bump actions/download-artifact from 6 to 7. PR [#14526](https://github.com/fastapi/fastapi/pull/14526) by [@dependabot[bot]](https://github.com/apps/dependabot). * ๐Ÿ‘ท Tweak CI input names. PR [#14688](https://github.com/fastapi/fastapi/pull/14688) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ”จ Refactor translation script to allow committing in place. PR [#14687](https://github.com/fastapi/fastapi/pull/14687) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ› Fix translation script path. PR [#14685](https://github.com/fastapi/fastapi/pull/14685) by [@tiangolo](https://github.com/tiangolo). From d05b18ec4012304355ec3df1d9cc4e7c78e5419a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Jan 2026 17:42:37 +0100 Subject: [PATCH 03/26] =?UTF-8?q?=E2=AC=86=20Bump=20actions/upload-artifac?= =?UTF-8?q?t=20from=205=20to=206=20(#14525)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/test.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 2198fe668..d1b38930c 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -97,7 +97,7 @@ jobs: path: docs/${{ matrix.lang }}/.cache - name: Build Docs run: uv run ./scripts/docs.py build-lang ${{ matrix.lang }} - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v6 with: name: docs-site-${{ matrix.lang }} path: ./site/** diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 605d7c812..891f76717 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,7 +84,7 @@ jobs: # Do not store coverage for all possible combinations to avoid file size max errors in Smokeshow - name: Store coverage files if: matrix.coverage == 'coverage' - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: coverage-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/coverage/.coverage.*') }} path: coverage @@ -121,7 +121,7 @@ jobs: - run: uv run coverage combine coverage - run: uv run coverage html --title "Coverage for ${{ github.sha }}" - name: Store coverage HTML - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: coverage-html path: htmlcov From 14f30687627bfde7056bea8066b3a006cd1abdad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Jan 2026 17:42:51 +0100 Subject: [PATCH 04/26] =?UTF-8?q?=E2=AC=86=20Bump=20actions/cache=20from?= =?UTF-8?q?=204=20to=205=20(#14511)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index d1b38930c..77bce055c 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -91,7 +91,7 @@ jobs: run: uv sync --locked --no-dev --group docs - name: Update Languages run: uv run ./scripts/docs.py update-languages - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: key: mkdocs-cards-${{ matrix.lang }}-${{ github.ref }} path: docs/${{ matrix.lang }}/.cache From 612a2d20bcbfa7041d2e87bbae7aa3db7bffee67 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 16:43:25 +0000 Subject: [PATCH 05/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 46488d326..491caaee8 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -22,6 +22,7 @@ hide: ### Internal +* โฌ† Bump actions/upload-artifact from 5 to 6. PR [#14525](https://github.com/fastapi/fastapi/pull/14525) by [@dependabot[bot]](https://github.com/apps/dependabot). * โฌ† Bump actions/download-artifact from 6 to 7. PR [#14526](https://github.com/fastapi/fastapi/pull/14526) by [@dependabot[bot]](https://github.com/apps/dependabot). * ๐Ÿ‘ท Tweak CI input names. PR [#14688](https://github.com/fastapi/fastapi/pull/14688) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ”จ Refactor translation script to allow committing in place. PR [#14687](https://github.com/fastapi/fastapi/pull/14687) by [@tiangolo](https://github.com/tiangolo). From effe493ae0d398e8237de1796ec3ec25ef19e4f0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 16:43:35 +0000 Subject: [PATCH 06/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 491caaee8..7727fa699 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -22,6 +22,7 @@ hide: ### Internal +* โฌ† Bump actions/cache from 4 to 5. PR [#14511](https://github.com/fastapi/fastapi/pull/14511) by [@dependabot[bot]](https://github.com/apps/dependabot). * โฌ† Bump actions/upload-artifact from 5 to 6. PR [#14525](https://github.com/fastapi/fastapi/pull/14525) by [@dependabot[bot]](https://github.com/apps/dependabot). * โฌ† Bump actions/download-artifact from 6 to 7. PR [#14526](https://github.com/fastapi/fastapi/pull/14526) by [@dependabot[bot]](https://github.com/apps/dependabot). * ๐Ÿ‘ท Tweak CI input names. PR [#14688](https://github.com/fastapi/fastapi/pull/14688) by [@tiangolo](https://github.com/tiangolo). From 1054fbd2563c91ac7798ae25c486c426df9d7909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 11 Jan 2026 10:18:38 -0800 Subject: [PATCH 07/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20docs=20for=20cont?= =?UTF-8?q?ributing=20with=20translations=20(#14701)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/contributing.md | 235 +++-------------------------------- scripts/docs.py | 11 +- 2 files changed, 17 insertions(+), 229 deletions(-) diff --git a/docs/en/docs/contributing.md b/docs/en/docs/contributing.md index 18b7f60f7..be4706f74 100644 --- a/docs/en/docs/contributing.md +++ b/docs/en/docs/contributing.md @@ -177,252 +177,45 @@ as Uvicorn by default will use the port `8000`, the documentation on port `8008` ### Translations -/// warning | Attention - -**Update on Translations** - -We're updating the way we handle documentation translations. - -Until now, we invited community members to translate pages via pull requests, which were then reviewed by at least two native speakers. While this has helped bring FastAPI to many more users, weโ€™ve also run into several challenges - some languages have only a few translated pages, others are outdated and hard to maintain over time. -To improve this, weโ€™re working on automation tools ๐Ÿค– to manage translations more efficiently. Once ready, documentation will be machine-translated and still reviewed by at least two native speakers โœ… before publishing. This will allow us to keep translations up-to-date while reducing the review burden on maintainers. - -Whatโ€™s changing now: - -* ๐Ÿšซ Weโ€™re no longer accepting new community-submitted translation PRs. - -* โณ Existing open PRs will be reviewed and can still be merged if completed within the next 3 weeks (since July 11 2025). - -* ๐ŸŒ In the future, we will only support languages where at least three active native speakers are available to review and maintain translations. - -This transition will help us keep translations more consistent and timely while better supporting our contributors ๐Ÿ™Œ. Thank you to everyone who has contributed so far โ€” your help has been invaluable! ๐Ÿ’– - -/// - - Help with translations is VERY MUCH appreciated! And it can't be done without the help from the community. ๐ŸŒŽ ๐Ÿš€ Here are the steps to help with translations. -#### Tips and guidelines - -* Check the currently existing pull requests for your language. You can filter the pull requests by the ones with the label for your language. For example, for Spanish, the label is `lang-es`. - -* Review those pull requests, requesting changes or approving them. For the languages I don't speak, I'll wait for several others to review the translation before merging. - -/// tip - -You can add comments with change suggestions to existing pull requests. - -Check the docs about adding a pull request review to approve it or request changes. - -/// +#### Review Translation PRs -* Check if there's a GitHub Discussion to coordinate translations for your language. You can subscribe to it, and when there's a new pull request to review, an automatic comment will be added to the discussion. - -* If you translate pages, add a single pull request per page translated. That will make it much easier for others to review it. - -* To check the 2-letter code for the language you want to translate, you can use the table List of ISO 639-1 codes. - -#### Existing language - -Let's say you want to translate a page for a language that already has translations for some pages, like Spanish. - -In the case of Spanish, the 2-letter code is `es`. So, the directory for Spanish translations is located at `docs/es/`. - -/// tip - -The main ("official") language is English, located at `docs/en/`. - -/// +Translation pull requests are made by LLMs guided with prompts designed by the FastAPI team together with the community of native speakers for each supported language. -Now run the live server for the docs in Spanish: +These translations are normally still reviewed by native speakers, and here's where you can help! -
- -```console -// Use the command "live" and pass the language code as a CLI argument -$ python ./scripts/docs.py live es - -[INFO] Serving on http://127.0.0.1:8008 -[INFO] Start watching changes -[INFO] Start detecting changes -``` - -
- -/// tip - -Alternatively, you can perform the same steps that scripts does manually. - -Go into the language directory, for the Spanish translations it's at `docs/es/`: - -```console -$ cd docs/es/ -``` - -Then run `mkdocs` in that directory: - -```console -$ mkdocs serve --dev-addr 127.0.0.1:8008 -``` - -/// - -Now you can go to http://127.0.0.1:8008 and see your changes live. - -You will see that every language has all the pages. But some pages are not translated and have an info box at the top, about the missing translation. - -Now let's say that you want to add a translation for the section [Features](features.md){.internal-link target=_blank}. - -* Copy the file at: - -``` -docs/en/docs/features.md -``` +* Check the currently existing pull requests for your language. You can filter the pull requests by the ones with the label for your language. For example, for Spanish, the label is `lang-es`. -* Paste it in exactly the same location but for the language you want to translate, e.g.: +* When reviewing a pull request, it's better not to suggest changes in the same pull request, because it is LLM generated, and it won't be possible to make sure that small individual changes are replicated in other similar sections, or that they are preserved when translating the same content again. -``` -docs/es/docs/features.md -``` +* Instead of adding suggestions to the translation PR, make the suggestions to the LLM prompt file for that language, in a new PR. For example, for Spanish, the LLM prompt file is at: `docs/es/llm-prompt.md`. /// tip -Notice that the only change in the path and file name is the language code, from `en` to `es`. +Check the docs about adding a pull request review to approve it or request changes. /// -If you go to your browser you will see that now the docs show your new section (the info box at the top is gone). ๐ŸŽ‰ - -Now you can translate it all and see how it looks as you save the file. +#### Subscribe to Notifications for Your Language -#### Don't Translate these Pages - -๐Ÿšจ Don't translate: - -* Files under `reference/` -* `release-notes.md` -* `fastapi-people.md` -* `external-links.md` -* `newsletter.md` -* `management-tasks.md` -* `management.md` -* `contributing.md` +* Check if there's a GitHub Discussion to coordinate translations for your language. You can subscribe to it, and when there's a new pull request to review, an automatic comment will be added to the discussion. -Some of these files are updated very frequently and a translation would always be behind, or they include the main content from English source files, etc. +* To check the 2-letter code for the language you want to translate, you can use the table List of ISO 639-1 codes. #### Request a New Language Let's say that you want to request translations for a language that is not yet translated, not even some pages. For example, Latin. -If there is no discussion for that language, you can start by requesting the new language. For that, you can follow these steps: - +* The first step would be for you to find other 2 people that would be willing to be reviewing translation PRs for that language with you. +* Once there are at least 3 people that would be willing to commit to help maintain that language, you can continue the next steps. * Create a new discussion following the template. -* Get a few native speakers to comment on the discussion and commit to help review translations for that language. +* Tag the other 2 people that will help with the language, and ask them to confirm there they will help. Once there are several people in the discussion, the FastAPI team can evaluate it and can make it an official translation. -Then the docs will be automatically translated using AI, and the team of native speakers can review the translation, and help tweak the AI prompts. +Then the docs will be automatically translated using LLMs, and the team of native speakers can review the translation, and help tweak the LLM prompts. Once there's a new translation, for example if docs are updated or there's a new section, there will be a comment in the same discussion with the link to the new translation to review. - -#### New Language - -/// note - -These steps will be performed by the FastAPI team. - -/// - -Checking the link from above (List of ISO 639-1 codes), you can see that the 2-letter code for Latin is `la`. - -Now you can create a new directory for the new language, running the following script: - -
- -```console -// Use the command new-lang, pass the language code as a CLI argument -$ python ./scripts/docs.py new-lang la - -Successfully initialized: docs/la -``` - -
- -Now you can check in your code editor the newly created directory `docs/la/`. - -That command created a file `docs/la/mkdocs.yml` with a simple config that inherits everything from the `en` version: - -```yaml -INHERIT: ../en/mkdocs.yml -``` - -/// tip - -You could also simply create that file with those contents manually. - -/// - -That command also created a dummy file `docs/la/index.md` for the main page, you can start by translating that one. - -You can continue with the previous instructions for an "Existing Language" for that process. - -You can make the first pull request with those two files, `docs/la/mkdocs.yml` and `docs/la/index.md`. ๐ŸŽ‰ - -#### Preview the result - -As already mentioned above, you can use the `./scripts/docs.py` with the `live` command to preview the results (or `mkdocs serve`). - -Once you are done, you can also test it all as it would look online, including all the other languages. - -To do that, first build all the docs: - -
- -```console -// Use the command "build-all", this will take a bit -$ python ./scripts/docs.py build-all - -Building docs for: en -Building docs for: es -Successfully built docs for: es -``` - -
- -This builds all those independent MkDocs sites for each language, combines them, and generates the final output at `./site/`. - -Then you can serve that with the command `serve`: - -
- -```console -// Use the command "serve" after running "build-all" -$ python ./scripts/docs.py serve - -Warning: this is a very simple server. For development, use mkdocs serve instead. -This is here only to preview a site with translations already built. -Make sure you run the build-all command first. -Serving at: http://127.0.0.1:8008 -``` - -
- -#### Translation specific tips and guidelines - -* Translate only the Markdown documents (`.md`). Do not translate the code examples at `./docs_src`. - -* In code blocks within the Markdown document, translate comments (`# a comment`), but leave the rest unchanged. - -* Do not change anything enclosed in "``" (inline code). - -* In lines starting with `///` translate only the text part after `|`. Leave the rest unchanged. - -* You can translate info boxes like `/// warning` with for example `/// warning | Achtung`. But do not change the word immediately after the `///`, it determines the color of the info box. - -* Do not change the paths in links to images, code files, Markdown documents. - -* However, when a Markdown document is translated, the `#hash-parts` in links to its headings may change. Update these links if possible. - * Search for such links in the translated document using the regex `#[^# ]`. - * Search in all documents already translated into your language for `your-translated-document.md`. For example VS Code has an option "Edit" -> "Find in Files". - * When translating a document, do not "pre-translate" `#hash-parts` that link to headings in untranslated documents. diff --git a/scripts/docs.py b/scripts/docs.py index c350ee831..20bf1c168 100644 --- a/scripts/docs.py +++ b/scripts/docs.py @@ -130,14 +130,9 @@ def new_lang(lang: str = typer.Argument(..., callback=lang_callback)): new_path.mkdir() new_config_path: Path = Path(new_path) / mkdocs_name new_config_path.write_text("INHERIT: ../en/mkdocs.yml\n", encoding="utf-8") - new_config_docs_path: Path = new_path / "docs" - new_config_docs_path.mkdir() - en_index_path: Path = en_docs_path / "docs" / "index.md" - new_index_path: Path = new_config_docs_path / "index.md" - en_index_content = en_index_path.read_text(encoding="utf-8") - new_index_content = f"{missing_translation_snippet}\n\n{en_index_content}" - new_index_path.write_text(new_index_content, encoding="utf-8") - typer.secho(f"Successfully initialized: {new_path}", color=typer.colors.GREEN) + new_llm_prompt_path: Path = new_path / "llm-prompt.md" + new_llm_prompt_path.write_text("", encoding="utf-8") + print(f"Successfully initialized: {new_path}") update_languages() From 97aa825422737b8fbafa9ad76390a86b67a88381 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 18:18:58 +0000 Subject: [PATCH 08/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 7727fa699..2a2b0d639 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Docs +* ๐Ÿ“ Update docs for contributing with translations. PR [#14701](https://github.com/fastapi/fastapi/pull/14701) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Specify language code for code block. PR [#14656](https://github.com/fastapi/fastapi/pull/14656) by [@YuriiMotov](https://github.com/YuriiMotov). ### Translations From 249a776b70dfc173d4c9b91a3b0c8d9319d0ee14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 11 Jan 2026 11:19:05 -0800 Subject: [PATCH 09/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20docs=20about=20ma?= =?UTF-8?q?naging=20translations=20(#14704)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/fastapi-people.md | 22 +----- docs/en/docs/management-tasks.md | 126 ++----------------------------- 2 files changed, 7 insertions(+), 141 deletions(-) diff --git a/docs/en/docs/fastapi-people.md b/docs/en/docs/fastapi-people.md index f2ca26013..2c07af764 100644 --- a/docs/en/docs/fastapi-people.md +++ b/docs/en/docs/fastapi-people.md @@ -196,31 +196,11 @@ They have contributed source code, documentation, etc. ๐Ÿ“ฆ There are hundreds of other contributors, you can see them all in the FastAPI GitHub Contributors page. ๐Ÿ‘ท -## Top Translators - -These are the **Top Translators**. ๐ŸŒ - -These users have created the most Pull Requests with [translations to other languages](contributing.md#translations){.internal-link target=_blank} that have been *merged*. - -
- -{% for user in (translators.values() | list)[:50] %} - -{% if user.login not in skip_users %} - -
@{{ user.login }}
Translations: {{ user.count }}
- -{% endif %} - -{% endfor %} - -
- ## Top Translation Reviewers These users are the **Top Translation Reviewers**. ๐Ÿ•ต๏ธ -I only speak a few languages (and not very well ๐Ÿ˜…). So, the reviewers are the ones that have the [**power to approve translations**](contributing.md#translations){.internal-link target=_blank} of the documentation. Without them, there wouldn't be documentation in several other languages. +Translation reviewers have the [**power to approve translations**](contributing.md#translations){.internal-link target=_blank} of the documentation. Without them, there wouldn't be documentation in several other languages.
{% for user in (translation_reviewers.values() | list)[:50] %} diff --git a/docs/en/docs/management-tasks.md b/docs/en/docs/management-tasks.md index aac4d6fe4..68d10a42c 100644 --- a/docs/en/docs/management-tasks.md +++ b/docs/en/docs/management-tasks.md @@ -106,129 +106,15 @@ This way, we can notice when there are new translations ready, because they have ## Merge Translation PRs -For Spanish, as I'm a native speaker and it's a language close to me, I will give it a final review myself and in most cases tweak the PR a bit before merging it. +Translations are generated automatically with LLMs and scripts. -For the other languages, confirm that: +There's one GitHub Action that can be manually run to add or update translations for a language: `translate.yml`. -* The title is correct following the instructions above. -* It has the labels `lang-all` and `lang-{lang code}`. -* The PR changes only one Markdown file adding a translation. - * Or in some cases, at most two files, if they are small, for the same language, and people reviewed them. - * If it's the first translation for that language, it will have additional `mkdocs.yml` files, for those cases follow the instructions below. -* The PR doesn't add any additional or extraneous files. -* The translation seems to have a similar structure as the original English file. -* The translation doesn't seem to change the original content, for example with obvious additional documentation sections. -* The translation doesn't use different Markdown structures, for example adding HTML tags when the original didn't have them. -* The "admonition" sections, like `tip`, `info`, etc. are not changed or translated. For example: - -``` -/// tip - -This is a tip. - -/// - -``` - -looks like this: - -/// tip - -This is a tip. - -/// - -...it could be translated as: - -``` -/// tip - -Esto es un consejo. - -/// - -``` - -...but needs to keep the exact `tip` keyword. If it was translated to `consejo`, like: - -``` -/// consejo +For these language translation PRs, confirm that: -Esto es un consejo. - -/// - -``` - -it would change the style to the default one, it would look like: - -/// consejo - -Esto es un consejo. - -/// - -Those don't have to be translated, but if they are, they need to be written as: - -``` -/// tip | consejo - -Esto es un consejo. - -/// - -``` - -Which looks like: - -/// tip | consejo - -Esto es un consejo. - -/// - -## First Translation PR - -When there's a first translation for a language, it will have a `docs/{lang code}/docs/index.md` translated file and a `docs/{lang code}/mkdocs.yml`. - -For example, for Bosnian, it would be: - -* `docs/bs/docs/index.md` -* `docs/bs/mkdocs.yml` - -The `mkdocs.yml` file will have only the following content: - -```YAML -INHERIT: ../en/mkdocs.yml -``` - -The language code would normally be in the ISO 639-1 list of language codes. - -In any case, the language code should be in the file docs/language_names.yml. - -There won't be yet a label for the language code, for example, if it was Bosnian, there wouldn't be a `lang-bs`. Before creating the label and adding it to the PR, create the GitHub Discussion: - -* Go to the Translations GitHub Discussions -* Create a new discussion with the title `Bosnian Translations` (or the language name in English) -* A description of: - -```Markdown -## Bosnian translations - -This is the issue to track translations of the docs to Bosnian. ๐Ÿš€ - -Here are the [PRs to review with the label `lang-bs`](https://github.com/fastapi/fastapi/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc+label%3Alang-bs+label%3A%22awaiting-review%22). ๐Ÿค“ -``` - -Update "Bosnian" with the new language. - -And update the search link to point to the new language label that will be created, like `lang-bs`. - -Create and add the label to that new Discussion just created, like `lang-bs`. - -Then go back to the PR, and add the label, like `lang-bs`, and `lang-all` and `awaiting-review`. - -Now the GitHub action will automatically detect the label `lang-bs` and will post in that Discussion that this PR is waiting to be reviewed. +* The PR was automated (authored by @tiangolo), not made by another user. +* It has the labels `lang-all` and `lang-{lang code}`. +* If the PR is approved by at least one native speaker, you can merge it. ## Review PRs From e9e0419ed0deee736e2b6ef01fc6dea1d6ed987e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 19:19:29 +0000 Subject: [PATCH 10/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 2a2b0d639..1df9e6351 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Docs +* ๐Ÿ“ Update docs about managing translations. PR [#14704](https://github.com/fastapi/fastapi/pull/14704) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Update docs for contributing with translations. PR [#14701](https://github.com/fastapi/fastapi/pull/14701) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Specify language code for code block. PR [#14656](https://github.com/fastapi/fastapi/pull/14656) by [@YuriiMotov](https://github.com/YuriiMotov). From 7b864acf370f3587cc29eb58f1f897c5c21d139d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 11 Jan 2026 13:19:26 -0800 Subject: [PATCH 11/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20docs=20for=20mana?= =?UTF-8?q?gement=20tasks=20(#14705)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/management-tasks.md | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/docs/en/docs/management-tasks.md b/docs/en/docs/management-tasks.md index 68d10a42c..6f34afb73 100644 --- a/docs/en/docs/management-tasks.md +++ b/docs/en/docs/management-tasks.md @@ -74,7 +74,7 @@ Make sure you use a supported label from the external_links.yml. - -* Make sure the new link is in the correct category (e.g. "Podcasts") and language (e.g. "Japanese"). -* A new link should be at the top of its list. -* The link URL should work (it should not return a 404). -* The content of the link should be about FastAPI. -* The new addition should have these fields: - * `author`: The name of the author. - * `link`: The URL with the content. - * `title`: The title of the link (the title of the article, podcast, etc). - -After checking all these things and ensuring the PR has the right labels, you can merge it. - ## Dependabot PRs Dependabot will create PRs to update dependencies for several things, and those PRs all look similar, but some are way more delicate than others. -* If the PR is for a direct dependency, so, Dependabot is modifying `pyproject.toml`, **don't merge it**. ๐Ÿ˜ฑ Let me check it first. There's a good chance that some additional tweaks or updates are needed. -* If the PR updates one of the internal dependencies, for example it's modifying `requirements.txt` files, or GitHub Action versions, if the tests are passing, the release notes (shown in a summary in the PR) don't show any obvious potential breaking change, you can merge it. ๐Ÿ˜Ž +* If the PR is for a direct dependency, so, Dependabot is modifying `pyproject.toml` in the main dependencies, **don't merge it**. ๐Ÿ˜ฑ Let me check it first. There's a good chance that some additional tweaks or updates are needed. +* If the PR updates one of the internal dependencies, for example the group `dev` in `pyproject.toml`, or GitHub Action versions, if the tests are passing, the release notes (shown in a summary in the PR) don't show any obvious potential breaking change, you can merge it. ๐Ÿ˜Ž ## Mark GitHub Discussions Answers From e63f382b0f189bc1ab76a58eaaf92ce83b775f48 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 21:19:50 +0000 Subject: [PATCH 12/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 1df9e6351..628eff348 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Docs +* ๐Ÿ“ Update docs for management tasks. PR [#14705](https://github.com/fastapi/fastapi/pull/14705) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Update docs about managing translations. PR [#14704](https://github.com/fastapi/fastapi/pull/14704) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Update docs for contributing with translations. PR [#14701](https://github.com/fastapi/fastapi/pull/14701) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Specify language code for code block. PR [#14656](https://github.com/fastapi/fastapi/pull/14656) by [@YuriiMotov](https://github.com/YuriiMotov). From 1be80f48851845d9e0a6ce8ee6a8b871cd9ff5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 11 Jan 2026 14:22:58 -0800 Subject: [PATCH 13/26] =?UTF-8?q?=F0=9F=93=9D=20Add=20contribution=20instr?= =?UTF-8?q?uctions=20about=20LLM=20generated=20code=20and=20comments=20and?= =?UTF-8?q?=20automated=20tools=20for=20PRs=20(#14706)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 6 ++++- docs/en/docs/contributing.md | 36 +++++++++++++++++++++++++++++ docs/en/docs/management-tasks.md | 2 ++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index f40ec4dc4..2ae588da1 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -41,11 +41,15 @@ jobs: "message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR.", "reminder": { "before": "P3D", - "message": "Heads-up: this will be closed in 3 days unless thereโ€™s new activity." + "message": "Heads-up: this will be closed in 3 days unless there's new activity." } }, "invalid": { "delay": 0, "message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details." + }, + "maybe-ai": { + "delay": 0, + "message": "This was marked as potentially AI generated and will be closed now. If this is an error, please provide additional details, make sure to read the docs about contributing and AI." } } diff --git a/docs/en/docs/contributing.md b/docs/en/docs/contributing.md index be4706f74..a4d896109 100644 --- a/docs/en/docs/contributing.md +++ b/docs/en/docs/contributing.md @@ -219,3 +219,39 @@ Once there are several people in the discussion, the FastAPI team can evaluate i Then the docs will be automatically translated using LLMs, and the team of native speakers can review the translation, and help tweak the LLM prompts. Once there's a new translation, for example if docs are updated or there's a new section, there will be a comment in the same discussion with the link to the new translation to review. + +## Automated Code and AI + +You are encouraged to use all the tools you want to do your work and contribute as efficiently as possible, this includes AI (LLM) tools, etc. Nevertheless, contributions should have meaningful human intervention, judgement, context, etc. + +If the **human effort** put in a PR, e.g. writing LLM prompts, is **less** than the **effort we would need to put** to **review it**, please **don't** submit the PR. + +Think of it this way: we can already write LLM prompts or run automated tools ourselves, and that would be faster than reviewing external PRs. + +### Closing Automated and AI PRs + +If we see PRs that seem AI generated or automated in similar ways, we'll flag them and close them. + +The same applies to comments and descriptions, please don't copy paste the content generated by an LLM. + +### Human Effort Denial of Service + +Using automated tools and AI to submit PRs or comments that we have to carefully review and handle would be the equivalent of a Denial-of-service attack on our human effort. + +It would be very little effort from the person submitting the PR (an LLM prompt) that generates a large amount of effort on our side (carefully reviewing code). + +Please don't do that. + +We'll need to block accounts that spam us with repeated automated PRs or comments. + +### Use Tools Wisely + +As Uncle Ben said: + +
+With great power tools comes great responsibility. +
+ +Avoid inadvertently doing harm. + +You have amazing tools at hand, use them wisely to help effectively. diff --git a/docs/en/docs/management-tasks.md b/docs/en/docs/management-tasks.md index 6f34afb73..fc6e1eb28 100644 --- a/docs/en/docs/management-tasks.md +++ b/docs/en/docs/management-tasks.md @@ -122,6 +122,8 @@ For these language translation PRs, confirm that: * If a PR seems to be spam, meaningless, only to change statistics (to appear as "contributor") or similar, you can simply mark it as `invalid`, and it will be automatically closed. +* If a PR seems to be AI generated, and seems like reviewing it would take more time from you than the time it took to write the prompt, mark it as `maybe-ai`, and it will be automatically closed. + * A PR should have a specific use case that it is solving. * If the PR is for a feature, it should have docs. From a456e92a21d5b94a67d6d0fd070218df09444443 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 22:23:21 +0000 Subject: [PATCH 14/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 628eff348..45bc9873e 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Docs +* ๐Ÿ“ Add contribution instructions about LLM generated code and comments and automated tools for PRs. PR [#14706](https://github.com/fastapi/fastapi/pull/14706) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Update docs for management tasks. PR [#14705](https://github.com/fastapi/fastapi/pull/14705) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Update docs about managing translations. PR [#14704](https://github.com/fastapi/fastapi/pull/14704) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Update docs for contributing with translations. PR [#14701](https://github.com/fastapi/fastapi/pull/14701) by [@tiangolo](https://github.com/tiangolo). From a96dd013a4d2ce4f5f758fe51bea19f541743164 Mon Sep 17 00:00:00 2001 From: fcharrier Date: Fri, 16 Jan 2026 11:53:45 +0100 Subject: [PATCH 15/26] =?UTF-8?q?=F0=9F=90=9B=20Fix=20copy=20button=20in?= =?UTF-8?q?=20custom.js=20(#14722)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/js/custom.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/en/docs/js/custom.js b/docs/en/docs/js/custom.js index 48e95901d..be326d302 100644 --- a/docs/en/docs/js/custom.js +++ b/docs/en/docs/js/custom.js @@ -81,8 +81,14 @@ function setupTermynal() { } } saveBuffer(); + const inputCommands = useLines + .filter(line => line.type === "input") + .map(line => line.value) + .join("\n"); + node.textContent = inputCommands; const div = document.createElement("div"); - node.replaceWith(div); + node.style.display = "none"; + node.after(div); const termynal = new Termynal(div, { lineData: useLines, noInit: true, From c597d9cb53415cf81188750ee946d69304922fbb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 16 Jan 2026 10:54:08 +0000 Subject: [PATCH 16/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 45bc9873e..233fdb743 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Docs +* ๐Ÿ› Fix copy button in custom.js. PR [#14722](https://github.com/fastapi/fastapi/pull/14722) by [@fcharrier](https://github.com/fcharrier). * ๐Ÿ“ Add contribution instructions about LLM generated code and comments and automated tools for PRs. PR [#14706](https://github.com/fastapi/fastapi/pull/14706) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Update docs for management tasks. PR [#14705](https://github.com/fastapi/fastapi/pull/14705) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Update docs about managing translations. PR [#14704](https://github.com/fastapi/fastapi/pull/14704) by [@tiangolo](https://github.com/tiangolo). From f317ede223131a2822723602fb3f576f1ca0f5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 16 Jan 2026 03:54:01 -0800 Subject: [PATCH 17/26] =?UTF-8?q?=F0=9F=8C=90=20Update=20translations=20fo?= =?UTF-8?q?r=20ko=20(add-missing)=20(#14699)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions[bot] --- docs/ko/docs/_llm-test.md | 503 +++++++++++++++++ docs/ko/docs/advanced/additional-responses.md | 247 +++++++++ docs/ko/docs/advanced/behind-a-proxy.md | 466 ++++++++++++++++ docs/ko/docs/advanced/dataclasses.md | 95 ++++ docs/ko/docs/advanced/generate-clients.md | 208 ++++++++ docs/ko/docs/advanced/middleware.md | 97 ++++ docs/ko/docs/advanced/openapi-callbacks.md | 186 +++++++ docs/ko/docs/advanced/openapi-webhooks.md | 55 ++ .../path-operation-advanced-configuration.md | 172 ++++++ .../docs/advanced/security/http-basic-auth.md | 107 ++++ docs/ko/docs/advanced/security/index.md | 19 + .../docs/advanced/security/oauth2-scopes.md | 274 ++++++++++ docs/ko/docs/advanced/settings.md | 302 +++++++++++ docs/ko/docs/alternatives.md | 485 +++++++++++++++++ docs/ko/docs/deployment/concepts.md | 321 +++++++++++ docs/ko/docs/deployment/fastapicloud.md | 65 +++ docs/ko/docs/deployment/https.md | 231 ++++++++ docs/ko/docs/deployment/manually.md | 157 ++++++ .../authentication-error-status-code.md | 17 + docs/ko/docs/how-to/custom-docs-ui-assets.md | 185 +++++++ .../docs/how-to/custom-request-and-route.md | 109 ++++ docs/ko/docs/how-to/extending-openapi.md | 80 +++ docs/ko/docs/how-to/general.md | 39 ++ docs/ko/docs/how-to/graphql.md | 60 +++ docs/ko/docs/how-to/index.md | 13 + ...migrate-from-pydantic-v1-to-pydantic-v2.md | 135 +++++ .../docs/how-to/separate-openapi-schemas.md | 102 ++++ docs/ko/docs/how-to/testing-database.md | 7 + docs/ko/docs/tutorial/bigger-applications.md | 504 ++++++++++++++++++ docs/ko/docs/tutorial/body-updates.md | 100 ++++ .../tutorial/dependencies/sub-dependencies.md | 105 ++++ docs/ko/docs/tutorial/handling-errors.md | 244 +++++++++ docs/ko/docs/tutorial/security/first-steps.md | 203 +++++++ docs/ko/docs/tutorial/security/index.md | 106 ++++ 34 files changed, 5999 insertions(+) create mode 100644 docs/ko/docs/_llm-test.md create mode 100644 docs/ko/docs/advanced/additional-responses.md create mode 100644 docs/ko/docs/advanced/behind-a-proxy.md create mode 100644 docs/ko/docs/advanced/dataclasses.md create mode 100644 docs/ko/docs/advanced/generate-clients.md create mode 100644 docs/ko/docs/advanced/middleware.md create mode 100644 docs/ko/docs/advanced/openapi-callbacks.md create mode 100644 docs/ko/docs/advanced/openapi-webhooks.md create mode 100644 docs/ko/docs/advanced/path-operation-advanced-configuration.md create mode 100644 docs/ko/docs/advanced/security/http-basic-auth.md create mode 100644 docs/ko/docs/advanced/security/index.md create mode 100644 docs/ko/docs/advanced/security/oauth2-scopes.md create mode 100644 docs/ko/docs/advanced/settings.md create mode 100644 docs/ko/docs/alternatives.md create mode 100644 docs/ko/docs/deployment/concepts.md create mode 100644 docs/ko/docs/deployment/fastapicloud.md create mode 100644 docs/ko/docs/deployment/https.md create mode 100644 docs/ko/docs/deployment/manually.md create mode 100644 docs/ko/docs/how-to/authentication-error-status-code.md create mode 100644 docs/ko/docs/how-to/custom-docs-ui-assets.md create mode 100644 docs/ko/docs/how-to/custom-request-and-route.md create mode 100644 docs/ko/docs/how-to/extending-openapi.md create mode 100644 docs/ko/docs/how-to/general.md create mode 100644 docs/ko/docs/how-to/graphql.md create mode 100644 docs/ko/docs/how-to/index.md create mode 100644 docs/ko/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md create mode 100644 docs/ko/docs/how-to/separate-openapi-schemas.md create mode 100644 docs/ko/docs/how-to/testing-database.md create mode 100644 docs/ko/docs/tutorial/bigger-applications.md create mode 100644 docs/ko/docs/tutorial/body-updates.md create mode 100644 docs/ko/docs/tutorial/dependencies/sub-dependencies.md create mode 100644 docs/ko/docs/tutorial/handling-errors.md create mode 100644 docs/ko/docs/tutorial/security/first-steps.md create mode 100644 docs/ko/docs/tutorial/security/index.md diff --git a/docs/ko/docs/_llm-test.md b/docs/ko/docs/_llm-test.md new file mode 100644 index 000000000..1b828c663 --- /dev/null +++ b/docs/ko/docs/_llm-test.md @@ -0,0 +1,503 @@ +# LLM ํ…Œ์ŠคํŠธ ํŒŒ์ผ { #llm-test-file } + +์ด ๋ฌธ์„œ๋Š” ๋ฌธ์„œ๋ฅผ ๋ฒˆ์—ญํ•˜๋Š” LLM์ด `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 ๋ณ€๊ฒฝ์„ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๊ทธ ์ด์œ ๋Š” LLM์€ ๊ฒฐ์ •๋ก ์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค). + +ํ…Œ์ŠคํŠธ: + +## ์ฝ”๋“œ ์Šค๋‹ˆํŽซ { #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 +$ fastapi run main.py + FastAPI 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} +* ์™ธ๋ถ€ ๋งํฌ +* ์Šคํƒ€์ผ๋กœ ๊ฐ€๋Š” ๋งํฌ +* ์Šคํฌ๋ฆฝํŠธ๋กœ ๊ฐ€๋Š” ๋งํฌ +* ์ด๋ฏธ์ง€๋กœ ๊ฐ€๋Š” ๋งํฌ + +๋งํฌ ํ…์ŠคํŠธ๋Š” ๋ฒˆ์—ญ๋˜์–ด์•ผ ํ•˜๊ณ , ๋งํฌ ์ฃผ์†Œ๋Š” ๋ฒˆ์—ญ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€๋ฆฌ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค: + +* FastAPI ๋งํฌ + +//// + +//// tab | ์ •๋ณด + +๋งํฌ๋Š” ๋ฒˆ์—ญ๋˜์–ด์•ผ ํ•˜์ง€๋งŒ, ์ฃผ์†Œ๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์™ธ๋Š” FastAPI ๋ฌธ์„œ ํŽ˜์ด์ง€๋กœ ํ–ฅํ•˜๋Š” ์ ˆ๋Œ€ ๋งํฌ์ด๋ฉฐ, ์ด ๊ฒฝ์šฐ ๋ฒˆ์—ญ ํŽ˜์ด์ง€๋กœ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +`scripts/translate.py`์˜ ์ผ๋ฐ˜ ํ”„๋กฌํ”„ํŠธ์—์„œ `### Links` ์„น์…˜์„ ์ฐธ๊ณ ํ•˜์„ธ์š”. + +//// + +## HTML "abbr" ์š”์†Œ { #html-abbr-elements } + +//// tab | ํ…Œ์ŠคํŠธ + +์—ฌ๊ธฐ HTML "abbr" ์š”์†Œ๋กœ ๊ฐ์‹ผ ๋ช‡ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค(์ผ๋ถ€๋Š” ์ž„์˜๋กœ ๋งŒ๋“  ๊ฒƒ์ž…๋‹ˆ๋‹ค): + +### abbr๊ฐ€ ์ „์ฒด ๋ฌธ๊ตฌ๋ฅผ ์ œ๊ณต { #the-abbr-gives-a-full-phrase } + +* GTD +* lt +* XWT +* PSGI + +### abbr๊ฐ€ ์„ค๋ช…์„ ์ œ๊ณต { #the-abbr-gives-an-explanation } + +* cluster +* Deep Learning + +### abbr๊ฐ€ ์ „์ฒด ๋ฌธ๊ตฌ์™€ ์„ค๋ช…์„ ์ œ๊ณต { #the-abbr-gives-a-full-phrase-and-an-explanation } + +* MDN +* I/O. + +//// + +//// 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` ์„น์…˜์„ ์ฐธ๊ณ ํ•˜์„ธ์š”. + +//// diff --git a/docs/ko/docs/advanced/additional-responses.md b/docs/ko/docs/advanced/additional-responses.md new file mode 100644 index 000000000..a6f51f5b9 --- /dev/null +++ b/docs/ko/docs/advanced/additional-responses.md @@ -0,0 +1,247 @@ +# 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 ๋ฌธ์„œ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค: + + + +## ๋ฏธ๋ฆฌ ์ •์˜๋œ ์‘๋‹ต๊ณผ ์ปค์Šคํ…€ ์‘๋‹ต ๊ฒฐํ•ฉํ•˜๊ธฐ { #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 ์‚ฌ์–‘์˜ ๋‹ค์Œ ์„น์…˜์„ ํ™•์ธํ•˜์„ธ์š”: + +* OpenAPI Responses Object: `Response Object`๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. +* OpenAPI Response Object: `responses` ํŒŒ๋ผ๋ฏธํ„ฐ ์•ˆ์˜ ๊ฐ ์‘๋‹ต์— ์ด๊ฒƒ์˜ ์–ด๋–ค ํ•ญ๋ชฉ์ด๋“  ์ง์ ‘ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. `description`, `headers`, `content`(์—ฌ๊ธฐ์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ๋ฏธ๋””์–ด ํƒ€์ž…๊ณผ JSON Schema๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค), `links` ๋“ฑ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/advanced/behind-a-proxy.md b/docs/ko/docs/advanced/behind-a-proxy.md new file mode 100644 index 000000000..92bddac51 --- /dev/null +++ b/docs/ko/docs/advanced/behind-a-proxy.md @@ -0,0 +1,466 @@ +# ํ”„๋ก์‹œ ๋’ค์—์„œ ์‹คํ–‰ํ•˜๊ธฐ { #behind-a-proxy } + +๋งŽ์€ ๊ฒฝ์šฐ FastAPI ์•ฑ ์•ž๋‹จ์— Traefik์ด๋‚˜ Nginx ๊ฐ™์€ **ํ”„๋ก์‹œ(proxy)**๋ฅผ ๋‘๊ณ  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +์ด๋Ÿฐ ํ”„๋ก์‹œ๋Š” HTTPS ์ธ์ฆ์„œ ์ฒ˜๋ฆฌ ๋“ฑ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋‹ด๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ํ”„๋ก์‹œ ์ „๋‹ฌ ํ—ค๋” { #proxy-forwarded-headers } + +์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•ž๋‹จ์˜ **ํ”„๋ก์‹œ**๋Š” ๋ณดํ†ต **์„œ๋ฒ„**๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์—, ํ•ด๋‹น ์š”์ฒญ์ด ํ”„๋ก์‹œ์— ์˜ํ•ด **์ „๋‹ฌ(forwarded)**๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์„œ๋ฒ„๊ฐ€ ์•Œ ์ˆ˜ ์žˆ๋„๋ก ๋ช‡๋ช‡ ํ—ค๋”๋ฅผ ๋™์ ์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„๋Š” ๋„๋ฉ”์ธ์„ ํฌํ•จํ•œ ์›๋ž˜์˜ (๊ณต๊ฐœ) URL, HTTPS ์‚ฌ์šฉ ์—ฌ๋ถ€ ๋“ฑ ์ •๋ณด๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +**์„œ๋ฒ„** ํ”„๋กœ๊ทธ๋žจ(์˜ˆ: **FastAPI CLI**๋ฅผ ํ†ตํ•ด ์‹คํ–‰๋˜๋Š” **Uvicorn**)์€ ์ด๋Ÿฐ ํ—ค๋”๋ฅผ ํ•ด์„ํ•  ์ˆ˜ ์žˆ๊ณ , ๊ทธ ์ •๋ณด๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ๋ณด์•ˆ์ƒ, ์„œ๋ฒ„๋Š” ์ž์‹ ์ด ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋ก์‹œ ๋’ค์— ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ชจ๋ฅด๋ฉด ํ•ด๋‹น ํ—ค๋”๋ฅผ ํ•ด์„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +/// note | ๊ธฐ์ˆ  ์„ธ๋ถ€์‚ฌํ•ญ + +ํ”„๋ก์‹œ ํ—ค๋”๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + +* X-Forwarded-For +* X-Forwarded-Proto +* X-Forwarded-Host + +/// + +### ํ”„๋ก์‹œ ์ „๋‹ฌ ํ—ค๋” ํ™œ์„ฑํ™”ํ•˜๊ธฐ { #enable-proxy-forwarded-headers } + +FastAPI CLI๋ฅผ *CLI ์˜ต์…˜* `--forwarded-allow-ips`๋กœ ์‹คํ–‰ํ•˜๊ณ , ์ „๋‹ฌ ํ—ค๋”๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์‹ ๋ขฐํ•  IP ์ฃผ์†Œ๋“ค์„ ๋„˜๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +`--forwarded-allow-ips="*"`๋กœ ์„ค์ •ํ•˜๋ฉด ๋“ค์–ด์˜ค๋Š” ๋ชจ๋“  IP๋ฅผ ์‹ ๋ขฐํ•ฉ๋‹ˆ๋‹ค. + +**์„œ๋ฒ„**๊ฐ€ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” **ํ”„๋ก์‹œ** ๋’ค์— ์žˆ๊ณ  ํ”„๋ก์‹œ๋งŒ ์„œ๋ฒ„์— ์ ‘๊ทผํ•œ๋‹ค๋ฉด, ์ด๋Š” ํ•ด๋‹น **ํ”„๋ก์‹œ**์˜ IP๊ฐ€ ๋ฌด์—‡์ด๋“  ๊ฐ„์— ๋ฐ›์•„๋“ค์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. + +
+ +```console +$ fastapi run --forwarded-allow-ips="*" + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +### 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
Host: mysuperapp.com
Path: /items + + Note over Proxy: Proxy adds forwarded headers + + Proxy->>Server: HTTP Request
X-Forwarded-For: [client IP]
X-Forwarded-Proto: https
X-Forwarded-Host: mysuperapp.com
Path: /items + + Note over Server: Server interprets headers
(if --forwarded-allow-ips is set) + + Server->>Proxy: HTTP Response
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`๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +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์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹œ์ž‘ํ•˜๋ฉด: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +์‘๋‹ต์€ ๋‹ค์Œ๊ณผ ๋น„์Šทํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค: + +```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`๋ฅผ ์•ฑ์— ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ ์™ธ์—๋Š” ๋‹ค๋ฅธ ์šฉ๋„๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ๊ธฐ์–ตํ•˜์„ธ์š”. + +ํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ €๋กœ http://127.0.0.1:8000/app์— ์ ‘์†ํ•˜๋ฉด ์ •์ƒ ์‘๋‹ต์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +```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 } + +Traefik์„ ์‚ฌ์šฉํ•˜๋ฉด, ๊ฒฝ๋กœ ์ ‘๋‘์‚ฌ๊ฐ€ ์ œ๊ฑฐ๋˜๋Š” ๊ตฌ์„ฑ์„ ๋กœ์ปฌ์—์„œ ์‰ฝ๊ฒŒ ์‹คํ—˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +Traefik ๋‹ค์šด๋กœ๋“œ๋Š” ๋‹จ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ์ด๋ฉฐ, ์••์ถ• ํŒŒ์ผ์„ ํ’€๊ณ  ํ„ฐ๋ฏธ๋„์—์„œ ๋ฐ”๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ ๋‹ค์Œ ๋‹ค์Œ ๋‚ด์šฉ์„ ๊ฐ€์ง„ `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์„ ์‹œ์ž‘ํ•˜์„ธ์š”: + +
+ +```console +$ ./traefik --configFile=traefik.toml + +INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml +``` + +
+ +๊ทธ๋ฆฌ๊ณ  `--root-path` ์˜ต์…˜์„ ์‚ฌ์šฉํ•ด ์•ฑ์„ ์‹œ์ž‘ํ•˜์„ธ์š”: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +### ์‘๋‹ต ํ™•์ธํ•˜๊ธฐ { #check-the-responses } + +์ด์ œ Uvicorn์˜ ํฌํŠธ๋กœ ๋œ URL์ธ http://127.0.0.1:8000/app๋กœ ์ ‘์†ํ•˜๋ฉด ์ •์ƒ ์‘๋‹ต์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +/// tip | ํŒ + +`http://127.0.0.1:8000/app`๋กœ ์ ‘๊ทผํ–ˆ๋Š”๋ฐ๋„ `/api/v1`์˜ `root_path`๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•˜์„ธ์š”. ์ด๋Š” ์˜ต์…˜ `--root-path`์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’์ž…๋‹ˆ๋‹ค. + +/// + +์ด์ œ Traefik์˜ ํฌํŠธ๊ฐ€ ํฌํ•จ๋˜๊ณ  ๊ฒฝ๋กœ ์ ‘๋‘์‚ฌ๊ฐ€ ํฌํ•จ๋œ URL http://127.0.0.1:9999/api/v1/app์„ ์—ฌ์„ธ์š”. + +๋™์ผํ•œ ์‘๋‹ต์„ ์–ป์Šต๋‹ˆ๋‹ค: + +```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๋ฅผ ์‹œ๋„ํ•˜๋ฉด, ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ๋œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +http://127.0.0.1:8000/docs์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + + +ํ•˜์ง€๋งŒ ํ”„๋ก์‹œ(ํฌํŠธ `9999`)๋ฅผ ์‚ฌ์šฉํ•ด "๊ณต์‹" URL์ธ `/api/v1/docs`์—์„œ docs UI์— ์ ‘๊ทผํ•˜๋ฉด, ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค! ๐ŸŽ‰ + +http://127.0.0.1:9999/api/v1/docs์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + + +์›ํ•˜๋˜ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค. โœ”๏ธ + +์ด๋Š” 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์— ์ฃผ๋ชฉํ•˜์„ธ์š”. + +/// + +http://127.0.0.1:9999/api/v1/docs์˜ docs UI์—์„œ๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค: + + + +/// 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`๋ฅผ ๋˜‘๋˜‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ๊ทธ๋ƒฅ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. โœจ diff --git a/docs/ko/docs/advanced/dataclasses.md b/docs/ko/docs/advanced/dataclasses.md new file mode 100644 index 000000000..92ad5545b --- /dev/null +++ b/docs/ko/docs/advanced/dataclasses.md @@ -0,0 +1,95 @@ +# Dataclasses ์‚ฌ์šฉํ•˜๊ธฐ { #using-dataclasses } + +FastAPI๋Š” **Pydantic** ์œ„์— ๊ตฌ์ถ•๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ง€๊ธˆ๊นŒ์ง€๋Š” Pydantic ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•ด ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ์„ ์–ธํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ๋“œ๋ ธ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ FastAPI๋Š” `dataclasses`๋„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค: + +{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *} + +์ด๋Š” **Pydantic** ๋•๋ถ„์— ์—ฌ์ „ํžˆ ์ง€์›๋˜๋Š”๋ฐ, Pydantic์ด `dataclasses`์— ๋Œ€ํ•œ ๋‚ด๋ถ€ ์ง€์›์„ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ 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 ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค: + + + +## ์ค‘์ฒฉ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์—์„œ 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์œผ๋กœ serializingํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +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 ๋ชจ๋ธ๊ณผ ์กฐํ•ฉํ•˜๊ฑฐ๋‚˜, ์ด๋ฅผ ์ƒ์†ํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ๋ถ„์˜ ๋ชจ๋ธ์— ํฌํ•จํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ž์„ธํ•œ ๋‚ด์šฉ์€ dataclasses์— ๊ด€ํ•œ Pydantic ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. + +## ๋ฒ„์ „ { #version } + +์ด ๊ธฐ๋Šฅ์€ FastAPI `0.67.0` ๋ฒ„์ „๋ถ€ํ„ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ”– diff --git a/docs/ko/docs/advanced/generate-clients.md b/docs/ko/docs/advanced/generate-clients.md new file mode 100644 index 000000000..1def3efe1 --- /dev/null +++ b/docs/ko/docs/advanced/generate-clients.md @@ -0,0 +1,208 @@ +# SDK ์ƒ์„ฑํ•˜๊ธฐ { #generating-sdks } + +**FastAPI**๋Š” **OpenAPI** ์‚ฌ์–‘์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฏ€๋กœ, FastAPI์˜ API๋Š” ๋งŽ์€ ๋„๊ตฌ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ํ‘œ์ค€ ํ˜•์‹์œผ๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋•๋ถ„์— ์—ฌ๋Ÿฌ ์–ธ์–ด์šฉ ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(**SDKs**), ์ตœ์‹  **๋ฌธ์„œ**, ๊ทธ๋ฆฌ๊ณ  ์ฝ”๋“œ์™€ ๋™๊ธฐํ™”๋œ **ํ…Œ์ŠคํŠธ** ๋˜๋Š” **์ž๋™ํ™” ์›Œํฌํ”Œ๋กœ**๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” FastAPI ๋ฐฑ์—”๋“œ์šฉ **TypeScript SDK**๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›๋‹ˆ๋‹ค. + +## ์˜คํ”ˆ ์†Œ์Šค SDK ์ƒ์„ฑ๊ธฐ { #open-source-sdk-generators } + +๋‹ค์–‘ํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜์œผ๋กœ OpenAPI Generator๊ฐ€ ์žˆ์œผ๋ฉฐ, **๋‹ค์–‘ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด**๋ฅผ ์ง€์›ํ•˜๊ณ  OpenAPI ์‚ฌ์–‘์œผ๋กœ๋ถ€ํ„ฐ SDK๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +**TypeScript ํด๋ผ์ด์–ธํŠธ**์˜ ๊ฒฝ์šฐ Hey API๋Š” TypeScript ์ƒํƒœ๊ณ„์— ์ตœ์ ํ™”๋œ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ๋ชฉ์ ์— ๋งž๊ฒŒ ์„ค๊ณ„๋œ ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค. + +๋” ๋งŽ์€ SDK ์ƒ์„ฑ๊ธฐ๋Š” OpenAPI.Tools์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +/// 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๋ฅผ ์ง€์›ํ•˜๋Š” ๋ฐ์—๋„ ๊ด€์‹ฌ์ด ์žˆ์Œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ๐Ÿ™‡ + +์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ์„ ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +* Speakeasy +* Stainless +* liblab + +์ด ์ค‘ ์ผ๋ถ€๋Š” ์˜คํ”ˆ ์†Œ์Šค์ด๊ฑฐ๋‚˜ ๋ฌด๋ฃŒ ํ‹ฐ์–ด๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ, ๋น„์šฉ ๋ถ€๋‹ด ์—†์ด ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ƒ์šฉ 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)**๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + + +์ด ์Šคํ‚ค๋งˆ๋Š” ์•ฑ์—์„œ ๋ชจ๋ธ๋กœ ์„ ์–ธ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ ์ •๋ณด๋Š” ์•ฑ์˜ **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๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. + +`@hey-api/openapi-ts` ์„ค์น˜ ๋ฐฉ๋ฒ•๊ณผ ์ƒ์„ฑ๋œ ๊ฒฐ๊ณผ๋ฌผ์€ ํ•ด๋‹น ์›น์‚ฌ์ดํŠธ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### SDK ์‚ฌ์šฉํ•˜๊ธฐ { #using-the-sdk } + +์ด์ œ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋ฅผ importํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ์ž๋™ ์™„์„ฑ์ด ์ œ๊ณต๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + + +๋ณด๋‚ผ ํŽ˜์ด๋กœ๋“œ์— ๋Œ€ํ•ด์„œ๋„ ์ž๋™ ์™„์„ฑ์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค: + + + +/// tip | ํŒ + +`name`๊ณผ `price`์— ๋Œ€ํ•œ ์ž๋™ ์™„์„ฑ์€ FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ `Item` ๋ชจ๋ธ์— ์ •์˜๋œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. + +/// + +์ „์†กํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ์ธ๋ผ์ธ ์˜ค๋ฅ˜๋„ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค: + + + +์‘๋‹ต ๊ฐ์ฒด๋„ ์ž๋™ ์™„์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค: + + + +## ํƒœ๊ทธ๊ฐ€ ์žˆ๋Š” 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 ์•ฑ์— ๋Œ€ํ•ด ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด, ์ผ๋ฐ˜์ ์œผ๋กœ ์ƒ์„ฑ๋œ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋„ ํƒœ๊ทธ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ถ„๋ฆฌ๋ฉ๋‹ˆ๋‹ค. + +์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์—์„œ ํ•ญ๋ชฉ๋“ค์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ •๋ ฌ๋˜๊ณ  ๊ทธ๋ฃนํ™”๋ฉ๋‹ˆ๋‹ค: + + + +์ด ๊ฒฝ์šฐ ๋‹ค์Œ์ด ์žˆ์Šต๋‹ˆ๋‹ค: + +* `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 } + +์ด์ œ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋‹ค์‹œ ์ƒ์„ฑํ•˜๋ฉด, ๊ฐœ์„ ๋œ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + + +๋ณด์‹œ๋‹ค์‹œํ”ผ, ์ด์ œ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์€ ํƒœ๊ทธ ๋‹ค์Œ์— ํ•จ์ˆ˜ ์ด๋ฆ„์ด ์˜ค๋ฉฐ, 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 +``` + +์ƒˆ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ƒ์„ฑํ•œ ํ›„์—๋Š” **๊น”๋”ํ•œ ๋ฉ”์„œ๋“œ ์ด๋ฆ„**์„ ๊ฐ€์ง€๋ฉด์„œ๋„, **์ž๋™ ์™„์„ฑ**, **์ธ๋ผ์ธ ์˜ค๋ฅ˜** ๋“ฑ์€ ๊ทธ๋Œ€๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค: + + + +## ์žฅ์  { #benefits } + +์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ์— ๋Œ€ํ•ด **์ž๋™ ์™„์„ฑ**์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +* ๋ฉ”์„œ๋“œ +* ๋ณธ๋ฌธ(body)์˜ ์š”์ฒญ ํŽ˜์ด๋กœ๋“œ, ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ๋“ฑ +* ์‘๋‹ต ํŽ˜์ด๋กœ๋“œ + +๋˜ํ•œ ๋ชจ๋“  ๊ฒƒ์— ๋Œ€ํ•ด **์ธ๋ผ์ธ ์˜ค๋ฅ˜**๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋ฆฌ๊ณ  ๋ฐฑ์—”๋“œ ์ฝ”๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ ๋’ค ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ **์žฌ์ƒ์„ฑ(regenerate)**ํ•˜๋ฉด, ์ƒˆ *path operations*๊ฐ€ ๋ฉ”์„œ๋“œ๋กœ ์ถ”๊ฐ€๋˜๊ณ  ๊ธฐ์กด ๊ฒƒ์€ ์ œ๊ฑฐ๋˜๋ฉฐ, ๊ทธ ๋ฐ–์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋„ ์ƒ์„ฑ๋œ ์ฝ”๋“œ์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๐Ÿค“ + +์ด๋Š” ๋ฌด์–ธ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๊ทธ ๋ณ€๊ฒฝ์ด ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์—๋„ ์ž๋™์œผ๋กœ **๋ฐ˜์˜**๋œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ํด๋ผ์ด์–ธํŠธ๋ฅผ **๋นŒ๋“œ(build)**ํ•˜๋ฉด ์‚ฌ์šฉ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ **๋ถˆ์ผ์น˜(mismatch)**ํ•  ๊ฒฝ์šฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ์ตœ์ข… ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ค๋ฅ˜๊ฐ€ ๋…ธ์ถœ๋œ ๋’ค ๋ฌธ์ œ๋ฅผ ์ถ”์ ํ•˜๋Š” ๋Œ€์‹ , ๊ฐœ๋ฐœ ์‚ฌ์ดํด ์ดˆ๊ธฐ์— **๋งŽ์€ ์˜ค๋ฅ˜๋ฅผ ๋งค์šฐ ๋นจ๋ฆฌ ๊ฐ์ง€**ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. โœจ diff --git a/docs/ko/docs/advanced/middleware.md b/docs/ko/docs/advanced/middleware.md new file mode 100644 index 000000000..be2c972a6 --- /dev/null +++ b/docs/ko/docs/advanced/middleware.md @@ -0,0 +1,97 @@ +# ๊ณ ๊ธ‰ 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๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ASGI ์‚ฌ์–‘์„ ๊ตฌํ˜„ํ•˜๋ฏ€๋กœ, ์–ด๋–ค 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๋„ ๋งŽ์ด ์žˆ์Šต๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด: + +* Uvicorn์˜ `ProxyHeadersMiddleware` +* MessagePack + +์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋‹ค๋ฅธ middleware๋ฅผ ๋ณด๋ ค๋ฉด Starlette์˜ Middleware ๋ฌธ์„œ์™€ ASGI Awesome List๋ฅผ ํ™•์ธํ•˜์„ธ์š”. diff --git a/docs/ko/docs/advanced/openapi-callbacks.md b/docs/ko/docs/advanced/openapi-callbacks.md new file mode 100644 index 000000000..e4bdea9d6 --- /dev/null +++ b/docs/ko/docs/advanced/openapi-callbacks.md @@ -0,0 +1,186 @@ +# 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์˜ Url ํƒ€์ž…์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +/// + +์œ ์ผํ•˜๊ฒŒ ์ƒˆ๋กœ์šด ๊ฒƒ์€ *๊ฒฝ๋กœ ์ฒ˜๋ฆฌ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ*์˜ ์ธ์ž๋กœ `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 ์š”์ฒญ์ž…๋‹ˆ๋‹ค. + +์ฝœ๋ฐฑ์„ ์ง์ ‘ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” HTTPX๋‚˜ Requests ๊ฐ™์€ ๊ฒƒ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +/// + +## ์ฝœ๋ฐฑ ๋ฌธ์„œํ™” ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ { #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*์—๋Š” OpenAPI 3 expression(์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ์ฐธ๊ณ )์ด ํฌํ•จ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด *๋‹น์‹ ์˜ API*๋กœ ๋ณด๋‚ด์ง„ ์›๋ž˜ ์š”์ฒญ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ผ๋ถ€ ๊ฐ’์„ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### ์ฝœ๋ฐฑ ๊ฒฝ๋กœ ํ‘œํ˜„์‹ { #the-callback-path-expression } + +์ฝœ๋ฐฑ *path*๋Š” *๋‹น์‹ ์˜ API*๋กœ ๋ณด๋‚ด์ง„ ์›๋ž˜ ์š”์ฒญ์˜ ์ผ๋ถ€๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” OpenAPI 3 expression์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝ์šฐ, ๋‹ค์Œ `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 } + +์ด์ œ ์•ฑ์„ ์‹คํ–‰ํ•˜๊ณ  http://127.0.0.1:8000/docs๋กœ ์ด๋™ํ•˜์„ธ์š”. + +*๊ฒฝ๋กœ ์ฒ˜๋ฆฌ*์— ๋Œ€ํ•ด "Callbacks" ์„น์…˜์„ ํฌํ•จํ•œ ๋ฌธ์„œ๊ฐ€ ํ‘œ์‹œ๋˜๋ฉฐ, *external API*๊ฐ€ ์–ด๋–ค ํ˜•ํƒœ์—ฌ์•ผ ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + diff --git a/docs/ko/docs/advanced/openapi-webhooks.md b/docs/ko/docs/advanced/openapi-webhooks.md new file mode 100644 index 000000000..89cacf7b7 --- /dev/null +++ b/docs/ko/docs/advanced/openapi-webhooks.md @@ -0,0 +1,55 @@ +# 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 } + +์ด์ œ ์•ฑ์„ ์‹คํ–‰ํ•˜๊ณ  http://127.0.0.1:8000/docs๋กœ ์ด๋™ํ•˜์„ธ์š”. + +๋ฌธ์„œ์— ์ผ๋ฐ˜์ ์ธ *๊ฒฝ๋กœ ์ฒ˜๋ฆฌ*๊ฐ€ ๋ณด์ด๊ณ , ์ด์ œ๋Š” ์ผ๋ถ€ **webhooks**๋„ ํ•จ๊ป˜ ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค: + + diff --git a/docs/ko/docs/advanced/path-operation-advanced-configuration.md b/docs/ko/docs/advanced/path-operation-advanced-configuration.md new file mode 100644 index 000000000..f20fa6d26 --- /dev/null +++ b/docs/ko/docs/advanced/path-operation-advanced-configuration.md @@ -0,0 +1,172 @@ +# ๊ฒฝ๋กœ ์ฒ˜๋ฆฌ ๊ณ ๊ธ‰ ๊ตฌ์„ฑ { #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 ๋ช…์„ธ์—์„œ๋Š” ์ด๋ฅผ Operation Object๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. + +/// + +์—ฌ๊ธฐ์—๋Š” *๊ฒฝ๋กœ ์ฒ˜๋ฆฌ*์— ๋Œ€ํ•œ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ์žˆ์œผ๋ฉฐ, ์ž๋™ ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +`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 ๋ฌธ์„œ๋ฅผ ์—ด๋ฉด, ํ•ด๋‹น ํŠน์ • *๊ฒฝ๋กœ ์ฒ˜๋ฆฌ*์˜ ํ•˜๋‹จ์— ํ™•์žฅ์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. + + + +๋˜ํ•œ 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์œผ๋กœ parsed๋˜์ง€๋„ ์•Š๊ณ , `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 ๋ชจ๋ธ์„ ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๊ฒ€์ฆํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +/// diff --git a/docs/ko/docs/advanced/security/http-basic-auth.md b/docs/ko/docs/advanced/security/http-basic-auth.md new file mode 100644 index 000000000..611aad795 --- /dev/null +++ b/docs/ko/docs/advanced/security/http-basic-auth.md @@ -0,0 +1,107 @@ +# 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" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด) ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์‚ฌ์šฉ์ž๋ช…๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฌผ์–ด๋ด…๋‹ˆ๋‹ค: + + + +## ์‚ฌ์šฉ์ž๋ช… ํ™•์ธํ•˜๊ธฐ { #check-the-username } + +๋” ์™„์ „ํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. + +dependency๋ฅผ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž๋ช…๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜์„ธ์š”. + +์ด๋ฅผ ์œ„ํ•ด Python ํ‘œ์ค€ ๋ชจ๋“ˆ `secrets`๋ฅผ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž๋ช…๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + +`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] *} diff --git a/docs/ko/docs/advanced/security/index.md b/docs/ko/docs/advanced/security/index.md new file mode 100644 index 000000000..4c7abfacc --- /dev/null +++ b/docs/ko/docs/advanced/security/index.md @@ -0,0 +1,19 @@ +# ๊ณ ๊ธ‰ ๋ณด์•ˆ { #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}์„ ์ด๋ฏธ ์ฝ์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. + +๋ชจ๋‘ ๋™์ผํ•œ ๊ฐœ๋…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜์ง€๋งŒ, ๋ช‡ ๊ฐ€์ง€ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/advanced/security/oauth2-scopes.md b/docs/ko/docs/advanced/security/oauth2-scopes.md new file mode 100644 index 000000000..0f90f92ae --- /dev/null +++ b/docs/ko/docs/advanced/security/oauth2-scopes.md @@ -0,0 +1,274 @@ +# 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 ๋“ฑ์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๋ฉด์„œ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค: + + + +## ์Šค์ฝ”ํ”„๋ฅผ ํฌํ•จํ•œ 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 ๋ฌธ์„œ๋ฅผ ์—ด๋ฉด, ์ธ์ฆํ•˜๊ณ  ์ธ๊ฐ€ํ•  ์Šค์ฝ”ํ”„๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + + + +์–ด๋–ค ์Šค์ฝ”ํ”„๋„ ์„ ํƒํ•˜์ง€ ์•Š์œผ๋ฉด โ€œ์ธ์ฆโ€์€ ๋˜์ง€๋งŒ, `/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`๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/advanced/settings.md b/docs/ko/docs/advanced/settings.md new file mode 100644 index 000000000..6fa7c644c --- /dev/null +++ b/docs/ko/docs/advanced/settings.md @@ -0,0 +1,302 @@ +# ์„ค์ •๊ณผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ { #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์€ Pydantic: Settings management๋ฅผ ํ†ตํ•ด ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ์˜ค๋Š” ์ด๋Ÿฌํ•œ ์„ค์ •์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ›Œ๋ฅญํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +### `pydantic-settings` ์„ค์น˜ํ•˜๊ธฐ { #install-pydantic-settings } + +๋จผ์ € [๊ฐ€์ƒ ํ™˜๊ฒฝ](../virtual-environments.md){.internal-link target=_blank}์„ ๋งŒ๋“ค๊ณ  ํ™œ์„ฑํ™”ํ•œ ๋‹ค์Œ, `pydantic-settings` ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜์„ธ์š”: + +
+ +```console +$ pip install pydantic-settings +---> 100% +``` + +
+ +๋˜๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ `all` extras๋ฅผ ์„ค์น˜ํ•˜๋ฉด ํ•จ๊ป˜ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค: + +
+ +```console +$ pip install "fastapi[all]" +---> 100% +``` + +
+ +### `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`์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +
+ +```console +$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +/// 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์€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ์ด๋Ÿฐ ์œ ํ˜•์˜ ํŒŒ์ผ์—์„œ ์ฝ๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Pydantic Settings: Dotenv (.env) support๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. + +/// 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 ์„ค์ •์„ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Pydantic: Concepts: Configuration์„ ์ฐธ๊ณ ํ•˜์„ธ์š”. + +/// + +์—ฌ๊ธฐ์„œ๋Š” 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`์— ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ž์„ธํ•œ ๋‚ด์šฉ์€ `@lru_cache`์— ๋Œ€ํ•œ Python ๋ฌธ์„œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ์ •๋ฆฌ { #recap } + +Pydantic Settings๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Pydantic ๋ชจ๋ธ์˜ ๋ชจ๋“  ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ค์ • ๋˜๋Š” ๊ตฌ์„ฑ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +* ์˜์กด์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ๋‹จ์ˆœํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +* `.env` ํŒŒ์ผ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +* `@lru_cache`๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ ์š”์ฒญ๋งˆ๋‹ค dotenv ํŒŒ์ผ์„ ๋ฐ˜๋ณตํ•ด์„œ ์ฝ๋Š” ๊ฒƒ์„ ํ”ผํ•˜๋ฉด์„œ๋„, ํ…Œ์ŠคํŠธ ์ค‘์—๋Š” ์ด๋ฅผ overrideํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/alternatives.md b/docs/ko/docs/alternatives.md new file mode 100644 index 000000000..d8c2df2d8 --- /dev/null +++ b/docs/ko/docs/alternatives.md @@ -0,0 +1,485 @@ +# ๋Œ€์•ˆ, ์˜๊ฐ, ๋น„๊ต { #alternatives-inspiration-and-comparisons } + +**FastAPI**์— ์˜๊ฐ์„ ์ค€ ๊ฒƒ๋“ค, ๋Œ€์•ˆ๊ณผ์˜ ๋น„๊ต, ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋กœ๋ถ€ํ„ฐ ๋ฌด์—‡์„ ๋ฐฐ์› ๋Š”์ง€์— ๋Œ€ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. + +## ์†Œ๊ฐœ { #intro } + +๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์˜ ์ด์ „ ์ž‘์—…์ด ์—†์—ˆ๋‹ค๋ฉด **FastAPI**๋Š” ์กด์žฌํ•˜์ง€ ์•Š์•˜์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +๊ทธ ์ „์— ๋งŒ๋“ค์–ด์ง„ ๋งŽ์€ ๋„๊ตฌ๋“ค์ด **FastAPI**์˜ ํƒ„์ƒ์— ์˜๊ฐ์„ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. + +์ €๋Š” ์—ฌ๋Ÿฌ ํ•ด ๋™์•ˆ ์ƒˆ๋กœ์šด framework๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋จผ์ € **FastAPI**๊ฐ€ ๋‹ค๋ฃจ๋Š” ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์—ฌ๋Ÿฌ ์„œ๋กœ ๋‹ค๋ฅธ framework, plug-in, ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด ํ•ด๊ฒฐํ•ด ๋ณด๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ์–ด๋А ์‹œ์ ์—๋Š”, ์ด์ „ ๋„๊ตฌ๋“ค์˜ ๊ฐ€์žฅ ์ข‹์€ ์•„์ด๋””์–ด๋ฅผ ๊ฐ€์ ธ์™€ ๊ฐ€๋Šฅํ•œ ์ตœ์„ ์˜ ๋ฐฉ์‹์œผ๋กœ ์กฐํ•ฉํ•˜๊ณ , ์ด์ „์—๋Š” ์กด์žฌํ•˜์ง€ ์•Š์•˜๋˜ ์–ธ์–ด ๊ธฐ๋Šฅ(Python 3.6+ type hints)์„ ํ™œ์šฉํ•ด ์ด ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฌด์–ธ๊ฐ€๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ ์™ธ์—๋Š” ๋‹ค๋ฅธ ์„ ํƒ์ง€๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. + +## ์ด์ „ ๋„๊ตฌ๋“ค { #previous-tools } + +### Django { #django } + +๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” Python framework์ด๋ฉฐ ๋„๋ฆฌ ์‹ ๋ขฐ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Instagram ๊ฐ™์€ ์‹œ์Šคํ…œ์„ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +์ƒ๋Œ€์ ์œผ๋กœ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(์˜ˆ: MySQL ๋˜๋Š” PostgreSQL)์™€ ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด ์žˆ์–ด์„œ, NoSQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(์˜ˆ: Couchbase, MongoDB, Cassandra ๋“ฑ)๋ฅผ ์ฃผ ์ €์žฅ ์—”์ง„์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๊ทธ๋ฆฌ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +๋ฐฑ์—”๋“œ์—์„œ HTML์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์กŒ์ง€, ํ˜„๋Œ€์ ์ธ ํ”„๋ŸฐํŠธ์—”๋“œ(์˜ˆ: React, Vue.js, Angular)๋‚˜ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ(์˜ˆ: IoT ๊ธฐ๊ธฐ)์—์„œ ์‚ฌ์šฉ๋˜๋Š” API๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์„ค๊ณ„๋œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. + +### Django REST Framework { #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 ๋ฌธ์„œํ™” ์›น ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๊ธฐ. + +/// + +### Flask { #flask } + +Flask๋Š” "microframework"๋กœ, Django์— ๊ธฐ๋ณธ์œผ๋กœ ํฌํ•จ๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ†ตํ•ฉ์ด๋‚˜ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ๋“ค์„ ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +์ด ๋‹จ์ˆœํ•จ๊ณผ ์œ ์—ฐ์„ฑ ๋•๋ถ„์— NoSQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ฃผ ๋ฐ์ดํ„ฐ ์ €์žฅ ์‹œ์Šคํ…œ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ™์€ ์ž‘์—…์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + +๋งค์šฐ ๋‹จ์ˆœํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„๊ต์  ์ง๊ด€์ ์œผ๋กœ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฌธ์„œ๊ฐ€ ์–ด๋–ค ์ง€์ ์—์„œ๋Š” ๋‹ค์†Œ ๊ธฐ์ˆ ์ ์œผ๋กœ ๊นŠ์–ด์ง€๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. + +๋˜ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ, ํ˜น์€ Django์— ๋ฏธ๋ฆฌ ๊ตฌ์ถ•๋˜์–ด ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์ด ๊ผญ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—๋„ ํ”ํžˆ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ด๋Ÿฐ ๊ธฐ๋Šฅ๋“ค ์ค‘ ๋‹ค์ˆ˜๋Š” plug-in์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฐ ๊ตฌ์„ฑ์š”์†Œ์˜ ๋ถ„๋ฆฌ์™€, ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์ •ํ™•ํžˆ ๋ง๋ถ™์—ฌ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” "microframework"๋ผ๋Š” ์ ์€ ์ œ๊ฐ€ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์—ˆ๋˜ ํ•ต์‹ฌ ํŠน์„ฑ์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +Flask์˜ ๋‹จ์ˆœํ•จ์„ ๊ณ ๋ คํ•˜๋ฉด API๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ์ž˜ ๋งž๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ ์ฐพ๊ณ ์ž ํ–ˆ๋˜ ๊ฒƒ์€ Flask์šฉ "Django REST Framework"์˜€์Šต๋‹ˆ๋‹ค. + +/// check | **FastAPI**์— ์˜๊ฐ์„ ์ค€ ๊ฒƒ + +micro-framework๊ฐ€ ๋˜๊ธฐ. ํ•„์š”ํ•œ ๋„๊ตฌ์™€ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‰ฝ๊ฒŒ ์กฐํ•ฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ. + +๋‹จ์ˆœํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด routing ์‹œ์Šคํ…œ์„ ๊ฐ–๊ธฐ. + +/// + +### Requests { #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)์„ ์ง์ ‘, ์ง๊ด€์ ์ด๊ณ  ๋ช…ํ™•ํ•œ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ. +* ํ•ฉ๋ฆฌ์ ์ธ ๊ธฐ๋ณธ๊ฐ’์„ ์ œ๊ณตํ•˜๋˜, ๊ฐ•๋ ฅํ•œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ธฐ. + +/// + +### Swagger / OpenAPI { #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 ์‚ฌ์–‘์„ ์œ„ํ•œ ์—ด๋ฆฐ ํ‘œ์ค€์„ ์ฑ„ํƒํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ. + +๋˜ํ•œ ํ‘œ์ค€ ๊ธฐ๋ฐ˜์˜ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ๋„๊ตฌ๋ฅผ ํ†ตํ•ฉํ•˜๊ธฐ: + +* Swagger UI +* ReDoc + +์ด ๋‘ ๊ฐ€์ง€๋Š” ๊ฝค ๋Œ€์ค‘์ ์ด๊ณ  ์•ˆ์ •์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์„ ํƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฐ„๋‹จํžˆ ๊ฒ€์ƒ‰ํ•ด๋ณด๋ฉด OpenAPI๋ฅผ ์œ„ํ•œ ๋Œ€์•ˆ UI๊ฐ€ ์ˆ˜์‹ญ ๊ฐ€์ง€๋‚˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(**FastAPI**์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค). + +/// + +### Flask REST framework๋“ค { #flask-rest-frameworks } + +Flask REST framework๋Š” ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ์žˆ์ง€๋งŒ, ์‹œ๊ฐ„์„ ๋“ค์—ฌ ์กฐ์‚ฌํ•ด ๋ณธ ๊ฒฐ๊ณผ, ์ƒ๋‹น์ˆ˜๊ฐ€ ์ค‘๋‹จ๋˜์—ˆ๊ฑฐ๋‚˜ ๋ฐฉ์น˜๋˜์–ด ์žˆ์—ˆ๊ณ , ํ•ด๊ฒฐ๋˜์ง€ ์•Š์€ ์—ฌ๋Ÿฌ ์ด์Šˆ ๋•Œ๋ฌธ์— ์ ํ•ฉํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. + +### Marshmallow { #marshmallow } + +API ์‹œ์Šคํ…œ์— ํ•„์š”ํ•œ ์ฃผ์š” ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ๋ฐ์ดํ„ฐ "serialization"์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ(Python)์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ๋„คํŠธ์›Œํฌ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์€ ๊ฐ์ฒด๋ฅผ JSON ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜, `datetime` ๊ฐ์ฒด๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์ž…๋‹ˆ๋‹ค. + +API์— ๋˜ ํ•˜๋‚˜ ํฌ๊ฒŒ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์€ ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ์ž…๋‹ˆ๋‹ค. ํŠน์ • ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ํšจํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์–ด๋–ค ํ•„๋“œ๊ฐ€ `int`์ธ์ง€, ์ž„์˜์˜ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ์ง€ ํ™•์ธํ•˜๋Š” ์‹์ž…๋‹ˆ๋‹ค. ์ด๋Š” ํŠนํžˆ ๋“ค์–ด์˜ค๋Š” ๋ฐ์ดํ„ฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. + +๋ฐ์ดํ„ฐ ๊ฒ€์ฆ ์‹œ์Šคํ…œ์ด ์—†๋‹ค๋ฉด, ๋ชจ๋“  ๊ฒ€์‚ฌ๋ฅผ ์ฝ”๋“œ์—์„œ ์ˆ˜๋™์œผ๋กœ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +์ด๋Ÿฐ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด Marshmallow๊ฐ€ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค. ํ›Œ๋ฅญํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฉฐ, ์ €๋„ ์ด์ „์— ๋งŽ์ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ Python type hints๊ฐ€ ์กด์žฌํ•˜๊ธฐ ์ „์— ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ schema๋ฅผ ์ •์˜ํ•˜๋ ค๋ฉด Marshmallow๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํŠน์ • ์œ ํ‹ธ๋ฆฌํ‹ฐ์™€ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +/// check | **FastAPI**์— ์˜๊ฐ์„ ์ค€ ๊ฒƒ + +๋ฐ์ดํ„ฐ ํƒ€์ž…๊ณผ ๊ฒ€์ฆ์„ ์ œ๊ณตํ•˜๋Š” "schema"๋ฅผ ์ฝ”๋“œ๋กœ ์ •์˜ํ•˜๊ณ , ์ด๋ฅผ ์ž๋™์œผ๋กœ ํ™œ์šฉํ•˜๊ธฐ. + +/// + +### Webargs { #webargs } + +API์— ํ•„์š”ํ•œ ๋˜ ๋‹ค๋ฅธ ํฐ ๊ธฐ๋Šฅ์€ ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ parsingํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +Webargs๋Š” Flask๋ฅผ ํฌํ•จํ•œ ์—ฌ๋Ÿฌ framework ์œ„์—์„œ ์ด๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. + +๋‚ด๋ถ€์ ์œผ๋กœ Marshmallow๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ™์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. + +์•„์ฃผ ํ›Œ๋ฅญํ•œ ๋„๊ตฌ์ด๋ฉฐ, ์ €๋„ **FastAPI**๋ฅผ ๋งŒ๋“ค๊ธฐ ์ „์— ๋งŽ์ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. + +/// info | ์ •๋ณด + +Webargs๋Š” Marshmallow์™€ ๊ฐ™์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. + +/// + +/// check | **FastAPI**์— ์˜๊ฐ์„ ์ค€ ๊ฒƒ + +๋“ค์–ด์˜ค๋Š” ์š”์ฒญ ๋ฐ์ดํ„ฐ์˜ ์ž๋™ ๊ฒ€์ฆ์„ ๊ฐ–๊ธฐ. + +/// + +### APISpec { #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๋ฅผ ์ง€์›ํ•˜๊ธฐ. + +/// + +### Flask-apispec { #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์ž…๋‹ˆ๋‹ค: + +* https://github.com/tiangolo/full-stack +* https://github.com/tiangolo/full-stack-flask-couchbase +* https://github.com/tiangolo/full-stack-flask-couchdb + +๊ทธ๋ฆฌ๊ณ  ์ด ๋™์ผํ•œ full-stack generator๋“ค์ด [**FastAPI** Project Generators](project-generation.md){.internal-link target=_blank}์˜ ๊ธฐ๋ฐ˜์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +/// info | ์ •๋ณด + +Flask-apispec์€ Marshmallow์™€ ๊ฐ™์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. + +/// + +/// check | **FastAPI**์— ์˜๊ฐ์„ ์ค€ ๊ฒƒ + +serialization๊ณผ validation์„ ์ •์˜ํ•˜๋Š” ๋™์ผํ•œ ์ฝ”๋“œ๋กœ๋ถ€ํ„ฐ OpenAPI schema๋ฅผ ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ. + +/// + +### NestJS (๊ทธ๋ฆฌ๊ณ  Angular) { #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 ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด ๋›ฐ์–ด๋‚œ ์—๋””ํ„ฐ ์ง€์›์„ ์ œ๊ณตํ•˜๊ธฐ. + +๊ฐ•๋ ฅํ•œ ์˜์กด์„ฑ ์ฃผ์ž… ์‹œ์Šคํ…œ์„ ๊ฐ–์ถ”๊ธฐ. ์ฝ”๋“œ ๋ฐ˜๋ณต์„ ์ตœ์†Œํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ๊ธฐ. + +/// + +### Sanic { #sanic } + +`asyncio` ๊ธฐ๋ฐ˜์˜ ๋งค์šฐ ๋น ๋ฅธ Python framework ์ค‘ ์ดˆ๊ธฐ ์‚ฌ๋ก€์˜€์Šต๋‹ˆ๋‹ค. Flask์™€ ๋งค์šฐ ์œ ์‚ฌํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค. + +/// note | ๊ธฐ์ˆ  ์„ธ๋ถ€์‚ฌํ•ญ + +๊ธฐ๋ณธ Python `asyncio` ๋ฃจํ”„ ๋Œ€์‹  `uvloop`๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋งค์šฐ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“  ์š”์ธ์ž…๋‹ˆ๋‹ค. + +์ด๋Š” Uvicorn๊ณผ Starlette์— ๋ช…ํ™•ํžˆ ์˜๊ฐ์„ ์ฃผ์—ˆ๊ณ , ํ˜„์žฌ ๊ณต๊ฐœ benchmark์—์„œ๋Š” ์ด ๋‘˜์ด Sanic๋ณด๋‹ค ๋” ๋น ๋ฆ…๋‹ˆ๋‹ค. + +/// + +/// check | **FastAPI**์— ์˜๊ฐ์„ ์ค€ ๊ฒƒ + +๋ฏธ์นœ ์„ฑ๋Šฅ์„ ๋‚ผ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ๊ธฐ. + +๊ทธ๋ž˜์„œ **FastAPI**๋Š” Starlette๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. Starlette๋Š” ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ framework ์ค‘ ๊ฐ€์žฅ ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค(์„œ๋“œํŒŒํ‹ฐ benchmark๋กœ ํ…Œ์ŠคํŠธ๋จ). + +/// + +### Falcon { #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๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +/// + +### Molten { #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์— ์ด๋ฏธ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค). + +/// + +### Hug { #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๋ฅผ ์ž๋™์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ํ›Œ๋ฅญํ•œ ๋„๊ตฌ์ธ `isort`์˜ ์ œ์ž‘์ž์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. + +/// + +/// check | **FastAPI**์— ์˜๊ฐ์„ ์ค€ ์•„์ด๋””์–ด๋“ค + +Hug๋Š” APIStar์˜ ์ผ๋ถ€์— ์˜๊ฐ์„ ์ฃผ์—ˆ๊ณ , ์ €๋Š” APIStar์™€ ํ•จ๊ป˜ Hug๋ฅผ ๊ฐ€์žฅ ์œ ๋งํ•œ ๋„๊ตฌ ์ค‘ ํ•˜๋‚˜๋กœ ๋ณด์•˜์Šต๋‹ˆ๋‹ค. + +Hug๋Š” Python type hints๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ ์–ธํ•˜๊ณ , API๋ฅผ ์ •์˜ํ•˜๋Š” schema๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋„๋ก **FastAPI**์— ์˜๊ฐ์„ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. + +Hug๋Š” ํ—ค๋”์™€ ์ฟ ํ‚ค๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ํ•จ์ˆ˜์— `response` ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ ์–ธํ•˜๋„๋ก **FastAPI**์— ์˜๊ฐ์„ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. + +/// + +### APIStar (<= 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 } + +### Pydantic { #pydantic } + +Pydantic์€ Python type hints๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ, serialization, ๋ฌธ์„œํ™”(JSON Schema ์‚ฌ์šฉ)๋ฅผ ์ •์˜ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. + +๊ทธ ๋•๋ถ„์— ๋งค์šฐ ์ง๊ด€์ ์ž…๋‹ˆ๋‹ค. + +Marshmallow์™€ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ benchmark์—์„œ Marshmallow๋ณด๋‹ค ๋น ๋ฆ…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋™์ผํ•œ Python type hints๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฏ€๋กœ ์—๋””ํ„ฐ ์ง€์›๋„ ํ›Œ๋ฅญํ•ฉ๋‹ˆ๋‹ค. + +/// check | **FastAPI**๊ฐ€ ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชฉ์  + +๋ชจ๋“  ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ, ๋ฐ์ดํ„ฐ serialization, ์ž๋™ ๋ชจ๋ธ ๋ฌธ์„œํ™”(JSON Schema ๊ธฐ๋ฐ˜)๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ. + +๊ทธ ๋‹ค์Œ **FastAPI**๋Š” ๊ทธ JSON Schema ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ OpenAPI์— ํฌํ•จ์‹œํ‚ค๋ฉฐ, ๊ทธ ์™ธ์—๋„ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. + +/// + +### Starlette { #starlette } + +Starlette๋Š” ๊ฒฝ๋Ÿ‰ ASGI 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์— ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ๋”ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +/// + +### Uvicorn { #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} ์„น์…˜์„ ํ™•์ธํ•˜์„ธ์š”. diff --git a/docs/ko/docs/deployment/concepts.md b/docs/ko/docs/deployment/concepts.md new file mode 100644 index 000000000..dd7edd1ba --- /dev/null +++ b/docs/ko/docs/deployment/concepts.md @@ -0,0 +1,321 @@ +# ๋ฐฐํฌ ๊ฐœ๋… { #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 ๋“ฑ)์„ ์‹คํ–‰ํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ ์žˆ๋Š” ๊ฒƒ๋„ ๋ณด์ผ ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค. ๋ณดํ†ต ํƒญ๋งˆ๋‹ค ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ๊ทธ ์™ธ์—๋„ ์ถ”๊ฐ€ ํ”„๋กœ์„ธ์Šค ๋ช‡ ๊ฐœ๊ฐ€ ๋” ์žˆ์Šต๋‹ˆ๋‹ค. + + + +--- + +์ด์ œ **ํ”„๋กœ์„ธ์Šค**์™€ **ํ”„๋กœ๊ทธ๋žจ**์˜ ์ฐจ์ด๋ฅผ ์•Œ์•˜์œผ๋‹ˆ, ๋ฐฐํฌ์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ๋ฅผ ๊ณ„์†ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +## ์‹œ์ž‘ ์‹œ ์‹คํ–‰ { #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์— ๋ณ€์ˆ˜๋กœ ๋กœ๋“œํ•œ ๋ชจ๋“  ๋‚ด์šฉ์„ ๋‹ด์Šต๋‹ˆ๋‹ค. + + + +๊ทธ๋ฆฌ๊ณ  ๋ฌผ๋ก  ๊ฐ™์€ ๋จธ์‹ ์—๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์™ธ์—๋„ **๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค**๋“ค์ด ์‹คํ–‰ ์ค‘์ผ ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค. + +ํฅ๋ฏธ๋กœ์šด ์ ์€ ๊ฐ ํ”„๋กœ์„ธ์Šค์˜ **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 +* ์‹œ์ž‘ ์‹œ ์‹คํ–‰ +* ์žฌ์‹œ์ž‘ +* ๋ณต์ œ(์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค ์ˆ˜) +* ๋ฉ”๋ชจ๋ฆฌ +* ์‹œ์ž‘ ์ „ ์‚ฌ์ „ ๋‹จ๊ณ„ + +์ด ์•„์ด๋””์–ด๋“ค์„ ์ดํ•ดํ•˜๊ณ  ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ฉด, ๋ฐฐํฌ๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ์กฐ์ •ํ•  ๋•Œ ํ•„์š”ํ•œ ์ง๊ด€์„ ์–ป๋Š” ๋ฐ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๐Ÿค“ + +๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ๋”ฐ๋ผ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋Šฅํ•œ ์ „๋žต์˜ ๋” ๊ตฌ์ฒด์ ์ธ ์˜ˆ์‹œ๋ฅผ ์ œ๊ณตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿš€ diff --git a/docs/ko/docs/deployment/fastapicloud.md b/docs/ko/docs/deployment/fastapicloud.md new file mode 100644 index 000000000..9a830b157 --- /dev/null +++ b/docs/ko/docs/deployment/fastapicloud.md @@ -0,0 +1,65 @@ +# FastAPI Cloud { #fastapi-cloud } + +**ํ•œ ๋ฒˆ์˜ ๋ช…๋ น**์œผ๋กœ FastAPI ์•ฑ์„ FastAPI Cloud์— ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„์ง์ด๋ผ๋ฉด ๋Œ€๊ธฐ์ž ๋ช…๋‹จ์— ๋“ฑ๋กํ•ด ๋ณด์„ธ์š”. ๐Ÿš€ + +## ๋กœ๊ทธ์ธํ•˜๊ธฐ { #login } + +๋จผ์ € **FastAPI Cloud** ๊ณ„์ •์ด ์ด๋ฏธ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”(๋Œ€๊ธฐ์ž ๋ช…๋‹จ์—์„œ ์ดˆ๋Œ€ํ•ด ๋“œ๋ ธ์„ ๊ฑฐ์˜ˆ์š” ๐Ÿ˜‰). + +๊ทธ๋‹ค์Œ ๋กœ๊ทธ์ธํ•ฉ๋‹ˆ๋‹ค: + +
+ +```console +$ fastapi login + +You are logged in to FastAPI Cloud ๐Ÿš€ +``` + +
+ +## ๋ฐฐํฌํ•˜๊ธฐ { #deploy } + +์ด์ œ **ํ•œ ๋ฒˆ์˜ ๋ช…๋ น**์œผ๋กœ ์•ฑ์„ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค: + +
+ +```console +$ fastapi deploy + +Deploying to FastAPI Cloud... + +โœ… Deployment successful! + +๐Ÿ” Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev +``` + +
+ +์ด๊ฒŒ ์ „๋ถ€์ž…๋‹ˆ๋‹ค! ์ด์ œ ํ•ด๋‹น URL์—์„œ ์•ฑ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. โœจ + +## FastAPI Cloud ์†Œ๊ฐœ { #about-fastapi-cloud } + +**FastAPI Cloud**๋Š” **FastAPI**๋ฅผ ๋งŒ๋“  ๋™์ผํ•œ ์ €์ž์™€ ํŒ€์ด ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ตœ์†Œํ•œ์˜ ๋…ธ๋ ฅ์œผ๋กœ API๋ฅผ **๊ตฌ์ถ•**, **๋ฐฐํฌ**, **์ ‘๊ทผ**ํ•˜๋Š” ๊ณผ์ •์„ ๊ฐ„์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค. + +FastAPI๋กœ ์•ฑ์„ ๋งŒ๋“ค ๋•Œ์˜ ๋™์ผํ•œ **๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜**์„, ํด๋ผ์šฐ๋“œ์— **๋ฐฐํฌ**ํ•  ๋•Œ๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๐ŸŽ‰ + +๋˜ํ•œ ์•ฑ์„ ๋ฐฐํฌํ•  ๋•Œ ๋ณดํ†ต ํ•„์š”ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒƒ๋“ค๋„ ์ฒ˜๋ฆฌํ•ด ์ค๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด: + +* HTTPS +* ์š”์ฒญ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž๋™ ์Šค์ผ€์ผ๋งํ•˜๋Š” ๋ณต์ œ(Replication) +* ๋“ฑ + +FastAPI Cloud๋Š” *FastAPI and friends* ์˜คํ”ˆ ์†Œ์Šค ํ”„๋กœ์ ํŠธ์˜ ์ฃผ์š” ์Šคํฐ์„œ์ด์ž ์ž๊ธˆ ์ง€์› ์ œ๊ณต์ž์ž…๋‹ˆ๋‹ค. โœจ + +## ๋‹ค๋ฅธ ํด๋ผ์šฐ๋“œ ์ œ๊ณต์—…์ฒด์— ๋ฐฐํฌํ•˜๊ธฐ { #deploy-to-other-cloud-providers } + +FastAPI๋Š” ์˜คํ”ˆ ์†Œ์Šค์ด๋ฉฐ ํ‘œ์ค€์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์›ํ•˜๋Š” ์–ด๋–ค ํด๋ผ์šฐ๋“œ ์ œ๊ณต์—…์ฒด์—๋„ FastAPI ์•ฑ์„ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํ•ด๋‹น ํด๋ผ์šฐ๋“œ ์ œ๊ณต์—…์ฒด์˜ ๊ฐ€์ด๋“œ๋ฅผ ๋”ฐ๋ผ FastAPI ์•ฑ์„ ๋ฐฐํฌํ•˜์„ธ์š”. ๐Ÿค“ + +## ์ž์ฒด ์„œ๋ฒ„์— ๋ฐฐํฌํ•˜๊ธฐ { #deploy-your-own-server } + +๋˜ํ•œ ์ด **Deployment** ๊ฐ€์ด๋“œ์—์„œ ์ดํ›„์— ๋ชจ๋“  ์„ธ๋ถ€์‚ฌํ•ญ์„ ์•Œ๋ ค๋“œ๋ฆด ๊ฑฐ์˜ˆ์š”. ๊ทธ๋ž˜์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€, ๋ฌด์—‡์ด ํ•„์š”ํ•˜๋ฉฐ, ๋ณธ์ธ์˜ ์„œ๋ฒ„๋ฅผ ํฌํ•จํ•ด ์ง์ ‘ FastAPI ์•ฑ์„ ์–ด๋–ป๊ฒŒ ๋ฐฐํฌํ•˜๋Š”์ง€๊นŒ์ง€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๐Ÿค“ diff --git a/docs/ko/docs/deployment/https.md b/docs/ko/docs/deployment/https.md new file mode 100644 index 000000000..888ec6159 --- /dev/null +++ b/docs/ko/docs/deployment/https.md @@ -0,0 +1,231 @@ +# HTTPS ์•Œ์•„๋ณด๊ธฐ { #about-https } + +HTTPS๋Š” ๊ทธ๋ƒฅ โ€œ์ผœ์ ธ ์žˆ๊ฑฐ๋‚˜โ€ ์•„๋‹ˆ๋ฉด โ€œ๊บผ์ ธ ์žˆ๋Š”โ€ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ํ›จ์”ฌ ๋” ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. + +/// tip | ํŒ + +๋ฐ”์˜๊ฑฐ๋‚˜ ๋ณ„๋กœ ์‹ ๊ฒฝ ์“ฐ๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด, ๋‹ค์Œ ์„น์…˜์—์„œ ๋‹ค์–‘ํ•œ ๊ธฐ๋ฒ•์œผ๋กœ ๋ชจ๋“  ๊ฒƒ์„ ์„ค์ •ํ•˜๋Š” ๋‹จ๊ณ„๋ณ„ ์•ˆ๋‚ด๋ฅผ ๊ณ„์† ๋ณด์„ธ์š”. + +/// + +์†Œ๋น„์ž ๊ด€์ ์—์„œ **HTTPS์˜ ๊ธฐ๋ณธ์„ ๋ฐฐ์šฐ๋ ค๋ฉด** https://howhttps.works/๋ฅผ ํ™•์ธํ•˜์„ธ์š”. + +์ด์ œ **๊ฐœ๋ฐœ์ž ๊ด€์ **์—์„œ HTTPS๋ฅผ ์ƒ๊ฐํ•  ๋•Œ ์—ผ๋‘์— ๋‘์–ด์•ผ ํ•  ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค: + +* HTTPS๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, **์„œ๋ฒ„**๊ฐ€ **์ œ3์ž**๊ฐ€ ๋ฐœ๊ธ‰ํ•œ **"์ธ์ฆ์„œ(certificates)"**๋ฅผ **๋ณด์œ **ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + * ์ด ์ธ์ฆ์„œ๋Š” ์‹ค์ œ๋กœ ์ œ3์ž๊ฐ€ โ€œ์ƒ์„ฑโ€ํ•ด ์ฃผ๋Š” ๊ฒƒ์ด๊ณ , ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ œ3์ž๋กœ๋ถ€ํ„ฐ **๋ฐœ๊ธ‰/ํš๋“**ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. +* ์ธ์ฆ์„œ์—๋Š” **์œ ํšจ ๊ธฐ๊ฐ„**์ด ์žˆ์Šต๋‹ˆ๋‹ค. + * ์ฆ‰, **๋งŒ๋ฃŒ**๋ฉ๋‹ˆ๋‹ค. + * ๊ทธ๋ฆฌ๊ณ  ๋‚˜๋ฉด ์ œ3์ž๋กœ๋ถ€ํ„ฐ ๋‹ค์‹œ **๊ฐฑ์‹ **ํ•ด์„œ **์žฌ๋ฐœ๊ธ‰/์žฌํš๋“**ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +* ์—ฐ๊ฒฐ์˜ ์•”ํ˜ธํ™”๋Š” **TCP ๋ ˆ๋ฒจ**์—์„œ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค. + * ์ด๋Š” **HTTP๋ณด๋‹ค ํ•œ ๊ณ„์ธต ์•„๋ž˜**์ž…๋‹ˆ๋‹ค. + * ๋”ฐ๋ผ์„œ **์ธ์ฆ์„œ์™€ ์•”ํ˜ธํ™”** ์ฒ˜๋ฆฌ๋Š” **HTTP ์ด์ „**์— ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. +* **TCP๋Š” "๋„๋ฉ”์ธ"์„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค.** IP ์ฃผ์†Œ๋งŒ ์••๋‹ˆ๋‹ค. + * ์–ด๋–ค **ํŠน์ • ๋„๋ฉ”์ธ**์„ ์š”์ฒญํ–ˆ๋Š”์ง€์— ๋Œ€ํ•œ ์ •๋ณด๋Š” **HTTP ๋ฐ์ดํ„ฐ**์— ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค. +* **HTTPS ์ธ์ฆ์„œ**๋Š” ํŠน์ • **๋„๋ฉ”์ธ**์„ โ€œ์ธ์ฆโ€ํ•˜์ง€๋งŒ, ํ”„๋กœํ† ์ฝœ๊ณผ ์•”ํ˜ธํ™”๋Š” TCP ๋ ˆ๋ฒจ์—์„œ ์ผ์–ด๋‚˜๋ฉฐ, ์–ด๋–ค ๋„๋ฉ”์ธ์„ ๋‹ค๋ฃจ๋Š”์ง€ **์•Œ๊ธฐ ์ „์—** ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. +* **๊ธฐ๋ณธ์ ์œผ๋กœ** ์ด๋Š” IP ์ฃผ์†Œ ํ•˜๋‚˜๋‹น **HTTPS ์ธ์ฆ์„œ ํ•˜๋‚˜๋งŒ** ๋‘˜ ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. + * ์„œ๋ฒ„๊ฐ€ ์•„๋ฌด๋ฆฌ ํฌ๋“ , ๊ทธ ์œ„์— ์˜ฌ๋ฆฐ ๊ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•„๋ฌด๋ฆฌ ์ž‘๋“  ์ƒ๊ด€์—†์Šต๋‹ˆ๋‹ค. + * ํ•˜์ง€๋งŒ ์ด์— ๋Œ€ํ•œ **ํ•ด๊ฒฐ์ฑ…**์ด ์žˆ์Šต๋‹ˆ๋‹ค. +* **TLS** ํ”„๋กœํ† ์ฝœ(HTTP ์ด์ „, TCP ๋ ˆ๋ฒจ์—์„œ ์•”ํ˜ธํ™”๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ)์— ๋Œ€ํ•œ **ํ™•์žฅ** ์ค‘์— **SNI**๋ผ๋Š” ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค. + * ์ด SNI ํ™•์žฅ์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋‹จ์ผ ์„œ๋ฒ„(**๋‹จ์ผ IP ์ฃผ์†Œ**)์—์„œ **์—ฌ๋Ÿฌ HTTPS ์ธ์ฆ์„œ**๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  **์—ฌ๋Ÿฌ HTTPS ๋„๋ฉ”์ธ/์• ํ”Œ๋ฆฌ์ผ€์ด์…˜**์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + * ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ์„œ๋ฒ„์—์„œ **๊ณต๊ฐœ IP ์ฃผ์†Œ**๋กœ ๋ฆฌ์Šค๋‹ํ•˜๋Š” **ํ•˜๋‚˜์˜** ์ปดํฌ๋„ŒํŠธ(ํ”„๋กœ๊ทธ๋žจ)๊ฐ€ ์„œ๋ฒ„์— ์žˆ๋Š” **๋ชจ๋“  HTTPS ์ธ์ฆ์„œ**์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +* ๋ณด์•ˆ ์—ฐ๊ฒฐ์„ ์–ป์€ **์ดํ›„์—๋„**, ํ†ต์‹  ํ”„๋กœํ† ์ฝœ ์ž์ฒด๋Š” **์—ฌ์ „ํžˆ HTTP**์ž…๋‹ˆ๋‹ค. + * **HTTP ํ”„๋กœํ† ์ฝœ**๋กœ ์ „์†ก๋˜๋”๋ผ๋„, ๋‚ด์šฉ์€ **์•”ํ˜ธํ™”**๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. + +์ผ๋ฐ˜์ ์œผ๋กœ ์„œ๋ฒ„(๋จธ์‹ , ํ˜ธ์ŠคํŠธ ๋“ฑ)์—๋Š” **ํ”„๋กœ๊ทธ๋žจ/HTTP ์„œ๋ฒ„ ํ•˜๋‚˜**๋ฅผ ์‹คํ–‰ํ•ด **HTTPS ๊ด€๋ จ ๋ถ€๋ถ„ ์ „์ฒด**๋ฅผ ๊ด€๋ฆฌํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค: **์•”ํ˜ธํ™”๋œ HTTPS ์š”์ฒญ**์„ ๋ฐ›๊ณ , ๋ณตํ˜ธํ™”๋œ **HTTP ์š”์ฒญ**์„ ๊ฐ™์€ ์„œ๋ฒ„์—์„œ ์‹คํ–‰ ์ค‘์ธ ์‹ค์ œ HTTP ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(์ด ๊ฒฝ์šฐ **FastAPI** ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜)์œผ๋กœ ์ „๋‹ฌํ•˜๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ **HTTP ์‘๋‹ต**์„ ๋ฐ›์•„ ์ ์ ˆํ•œ **HTTPS ์ธ์ฆ์„œ**๋กœ **์•”ํ˜ธํ™”**ํ•œ ๋’ค **HTTPS**๋กœ ํด๋ผ์ด์–ธํŠธ์— ๋‹ค์‹œ ๋ณด๋‚ด๋Š” ์—ญํ• ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์„œ๋ฒ„๋ฅผ ํ”ํžˆ **TLS Termination Proxy**๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. + +TLS Termination Proxy๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + +* Traefik (์ธ์ฆ์„œ ๊ฐฑ์‹ ๋„ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ) +* Caddy (์ธ์ฆ์„œ ๊ฐฑ์‹ ๋„ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ) +* Nginx +* HAProxy + +## Let's Encrypt { #lets-encrypt } + +Let's Encrypt ์ด์ „์—๋Š” ์ด๋Ÿฌํ•œ **HTTPS ์ธ์ฆ์„œ**๊ฐ€ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ œ3์ž์— ์˜ํ•ด ํŒ๋งค๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +์ธ์ฆ์„œ๋ฅผ ํš๋“ํ•˜๋Š” ๊ณผ์ •์€ ๋ฒˆ๊ฑฐ๋กญ๊ณ , ๊ฝค ๋งŽ์€ ์„œ๋ฅ˜ ์ž‘์—…์ด ํ•„์š”ํ–ˆ์œผ๋ฉฐ, ์ธ์ฆ์„œ๋„ ์ƒ๋‹นํžˆ ๋น„์ŒŒ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ๊ทธ ํ›„ **Let's Encrypt**๊ฐ€ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค. + +์ด๋Š” Linux Foundation์˜ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ํ‘œ์ค€ ์•”ํ˜ธํ•™์  ๋ณด์•ˆ์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•˜๋Š” **HTTPS ์ธ์ฆ์„œ**๋ฅผ **๋ฌด๋ฃŒ๋กœ**, ์ž๋™ํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ์ธ์ฆ์„œ๋“ค์€ ์ˆ˜๋ช…์ด ์งง๊ณ (์•ฝ 3๊ฐœ์›”) ๊ทธ๋ž˜์„œ ์œ ํšจ ๊ธฐ๊ฐ„์ด ์งง์€ ๋งŒํผ **์‹ค์ œ๋กœ ๋ณด์•ˆ์ด ๋” ์ข‹์•„์ง€๊ธฐ๋„** ํ•ฉ๋‹ˆ๋‹ค. + +๋„๋ฉ”์ธ์€ ์•ˆ์ „ํ•˜๊ฒŒ ๊ฒ€์ฆ๋˜๋ฉฐ ์ธ์ฆ์„œ๋Š” ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด๋กœ ์ธํ•ด ์ธ์ฆ์„œ ๊ฐฑ์‹ ๋„ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋ชฉํ‘œ๋Š” ์ธ์ฆ์„œ์˜ ๋ฐœ๊ธ‰๊ณผ ๊ฐฑ์‹ ์„ ์ž๋™ํ™”ํ•˜์—ฌ **๋ฌด๋ฃŒ๋กœ, ์˜๊ตฌํžˆ, ์•ˆ์ „ํ•œ HTTPS**๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +## ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ HTTPS { #https-for-developers } + +๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ค‘์š”ํ•œ ๊ฐœ๋…๋“ค์„ ์ค‘์‹ฌ์œผ๋กœ, HTTPS API๊ฐ€ ๋‹จ๊ณ„๋ณ„๋กœ ์–ด๋–ป๊ฒŒ ๋ณด์ผ ์ˆ˜ ์žˆ๋Š”์ง€ ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +### ๋„๋ฉ”์ธ ์ด๋ฆ„ { #domain-name } + +์•„๋งˆ๋„ ์‹œ์ž‘์€ **๋„๋ฉ”์ธ ์ด๋ฆ„**์„ **ํš๋“**ํ•˜๋Š” ๊ฒƒ์ผ ๊ฒ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ DNS ์„œ๋ฒ„(์•„๋งˆ ๊ฐ™์€ ํด๋ผ์šฐ๋“œ ์ œ๊ณต์—…์ฒด)์—์„œ ์ด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. + +๋Œ€๊ฐœ ํด๋ผ์šฐ๋“œ ์„œ๋ฒ„(๊ฐ€์ƒ ๋จธ์‹ ) ๊ฐ™์€ ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๊ณ , ๊ฑฐ๊ธฐ์—๋Š” fixed **๊ณต๊ฐœ IP ์ฃผ์†Œ**๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. + +DNS ์„œ๋ฒ„(๋“ค)์—์„œ **๋„๋ฉ”์ธ**์ด ์„œ๋ฒ„์˜ **๊ณต๊ฐœ IP ์ฃผ์†Œ**๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ๋ ˆ์ฝ”๋“œ(โ€œ`A record`โ€)๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. + +๋ณดํ†ต์€ ์ฒ˜์Œ ํ•œ ๋ฒˆ, ๋ชจ๋“  ๊ฒƒ์„ ์„ค์ •ํ•  ๋•Œ๋งŒ ์ด ์ž‘์—…์„ ํ•ฉ๋‹ˆ๋‹ค. + +/// tip | ํŒ + +๋„๋ฉ”์ธ ์ด๋ฆ„ ๋ถ€๋ถ„์€ HTTPS๋ณด๋‹ค ํ›จ์”ฌ ์ด์ „ ๋‹จ๊ณ„์ง€๋งŒ, ๋ชจ๋“  ๊ฒƒ์ด ๋„๋ฉ”์ธ๊ณผ IP ์ฃผ์†Œ์— ์˜์กดํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ์–ธ๊ธ‰ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. + +/// + +### DNS { #dns } + +์ด์ œ ์‹ค์ œ HTTPS ๋ถ€๋ถ„์— ์ง‘์ค‘ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +๋จผ์ € ๋ธŒ๋ผ์šฐ์ €๋Š” **DNS ์„œ๋ฒ„**์— ์งˆ์˜ํ•˜์—ฌ, ์—ฌ๊ธฐ์„œ๋Š” `someapp.example.com`์ด๋ผ๋Š” **๋„๋ฉ”์ธ์— ๋Œ€ํ•œ IP**๊ฐ€ ๋ฌด์—‡์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + +DNS ์„œ๋ฒ„๋Š” ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ํŠน์ • **IP ์ฃผ์†Œ**๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์ด๋Š” DNS ์„œ๋ฒ„์— ์„ค์ •ํ•ด ๋‘”, ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ณต๊ฐœ IP ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค. + + + +### TLS ํ•ธ๋“œ์…ฐ์ดํฌ ์‹œ์ž‘ { #tls-handshake-start } + +๊ทธ ๋‹ค์Œ ๋ธŒ๋ผ์šฐ์ €๋Š” **ํฌํŠธ 443**(HTTPS ํฌํŠธ)์—์„œ ํ•ด๋‹น IP ์ฃผ์†Œ์™€ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค. + +ํ†ต์‹ ์˜ ์ฒซ ๋ถ€๋ถ„์€ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ์‚ฌ์ด์˜ ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•˜๊ณ , ์‚ฌ์šฉํ•  ์•”ํ˜ธํ™” ํ‚ค ๋“ฑ์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. + + + +ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ 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`์— ๋Œ€ํ•œ ์ธ์ฆ์„œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + + + +ํด๋ผ์ด์–ธํŠธ๋Š” ์ด๋ฏธ ํ•ด๋‹น 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 ์š”์ฒญ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. + + + +### ์š”์ฒญ ๋ณตํ˜ธํ™” { #decrypt-the-request } + +TLS Termination Proxy๋Š” ํ•ฉ์˜๋œ ์•”ํ˜ธํ™”๋ฅผ ์‚ฌ์šฉํ•ด **์š”์ฒญ์„ ๋ณตํ˜ธํ™”**ํ•˜๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค(์˜ˆ: FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋Š” Uvicorn ํ”„๋กœ์„ธ์Šค)์— **์ผ๋ฐ˜(๋ณตํ˜ธํ™”๋œ) HTTP ์š”์ฒญ**์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. + + + +### HTTP ์‘๋‹ต { #http-response } + +์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  **์ผ๋ฐ˜(์•”ํ˜ธํ™”๋˜์ง€ ์•Š์€) HTTP ์‘๋‹ต**์„ TLS Termination Proxy๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค. + + + +### HTTPS ์‘๋‹ต { #https-response } + +๊ทธ ๋‹ค์Œ TLS Termination Proxy๋Š” ์ด์ „์— ํ•ฉ์˜ํ•œ ์•”ํ˜ธํ™”( `someapp.example.com` ์ธ์ฆ์„œ๋กœ ์‹œ์ž‘๋œ ๊ฒƒ)๋ฅผ ์‚ฌ์šฉํ•ด **์‘๋‹ต์„ ์•”ํ˜ธํ™”**ํ•˜๊ณ , ๋ธŒ๋ผ์šฐ์ €๋กœ ๋‹ค์‹œ ๋ณด๋ƒ…๋‹ˆ๋‹ค. + +์ดํ›„ ๋ธŒ๋ผ์šฐ์ €๋Š” ์‘๋‹ต์ด ์œ ํšจํ•œ์ง€, ์˜ฌ๋ฐ”๋ฅธ ์•”ํ˜ธํ™” ํ‚ค๋กœ ์•”ํ˜ธํ™”๋˜์—ˆ๋Š”์ง€ ๋“ฑ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ **์‘๋‹ต์„ ๋ณตํ˜ธํ™”**ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. + + + +ํด๋ผ์ด์–ธํŠธ(๋ธŒ๋ผ์šฐ์ €)๋Š” ์•ž์„œ **HTTPS ์ธ์ฆ์„œ**๋กœ ํ•ฉ์˜ํ•œ ์•”ํ˜ธํ™”๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ํ•ด๋‹น ์‘๋‹ต์ด ์˜ฌ๋ฐ”๋ฅธ ์„œ๋ฒ„์—์„œ ์™”๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### ์—ฌ๋Ÿฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ { #multiple-applications } + +๊ฐ™์€ ์„œ๋ฒ„(๋˜๋Š” ์—ฌ๋Ÿฌ ์„œ๋ฒ„)์—๋Š” ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค๋ฅธ API ํ”„๋กœ๊ทธ๋žจ์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ฒ˜๋Ÿผ **์—ฌ๋Ÿฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜**์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํŠน์ • IP์™€ ํฌํŠธ ์กฐํ•ฉ์€ ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ(์˜ˆ์‹œ์—์„œ๋Š” TLS Termination Proxy), ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜/ํ”„๋กœ์„ธ์Šค๋„ **๊ณต๊ฐœ IP์™€ ํฌํŠธ ์กฐํ•ฉ**์„ ๋™์ผํ•˜๊ฒŒ ์“ฐ๋ ค๊ณ ๋งŒ ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์„œ๋ฒ„์—์„œ ํ•จ๊ป˜ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + + + +์ด๋ ‡๊ฒŒ ํ•˜๋ฉด TLS Termination Proxy๊ฐ€ **์—ฌ๋Ÿฌ ๋„๋ฉ”์ธ**์— ๋Œ€ํ•œ HTTPS์™€ ์ธ์ฆ์„œ๋ฅผ **์—ฌ๋Ÿฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜**์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌํ•˜๊ณ , ๊ฐ ๊ฒฝ์šฐ์— ๋งž๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์š”์ฒญ์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### ์ธ์ฆ์„œ ๊ฐฑ์‹  { #certificate-renewal } + +๋ฏธ๋ž˜์˜ ์–ด๋А ์‹œ์ ์—๋Š” ๊ฐ ์ธ์ฆ์„œ๊ฐ€ **๋งŒ๋ฃŒ**๋ฉ๋‹ˆ๋‹ค(ํš๋“ ํ›„ ์•ฝ 3๊ฐœ์›”). + +๊ทธ ๋‹ค์Œ์—๋Š” ๋˜ ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ(๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๋ณ„๋„ ํ”„๋กœ๊ทธ๋žจ์ผ ์ˆ˜๋„ ์žˆ๊ณ , ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๊ฐ™์€ TLS Termination Proxy์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค)์ด Let's Encrypt์™€ ํ†ต์‹ ํ•˜์—ฌ ์ธ์ฆ์„œ๋ฅผ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค. + + + +**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 | ๊ธฐ์ˆ  ์„ธ๋ถ€์‚ฌํ•ญ + +ํ”„๋ก์‹œ ํ—ค๋”๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + +* X-Forwarded-For +* X-Forwarded-Proto +* X-Forwarded-Host + +/// + +๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  **์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„**๋Š” ์ž์‹ ์ด ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” **ํ”„๋ก์‹œ** ๋’ค์— ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ชจ๋ฅด๋ฏ€๋กœ, ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ๊ทธ ํ—ค๋”๋“ค์„ ์‹ ๋ขฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ **์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„**๊ฐ€ **ํ”„๋ก์‹œ**๊ฐ€ ๋ณด๋‚ธ *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** ์„ค์ • ๋ฐฉ๋ฒ•์„ ์—ฌ๋Ÿฌ ๊ตฌ์ฒด์ ์ธ ์˜ˆ์‹œ๋กœ ๋ณด์—ฌ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿ”’ diff --git a/docs/ko/docs/deployment/manually.md b/docs/ko/docs/deployment/manually.md new file mode 100644 index 000000000..e85dd02a3 --- /dev/null +++ b/docs/ko/docs/deployment/manually.md @@ -0,0 +1,157 @@ +# ์„œ๋ฒ„๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰ํ•˜๊ธฐ { #run-a-server-manually } + +## `fastapi run` ๋ช…๋ น ์‚ฌ์šฉํ•˜๊ธฐ { #use-the-fastapi-run-command } + +์š”์•ฝํ•˜๋ฉด, `fastapi run`์„ ์‚ฌ์šฉํ•ด FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„œ๋น„์Šคํ•˜์„ธ์š”: + +
+ +```console +$ fastapi run main.py + + FastAPI Starting production server ๐Ÿš€ + + Searching for package file structure from directories + with __init__.py files + Importing from /home/user/code/awesomeapp + + module ๐Ÿ main.py + + code Importing the FastAPI app object from the module with + the following code: + + from main import app + + app Using import string: main:app + + server Server started at http://0.0.0.0:8000 + server Documentation at http://0.0.0.0:8000/docs + + Logs: + + INFO Started server process [2306215] + INFO Waiting for application startup. + INFO Application startup complete. + INFO Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C + to quit) +``` + +
+ +๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์—๋Š” ์ด๊ฒƒ์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ˜Ž + +์˜ˆ๋ฅผ ๋“ค์–ด ์ด ๋ช…๋ น์€ ์ปจํ…Œ์ด๋„ˆ๋‚˜ ์„œ๋ฒ„ ๋“ฑ์—์„œ **FastAPI** ์•ฑ์„ ์‹œ์ž‘ํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ASGI ์„œ๋ฒ„ { #asgi-servers } + +์ด์ œ ์กฐ๊ธˆ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +FastAPI๋Š” ASGI๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š”, Python ์›น ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ํ‘œ์ค€์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. FastAPI๋Š” ASGI ์›น ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. + +์›๊ฒฉ ์„œ๋ฒ„ ๋จธ์‹ ์—์„œ **FastAPI** ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(๋˜๋Š” ๋‹ค๋ฅธ ASGI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜)์„ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ํ•ต์‹ฌ ์š”์†Œ๋Š” **Uvicorn** ๊ฐ™์€ ASGI ์„œ๋ฒ„ ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค. `fastapi` ๋ช…๋ น์—๋Š” ๊ธฐ๋ณธ์œผ๋กœ ์ด๊ฒƒ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. + +๋‹ค์Œ์„ ํฌํ•จํ•ด ์—ฌ๋Ÿฌ ๋Œ€์•ˆ์ด ์žˆ์Šต๋‹ˆ๋‹ค: + +* Uvicorn: ๊ณ ์„ฑ๋Šฅ ASGI ์„œ๋ฒ„. +* Hypercorn: HTTP/2 ๋ฐ Trio ๋“ฑ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ๊ณผ ํ˜ธํ™˜๋˜๋Š” ASGI ์„œ๋ฒ„. +* Daphne: Django Channels๋ฅผ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ASGI ์„œ๋ฒ„. +* Granian: Python ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ Rust HTTP ์„œ๋ฒ„. +* NGINX Unit: 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์„ ์„ค์น˜ํ•˜๋ ค๋ฉด: + +
+ +```console +$ pip install "uvicorn[standard]" + +---> 100% +``` + +
+ +๋‹ค๋ฅธ ์–ด๋–ค ASGI ์„œ๋ฒ„ ํ”„๋กœ๊ทธ๋žจ๋„ ๋น„์Šทํ•œ ๊ณผ์ •์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. + +/// tip | ํŒ + +`standard`๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด Uvicorn์ด ๊ถŒ์žฅ๋˜๋Š” ์ถ”๊ฐ€ ์˜์กด์„ฑ ๋ช‡ ๊ฐ€์ง€๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +์—ฌ๊ธฐ์—๋Š” `asyncio`๋ฅผ ๊ณ ์„ฑ๋Šฅ์œผ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋Š” ๋“œ๋กญ์ธ ๋Œ€์ฒด์žฌ์ธ `uvloop`๊ฐ€ ํฌํ•จ๋˜๋ฉฐ, ํฐ ๋™์‹œ์„ฑ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +`pip install "fastapi[standard]"` ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ FastAPI๋ฅผ ์„ค์น˜ํ•˜๋ฉด `uvicorn[standard]`๋„ ํ•จ๊ป˜ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค. + +/// + +## ์„œ๋ฒ„ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ํ•˜๊ธฐ { #run-the-server-program } + +ASGI ์„œ๋ฒ„๋ฅผ ์ˆ˜๋™์œผ๋กœ ์„ค์น˜ํ–ˆ๋‹ค๋ฉด, ๋ณดํ†ต FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ž„ํฌํŠธํ•˜๊ธฐ ์œ„ํ•ด ํŠน๋ณ„ํ•œ ํ˜•์‹์˜ import string์„ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค: + +
+ +```console +$ uvicorn main:app --host 0.0.0.0 --port 80 + +INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit) +``` + +
+ +/// 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 +* ์‹œ์ž‘ ์‹œ ์ž๋™ ์‹คํ–‰ +* ์žฌ์‹œ์ž‘ +* ๋ณต์ œ(์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค ์ˆ˜) +* ๋ฉ”๋ชจ๋ฆฌ +* ์‹œ์ž‘ ์ „ ์„ ํ–‰ ๋‹จ๊ณ„ + +๋‹ค์Œ ์žฅ๋“ค์—์„œ ์ด ๊ฐ๊ฐ์˜ ๊ฐœ๋…์„ ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•ด์•ผ ํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ์ „๋žต์˜ ๊ตฌ์ฒด์ ์ธ ์˜ˆ์‹œ๋ฅผ ๋” ์•Œ๋ ค๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿš€ diff --git a/docs/ko/docs/how-to/authentication-error-status-code.md b/docs/ko/docs/how-to/authentication-error-status-code.md new file mode 100644 index 000000000..47120cae6 --- /dev/null +++ b/docs/ko/docs/how-to/authentication-error-status-code.md @@ -0,0 +1,17 @@ +# ์ด์ „ 403 ์ธ์ฆ ์˜ค๋ฅ˜ ์ƒํƒœ ์ฝ”๋“œ ์‚ฌ์šฉํ•˜๊ธฐ { #use-old-403-authentication-error-status-codes } + +FastAPI ๋ฒ„์ „ `0.122.0` ์ด์ „์—๋Š”, ํ†ตํ•ฉ ๋ณด์•ˆ ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ์ธ์ฆ ์‹คํŒจ ํ›„ ํด๋ผ์ด์–ธํŠธ์— ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ HTTP ์ƒํƒœ ์ฝ”๋“œ `403 Forbidden`์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. + +FastAPI ๋ฒ„์ „ `0.122.0`๋ถ€ํ„ฐ๋Š” ๋” ์ ์ ˆํ•œ HTTP ์ƒํƒœ ์ฝ”๋“œ `401 Unauthorized`๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, HTTP ๋ช…์„ธ์ธ RFC 7235, RFC 9110๋ฅผ ๋”ฐ๋ผ ์‘๋‹ต์— ํ•ฉ๋ฆฌ์ ์ธ `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`) ์ž‘์—…์€ ๋‚ด๋ถ€ ์ฝ”๋“œ์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์—์„œ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. + +/// diff --git a/docs/ko/docs/how-to/custom-docs-ui-assets.md b/docs/ko/docs/how-to/custom-docs-ui-assets.md new file mode 100644 index 000000000..d6383c29c --- /dev/null +++ b/docs/ko/docs/how-to/custom-docs-ui-assets.md @@ -0,0 +1,185 @@ +# ์ปค์Šคํ…€ Docs UI ์ •์  ์—์…‹(์ž์ฒด ํ˜ธ์ŠคํŒ…) { #custom-docs-ui-static-assets-self-hosting } + +API ๋ฌธ์„œ๋Š” **Swagger UI**์™€ **ReDoc**์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ๊ฐ๊ฐ JavaScript์™€ CSS ํŒŒ์ผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. + +๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๋Ÿฌํ•œ ํŒŒ์ผ์€ CDN์—์„œ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ์ด๋ฅผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŠน์ • CDN์„ ์ง€์ •ํ•˜๊ฑฐ๋‚˜ ํŒŒ์ผ์„ ์ง์ ‘ ์ œ๊ณตํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +## JavaScript์™€ CSS์šฉ ์ปค์Šคํ…€ CDN { #custom-cdn-for-javascript-and-css } + +์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค๋ฅธ CDN์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด `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 } + +์ด์ œ http://127.0.0.1:8000/docs์—์„œ ๋ฌธ์„œ์— ์ ‘์†ํ•œ ๋’ค ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด, ์ƒˆ 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**๋Š” ๋‹ค์Œ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค: + +* `swagger-ui-bundle.js` +* `swagger-ui.css` + +**ReDoc**์€ ๋‹ค์Œ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค: + +* `redoc.standalone.js` + +์ดํ›„ ํŒŒ์ผ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +``` +. +โ”œโ”€โ”€ 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 } + +์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹œ์ž‘ํ•˜๊ณ  http://127.0.0.1:8000/static/redoc.standalone.js๋กœ ์ด๋™ํ•˜์„ธ์š”. + +**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 ์—ฐ๊ฒฐ์„ ๋Š๊ณ  http://127.0.0.1:8000/docs์—์„œ ๋ฌธ์„œ์— ์ ‘์†ํ•œ ๋’ค ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•ด ๋ณด์„ธ์š”. + +์ธํ„ฐ๋„ท์ด ์—†์–ด๋„ API ๋ฌธ์„œ๋ฅผ ๋ณด๊ณ , API์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/how-to/custom-request-and-route.md b/docs/ko/docs/how-to/custom-request-and-route.md new file mode 100644 index 000000000..335193bb3 --- /dev/null +++ b/docs/ko/docs/how-to/custom-request-and-route.md @@ -0,0 +1,109 @@ +# ์ปค์Šคํ…€ Request ๋ฐ APIRoute ํด๋ž˜์Šค { #custom-request-and-apiroute-class } + +์ผ๋ถ€ ๊ฒฝ์šฐ์—๋Š” `Request`์™€ `APIRoute` ํด๋ž˜์Šค์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋กœ์ง์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํŠนํžˆ, ์ด๋Š” middleware์— ์žˆ๋Š” ๋กœ์ง์˜ ์ข‹์€ ๋Œ€์•ˆ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ฒ˜๋ฆฌ๋˜๊ธฐ ์ „์— ์š”์ฒญ ๋ฐ”๋””๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์กฐ์ž‘ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. + +/// danger | ์œ„ํ—˜ + +์ด ๊ธฐ๋Šฅ์€ "๊ณ ๊ธ‰" ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. + +**FastAPI**๋ฅผ ์ด์ œ ๋ง‰ ์‹œ์ž‘ํ–ˆ๋‹ค๋ฉด ์ด ์„น์…˜์€ ๊ฑด๋„ˆ๋›ฐ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. + +/// + +## ์‚ฌ์šฉ ์‚ฌ๋ก€ { #use-cases } + +์‚ฌ์šฉ ์‚ฌ๋ก€์—๋Š” ๋‹ค์Œ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค: + +* JSON์ด ์•„๋‹Œ ์š”์ฒญ ๋ฐ”๋””๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ(์˜ˆ: `msgpack`). +* 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`์— ๋Œ€ํ•ด ๋” ์•Œ์•„๋ณด๋ ค๋ฉด Starlette์˜ Requests ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜์„ธ์š”. + +/// + +`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] *} diff --git a/docs/ko/docs/how-to/extending-openapi.md b/docs/ko/docs/how-to/extending-openapi.md new file mode 100644 index 000000000..d04d6c23e --- /dev/null +++ b/docs/ko/docs/how-to/extending-openapi.md @@ -0,0 +1,80 @@ +# 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 ์Šคํ‚ค๋งˆ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ•„์š”ํ•œ ๊ฐ ๋ถ€๋ถ„์„ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ์ปค์Šคํ…€ ๋กœ๊ณ ๋ฅผ ํฌํ•จํ•˜๊ธฐ ์œ„ํ•œ ReDoc์˜ OpenAPI ํ™•์žฅ์„ ์ถ”๊ฐ€ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +### ์ผ๋ฐ˜์ ์ธ **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 } + +http://127.0.0.1:8000/redoc๋กœ ์ด๋™ํ•˜๋ฉด ์ปค์Šคํ…€ ๋กœ๊ณ (์ด ์˜ˆ์‹œ์—์„œ๋Š” **FastAPI** ๋กœ๊ณ )๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + diff --git a/docs/ko/docs/how-to/general.md b/docs/ko/docs/how-to/general.md new file mode 100644 index 000000000..a18dc68a2 --- /dev/null +++ b/docs/ko/docs/how-to/general.md @@ -0,0 +1,39 @@ +# ์ผ๋ฐ˜ - ์‚ฌ์šฉ ๋ฐฉ๋ฒ• - ๋ ˆ์‹œํ”ผ { #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} ๋ฌธ์„œ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”. diff --git a/docs/ko/docs/how-to/graphql.md b/docs/ko/docs/how-to/graphql.md new file mode 100644 index 000000000..3cc467eb7 --- /dev/null +++ b/docs/ko/docs/how-to/graphql.md @@ -0,0 +1,60 @@ +# GraphQL { #graphql } + +**FastAPI**๋Š” **ASGI** ํ‘œ์ค€์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฏ€๋กœ, ASGI์™€๋„ ํ˜ธํ™˜๋˜๋Š” ์–ด๋–ค **GraphQL** ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“  ๋งค์šฐ ์‰ฝ๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ผ๋ฐ˜ FastAPI **๊ฒฝ๋กœ ์ฒ˜๋ฆฌ**์™€ GraphQL์„ ํ•จ๊ป˜ ์กฐํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +/// tip | ํŒ + +**GraphQL**์€ ๋ช‡ ๊ฐ€์ง€ ๋งค์šฐ ํŠน์ •ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. + +์ผ๋ฐ˜์ ์ธ **web API**์™€ ๋น„๊ตํ–ˆ์„ ๋•Œ **์žฅ์ **๊ณผ **๋‹จ์ **์ด ์žˆ์Šต๋‹ˆ๋‹ค. + +์—ฌ๋Ÿฌ๋ถ„์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ **์ด์ **์ด **๋‹จ์ **์„ ์ƒ์‡„ํ•˜๋Š”์ง€ ๊ผญ ํ‰๊ฐ€ํ•ด ๋ณด์„ธ์š”. ๐Ÿค“ + +/// + +## GraphQL ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ { #graphql-libraries } + +๋‹ค์Œ์€ **ASGI** ์ง€์›์ด ์žˆ๋Š” **GraphQL** ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ž…๋‹ˆ๋‹ค. **FastAPI**์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +* Strawberry ๐Ÿ“ + * FastAPI์šฉ ๋ฌธ์„œ ์ œ๊ณต +* Ariadne + * FastAPI์šฉ ๋ฌธ์„œ ์ œ๊ณต +* Tartiflette + * ASGI ํ†ตํ•ฉ์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด Tartiflette ASGI ์‚ฌ์šฉ +* Graphene + * starlette-graphene3 ์‚ฌ์šฉ + +## Strawberry๋กœ GraphQL ์‚ฌ์šฉํ•˜๊ธฐ { #graphql-with-strawberry } + +**GraphQL**๋กœ ์ž‘์—…ํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜ ์ž‘์—…ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, **Strawberry**๋ฅผ **๊ถŒ์žฅ**ํ•ฉ๋‹ˆ๋‹ค. **FastAPI**์˜ ์„ค๊ณ„์™€ ๊ฐ€์žฅ ๊ฐ€๊น๊ณ , ๋ชจ๋“  ๊ฒƒ์ด **type annotations**์— ๊ธฐ๋ฐ˜ํ•ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ ํ˜ธํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ œ๊ฒŒ ๋ฌป๋Š”๋‹ค๋ฉด ์•„๋งˆ **Strawberry**๋ฅผ ๋จผ์ € ์‹œ๋„ํ•ด ๋ณด๋ผ๊ณ  ์ œ์•ˆํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +๋‹ค์Œ์€ Strawberry๋ฅผ FastAPI์™€ ํ†ตํ•ฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์ž…๋‹ˆ๋‹ค: + +{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *} + +Strawberry ๋ฌธ์„œ์—์„œ Strawberry์— ๋Œ€ํ•ด ๋” ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋˜ํ•œ FastAPI์—์„œ Strawberry ์‚ฌ์šฉ์— ๋Œ€ํ•œ ๋ฌธ์„œ๋„ ํ™•์ธํ•ด ๋ณด์„ธ์š”. + +## Starlette์˜ ์˜ˆ์ „ `GraphQLApp` { #older-graphqlapp-from-starlette } + +์ด์ „ ๋ฒ„์ „์˜ Starlette์—๋Š” Graphene๊ณผ ํ†ตํ•ฉํ•˜๊ธฐ ์œ„ํ•œ `GraphQLApp` ํด๋ž˜์Šค๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. + +์ด๊ฒƒ์€ Starlette์—์„œ deprecated ๋˜์—ˆ์ง€๋งŒ, ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋˜ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ฐ™์€ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๋‹ค๋ฃจ๊ณ  **๊ฑฐ์˜ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค**๋ฅผ ๊ฐ€์ง„ starlette-graphene3๋กœ ์‰ฝ๊ฒŒ **๋งˆ์ด๊ทธ๋ ˆ์ด์…˜**ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +/// tip | ํŒ + +GraphQL์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, ์ปค์Šคํ…€ ํด๋ž˜์Šค์™€ ํƒ€์ž… ๋Œ€์‹  type annotations์— ๊ธฐ๋ฐ˜ํ•œ Strawberry๋ฅผ ์—ฌ์ „ํžˆ ํ™•์ธํ•ด ๋ณด์‹œ๊ธธ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. + +/// + +## ๋” ์•Œ์•„๋ณด๊ธฐ { #learn-more } + +๊ณต์‹ GraphQL ๋ฌธ์„œ์—์„œ **GraphQL**์— ๋Œ€ํ•ด ๋” ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋˜ํ•œ ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด์„œ๋„ ํ•ด๋‹น ๋งํฌ์—์„œ ๋” ์ž์„ธํžˆ ์ฝ์–ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/how-to/index.md b/docs/ko/docs/how-to/index.md new file mode 100644 index 000000000..9321c488b --- /dev/null +++ b/docs/ko/docs/how-to/index.md @@ -0,0 +1,13 @@ +# How To - ๋ ˆ์‹œํ”ผ { #how-to-recipes } + +์—ฌ๊ธฐ์—์„œ๋Š” **์—ฌ๋Ÿฌ ์ฃผ์ œ**์— ๋Œ€ํ•œ ๋‹ค์–‘ํ•œ ๋ ˆ์‹œํ”ผ(โ€œhow toโ€ ๊ฐ€์ด๋“œ)๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋Œ€๋ถ€๋ถ„์˜ ์•„์ด๋””์–ด๋Š” ์–ด๋А ์ •๋„ **์„œ๋กœ ๋…๋ฆฝ์ **์ด๋ฉฐ, ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ **์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ**์— ์ง์ ‘ ์ ์šฉ๋˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ํ•™์Šตํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. + +ํ”„๋กœ์ ํŠธ์— ํฅ๋ฏธ๋กญ๊ณ  ์œ ์šฉํ•ด ๋ณด์ด๋Š” ๊ฒƒ์ด ์žˆ๋‹ค๋ฉด ํ™•์ธํ•ด ๋ณด์„ธ์š”. ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ์•„๋งˆ ๊ฑด๋„ˆ๋›ฐ์–ด๋„ ๋ฉ๋‹ˆ๋‹ค. + +/// tip | ํŒ + +**FastAPI๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ํ•™์Šต**ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด(๊ถŒ์žฅ), ๋Œ€์‹  [ํŠœํ† ๋ฆฌ์–ผ - ์‚ฌ์šฉ์ž ๊ฐ€์ด๋“œ](../tutorial/index.md){.internal-link target=_blank}๋ฅผ ์žฅ๋ณ„๋กœ ์ฝ์–ด๋ณด์„ธ์š”. + +/// diff --git a/docs/ko/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md b/docs/ko/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md new file mode 100644 index 000000000..6e528ecaf --- /dev/null +++ b/docs/ko/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md @@ -0,0 +1,135 @@ +# 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๋กœ์˜ ๊ณต์‹ Migration Guide๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. + +์—ฌ๊ธฐ์—๋Š” ๋ฌด์—‡์ด ๋ฐ”๋€Œ์—ˆ๋Š”์ง€, ๊ฒ€์ฆ์ด ์ด์ œ ์–ด๋–ป๊ฒŒ ๋” ์ •ํ™•ํ•˜๊ณ  ์—„๊ฒฉํ•ด์กŒ๋Š”์ง€, ๊ฐ€๋Šฅํ•œ ์ฃผ์˜์‚ฌํ•ญ ๋“ฑ๋„ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. + +๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ์„ ๋” ์ž˜ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ์ฝ์–ด๋ณด๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค. + +## ํ…Œ์ŠคํŠธ { #tests } + +์•ฑ์— ๋Œ€ํ•œ [tests](../tutorial/testing.md){.internal-link target=_blank}๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์ง€์†์  ํ†ตํ•ฉ(CI)์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”. + +์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ๋„ ๋ชจ๋“  ๊ฒƒ์ด ๊ธฐ๋Œ€ํ•œ ๋Œ€๋กœ ๊ณ„์† ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## `bump-pydantic` { #bump-pydantic } + +๋งŽ์€ ๊ฒฝ์šฐ, ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ์—†์ด ์ผ๋ฐ˜์ ์ธ Pydantic ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด Pydantic v1์—์„œ Pydantic v2๋กœ์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ณผ์ • ๋Œ€๋ถ€๋ถ„์„ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ฐ™์€ Pydantic ํŒ€์ด ์ œ๊ณตํ•˜๋Š” `bump-pydantic`๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ๋„๊ตฌ๋Š” ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ์˜ ๋Œ€๋ถ€๋ถ„์„ ์ž๋™์œผ๋กœ ๋ฐ”๊พธ๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค. + +๊ทธ ๋‹ค์Œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด์„œ ๋ชจ๋“  ๊ฒƒ์ด ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ž˜ ๋œ๋‹ค๋ฉด ๋์ž…๋‹ˆ๋‹ค. ๐Ÿ˜Ž + +## 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๋กœ ๋ชจ๋ธ์„ ๊ทธ๋ฃน ๋‹จ์œ„๋กœ, ์ ์ง„์ ์ธ ๋‹จ๊ณ„๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹œ์ž‘ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๐Ÿšถ diff --git a/docs/ko/docs/how-to/separate-openapi-schemas.md b/docs/ko/docs/how-to/separate-openapi-schemas.md new file mode 100644 index 000000000..055429c26 --- /dev/null +++ b/docs/ko/docs/how-to/separate-openapi-schemas.md @@ -0,0 +1,102 @@ +# ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ์— ๋Œ€ํ•ด 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` ํ•„๋“œ์— **๋นจ๊ฐ„ ๋ณ„ํ‘œ**๊ฐ€ ์—†๊ณ , ํ•„์ˆ˜๋กœ ํ‘œ์‹œ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +
+ +
+ +### ์ถœ๋ ฅ์šฉ ๋ชจ๋ธ { #model-for-output } + +ํ•˜์ง€๋งŒ ๊ฐ™์€ ๋ชจ๋ธ์„ ๋‹ค์Œ์ฒ˜๋Ÿผ ์ถœ๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด: + +{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *} + +...`description`์— ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ทธ ํ•„๋“œ์— ๋Œ€ํ•ด **์•„๋ฌด๊ฒƒ๋„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋”๋ผ๋„** ์—ฌ์ „ํžˆ ๊ทธ **๊ธฐ๋ณธ๊ฐ’**์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. + +### ์ถœ๋ ฅ ์‘๋‹ต ๋ฐ์ดํ„ฐ์šฉ ๋ชจ๋ธ { #model-for-output-response-data } + +๋ฌธ์„œ์—์„œ ์ง์ ‘ ๋™์ž‘์‹œ์ผœ ์‘๋‹ต์„ ํ™•์ธํ•ด ๋ณด๋ฉด, ์ฝ”๋“œ๊ฐ€ `description` ํ•„๋“œ ์ค‘ ํ•˜๋‚˜์— ์•„๋ฌด๊ฒƒ๋„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•˜๋”๋ผ๋„ JSON ์‘๋‹ต์—๋Š” ๊ธฐ๋ณธ๊ฐ’(`null`)์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: + +
+ +
+ +์ด๋Š” ํ•ด๋‹น ํ•„๋“œ๊ฐ€ **ํ•ญ์ƒ ๊ฐ’์„ ๊ฐ€์ง„๋‹ค๋Š” ๊ฒƒ**์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๊ทธ ๊ฐ’์ด ๋•Œ๋กœ๋Š” `None`(JSON์—์„œ๋Š” `null`)์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ฆ‰, API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ๋Š” ๊ฐ’์ด ์กด์žฌํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ํ•„์š”๊ฐ€ ์—†๊ณ , **ํ•„๋“œ๊ฐ€ ํ•ญ์ƒ ์กด์žฌํ•œ๋‹ค๊ณ  ๊ฐ€์ •**ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์–ด๋–ค ๊ฒฝ์šฐ์—๋Š” ๊ธฐ๋ณธ๊ฐ’ `None`์ด ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. + +์ด๋ฅผ OpenAPI์—์„œ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€, ๊ทธ ํ•„๋“œ๋ฅผ **required**๋กœ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•ญ์ƒ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +์ด ๋•Œ๋ฌธ์—, ํ•˜๋‚˜์˜ ๋ชจ๋ธ์ด๋ผ๋„ **์ž…๋ ฅ์šฉ์ธ์ง€ ์ถœ๋ ฅ์šฉ์ธ์ง€**์— ๋”ฐ๋ผ JSON Schema๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +* **์ž…๋ ฅ**์—์„œ๋Š” `description`์ด **ํ•„์ˆ˜๊ฐ€ ์•„๋‹˜** +* **์ถœ๋ ฅ**์—์„œ๋Š” **ํ•„์ˆ˜์ž„** (๊ทธ๋ฆฌ๊ณ  ๊ฐ’์€ `None`์ผ ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, JSON ์šฉ์–ด๋กœ๋Š” `null`) + +### ๋ฌธ์„œ์—์„œ์˜ ์ถœ๋ ฅ์šฉ ๋ชจ๋ธ { #model-for-output-in-docs } + +๋ฌธ์„œ์—์„œ ์ถœ๋ ฅ ๋ชจ๋ธ์„ ํ™•์ธํ•ด ๋ณด๋ฉด, `name`๊ณผ `description` **๋‘˜ ๋‹ค** **๋นจ๊ฐ„ ๋ณ„ํ‘œ**๋กœ **ํ•„์ˆ˜**๋กœ ํ‘œ์‹œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: + +
+ +
+ +### ๋ฌธ์„œ์—์„œ์˜ ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ ๋ชจ๋ธ { #model-for-input-and-output-in-docs } + +๋˜ OpenAPI์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  Schemas(JSON Schemas)๋ฅผ ํ™•์ธํ•ด ๋ณด๋ฉด, `Item-Input` ํ•˜๋‚˜์™€ `Item-Output` ํ•˜๋‚˜, ์ด๋ ‡๊ฒŒ ๋‘ ๊ฐœ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +`Item-Input`์—์„œ๋Š” `description`์ด **ํ•„์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ฉฐ**, ๋นจ๊ฐ„ ๋ณ„ํ‘œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ `Item-Output`์—์„œ๋Š” `description`์ด **ํ•„์ˆ˜์ด๋ฉฐ**, ๋นจ๊ฐ„ ๋ณ„ํ‘œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. + +
+ +
+ +**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`์€ **ํ•„์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฒƒ**์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค: + +
+ +
diff --git a/docs/ko/docs/how-to/testing-database.md b/docs/ko/docs/how-to/testing-database.md new file mode 100644 index 000000000..2d7798d70 --- /dev/null +++ b/docs/ko/docs/how-to/testing-database.md @@ -0,0 +1,7 @@ +# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ŠคํŠธํ•˜๊ธฐ { #testing-a-database } + +๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, SQL, SQLModel์— ๋Œ€ํ•ด์„œ๋Š” SQLModel ๋ฌธ์„œ์—์„œ ํ•™์Šตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿค“ + +FastAPI์—์„œ SQLModel์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋ฏธ๋‹ˆ ํŠœํ† ๋ฆฌ์–ผ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. โœจ + +ํ•ด๋‹น ํŠœํ† ๋ฆฌ์–ผ์—๋Š” SQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ŠคํŠธ์— ๋Œ€ํ•œ ์„น์…˜๋„ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜Ž diff --git a/docs/ko/docs/tutorial/bigger-applications.md b/docs/ko/docs/tutorial/bigger-applications.md new file mode 100644 index 000000000..cfc3900d4 --- /dev/null +++ b/docs/ko/docs/tutorial/bigger-applications.md @@ -0,0 +1,504 @@ +# ๋” ํฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ - ์—ฌ๋Ÿฌ ํŒŒ์ผ { #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`. + + + +๊ฐ™์€ ํŒŒ์ผ ๊ตฌ์กฐ์— ์ฃผ์„์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + +```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` ํŒŒ์ผ์— ์žˆ์Šต๋‹ˆ๋‹ค. + +์šฐ๋ฆฌ ์•ฑ/ํŒŒ์ผ ๊ตฌ์กฐ๋ฅผ ๋‹ค์‹œ ๋– ์˜ฌ๋ ค ๋ณด์„ธ์š”: + + + +--- + +๋‹ค์Œ์ฒ˜๋Ÿผ ์  ๋‘ ๊ฐœ `..`๋ฅผ ์“ฐ๋ฉด: + +```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์— ๋Œ€ํ•ด ๋” ์•Œ์•„๋ณด๋ ค๋ฉด Modules์— ๋Œ€ํ•œ Python ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”. + +/// + +### ์ด๋ฆ„ ์ถฉ๋Œ ํ”ผํ•˜๊ธฐ { #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 } + +์ด์ œ ์•ฑ์„ ์‹คํ–‰ํ•˜์„ธ์š”: + +
+ +```console +$ fastapi dev app/main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +๊ทธ๋ฆฌ๊ณ  http://127.0.0.1:8000/docs์—์„œ ๋ฌธ์„œ๋ฅผ ์—ฌ์„ธ์š”. + +์˜ฌ๋ฐ”๋ฅธ ๊ฒฝ๋กœ(๋ฐ prefix)์™€ ์˜ฌ๋ฐ”๋ฅธ ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด, ๋ชจ๋“  submodule์˜ ๊ฒฝ๋กœ๋ฅผ ํฌํ•จํ•œ ์ž๋™ API ๋ฌธ์„œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + + +## ๊ฐ™์€ 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*๋„ ํ•จ๊ป˜ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/tutorial/body-updates.md b/docs/ko/docs/tutorial/body-updates.md new file mode 100644 index 000000000..3719e1ffa --- /dev/null +++ b/docs/ko/docs/tutorial/body-updates.md @@ -0,0 +1,100 @@ +# Body - ์—…๋ฐ์ดํŠธ { #body-updates } + +## `PUT`์œผ๋กœ ๊ต์ฒด ์—…๋ฐ์ดํŠธํ•˜๊ธฐ { #update-replacing-with-put } + +ํ•ญ๋ชฉ์„ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด HTTP `PUT` ์ž‘์—…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +`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 } + +HTTP `PATCH` ์ž‘์—…์„ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ *๋ถ€๋ถ„์ ์œผ๋กœ* ์—…๋ฐ์ดํŠธํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Š” ์—…๋ฐ์ดํŠธํ•˜๋ ค๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๋ณด๋‚ด๊ณ , ๋‚˜๋จธ์ง€๋Š” ๊ทธ๋Œ€๋กœ ๋‘๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. + +/// 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}์— ์„ค๋ช…๋œ ์•„์ด๋””์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +/// diff --git a/docs/ko/docs/tutorial/dependencies/sub-dependencies.md b/docs/ko/docs/tutorial/dependencies/sub-dependencies.md new file mode 100644 index 000000000..d81ccf00d --- /dev/null +++ b/docs/ko/docs/tutorial/dependencies/sub-dependencies.md @@ -0,0 +1,105 @@ +# ํ•˜์œ„ ์˜์กด์„ฑ { #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**๋Š” ๊ทธ ํ•˜์œ„ ์˜์กด์„ฑ์„ ์š”์ฒญ๋‹น ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋ฆฌ๊ณ  ๊ฐ™์€ ์š”์ฒญ์— ๋Œ€ํ•ด ๋™์ผํ•œ ์˜์กด์„ฑ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹ , ๋ฐ˜ํ™˜๊ฐ’์„ "cache"์— ์ €์žฅํ•˜๊ณ , ๊ทธ ์š”์ฒญ์—์„œ ํ•ด๋‹น ๊ฐ’์ด ํ•„์š”ํ•œ ๋ชจ๋“  "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 | ํŒ + +์ด ๋‹จ์ˆœํ•œ ์˜ˆ์‹œ๋งŒ ๋ณด๋ฉด ๊ทธ๋‹ค์ง€ ์œ ์šฉํ•ด ๋ณด์ด์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ **๋ณด์•ˆ**์— ๊ด€ํ•œ ์ฑ•ํ„ฐ์—์„œ ์ด๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ์œ ์šฉํ•œ์ง€ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +๋˜ํ•œ ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ์•„๊ปด์ฃผ๋Š”์ง€๋„ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +/// diff --git a/docs/ko/docs/tutorial/handling-errors.md b/docs/ko/docs/tutorial/handling-errors.md new file mode 100644 index 000000000..7cc37e80c --- /dev/null +++ b/docs/ko/docs/tutorial/handling-errors.md @@ -0,0 +1,244 @@ +# ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ { #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 } + +Starlette์˜ ๋™์ผํ•œ ์˜ˆ์™ธ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•ด ์ปค์Šคํ…€ ์˜ˆ์™ธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์—ฌ๋Ÿฌ๋ถ„(๋˜๋Š” ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)์ด `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] *} + +์ด ์˜ˆ์‹œ์—์„œ๋Š” ๋งค์šฐ ํ‘œํ˜„๋ ฅ ์žˆ๋Š” ๋ฉ”์‹œ์ง€๋กœ ์˜ค๋ฅ˜๋ฅผ ์ถœ๋ ฅ๋งŒ ํ•˜๊ณ  ์žˆ์ง€๋งŒ, ์š”์ง€๋Š” ์ดํ•ดํ•˜์…จ์„ ๊ฒ๋‹ˆ๋‹ค. ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•œ ๋’ค ๊ธฐ๋ณธ ์˜ˆ์™ธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/tutorial/security/first-steps.md b/docs/ko/docs/tutorial/security/first-steps.md new file mode 100644 index 000000000..4c9181b31 --- /dev/null +++ b/docs/ko/docs/tutorial/security/first-steps.md @@ -0,0 +1,203 @@ +# ๋ณด์•ˆ - ์ฒซ ๋‹จ๊ณ„ { #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 | ์ •๋ณด + +`python-multipart` ํŒจํ‚ค์ง€๋Š” `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"๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +/// + +๋‹ค์Œ์œผ๋กœ ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”: + +
+ +```console +$ fastapi dev main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +## ํ™•์ธํ•˜๊ธฐ { #check-it } + +๋Œ€ํ™”ํ˜• ๋ฌธ์„œ๋กœ ์ด๋™ํ•˜์„ธ์š”: http://127.0.0.1:8000/docs. + +๋‹ค์Œ๊ณผ ๋น„์Šทํ•œ ํ™”๋ฉด์ด ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค: + + + +/// check | Authorize ๋ฒ„ํŠผ! + +๋ฐ˜์ง์ด๋Š” ์ƒˆ "Authorize" ๋ฒ„ํŠผ์ด ์ด๋ฏธ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋ฆฌ๊ณ  *๊ฒฝ๋กœ ์ฒ˜๋ฆฌ*์—๋Š” ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ์— ํด๋ฆญํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์€ ์ž๋ฌผ์‡ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. + +/// + +๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ ํด๋ฆญํ•˜๋ฉด `username`๊ณผ `password`(๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ ์„ ํƒ์  ํ•„๋“œ๋“ค)๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์€ ์ธ์ฆ ํผ์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค: + + + +/// 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`์ด ๋“ค์–ด ์žˆ๋‹ค๊ณ  ํ™•์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋Œ€ํ™”ํ˜• ๋ฌธ์„œ์—์„œ ์ด๋ฏธ ์‹œ๋„ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + + + +์•„์ง token์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•˜์ง„ ์•Š์ง€๋งŒ, ์ด๊ฒƒ๋งŒ์œผ๋กœ๋„ ์‹œ์ž‘์€ ๋œ ์…ˆ์ž…๋‹ˆ๋‹ค. + +## ์š”์•ฝ { #recap } + +์ฆ‰, ์ถ”๊ฐ€๋กœ 3~4์ค„๋งŒ์œผ๋กœ๋„ ์ด๋ฏธ ์›์‹œ์ ์ธ ํ˜•ํƒœ์˜ ๋ณด์•ˆ์„ ๊ฐ–์ถ”๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. diff --git a/docs/ko/docs/tutorial/security/index.md b/docs/ko/docs/tutorial/security/index.md new file mode 100644 index 000000000..2320b0657 --- /dev/null +++ b/docs/ko/docs/tutorial/security/index.md @@ -0,0 +1,106 @@ +# ๋ณด์•ˆ { #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์— ๋ณด์•ˆ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +๋˜ํ•œ ๋Œ€ํ™”ํ˜• ๋ฌธ์„œ ์‹œ์Šคํ…œ์— ์–ด๋–ป๊ฒŒ ์ž๋™์œผ๋กœ ํ†ตํ•ฉ๋˜๋Š”์ง€๋„ ํ™•์ธํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. From 23bcfa094d9baefbf0150cbcddd1b77ec33a23fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 16 Jan 2026 11:54:26 +0000 Subject: [PATCH 18/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 233fdb743..912f215f0 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Translations +* ๐ŸŒ Update translations for ko (add-missing). PR [#14699](https://github.com/fastapi/fastapi/pull/14699) by [@tiangolo](https://github.com/tiangolo). * ๐ŸŒ Update translations for ko (update-outdated). PR [#14589](https://github.com/fastapi/fastapi/pull/14589) by [@tiangolo](https://github.com/tiangolo). * ๐ŸŒ Update translations for uk (update-outdated). PR [#14587](https://github.com/fastapi/fastapi/pull/14587) by [@tiangolo](https://github.com/tiangolo). * ๐ŸŒ Update translations for es (update-outdated). PR [#14686](https://github.com/fastapi/fastapi/pull/14686) by [@tiangolo](https://github.com/tiangolo). From fb15bba819a71df10fee28891c6c7c29fdf48d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 16 Jan 2026 03:57:08 -0800 Subject: [PATCH 19/26] =?UTF-8?q?=F0=9F=8C=90=20Update=20LLM=20prompt=20in?= =?UTF-8?q?structions=20file=20for=20French=20(#14618)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/fr/llm-prompt.md | 154 ++++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 75 deletions(-) diff --git a/docs/fr/llm-prompt.md b/docs/fr/llm-prompt.md index 5ff18c4e8..228500fe2 100644 --- a/docs/fr/llm-prompt.md +++ b/docs/fr/llm-prompt.md @@ -6,123 +6,127 @@ Language code: fr. ### Grammar to use when talking to the reader -Use the formal grammar (use ยซvousยป instead of ยซtuยป). +Use the formal grammar (use `vous` instead of `tu`). -### Quotes +Additionally, in instructional sentences, prefer the present tense for obligations: + +- Prefer `vous devez โ€ฆ` over `vous devrez โ€ฆ`, unless the English source explicitly refers to a future requirement. + +- When translating โ€œmake sure (that) โ€ฆ is โ€ฆโ€, prefer the indicative after `vous assurer que` (e.g. `Vous devez vous assurer qu'il est โ€ฆ`) instead of the subjunctive (e.g. `qu'il soit โ€ฆ`). -1) Convert neutral double quotes (ยซ"ยป) and English double typographic quotes (ยซโ€œยป and ยซโ€ยป) to French guillemets (ยซยซยป and ยซยปยป). +### Quotes -2) In the French docs, guillemets are written without extra spaces: use ยซtexteยป, not ยซ texte ยป. +- Convert neutral double quotes (`"`) to French guillemets (`ยซ` and `ยป`). -3) Do not convert quotes inside code blocks, inline code, paths, URLs, or anything wrapped in backticks. +- Do not convert quotes inside code blocks, inline code, paths, URLs, or anything wrapped in backticks. Examples: - Source (English): +Source (English): - ยซยซยซ - "Hello world" - โ€œHello Universeโ€ - "He said: 'Hello'" - "The module is `__main__`" - ยปยปยป +``` +"Hello world" +โ€œHello Universeโ€ +"He said: 'Hello'" +"The module is `__main__`" +``` - Result (French): +Result (French): - ยซยซยซ - ยซHello worldยป - ยซHello Universeยป - ยซHe said: 'Hello'ยป - ยซThe module is `__main__`ยป - ยปยปยป +``` +"Hello world" +โ€œHello Universeโ€ +"He said: 'Hello'" +"The module is `__main__`" +``` ### Ellipsis -1) Make sure there is a space between an ellipsis and a word following or preceding the ellipsis. +- Make sure there is a space between an ellipsis and a word following or preceding the ellipsis. Examples: - Source (English): +Source (English): - ยซยซยซ - ...as we intended. - ...this would work: - ...etc. - others... - More to come... - ยปยปยป +``` +...as we intended. +...this would work: +...etc. +others... +More to come... +``` - Result (French): +Result (French): - ยซยซยซ - ... comme prรฉvu. - ... cela fonctionnerait : - ... etc. - D'autres ... - La suite ... - ยปยปยป +``` +... comme prรฉvu. +... cela fonctionnerait : +... etc. +D'autres ... +La suite ... +``` -2) This does not apply in URLs, code blocks, and code snippets. Do not remove or add spaces there. +- This does not apply in URLs, code blocks, and code snippets. Do not remove or add spaces there. ### Headings -1) Prefer translating headings using the infinitive form (as is common in the existing French docs): ยซCrรฉerโ€ฆยป, ยซUtiliserโ€ฆยป, ยซAjouterโ€ฆยป. - -2) For headings that are instructions written in imperative in English (e.g. โ€œGo check โ€ฆโ€), keep them in imperative in French, using the formal grammar (e.g. ยซAllez voir โ€ฆยป). +- Prefer translating headings using the infinitive form (as is common in the existing French docs): `Crรฉerโ€ฆ`, `Utiliserโ€ฆ`, `Ajouterโ€ฆ`. -3) Keep heading punctuation as in the source. In particular, keep occurrences of literal ยซ - ยป (space-hyphen-space) as ยซ - ยป (the existing French docs use a hyphen here). +- For headings that are instructions written in imperative in English (e.g. `Go check โ€ฆ`), keep them in imperative in French, using the formal grammar (e.g. `Allez voir โ€ฆ`). ### French instructions about technical terms -Do not try to translate everything. In particular, keep common programming terms when that is the established usage in the French docs (e.g. ยซframeworkยป, ยซendpointยป, ยซplug-inยป, ยซpayloadยป). Use French where the existing docs already consistently use French (e.g. ยซrequรชteยป, ยซrรฉponseยป). +Do not try to translate everything. In particular, keep common programming terms (e.g. `framework`, `endpoint`, `plug-in`, `payload`). Keep class names, function names, modules, file names, and CLI commands unchanged. ### List of English terms and their preferred French translations -Below is a list of English terms and their preferred French translations, separated by a colon (ยซ:ยป). Use these translations, do not use your own. If an existing translation does not use these terms, update it to use them. +Below is a list of English terms and their preferred French translations, separated by a colon (:). Use these translations, do not use your own. If an existing translation does not use these terms, update it to use them. + +- /// note | Technical Detailsยป: /// note | Dรฉtails techniques +- /// note: /// note | Remarque +- /// tip: /// tip | Astuce +- /// warning: /// warning | Alertes +- /// check: /// check | Vรฉrifications +- /// info: /// info -* ยซ/// note | Technical Detailsยป: ยซ/// note | Dรฉtails techniquesยป -* ยซ/// noteยป: ยซ/// note | Remarqueยป -* ยซ/// tipยป: ยซ/// tip | Astuceยป -* ยซ/// warningยป: ยซ/// warning | Attentionยป -* ยซ/// checkยป: ยซ/// check | vรฉrifierยป -* ยซ/// infoยป: ยซ/// infoยป +- the docs: les documents +- the documentation: la documentation -* ยซthe docsยป: ยซles documentsยป -* ยซthe documentationยป: ยซla documentationยป +- Exclude from OpenAPI: Exclusion d'OpenAPI -* ยซframeworkยป: ยซframeworkยป (do not translate to ยซcadreยป) -* ยซperformanceยป: ยซperformanceยป +- framework: framework (do not translate to cadre) +- performance: performance -* ยซtype hintsยป: ยซannotations de typeยป -* ยซtype annotationsยป: ยซannotations de typeยป +- type hints: annotations de type +- type annotations: annotations de type -* ยซautocompleteยป: ยซautocomplรฉtionยป -* ยซautocompletionยป: ยซautocomplรฉtionยป +- autocomplete: autocomplรฉtion +- autocompletion: autocomplรฉtion -* ยซthe requestยป (what the client sends to the server): ยซla requรชteยป -* ยซthe responseยป (what the server sends back to the client): ยซla rรฉponseยป +- the request (what the client sends to the server): la requรชte +- the response (what the server sends back to the client): la rรฉponse -* ยซthe request bodyยป: ยซle corps de la requรชteยป -* ยซthe response bodyยป: ยซle corps de la rรฉponseยป +- the request body: le corps de la requรชte +- the response body: le corps de la rรฉponse -* ยซpath operationยป: ยซopรฉration de cheminยป -* ยซpath operationsยป (plural): ยซopรฉrations de cheminยป -* ยซpath operation functionยป: ยซfonction de cheminยป -* ยซpath operation decoratorยป: ยซdรฉcorateur d'opรฉration de cheminยป +- path operation: chemin d'accรจs +- path operations (plural): chemins d'accรจs +- path operation function: fonction de chemin d'accรจs +- path operation decorator: dรฉcorateur de chemin d'accรจs -* ยซpath parameterยป: ยซparamรจtre de cheminยป -* ยซquery parameterยป: ยซparamรจtre de requรชteยป +- path parameter: paramรจtre de chemin +- query parameter: paramรจtre de requรชte -* ยซthe `Request`ยป: ยซ`Request`ยป (keep as code identifier) -* ยซthe `Response`ยป: ยซ`Response`ยป (keep as code identifier) +- the `Request`: `Request` (keep as code identifier) +- the `Response`: `Response` (keep as code identifier) -* ยซdeploymentยป: ยซdรฉploiementยป -* ยซto upgradeยป: ยซmettre ร  niveauยป +- deployment: dรฉploiement +- to upgrade: mettre ร  niveau -* ยซdeprecatedยป: ยซdรฉprรฉciรฉยป -* ยซto deprecateยป: ยซdรฉprรฉcierยป +- deprecated: dรฉprรฉciรฉ +- to deprecate: dรฉprรฉcier -* ยซcheat sheetยป: ยซaide-mรฉmoireยป -* ยซplug-inยป: ยซplug-inยป +- cheat sheet: aide-mรฉmoire +- plug-in: plug-in From 8fa635c718fe71fa2e761b7fb33978d756e77a45 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 16 Jan 2026 11:57:32 +0000 Subject: [PATCH 20/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 912f215f0..404a4e41d 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Translations +* ๐ŸŒ Update LLM prompt instructions file for French. PR [#14618](https://github.com/fastapi/fastapi/pull/14618) by [@tiangolo](https://github.com/tiangolo). * ๐ŸŒ Update translations for ko (add-missing). PR [#14699](https://github.com/fastapi/fastapi/pull/14699) by [@tiangolo](https://github.com/tiangolo). * ๐ŸŒ Update translations for ko (update-outdated). PR [#14589](https://github.com/fastapi/fastapi/pull/14589) by [@tiangolo](https://github.com/tiangolo). * ๐ŸŒ Update translations for uk (update-outdated). PR [#14587](https://github.com/fastapi/fastapi/pull/14587) by [@tiangolo](https://github.com/tiangolo). From 9fec72687f48db48d72bff6ba2aa023fe792206b Mon Sep 17 00:00:00 2001 From: Rafael de Oliveira Marques Date: Fri, 16 Jan 2026 09:27:02 -0300 Subject: [PATCH 21/26] =?UTF-8?q?=F0=9F=8C=90=20Update=20portuguese=20llm-?= =?UTF-8?q?prompt.md=20(#14702)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Sofie Van Landeghem --- docs/pt/llm-prompt.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/pt/llm-prompt.md b/docs/pt/llm-prompt.md index 3f5208e91..dd72e8d16 100644 --- a/docs/pt/llm-prompt.md +++ b/docs/pt/llm-prompt.md @@ -10,6 +10,26 @@ Keep existing translations as they are if the term is already translated. When translating documentation into Portuguese, use neutral and widely understandable language. Although Portuguese originated in Portugal and has its largest number of speakers in Brazil, it is also an official language in several countries and regions, such as Equatorial Guinea, Mozambique, Angola, Cape Verde, and Sรฃo Tomรฉ and Prรญncipe. Avoid words or expressions that are specific to a single country or region. +Only keep parentheses if they exist in the source text. Do not add parentheses to terms that do not have them. + +### Avoiding Repetition in Translation + +When translating sentences, avoid unnecessary repetition of words or phrases that are implied in context. +- Merge repeated words naturally while keeping the meaning. +- Do **not** introduce extra words to replace repeated phrases unnecessarily. +- Keep translations fluent and concise, but maintain the original meaning. + +**Example:** + +Source: +Let's see how that works and how to change it if you need to do that. + +Avoid translating literally as: +Vamos ver como isso funciona e como alterar isso se vocรช precisar fazer isso. + +Better translation: +Vamos ver como isso funciona e como alterar se vocรช precisar. + --- For the next terms, use the following translations: @@ -22,10 +42,11 @@ For the next terms, use the following translations: * /// note: /// note | Nota * /// tip: /// tip | Dica * /// warning: /// warning | Atenรงรฃo -* (you should): (vocรช deveria) +* you should: vocรช deveria * async context manager: gerenciador de contexto assรญncrono * autocomplete: autocompletar * autocompletion: preenchimento automรกtico +* auto-completion: preenchimento automรกtico * bug: bug * context manager: gerenciador de contexto * cross domain: cross domain (do not translate to "domรญnio cruzado") From 0c7f2b66d769d05df42ea73f85205a3566ba127b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 16 Jan 2026 12:27:29 +0000 Subject: [PATCH 22/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 404a4e41d..d3f80fac9 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Translations +* ๐ŸŒ Update portuguese llm-prompt.md. PR [#14702](https://github.com/fastapi/fastapi/pull/14702) by [@ceb10n](https://github.com/ceb10n). * ๐ŸŒ Update LLM prompt instructions file for French. PR [#14618](https://github.com/fastapi/fastapi/pull/14618) by [@tiangolo](https://github.com/tiangolo). * ๐ŸŒ Update translations for ko (add-missing). PR [#14699](https://github.com/fastapi/fastapi/pull/14699) by [@tiangolo](https://github.com/tiangolo). * ๐ŸŒ Update translations for ko (update-outdated). PR [#14589](https://github.com/fastapi/fastapi/pull/14589) by [@tiangolo](https://github.com/tiangolo). From 536a5bafe74a77f207b57cf6fb0cf3059121fffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 19 Jan 2026 12:40:17 -0800 Subject: [PATCH 23/26] =?UTF-8?q?=F0=9F=94=A7=20Update=20sponsors,=20Lambd?= =?UTF-8?q?aTest=20changes=20to=20TestMu=20AI=20(#14734)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/data/sponsors.yml | 6 +++--- docs/en/docs/img/sponsors/testmu.png | Bin 0 -> 5725 bytes 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 docs/en/docs/img/sponsors/testmu.png diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 50b114530..c0bdb15f6 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -65,9 +65,9 @@ bronze: # - url: https://testdriven.io/courses/tdd-fastapi/ # title: Learn to build high-quality web apps with best practices # img: https://fastapi.tiangolo.com/img/sponsors/testdriven.svg - - url: https://lambdatest.com/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage - title: LambdaTest, AI-Powered Cloud-based Test Orchestration Platform - img: https://fastapi.tiangolo.com/img/sponsors/lambdatest.png + - url: https://www.testmu.ai/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage + title: TestMu AI. The Native AI-Agentic Cloud Platform to Supercharge Quality Engineering. + img: https://fastapi.tiangolo.com/img/sponsors/testmu.png - url: https://requestly.com/fastapi title: All-in-one platform to Test, Mock and Intercept APIs. Built for speed, privacy and offline support. img: https://fastapi.tiangolo.com/img/sponsors/requestly.png diff --git a/docs/en/docs/img/sponsors/testmu.png b/docs/en/docs/img/sponsors/testmu.png new file mode 100644 index 0000000000000000000000000000000000000000..5603b04faed5e917bd0ea78f1684ba3c7a747718 GIT binary patch literal 5725 zcmd6L_cvT`w6`7vBf5#+iOyGzE?S}l(Pc#RE_x4%8iPa-MvdO0M2kK~8-hsG=msN1 z57FHx>-`7b-|n5Y&YZQ*oO#;bpR#wnfu1HgDU=ik2Zvl+OWhFM_rYaHOa#7LK6VU% z8;O^exepEw1?u00JK$Mn2Ocu|J}~n&@^tVGc=o~`Cmj+ zP#hfEEp2rb{ag3)7-z&_{!ZjqU!N$&bPpwh=N-56}#<+myBe0Py}CX z;uv^rh}3#TaU~cyVn{w9Da9Ujlf}e6!*dW-C;MM~^{wpiodNF@EjJ~|Bc4>$H5V0$ zOw#tzeRcwe&tJxlJUl$4wjnefPa%clo4#H8x52qbj+jrE84Iwdjw^|!q6u>Jq@9*f z(Tx{BW@lyjSYJ~*HU3oTvqw7PCB?-lo0z1Jh)10N$YZ+3R9k(aLL!l2xm|$=9HTk% z)7Z1gN>kL>*jT~d7Gxla8MQu~k+b{PIp0{|;9}|K>Hc!Zi*?fLljbh+A?X2cv(FCX zmseN(7b6Jix`4e!l`|G$Jbe6vQTaUs5j<65|DB)F=jXw(S8nqycV2wSzL}q&Ph^(& zml4uiO?*C@E!)eoxVT7h(e`3JK^GQreU#@uRa0JGcXv#AaAd@9=n7h)R&4k-E{=FB z1#M?C92^|{ZgRF(Nb?ZPwz&Zg1JIeCga@n zmm?pLO}X$nIX3oIx(IC16ZaksY5Um*!rzfA*S@{IT{K|V3Ss~K`}bP=VY-IqW{8pT zRFDT@=+4jo5Lit(l)Dxzab+m&9{#yv5{4}mtxOz=w!>q$Qw|r@DmLV!A;o!#v|%tX z@~Q43q0mWK^5Au&^k*b6i_1`t^et{>|xt zQjGll1guFnDl#L3zCW2Yd4J#Q^x^_FG6K)TxH>x{t1X)i%+2GIk|<)Y`UVC##l@+v zudgpJ3rs4bH42enZw8MaNA~w?bW#j>pRS8Upd*@ia;vHY8yXuQH|Z;$^?KX~89q2T z*!{`6jVXZ@$E3;(*VNQBc8G|8fQ^&$Rejp~_xU9y{SF=7-O5F!rAAGwtG1yxSAQu6 zoZtNZ?N&iJJ~NTNa{f`WQmS;t5AuSNk&y%j zgDH~qI7xmiEW~g2T+(>(fG{B;!R!2wv#+0DBu1uU*0Xx)1mtIyPB8oH<(azY-?opX zYEm&WYin!ywY8MVtV&HGu%4bCQO8lHwspbRuU`|l3c9LR4rxw#*B zjk-zQ{{BEVNeuw>@bIwK1{sBf8H+%n&@xFK5Y*kG7Ie`FB*mQliiO+-XLWz`&H z8%P03Y3Wy#c&x0f02FKj0tq)_1Ox;&3$1=F{yR_*?$cJ+=}!+HJtDiwq>LIG(s`ee zfw{iAz`;>cQsUy`V&mb7`HRJ3WBsJJ_~ZldPZv*}Bq{dlI^3-BZ*mHrO^`_8Fp zX=!C;W%>P#wzjsS-mB`NXX_a#XeD7O$?3)jLdJKKvcA5)N(#R(sjW>O92X@ORnHtT zJ0Cwkas?IZeZ2JqY!DwOG&GcwnHes<7>Ex7x_o4#nZy*|-hMyy>Uer{Yb(m4x?ey1 zka{Z~W?_-wKG#$La8u;0e3&6z%p&FRnei3{)3VZCG9o6$P=-_<-PJv~m9VCX$&?Yb=yLD;IN+Wa%iXxP zlF;gnI}k_VhK}bGWsmXlBCfV!7lKOQ06$J|mmk{O+uLnTl!r~l$HlRg>g9ag+B^>K z)N^yIVv7NEs(4jgQbKCW$H$jHR0ZpLx-20nIZ>hH`sLd|GAl1TJ9|+R#Kpx8J7*cPawVF7L6Bba)PD0+}pT3JK_@-)^K*_ zkN6Ydi~vt1m)Fp#??=Ro?cmwjOfKkbv|Z0)M@Zh#{IPP-PUCdh6fMr>QQl4Ot6LC0 zOKWRp9UUD$O9KN+hp}9RH8Bg+FZYG4EU4RTJ)w9e*{@%}K90ujpeM?W2%8Qt14chc zr#8PNevK-IOwP>2jgIPV4dmtKQWP7;$HWjdG&Ozf=wRO2*=dQmQH0*TdvJZRXQT^z zC^0unLr2FUFONuu16GE0T8S3ZP*FuSG)Pu>Jb6Mpng@X6O-^W7Zd~i7QuSPZ8ke-f?C<&84?zDe~&Epgjf_G^~Mn<0YiG<^(qF>*Fu;c^s28{d7NfPKEOZe%^ zE#oIoRw`>cq2DDt0iLZ|yl_7|jI6rX)zxWfYa>C12U!LO%l%iTNsI(QKR7*jK2G)& z&MZ#{f?nbV#v&#r?*5u7LAai#!`)yZq>;#goa52WdR59CPQz zuKYh?0s=B!0}HUQQV@f}XPP}(4ULS>q~U-{Ej>G;eXyfQJMm||ad!sM=z>)b7nh#8 z=L0cZB?TN35~)<2`*u%*F#Yhbsb33M-T?uc4h|bfoHjTu!N&ssjuU7|rM`F_Tbd3b zgiSo|k8FFDT!k4+Y%E^yyZHU&Gwtzj`o9iq5!|CyH zz?VSqTZrOm&ayio3#DXS?}S@;YK9abu&Sk7ad8?q|X-50WDUwZu2y++1Rnq;9Fk_$=yz#g>=H zZ{6BH4t85;o!s53lFXHu0<^x0#nlYAMy%cu|rB_jeq(yFMa5R;L;PELjx3(x{S zUf$bF1`%65o`zu&M*x4ava+YkuOKmQ6E~aVg=WA9H+Gq6Xb>r0{FXqz_*&C-{c|^X zD;IbV5<}^o;%Gd5e==`6z(-Mn5}MN47@4Fk3v(irI{z|o=9epwh+6ZFANooU08MKo zF*#2!{hs}Nuyk|XJa!ajPeo1$Aa^t({eDIR4vlVUk!c%-pirnf=N~*+dxa=#32rkpDW$2L}gI+bylFa_&PsAdoek z=K}U^fJgj)&#V~GYnL0ZHg{bKaj%-f!e|Q$3Q(;FuZ(!!GtW+p0=BKPY)-l#{AbW( z_VhHcRM+29{v0t_7kFT6E1p?)sF}>d4a{iR6fjNP@&UU=TVTd|;KuP@u3@o8*hf6n znNsdqBP~j&%lInHX*zv*&FO<;!GD}8JI{62HmyB=HaM%7{vi2O$P4gSx{9MrLmEAz zPEl?wpq1<7;=&;&CT4 zoc4*HWIWT!=I(BPXV}F)nl~@bTrGdbOYhZXSr8x0%4Y$*mD+2MRRTP5CT&J=m1d8YKDdm7gZvYES0&r`QY%d z&ro23YC-oq?uTXDqEI+Rb!yYMg$4N4#a|;&S5RWvq2E12sNOv(S_f=u1FFx!gFbq~ zpzbkoAt9l`v9ST5S%dnrAegH=%}f2j1wRI*L3%nJIQGDh5KM8#Q&krifkuhKai?CO zPU9031k317Yd=3fVsi5J;r5fCu3kPqsOa1D2BxOBs!eW3t48MMvvF~AKl@&!ZES@d z%_(f9K0ZE<3SZCq&ow10OXoPXva+(8N5D+2W*UP@R?>L!qJFZ=)YbJ~5{trCO3fmZ zoGseBA7lBOmppd+1M=0ZvnBdO@=HHBtyUT#mp&y)9^EUq?%_LLlckh{}ca1u)h=Y6LVKpd^Q#q7A_?v*f%bY*OZ0h zT0iIJv|U{**SpFijP;_wDD=POf=0Y?^FOma0rK=xA5toEg(l$3?gg&-*d z4haPX&s?)7udc4Hf6a81NhLRMf$ZMi-gpr%jahDUP1Ue#V)mkniUgqA*;-swLwV{P z?0|@OeN^J3ufYhjt$vltT5>r_MMXtsLVIy9aZ>HYkCUc5!t@1E8TnP~)tGo3}*7#ozs2 zXv>&y4>HMcM`LJz*Q&0221kQo42AN;_9A9Jc|!ss*eV*vC6}?nHtY0|Nt<1=3V&rVASz zX+bY$-6z3a_sHxrEj`_~=QRm~gag^0zHkQ0NpcnJ%E`@5U00V}*t*ruhqYv{>xKm( z?b)9J3y&9j-mhG2NK)TcQBh$d%1|5f*TV%`3cSC5*FRAV4_Cy)!wa<3*4ECu*&C`C zD97gi0M%Aiy3oCQYGGjto#$Kk67Nb=h_vtH-B(axPt*BVKLeCbOig`kXrM8zc?xv& zV?V|3ox(}^PYZ1UMomaJJx5W7l)OBV5BSY%#G!x2H7D}Z?a6u>y-vHP_iZvI9FrEp zQjG=DCYhlimH5quo>*Hea6McX1j%!HdTJom@iaLW!g?~70jd+wk^+4a+{{P)w`au5 zCSjoKw2Z~x(%0A5=^hywxw;+;2P(*^kw{sUAkIJ*c)DM}R*7Cz*#GmVh~R90MGb+# zoSmN=LMUD#ZT_xQ84KK@p&<|v5do8J`pPvlG~`!T!|5|Y&w`sY#QRV0+?=JC5Yr3?qW%ZABJ|Aw literal 0 HcmV?d00001 From db5441eba1d4c3bee11dac239a685e3c0630cda5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 19 Jan 2026 20:40:38 +0000 Subject: [PATCH 24/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index d3f80fac9..8b338f60d 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -30,6 +30,7 @@ hide: ### Internal +* ๐Ÿ”ง Update sponsors, LambdaTest changes to TestMu AI. PR [#14734](https://github.com/fastapi/fastapi/pull/14734) by [@tiangolo](https://github.com/tiangolo). * โฌ† Bump actions/cache from 4 to 5. PR [#14511](https://github.com/fastapi/fastapi/pull/14511) by [@dependabot[bot]](https://github.com/apps/dependabot). * โฌ† Bump actions/upload-artifact from 5 to 6. PR [#14525](https://github.com/fastapi/fastapi/pull/14525) by [@dependabot[bot]](https://github.com/apps/dependabot). * โฌ† Bump actions/download-artifact from 6 to 7. PR [#14526](https://github.com/fastapi/fastapi/pull/14526) by [@dependabot[bot]](https://github.com/apps/dependabot). From 463a3a24d7aee142bede2bcdd0ef542646b1fe3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 19 Jan 2026 12:55:32 -0800 Subject: [PATCH 25/26] =?UTF-8?q?=F0=9F=94=A7=20Update=20sponsors:=20remov?= =?UTF-8?q?e=20Requestly=20(#14735)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/data/sponsors.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index c0bdb15f6..f8085b452 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -68,6 +68,3 @@ bronze: - url: https://www.testmu.ai/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage title: TestMu AI. The Native AI-Agentic Cloud Platform to Supercharge Quality Engineering. img: https://fastapi.tiangolo.com/img/sponsors/testmu.png - - url: https://requestly.com/fastapi - title: All-in-one platform to Test, Mock and Intercept APIs. Built for speed, privacy and offline support. - img: https://fastapi.tiangolo.com/img/sponsors/requestly.png From ad6b2901a6ab9350c55f261b06dfd1dc363e1fde Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 19 Jan 2026 20:56:10 +0000 Subject: [PATCH 26/26] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 8b338f60d..4cc8bf018 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -30,6 +30,7 @@ hide: ### Internal +* ๐Ÿ”ง Update sponsors: remove Requestly. PR [#14735](https://github.com/fastapi/fastapi/pull/14735) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ”ง Update sponsors, LambdaTest changes to TestMu AI. PR [#14734](https://github.com/fastapi/fastapi/pull/14734) by [@tiangolo](https://github.com/tiangolo). * โฌ† Bump actions/cache from 4 to 5. PR [#14511](https://github.com/fastapi/fastapi/pull/14511) by [@dependabot[bot]](https://github.com/apps/dependabot). * โฌ† Bump actions/upload-artifact from 5 to 6. PR [#14525](https://github.com/fastapi/fastapi/pull/14525) by [@dependabot[bot]](https://github.com/apps/dependabot).