committed by
GitHub
12 changed files with 930 additions and 3 deletions
@ -64,7 +64,7 @@ jobs: |
|||
BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }} |
|||
# TODO: Use v3 when it's fixed, probably in v3.11 |
|||
# https://github.com/cloudflare/wrangler-action/issues/307 |
|||
uses: cloudflare/[email protected]1 |
|||
uses: cloudflare/[email protected]2 |
|||
# uses: cloudflare/wrangler-action@v3 |
|||
with: |
|||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} |
|||
|
@ -35,7 +35,7 @@ jobs: |
|||
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }} |
|||
run: python -m build |
|||
- name: Publish |
|||
uses: pypa/[email protected]1.0 |
|||
uses: pypa/[email protected]2.2 |
|||
- name: Dump GitHub context |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
|
@ -0,0 +1,179 @@ |
|||
# 고급 의존성 |
|||
|
|||
## 매개변수화된 의존성 |
|||
|
|||
지금까지 본 모든 의존성은 고정된 함수 또는 클래스입니다. |
|||
|
|||
하지만 여러 개의 함수나 클래스를 선언하지 않고도 의존성에 매개변수를 설정해야 하는 경우가 있을 수 있습니다. |
|||
|
|||
예를 들어, `q` 쿼리 매개변수가 특정 고정된 내용을 포함하고 있는지 확인하는 의존성을 원한다고 가정해 봅시다. |
|||
|
|||
이때 해당 고정된 내용을 매개변수화할 수 있길 바랍니다. |
|||
|
|||
## "호출 가능한" 인스턴스 |
|||
|
|||
Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법이 있습니다. |
|||
|
|||
클래스 자체(이미 호출 가능함)가 아니라 해당 클래스의 인스턴스에 대해 호출 가능하게 하는 것입니다. |
|||
|
|||
이를 위해 `__call__` 메서드를 선언합니다: |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../docs_src/dependencies/tutorial011_an_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python hl_lines="11" |
|||
{!> ../../docs_src/dependencies/tutorial011_an.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ non-Annotated |
|||
|
|||
/// tip | 참고 |
|||
|
|||
가능하다면 `Annotated` 버전을 사용하는 것이 좋습니다. |
|||
|
|||
/// |
|||
|
|||
```Python hl_lines="10" |
|||
{!> ../../docs_src/dependencies/tutorial011.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
이 경우, **FastAPI**는 추가 매개변수와 하위 의존성을 확인하기 위해 `__call__`을 사용하게 되며, |
|||
나중에 *경로 연산 함수*에서 매개변수에 값을 전달할 때 이를 호출하게 됩니다. |
|||
|
|||
## 인스턴스 매개변수화하기 |
|||
|
|||
이제 `__init__`을 사용하여 의존성을 "매개변수화"할 수 있는 인스턴스의 매개변수를 선언할 수 있습니다: |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../docs_src/dependencies/tutorial011_an_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python hl_lines="8" |
|||
{!> ../../docs_src/dependencies/tutorial011_an.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ non-Annotated |
|||
|
|||
/// tip | 참고 |
|||
|
|||
가능하다면 `Annotated` 버전을 사용하는 것이 좋습니다. |
|||
|
|||
/// |
|||
|
|||
```Python hl_lines="7" |
|||
{!> ../../docs_src/dependencies/tutorial011.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
이 경우, **FastAPI**는 `__init__`에 전혀 관여하지 않으며, 우리는 이 메서드를 코드에서 직접 사용하게 됩니다. |
|||
|
|||
## 인스턴스 생성하기 |
|||
|
|||
다음과 같이 이 클래스의 인스턴스를 생성할 수 있습니다: |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../docs_src/dependencies/tutorial011_an_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python hl_lines="17" |
|||
{!> ../../docs_src/dependencies/tutorial011_an.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ non-Annotated |
|||
|
|||
/// tip | 참고 |
|||
|
|||
가능하다면 `Annotated` 버전을 사용하는 것이 좋습니다. |
|||
|
|||
/// |
|||
|
|||
```Python hl_lines="16" |
|||
{!> ../../docs_src/dependencies/tutorial011.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
이렇게 하면 `checker.fixed_content` 속성에 `"bar"`라는 값을 담아 의존성을 "매개변수화"할 수 있습니다. |
|||
|
|||
## 인스턴스를 의존성으로 사용하기 |
|||
|
|||
그런 다음, `Depends(FixedContentQueryChecker)` 대신 `Depends(checker)`에서 이 `checker` 인스턴스를 사용할 수 있으며, |
|||
클래스 자체가 아닌 인스턴스 `checker`가 의존성이 됩니다. |
|||
|
|||
의존성을 해결할 때 **FastAPI**는 이 `checker`를 다음과 같이 호출합니다: |
|||
|
|||
```Python |
|||
checker(q="somequery") |
|||
``` |
|||
|
|||
...그리고 이때 반환되는 값을 *경로 연산 함수*의 `fixed_content_included` 매개변수로 전달합니다: |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
```Python hl_lines="22" |
|||
{!> ../../docs_src/dependencies/tutorial011_an_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python hl_lines="21" |
|||
{!> ../../docs_src/dependencies/tutorial011_an.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ non-Annotated |
|||
|
|||
/// tip | 참고 |
|||
|
|||
가능하다면 `Annotated` 버전을 사용하는 것이 좋습니다. |
|||
|
|||
/// |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../docs_src/dependencies/tutorial011.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
/// tip | 참고 |
|||
|
|||
이 모든 과정이 복잡하게 느껴질 수 있습니다. 그리고 지금은 이 방법이 얼마나 유용한지 명확하지 않을 수도 있습니다. |
|||
|
|||
이 예시는 의도적으로 간단하게 만들었지만, 전체 구조가 어떻게 작동하는지 보여줍니다. |
|||
|
|||
보안 관련 장에서는 이와 같은 방식으로 구현된 편의 함수들이 있습니다. |
|||
|
|||
이 모든 과정을 이해했다면, 이러한 보안 도구들이 내부적으로 어떻게 작동하는지 이미 파악한 것입니다. |
|||
|
|||
/// |
@ -0,0 +1,7 @@ |
|||
# 이벤트 테스트: 시작 - 종료 |
|||
|
|||
테스트에서 이벤트 핸들러(`startup` 및 `shutdown`)를 실행해야 하는 경우, `with` 문과 함께 `TestClient`를 사용할 수 있습니다. |
|||
|
|||
```Python hl_lines="9-12 20-24" |
|||
{!../../docs_src/app_testing/tutorial003.py!} |
|||
``` |
@ -0,0 +1,58 @@ |
|||
# `Request` 직접 사용하기 |
|||
|
|||
지금까지 요청에서 필요한 부분을 각 타입으로 선언하여 사용해 왔습니다. |
|||
|
|||
다음과 같은 곳에서 데이터를 가져왔습니다: |
|||
|
|||
* 경로의 파라미터로부터. |
|||
* 헤더. |
|||
* 쿠키. |
|||
* 기타 등등. |
|||
|
|||
이렇게 함으로써, **FastAPI**는 데이터를 검증하고 변환하며, API에 대한 문서를 자동화로 생성합니다. |
|||
|
|||
하지만 `Request` 객체에 직접 접근해야 하는 상황이 있을 수 있습니다. |
|||
|
|||
## `Request` 객체에 대한 세부 사항 |
|||
|
|||
**FastAPI**는 실제로 내부에 **Starlette**을 사용하며, 그 위에 여러 도구를 덧붙인 구조입니다. 따라서 여러분이 필요할 때 Starlette의 <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request`</a> 객체를 직접 사용할 수 있습니다. |
|||
|
|||
`Request` 객체에서 데이터를 직접 가져오는 경우(예: 본문을 읽기)에는 FastAPI가 해당 데이터를 검증하거나 변환하지 않으며, 문서화(OpenAPI를 통한 문서 자동화(로 생성된) API 사용자 인터페이스)도 되지 않습니다. |
|||
|
|||
그러나 다른 매개변수(예: Pydantic 모델을 사용한 본문)는 여전히 검증, 변환, 주석 추가 등이 이루어집니다. |
|||
|
|||
하지만 특정한 경우에는 `Request` 객체에 직접 접근하는 것이 유용할 수 있습니다. |
|||
|
|||
## `Request` 객체를 직접 사용하기 |
|||
|
|||
여러분이 클라이언트의 IP 주소/호스트 정보를 *경로 작동 함수* 내부에서 가져와야 한다고 가정해 보겠습니다. |
|||
|
|||
이를 위해서는 요청에 직접 접근해야 합니다. |
|||
|
|||
```Python hl_lines="1 7-8" |
|||
{!../../docs_src/using_request_directly/tutorial001.py!} |
|||
``` |
|||
|
|||
*경로 작동 함수* 매개변수를 `Request` 타입으로 선언하면 **FastAPI**가 해당 매개변수에 `Request` 객체를 전달하는 것을 알게 됩니다. |
|||
|
|||
/// tip | 팁 |
|||
|
|||
이 경우, 요청 매개변수와 함께 경로 매개변수를 선언한 것을 볼 수 있습니다. |
|||
|
|||
따라서, 경로 매개변수는 추출되고 검증되며 지정된 타입으로 변환되고 OpenAPI로 주석이 추가됩니다. |
|||
|
|||
이와 같은 방식으로, 다른 매개변수들을 평소처럼 선언하면서, 부가적으로 `Request`도 가져올 수 있습니다. |
|||
|
|||
/// |
|||
|
|||
## `Request` 설명서 |
|||
|
|||
여러분은 `Request` 객체에 대한 더 자세한 내용을 <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">공식 Starlette 설명서 사이트</a>에서 읽어볼 수 있습니다. |
|||
|
|||
/// note | 기술 세부사항 |
|||
|
|||
`from starlette.requests import Request`를 사용할 수도 있습니다. |
|||
|
|||
**FastAPI**는 여러분(개발자)를 위한 편의를 위해 이를 직접 제공하지만, 실제로는 Starlette에서 가져온 것입니다. |
|||
|
|||
/// |
@ -0,0 +1,298 @@ |
|||
# 환경 변수 |
|||
|
|||
/// tip | "팁" |
|||
|
|||
만약 "환경 변수"가 무엇이고, 어떻게 사용하는지 알고 계시다면, 이 챕터를 스킵하셔도 좋습니다. |
|||
|
|||
/// |
|||
|
|||
환경 변수는 파이썬 코드의 **바깥**인, **운영 체제**에 존재하는 변수입니다. 파이썬 코드나 다른 프로그램에서 읽을 수 있습니다. |
|||
|
|||
환경 변수는 애플리케이션 **설정**을 처리하거나, 파이썬의 **설치** 과정의 일부로 유용합니다. |
|||
|
|||
## 환경 변수를 만들고 사용하기 |
|||
|
|||
파이썬 없이도, **셸 (터미널)** 에서 환경 변수를 **생성** 하고 사용할 수 있습니다. |
|||
|
|||
//// tab | Linux, macOS, Windows Bash |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// You could create an env var MY_NAME with |
|||
$ export MY_NAME="Wade Wilson" |
|||
|
|||
// Then you could use it with other programs, like |
|||
$ echo "Hello $MY_NAME" |
|||
|
|||
Hello Wade Wilson |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows PowerShell |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Create an env var MY_NAME |
|||
$ $Env:MY_NAME = "Wade Wilson" |
|||
|
|||
// Use it with other programs, like |
|||
$ echo "Hello $Env:MY_NAME" |
|||
|
|||
Hello Wade Wilson |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
## 파이썬에서 환경 변수 읽기 |
|||
|
|||
파이썬 **바깥**인 터미널에서(다른 도구로도 가능) 환경 변수를 생성도 할 수도 있고, 이를 **파이썬에서 읽을 수 있습니다.** |
|||
|
|||
예를 들어 다음과 같은 `main.py` 파일이 있다고 합시다: |
|||
|
|||
```Python hl_lines="3" |
|||
import os |
|||
|
|||
name = os.getenv("MY_NAME", "World") |
|||
print(f"Hello {name} from Python") |
|||
``` |
|||
|
|||
/// tip | "팁" |
|||
|
|||
<a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> 의 두 번째 인자는 반환할 기본값입니다. |
|||
|
|||
여기서는 `"World"`를 넣었기에 기본값으로써 사용됩니다. 넣지 않으면 `None` 이 기본값으로 사용됩니다. |
|||
|
|||
/// |
|||
|
|||
그러면 해당 파이썬 프로그램을 다음과 같이 호출할 수 있습니다: |
|||
|
|||
//// tab | Linux, macOS, Windows Bash |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Here we don't set the env var yet |
|||
$ python main.py |
|||
|
|||
// As we didn't set the env var, we get the default value |
|||
|
|||
Hello World from Python |
|||
|
|||
// But if we create an environment variable first |
|||
$ export MY_NAME="Wade Wilson" |
|||
|
|||
// And then call the program again |
|||
$ python main.py |
|||
|
|||
// Now it can read the environment variable |
|||
|
|||
Hello Wade Wilson from Python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows PowerShell |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Here we don't set the env var yet |
|||
$ python main.py |
|||
|
|||
// As we didn't set the env var, we get the default value |
|||
|
|||
Hello World from Python |
|||
|
|||
// But if we create an environment variable first |
|||
$ $Env:MY_NAME = "Wade Wilson" |
|||
|
|||
// And then call the program again |
|||
$ python main.py |
|||
|
|||
// Now it can read the environment variable |
|||
|
|||
Hello Wade Wilson from Python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
환경변수는 코드 바깥에서 설정될 수 있지만, 코드에서 읽을 수 있고, 나머지 파일과 함께 저장(`git`에 커밋)할 필요가 없으므로, 구성이나 **설정** 에 사용하는 것이 일반적입니다. |
|||
|
|||
**특정 프로그램 호출**에 대해서만 사용할 수 있는 환경 변수를 만들 수도 있습니다. 해당 프로그램에서만 사용할 수 있고, 해당 프로그램이 실행되는 동안만 사용할 수 있습니다. |
|||
|
|||
그렇게 하려면 프로그램 바로 앞, 같은 줄에 환경 변수를 만들어야 합니다: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Create an env var MY_NAME in line for this program call |
|||
$ MY_NAME="Wade Wilson" python main.py |
|||
|
|||
// Now it can read the environment variable |
|||
|
|||
Hello Wade Wilson from Python |
|||
|
|||
// The env var no longer exists afterwards |
|||
$ python main.py |
|||
|
|||
Hello World from Python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
/// tip | "팁" |
|||
|
|||
<a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App: Config</a> 에서 좀 더 자세히 알아볼 수 있습니다. |
|||
|
|||
/// |
|||
|
|||
## 타입과 검증 |
|||
|
|||
이 환경변수들은 오직 **텍스트 문자열**로만 처리할 수 있습니다. 텍스트 문자열은 파이썬 외부에 있으며 다른 프로그램 및 나머지 시스템(Linux, Windows, macOS 등 다른 운영 체제)과 호환되어야 합니다. |
|||
|
|||
즉, 파이썬에서 환경 변수로부터 읽은 **모든 값**은 **`str`**이 되고, 다른 타입으로의 변환이나 검증은 코드에서 수행해야 합니다. |
|||
|
|||
**애플리케이션 설정**을 처리하기 위한 환경 변수 사용에 대한 자세한 내용은 [고급 사용자 가이드 - 설정 및 환경 변수](./advanced/settings.md){.internal-link target=\_blank} 에서 확인할 수 있습니다. |
|||
|
|||
## `PATH` 환경 변수 |
|||
|
|||
**`PATH`**라고 불리는, **특별한** 환경변수가 있습니다. 운영체제(Linux, Windows, macOS 등)에서 실행할 프로그램을 찾기위해 사용됩니다. |
|||
|
|||
변수 `PATH`의 값은 Linux와 macOS에서는 콜론 `:`, Windows에서는 세미콜론 `;`으로 구분된 디렉토리로 구성된 긴 문자열입니다. |
|||
|
|||
예를 들어, `PATH` 환경 변수는 다음과 같습니다: |
|||
|
|||
//// tab | Linux, macOS |
|||
|
|||
```plaintext |
|||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin |
|||
``` |
|||
|
|||
이는 시스템이 다음 디렉토리에서 프로그램을 찾아야 함을 의미합니다: |
|||
|
|||
- `/usr/local/bin` |
|||
- `/usr/bin` |
|||
- `/bin` |
|||
- `/usr/sbin` |
|||
- `/sbin` |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows |
|||
|
|||
```plaintext |
|||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32 |
|||
``` |
|||
|
|||
이는 시스템이 다음 디렉토리에서 프로그램을 찾아야 함을 의미합니다: |
|||
|
|||
- `C:\Program Files\Python312\Scripts` |
|||
- `C:\Program Files\Python312` |
|||
- `C:\Windows\System32` |
|||
|
|||
//// |
|||
|
|||
터미널에 **명령어**를 입력하면 운영 체제는 `PATH` 환경 변수에 나열된 **각 디렉토리**에서 프로그램을 **찾습니다.** |
|||
|
|||
예를 들어 터미널에 `python`을 입력하면 운영 체제는 해당 목록의 **첫 번째 디렉토리**에서 `python`이라는 프로그램을 찾습니다. |
|||
|
|||
찾으면 **사용합니다**. 그렇지 않으면 **다른 디렉토리**에서 계속 찾습니다. |
|||
|
|||
### 파이썬 설치와 `PATH` 업데이트 |
|||
|
|||
파이썬을 설치할 때, 아마 `PATH` 환경 변수를 업데이트 할 것이냐고 물어봤을 겁니다. |
|||
|
|||
//// tab | Linux, macOS |
|||
|
|||
파이썬을 설치하고 그것이 `/opt/custompython/bin` 디렉토리에 있다고 가정해 보겠습니다. |
|||
|
|||
`PATH` 환경 변수를 업데이트하도록 "예"라고 하면 설치 관리자가 `/opt/custompython/bin`을 `PATH` 환경 변수에 추가합니다. |
|||
|
|||
다음과 같이 보일 수 있습니다: |
|||
|
|||
```plaintext |
|||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin |
|||
``` |
|||
|
|||
이렇게 하면 터미널에 `python`을 입력할 때, 시스템이 `/opt/custompython/bin`(마지막 디렉토리)에서 파이썬 프로그램을 찾아 사용합니다. |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows |
|||
|
|||
파이썬을 설치하고 그것이 `C:\opt\custompython\bin` 디렉토리에 있다고 가정해 보겠습니다. |
|||
|
|||
`PATH` 환경 변수를 업데이트하도록 "예"라고 하면 설치 관리자가 `C:\opt\custompython\bin`을 `PATH` 환경 변수에 추가합니다. |
|||
|
|||
```plaintext |
|||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin |
|||
``` |
|||
|
|||
이렇게 하면 터미널에 `python`을 입력할 때, 시스템이 `C:\opt\custompython\bin`(마지막 디렉토리)에서 파이썬 프로그램을 찾아 사용합니다. |
|||
|
|||
//// |
|||
|
|||
그래서, 다음과 같이 입력한다면: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// tab | Linux, macOS |
|||
|
|||
시스템은 `/opt/custompython/bin`에서 `python` 프로그램을 **찾아** 실행합니다. |
|||
|
|||
다음과 같이 입력하는 것과 거의 같습니다: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ /opt/custompython/bin/python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows |
|||
|
|||
시스템은 `C:\opt\custompython\bin\python`에서 `python` 프로그램을 **찾아** 실행합니다. |
|||
|
|||
다음과 같이 입력하는 것과 거의 같습니다: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ C:\opt\custompython\bin\python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
이 정보는 [가상 환경](virtual-environments.md){.internal-link target=\_blank} 에 대해 알아볼 때 유용할 것입니다. |
|||
|
|||
## 결론 |
|||
|
|||
이 문서를 읽고 **환경 변수**가 무엇이고 파이썬에서 어떻게 사용하는지 기본적으로 이해하셨을 겁니다. |
|||
|
|||
또한 <a href="https://ko.wikipedia.org/wiki/환경_변수" class="external-link" target="_blank">환경 변수에 대한 위키피디아(한국어)</a>에서 이에 대해 자세히 알아볼 수 있습니다. |
|||
|
|||
많은 경우에서, 환경 변수가 어떻게 유용하고 적용 가능한지 바로 명확하게 알 수는 없습니다. 하지만 개발할 때 다양한 시나리오에서 계속 나타나므로 이에 대해 아는 것이 좋습니다. |
|||
|
|||
예를 들어, 다음 섹션인 [가상 환경](virtual-environments.md)에서 이 정보가 필요합니다. |
@ -0,0 +1,81 @@ |
|||
# 역사, 디자인 그리고 미래 |
|||
|
|||
어느 날, [한 FastAPI 사용자](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920)가 이렇게 물었습니다: |
|||
|
|||
> 이 프로젝트의 역사를 알려 주실 수 있나요? 몇 주 만에 멋진 결과를 낸 것 같아요. [...] |
|||
|
|||
여기서 그 역사에 대해 간단히 설명하겠습니다. |
|||
|
|||
--- |
|||
|
|||
## 대안 |
|||
|
|||
저는 여러 해 동안 머신러닝, 분산 시스템, 비동기 작업, NoSQL 데이터베이스 같은 복잡한 요구사항을 가진 API를 개발하며 여러 팀을 이끌어 왔습니다. |
|||
|
|||
이 과정에서 많은 대안을 조사하고, 테스트하며, 사용해야 했습니다. **FastAPI**의 역사는 그 이전에 나왔던 여러 도구의 역사와 밀접하게 연관되어 있습니다. |
|||
|
|||
[대안](alternatives.md){.internal-link target=_blank} 섹션에서 언급된 것처럼: |
|||
|
|||
> **FastAPI**는 이전에 나왔던 많은 도구들의 노력 없이는 존재하지 않았을 것입니다. |
|||
> |
|||
> 이전에 개발된 여러 도구들이 이 프로젝트에 영감을 주었습니다. |
|||
> |
|||
> 저는 오랫동안 새로운 프레임워크를 만드는 것을 피하고자 했습니다. 처음에는 **FastAPI**가 제공하는 기능들을 다양한 프레임워크와 플러그인, 도구들을 조합해 해결하려 했습니다. |
|||
> |
|||
> 하지만 결국에는 이 모든 기능을 통합하는 도구가 필요해졌습니다. 이전 도구들로부터 최고의 아이디어들을 모으고, 이를 최적의 방식으로 조합해야만 했습니다. 이는 :term:Python 3.6+ 타입 힌트 <type hints>와 같은, 이전에는 사용할 수 없었던 언어 기능이 가능했기 때문입니다. |
|||
|
|||
--- |
|||
|
|||
## 조사 |
|||
|
|||
여러 대안을 사용해 보며 다양한 도구에서 배운 점들을 모아 저와 개발팀에게 가장 적합한 방식을 찾았습니다. |
|||
|
|||
예를 들어, 표준 :term:Python 타입 힌트 <type hints>에 기반하는 것이 이상적이라는 점이 명확했습니다. |
|||
|
|||
또한, 이미 존재하는 표준을 활용하는 것이 가장 좋은 접근법이라 판단했습니다. |
|||
|
|||
그래서 **FastAPI**의 코드를 작성하기 전에 몇 달 동안 OpenAPI, JSON Schema, OAuth2 명세를 연구하며 이들의 관계와 겹치는 부분, 차이점을 이해했습니다. |
|||
|
|||
--- |
|||
|
|||
## 디자인 |
|||
|
|||
그 후, **FastAPI** 사용자가 될 개발자로서 사용하고 싶은 개발자 "API"를 디자인했습니다. |
|||
|
|||
[Python Developer Survey](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools)에 따르면 약 80%의 Python 개발자가 PyCharm, VS Code, Jedi 기반 편집기 등에서 개발합니다. 이 과정에서 여러 아이디어를 테스트했습니다. |
|||
|
|||
대부분의 다른 편집기도 유사하게 동작하기 때문에, **FastAPI**의 이점은 거의 모든 편집기에서 누릴 수 있습니다. |
|||
|
|||
이 과정을 통해 코드 중복을 최소화하고, 모든 곳에서 자동 완성, 타입 검사, 에러 확인 기능이 제공되는 최적의 방식을 찾아냈습니다. |
|||
|
|||
이 모든 것은 개발자들에게 최고의 개발 경험을 제공하기 위해 설계되었습니다. |
|||
|
|||
--- |
|||
|
|||
## 필요조건 |
|||
|
|||
여러 대안을 테스트한 후, [Pydantic](https://docs.pydantic.dev/)을 사용하기로 결정했습니다. |
|||
|
|||
이후 저는 **Pydantic**이 JSON Schema와 완벽히 호환되도록 개선하고, 다양한 제약 조건 선언을 지원하며, 여러 편집기에서의 자동 완성과 타입 검사 기능을 향상하기 위해 기여했습니다. |
|||
|
|||
또한, 또 다른 주요 필요조건이었던 [Starlette](https://www.starlette.io/)에도 기여했습니다. |
|||
|
|||
--- |
|||
|
|||
## 개발 |
|||
|
|||
**FastAPI**를 개발하기 시작할 즈음에는 대부분의 준비가 이미 완료된 상태였습니다. 설계가 정의되었고, 필요조건과 도구가 준비되었으며, 표준과 명세에 대한 지식도 충분했습니다. |
|||
|
|||
--- |
|||
|
|||
## 미래 |
|||
|
|||
현시점에서 **FastAPI**가 많은 사람들에게 유용하다는 것이 명백해졌습니다. |
|||
|
|||
여러 용도에 더 적합한 도구로서 기존 대안보다 선호되고 있습니다. |
|||
이미 많은 개발자와 팀들이 **FastAPI**에 의존해 프로젝트를 진행 중입니다 (저와 제 팀도 마찬가지입니다). |
|||
|
|||
하지만 여전히 개선해야 할 점과 추가할 기능들이 많이 남아 있습니다. |
|||
|
|||
**FastAPI**는 밝은 미래로 나아가고 있습니다. |
|||
그리고 [여러분의 도움](help-fastapi.md){.internal-link target=_blank}은 큰 힘이 됩니다. |
@ -0,0 +1,61 @@ |
|||
# 조건부적인 OpenAPI |
|||
|
|||
필요한 경우, 설정 및 환경 변수를 사용하여 환경에 따라 조건부로 OpenAPI를 구성하고 완전히 비활성화할 수도 있습니다. |
|||
|
|||
## 보안, API 및 docs에 대해서 |
|||
|
|||
프로덕션에서, 문서화된 사용자 인터페이스(UI)를 숨기는 것이 API를 보호하는 방법이 *되어서는 안 됩니다*. |
|||
|
|||
이는 API에 추가적인 보안을 제공하지 않으며, *경로 작업*은 여전히 동일한 위치에서 사용 할 수 있습니다. |
|||
|
|||
코드에 보안 결함이 있다면, 그 결함은 여전히 존재할 것입니다. |
|||
|
|||
문서를 숨기는 것은 API와 상호작용하는 방법을 이해하기 어렵게 만들며, 프로덕션에서 디버깅을 더 어렵게 만들 수 있습니다. 이는 단순히 <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">'모호성에 의한 보안'</a>의 한 형태로 간주될 수 있습니다. |
|||
|
|||
API를 보호하고 싶다면, 예를 들어 다음과 같은 더 나은 방법들이 있습니다: |
|||
|
|||
* 요청 본문과 응답에 대해 잘 정의된 Pydantic 모델을 사용하도록 하세요. |
|||
|
|||
* 종속성을 사용하여 필요한 권한과 역할을 구성하세요. |
|||
|
|||
* 평문 비밀번호를 절대 저장하지 말고, 오직 암호화된 비밀번호만 저장하세요. |
|||
|
|||
* Passlib과 JWT 토큰과 같은 잘 알려진 암호화 도구들을 구현하고 사용하세요. |
|||
|
|||
* 필요한 곳에 OAuth2 범위를 사용하여 더 세분화된 권한 제어를 추가하세요. |
|||
|
|||
* 등등.... |
|||
|
|||
그럼에도 불구하고, 특정 환경(예: 프로덕션)에서 또는 환경 변수의 설정에 따라 API 문서를 비활성화해야 하는 매우 특정한 사용 사례가 있을 수 있습니다. |
|||
|
|||
## 설정 및 환경변수의 조건부 OpenAPI |
|||
|
|||
동일한 Pydantic 설정을 사용하여 생성된 OpenAPI 및 문서 UI를 쉽게 구성할 수 있습니다. |
|||
|
|||
예를 들어: |
|||
|
|||
{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *} |
|||
|
|||
여기서 `openapi_url` 설정을 기본값인 `"/openapi.json"`으로 선언합니다. |
|||
|
|||
그런 뒤, 우리는 `FastAPI` 앱을 만들 때 그것을 사용합니다. |
|||
|
|||
환경 변수 `OPENAPI_URL`을 빈 문자열로 설정하여 OpenAPI(문서 UI 포함)를 비활성화할 수도 있습니다. 예를 들어: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ OPENAPI_URL= uvicorn main:app |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
그리고 `/openapi.json`, `/docs` 또는 `/redoc`의 URL로 이동하면 `404 Not Found`라는 오류가 다음과 같이 표시됩니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"detail": "Not Found" |
|||
} |
|||
``` |
@ -0,0 +1,19 @@ |
|||
# 고급 보안 |
|||
|
|||
## 추가 기능 |
|||
|
|||
[자습서 - 사용자 가이드: 보안](../../tutorial/security/index.md){.internal-link target=_blank} 문서에서 다룬 내용 외에도 보안 처리를 위한 몇 가지 추가 기능이 있습니다. |
|||
|
|||
/// tip |
|||
|
|||
다음 섹션은 **반드시 "고급"** 기능은 아닙니다. |
|||
|
|||
그리고 여러분의 사용 사례에 따라, 적합한 해결책이 그 중 하나에 있을 가능성이 있습니다. |
|||
|
|||
/// |
|||
|
|||
## 먼저 자습서 읽기 |
|||
|
|||
다음 섹션은 이미 [자습서 - 사용자 가이드: 보안](../../tutorial/security/index.md){.internal-link target=_blank} 문서를 읽었다고 가정합니다. |
|||
|
|||
이 섹션들은 모두 동일한 개념을 바탕으로 하며, 추가 기능을 제공합니다. |
@ -0,0 +1,212 @@ |
|||
# Configuração Avançada da Operação de Rota |
|||
|
|||
## operationId do OpenAPI |
|||
|
|||
/// warning | Aviso |
|||
|
|||
Se você não é um "especialista" no OpenAPI, você provavelmente não precisa disso. |
|||
|
|||
/// |
|||
|
|||
Você pode definir o `operationId` do OpenAPI que será utilizado na sua *operação de rota* com o parâmetro `operation_id`. |
|||
|
|||
Você precisa ter certeza que ele é único para cada operação. |
|||
|
|||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *} |
|||
|
|||
### Utilizando o nome da *função de operação de rota* como o operationId |
|||
|
|||
Se você quiser utilizar o nome das funções da sua API como `operationId`s, você pode iterar sobre todos esses nomes e sobrescrever o `operationId` em cada *operação de rota* utilizando o `APIRoute.name` dela. |
|||
|
|||
Você deve fazer isso depois de adicionar todas as suas *operações de rota*. |
|||
|
|||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12:21,24] *} |
|||
|
|||
/// tip | Dica |
|||
|
|||
Se você chamar `app.openapi()` manualmente, os `operationId`s devem ser atualizados antes dessa chamada. |
|||
|
|||
/// |
|||
|
|||
/// warning | Aviso |
|||
|
|||
Se você fizer isso, você tem que ter certeza de que cada uma das suas *funções de operação de rota* tem um nome único. |
|||
|
|||
Mesmo que elas estejam em módulos (arquivos Python) diferentes. |
|||
|
|||
/// |
|||
|
|||
## Excluir do OpenAPI |
|||
|
|||
Para excluir uma *operação de rota* do esquema OpenAPI gerado (e por consequência, dos sistemas de documentação automáticos), utilize o parâmetro `include_in_schema` e defina ele como `False`: |
|||
|
|||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *} |
|||
|
|||
## Descrição avançada a partir de docstring |
|||
|
|||
Você pode limitar as linhas utilizadas a partir de uma docstring de uma *função de operação de rota* para o OpenAPI. |
|||
|
|||
Adicionar um `\f` (um caractere de escape para alimentação de formulário) faz com que o **FastAPI** restrinja a saída utilizada pelo OpenAPI até esse ponto. |
|||
|
|||
Ele não será mostrado na documentação, mas outras ferramentas (como o Sphinx) serão capazes de utilizar o resto do texto. |
|||
|
|||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *} |
|||
|
|||
## Respostas Adicionais |
|||
|
|||
Você provavelmente já viu como declarar o `response_model` e `status_code` para uma *operação de rota*. |
|||
|
|||
Isso define os metadados sobre a resposta principal da *operação de rota*. |
|||
|
|||
Você também pode declarar respostas adicionais, com seus modelos, códigos de status, etc. |
|||
|
|||
Existe um capítulo inteiro da nossa documentação sobre isso, você pode ler em [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}. |
|||
|
|||
## Extras do OpenAPI |
|||
|
|||
Quando você declara uma *operação de rota* na sua aplicação, o **FastAPI** irá gerar os metadados relevantes da *operação de rota* automaticamente para serem incluídos no esquema do OpenAPI. |
|||
|
|||
/// note | Nota |
|||
|
|||
Na especificação do OpenAPI, isso é chamado de um <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Objeto de Operação</a>. |
|||
|
|||
/// |
|||
|
|||
Ele possui toda a informação sobre a *operação de rota* e é usado para gerar a documentação automaticamente. |
|||
|
|||
Ele inclui os atributos `tags`, `parameters`, `requestBody`, `responses`, etc. |
|||
|
|||
Esse esquema específico para uma *operação de rota* normalmente é gerado automaticamente pelo **FastAPI**, mas você também pode estender ele. |
|||
|
|||
/// tip | Dica |
|||
|
|||
Esse é um ponto de extensão de baixo nível. |
|||
|
|||
Caso você só precise declarar respostas adicionais, uma forma conveniente de fazer isso é com [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}. |
|||
|
|||
/// |
|||
|
|||
Você pode estender o esquema do OpenAPI para uma *operação de rota* utilizando o parâmetro `openapi_extra`. |
|||
|
|||
### Extensões do OpenAPI |
|||
|
|||
Esse parâmetro `openapi_extra` pode ser útil, por exemplo, para declarar [Extensões do OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions): |
|||
|
|||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *} |
|||
|
|||
Se você abrir os documentos criados automaticamente para a API, sua extensão aparecerá no final da *operação de rota* específica. |
|||
|
|||
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png"> |
|||
|
|||
E se você olhar o esquema OpenAPI resultante (na rota `/openapi.json` da sua API), você verá que a sua extensão também faz parte da *operação de rota* específica: |
|||
|
|||
```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" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Esquema de *operação de rota* do OpenAPI personalizado |
|||
|
|||
O dicionário em `openapi_extra` vai ter todos os seus níveis mesclados dentro do esquema OpenAPI gerado automaticamente para a *operação de rota*. |
|||
|
|||
Então, você pode adicionar dados extras para o esquema gerado automaticamente. |
|||
|
|||
Por exemplo, você poderia optar por ler e validar a requisição com seu próprio código, sem utilizar funcionalidades automatizadas do FastAPI com o Pydantic, mas você ainda pode quere definir a requisição no esquema OpenAPI. |
|||
|
|||
Você pode fazer isso com `openapi_extra`: |
|||
|
|||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36,39:40] *} |
|||
|
|||
Nesse exemplo, nós não declaramos nenhum modelo do Pydantic. Na verdade, o corpo da requisição não está nem mesmo <abbr title="convertido de um formato plano, como bytes, para objetos Python">analisado</abbr> como JSON, ele é lido diretamente como `bytes` e a função `magic_data_reader()` seria a responsável por analisar ele de alguma forma. |
|||
|
|||
De toda forma, nós podemos declarar o esquema esperado para o corpo da requisição. |
|||
|
|||
### Tipo de conteúdo do OpenAPI personalizado |
|||
|
|||
Utilizando esse mesmo truque, você pode utilizar um modelo Pydantic para definir o esquema JSON que é então incluído na seção do esquema personalizado do OpenAPI na *operação de rota*. |
|||
|
|||
E você pode fazer isso até mesmo quando os dados da requisição não seguem o formato JSON. |
|||
|
|||
Por exemplo, nesta aplicação nós não usamos a funcionalidade integrada ao FastAPI de extrair o esquema JSON dos modelos Pydantic nem a validação automática do JSON. Na verdade, estamos declarando o tipo do conteúdo da requisição como YAML, em vez de JSON: |
|||
|
|||
//// tab | Pydantic v2 |
|||
|
|||
```Python hl_lines="17-22 24" |
|||
{!> ../../docs_src/path_operation_advanced_configuration/tutorial007.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Pydantic v1 |
|||
|
|||
```Python hl_lines="17-22 24" |
|||
{!> ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
/// info | Informação |
|||
|
|||
Na versão 1 do Pydantic, o método para obter o esquema JSON de um modelo é `Item.schema()`, na versão 2 do Pydantic, o método é `Item.model_json_schema()` |
|||
|
|||
/// |
|||
|
|||
Entretanto, mesmo que não utilizemos a funcionalidade integrada por padrão, ainda estamos usando um modelo Pydantic para gerar um esquema JSON manualmente para os dados que queremos receber no formato YAML. |
|||
|
|||
Então utilizamos a requisição diretamente, e extraímos o corpo como `bytes`. Isso significa que o FastAPI não vai sequer tentar analisar o corpo da requisição como JSON. |
|||
|
|||
E então no nosso código, nós analisamos o conteúdo YAML diretamente, e estamos utilizando o mesmo modelo Pydantic para validar o conteúdo YAML: |
|||
|
|||
//// tab | Pydantic v2 |
|||
|
|||
```Python hl_lines="26-33" |
|||
{!> ../../docs_src/path_operation_advanced_configuration/tutorial007.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Pydantic v1 |
|||
|
|||
```Python hl_lines="26-33" |
|||
{!> ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
/// info | Informação |
|||
|
|||
Na versão 1 do Pydantic, o método para analisar e validar um objeto era `Item.parse_obj()`, na versão 2 do Pydantic, o método é chamado de `Item.model_validate()`. |
|||
|
|||
/// |
|||
|
|||
///tip | Dica |
|||
|
|||
Aqui reutilizamos o mesmo modelo do Pydantic. |
|||
|
|||
Mas da mesma forma, nós poderíamos ter validado de alguma outra forma. |
|||
|
|||
/// |
Loading…
Reference in new issue