🌐 Update translations for ko (update-outdated)

This commit is contained in:
github-actions[bot]
2025-12-21 18:14:15 +00:00
parent e1bd9f3e33
commit 3c25d0b788
76 changed files with 3828 additions and 3107 deletions

View File

@@ -1,3 +1,3 @@
# 소개
# 소개 { #about }
FastAPI에 대한 디자인, 영감 등에 대해 🤓
FastAPI, 그 디자인, 영감 등에 대해 🤓

View File

@@ -1,16 +1,16 @@
# 추가 상태 코드
# 추가 상태 코드 { #additional-status-codes }
기본적으로 **FastAPI**는 응답을 `JSONResponse`를 사용하여 반환하며, *경로 작업(path operation)*에서 반환한 내용을 해당 `JSONResponse` 안에 넣어 반환합니다.
기본 상태 코드 또는 *경로 작업*에서 설정한 상태 코드를 사용합니다.
## 추가 상태 코드
## 추가 상태 코드 { #additional-status-codes_1 }
기본 상태 코드와 별도로 추가 상태 코드를 반환하려면 `JSONResponse`와 같이 `Response`를 직접 반환하고 추가 상태 코드를 직접 설정할 수 있습니다.
예를 들어 항목을 업데이트할 수 있는 *경로 작업*이 있고 성공 시 200 “OK”의 HTTP 상태 코드를 반환한다고 가정해 보겠습니다.
하지만 새로운 항목을 허용하기를 원할 것입니다. 항목이 이전에 존재하지 않았다면 이를 생성하고 HTTP 상태 코드 201 "Created"를 반환합니다.
하지만 새로운 항목을 허용하기를 원할 것입니다. 그리고 항목이 이전에 존재하지 않았다면 이를 생성하고 HTTP 상태 코드 201 "Created"를 반환합니다.
이를 위해서는 `JSONResponse`를 가져와서 원하는 `status_code`를 설정하여 콘텐츠를 직접 반환합니다:
@@ -26,7 +26,7 @@
///
/// note | 기술 세부 정보
/// note | 기술 세부사항
`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
@@ -34,7 +34,7 @@
///
## OpenAPI 및 API 문서
## OpenAPI 및 API 문서 { #openapi-and-api-docs }
추가 상태 코드와 응답을 직접 반환하는 경우, FastAPI는 반환할 내용을 미리 알 수 있는 방법이 없기 때문에 OpenAPI 스키마(API 문서)에 포함되지 않습니다.

View File

@@ -1,6 +1,6 @@
# 고급 의존성
# 고급 의존성 { #advanced-dependencies }
## 매개변수화된 의존성
## 매개변수화된 의존성 { #parameterized-dependencies }
지금까지 본 모든 의존성은 고정된 함수 또는 클래스입니다.
@@ -10,7 +10,7 @@
이때 해당 고정된 내용을 매개변수화할 수 있길 바랍니다.
## "호출 가능한" 인스턴스
## "호출 가능한" 인스턴스 { #a-callable-instance }
Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법이 있습니다.
@@ -21,9 +21,9 @@ Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
이 경우, **FastAPI**는 추가 매개변수와 하위 의존성을 확인하기 위해 `__call__`을 사용하게 되며,
나중에 *경로 연산 함수*에서 매개변수에 값을 전달할 때 이를 호출하게 됩니다.
나중에 *경로 처리 함수*에서 매개변수에 값을 전달할 때 이를 호출하게 됩니다.
## 인스턴스 매개변수화하기
## 인스턴스 매개변수화하기 { #parameterize-the-instance }
이제 `__init__`을 사용하여 의존성을 "매개변수화"할 수 있는 인스턴스의 매개변수를 선언할 수 있습니다:
@@ -31,7 +31,7 @@ Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법
이 경우, **FastAPI**는 `__init__`에 전혀 관여하지 않으며, 우리는 이 메서드를 코드에서 직접 사용하게 됩니다.
## 인스턴스 생성하기
## 인스턴스 생성하기 { #create-an-instance }
다음과 같이 이 클래스의 인스턴스를 생성할 수 있습니다:
@@ -39,10 +39,9 @@ Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법
이렇게 하면 `checker.fixed_content` 속성에 `"bar"`라는 값을 담아 의존성을 "매개변수화"할 수 있습니다.
## 인스턴스를 의존성으로 사용하기
## 인스턴스를 의존성으로 사용하기 { #use-the-instance-as-a-dependency }
그런 다음, `Depends(FixedContentQueryChecker)` 대신 `Depends(checker)`에서 이 `checker` 인스턴스를 사용할 수 있으며,
클래스 자체가 아닌 인스턴스 `checker`가 의존성이 됩니다.
그런 다음, 클래스 자체가 아닌 인스턴스 `checker`가 의존성이 되므로, `Depends(FixedContentQueryChecker)` 대신 `Depends(checker)`에서 이 `checker` 인스턴스를 사용할 수 있습니다.
의존성을 해결할 때 **FastAPI**는 이 `checker`를 다음과 같이 호출합니다:
@@ -50,18 +49,116 @@ Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법
checker(q="somequery")
```
...그리고 이때 반환되는 값을 *경로 연산 함수*의 `fixed_content_included` 매개변수 전달합니다:
...그리고 이때 반환되는 값을 *경로 처리 함수*의 의존성 값으로, `fixed_content_included` 매개변수 전달합니다:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
/// tip | 참고
/// tip |
이 모든 과정이 복잡하게 느껴질 수 있습니다. 그리고 지금은 이 방법이 얼마나 유용한지 명확하지 않을 수도 있습니다.
이 예시는 의도적으로 간단하게 만들었지만, 전체 구조가 어떻게 작동하는지 보여줍니다.
보안 관련 장에서는 이와 같은 방식으로 구현된 편의 함수들이 있습니다.
보안 관련 장에서는 이와 같은 방식으로 구현된 유틸리티 함수들이 있습니다.
이 모든 과정을 이해했다면, 이러한 보안 도구들이 내부적으로 어떻게 작동하는지 이미 파악한 것입니다.
이 모든 과정을 이해했다면, 이러한 보안용 유틸리티 도구들이 내부적으로 어떻게 작동하는지 이미 파악한 것입니다.
///
## `yield`, `HTTPException`, `except`, 백그라운드 태스크가 있는 의존성 { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | 경고
대부분의 경우 이러한 기술 세부사항이 필요하지 않을 것입니다.
이 세부사항은 주로 0.121.0 이전의 FastAPI 애플리케이션이 있고 `yield`가 있는 의존성에서 문제가 발생하는 경우에 유용합니다.
///
`yield`가 있는 의존성은 여러 사용 사례를 수용하고 일부 문제를 해결하기 위해 시간이 지나며 발전해 왔습니다. 다음은 변경된 내용의 요약입니다.
### `yield`와 `scope`가 있는 의존성 { #dependencies-with-yield-and-scope }
0.121.0 버전에서 FastAPI는 `yield`가 있는 의존성에 대해 `Depends(scope="function")` 지원을 추가했습니다.
`Depends(scope="function")`를 사용하면, `yield` 이후의 종료 코드는 *경로 처리 함수*가 끝난 직후(클라이언트에 응답이 반환되기 전)에 실행됩니다.
그리고 `Depends(scope="request")`(기본값)를 사용하면, `yield` 이후의 종료 코드는 응답이 전송된 후에 실행됩니다.
자세한 내용은 [Dependencies with `yield` - Early exit and `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) 문서를 참고하세요.
### `yield`가 있는 의존성과 `StreamingResponse`, 기술 세부사항 { #dependencies-with-yield-and-streamingresponse-technical-details }
FastAPI 0.118.0 이전에는 `yield`가 있는 의존성을 사용하면, *경로 처리 함수*가 반환된 뒤 응답을 보내기 직전에 `yield` 이후의 종료 코드가 실행되었습니다.
의도는 응답이 네트워크를 통해 전달되기를 기다리면서 필요한 것보다 더 오래 리소스를 점유하지 않도록 하는 것이었습니다.
이 변경은 `StreamingResponse`를 반환하는 경우에도 `yield`가 있는 의존성의 종료 코드가 이미 실행된다는 의미이기도 했습니다.
예를 들어, `yield`가 있는 의존성에 데이터베이스 세션이 있다면, `StreamingResponse`는 데이터를 스트리밍하는 동안 해당 세션을 사용할 수 없게 됩니다. `yield` 이후의 종료 코드에서 세션이 이미 닫혔기 때문입니다.
이 동작은 0.118.0에서 되돌려져, `yield` 이후의 종료 코드가 응답이 전송된 뒤 실행되도록 변경되었습니다.
/// info | 정보
아래에서 보시겠지만, 이는 0.106.0 버전 이전의 동작과 매우 비슷하지만, 여러 개선 사항과 코너 케이스에 대한 버그 수정이 포함되어 있습니다.
///
#### 종료 코드를 조기에 실행하는 사용 사례 { #use-cases-with-early-exit-code }
특정 조건의 일부 사용 사례에서는 응답을 보내기 전에 `yield`가 있는 의존성의 종료 코드를 실행하던 예전 동작이 도움이 될 수 있습니다.
예를 들어, `yield`가 있는 의존성에서 데이터베이스 세션을 사용해 사용자를 검증만 하고, *경로 처리 함수*에서는 그 데이터베이스 세션을 다시는 사용하지 않으며(의존성에서만 사용), **그리고** 응답을 전송하는 데 오랜 시간이 걸리는 경우를 생각해 봅시다. 예를 들어 데이터를 천천히 보내는 `StreamingResponse`인데, 어떤 이유로든 데이터베이스를 사용하지는 않는 경우입니다.
이 경우 데이터베이스 세션은 응답 전송이 끝날 때까지 유지되지만, 사용하지 않는다면 굳이 유지할 필요가 없습니다.
다음과 같이 보일 수 있습니다:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
다음에서 `Session`을 자동으로 닫는 종료 코드는:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...응답이 느린 데이터 전송을 마친 뒤에 실행됩니다:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
하지만 `generate_stream()`는 데이터베이스 세션을 사용하지 않으므로, 응답을 전송하는 동안 세션을 열린 채로 유지할 필요는 없습니다.
SQLModel(또는 SQLAlchemy)을 사용하면서 이런 특정 사용 사례가 있다면, 더 이상 필요하지 않을 때 세션을 명시적으로 닫을 수 있습니다:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
그러면 세션이 데이터베이스 연결을 해제하여, 다른 요청들이 이를 사용할 수 있게 됩니다.
`yield`가 있는 의존성에서 조기 종료가 필요한 다른 사용 사례가 있다면, 여러분의 구체적인 사용 사례와 `yield`가 있는 의존성에 대한 조기 종료가 어떤 점에서 이득이 되는지를 포함해 <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussion Question</a>을 생성해 주세요.
`yield`가 있는 의존성에서 조기 종료에 대한 설득력 있는 사용 사례가 있다면, 조기 종료를 선택적으로 활성화할 수 있는 새로운 방법을 추가하는 것을 고려하겠습니다.
### `yield`가 있는 의존성과 `except`, 기술 세부사항 { #dependencies-with-yield-and-except-technical-details }
FastAPI 0.110.0 이전에는 `yield`가 있는 의존성을 사용한 다음 그 의존성에서 `except`로 예외를 잡고, 예외를 다시 발생시키지 않으면, 예외가 자동으로 어떤 예외 핸들러 또는 내부 서버 오류 핸들러로 raise/forward 되었습니다.
이는 핸들러 없이 전달된 예외(내부 서버 오류)로 인해 처리되지 않은 메모리 사용이 발생하는 문제를 수정하고, 일반적인 Python 코드의 동작과 일관되게 하기 위해 0.110.0 버전에서 변경되었습니다.
### 백그라운드 태스크와 `yield`가 있는 의존성, 기술 세부사항 { #background-tasks-and-dependencies-with-yield-technical-details }
FastAPI 0.106.0 이전에는 `yield` 이후에 예외를 발생시키는 것이 불가능했습니다. `yield`가 있는 의존성의 종료 코드는 응답이 전송된 *후에* 실행되었기 때문에, [Exception Handlers](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}가 이미 실행된 뒤였습니다.
이는 주로 백그라운드 태스크 안에서 의존성이 "yield"한 동일한 객체들을 사용할 수 있게 하기 위한 설계였습니다. 백그라운드 태스크가 끝난 뒤에 종료 코드가 실행되었기 때문입니다.
이는 응답이 네트워크를 통해 전달되기를 기다리는 동안 리소스를 점유하지 않기 위한 의도로 FastAPI 0.106.0에서 변경되었습니다.
/// tip | 팁
추가로, 백그라운드 태스크는 보통 별도의 리소스(예: 자체 데이터베이스 연결)를 가지고 따로 처리되어야 하는 독립적인 로직 집합입니다.
따라서 이 방식이 코드를 더 깔끔하게 만들어줄 가능성이 큽니다.
///
이 동작에 의존하던 경우라면, 이제는 백그라운드 태스크를 위한 리소스를 백그라운드 태스크 내부에서 생성하고, 내부적으로는 `yield`가 있는 의존성의 리소스에 의존하지 않는 데이터만 사용해야 합니다.
예를 들어, 동일한 데이터베이스 세션을 사용하는 대신, 백그라운드 태스크 내부에서 새 데이터베이스 세션을 생성하고, 이 새 세션을 사용해 데이터베이스에서 객체를 가져오면 됩니다. 그리고 데이터베이스에서 가져온 객체를 백그라운드 태스크 함수의 매개변수로 전달하는 대신, 해당 객체의 ID를 전달한 다음 백그라운드 태스크 함수 내부에서 객체를 다시 가져오면 됩니다.

View File

@@ -1,31 +1,26 @@
# 비동기 테스트 코드 작성
# 비동기 테스트 { #async-tests }
이전 장에서 `TestClient` 이용해 **FastAPI** 플리케이션 테스트를 작성하는 법을 배우셨을텐데요.
지금까지는 `async` 키워드 사용없이 동기 함수의 테스트 코드를 작성하는 법만 익혔습니다.
제공된 `TestClient`사용하여 **FastAPI** 플리케이션을 테스트하는 방법을 이미 살펴보았습니다. 지금까지는 `async` 함수를 사용하지 않고, 동기 테스트를 작성하는 방법만 보았습니다.
하지만 비동기 함수를 사용하여 테스트 코드를 작성하는 것은 매우 유용할 수 있습니다.
예를 들면 데이터베이스에 비동기로 쿼리하는 경우를 생각해봅시다.
FastAPI 애플리케이션에 요청을 보내고, 비동기 데이터베이스 라이브러리를 사용하여 백엔드가 데이터베이스에 올바르게 데이터를 기록했는지 확인하고 싶을 때가 있을 겁니다.
테스트에서 비동기 함수를 사용할 수 있으면 유용할 수 있습니다. 예를 들어 데이터베이스를 비동기로 쿼리하는 경우를 생각해 보세요. FastAPI 애플리케이션에 요청을 보낸 다음, async 데이터베이스 라이브러리를 사용하면서 백엔드가 데이터베이스에 올바른 데이터를 성공적으로 기록했는지 검증하고 싶을 수 있습니다.
이런 경우의 테스트 코드를 어떻게 비동기로 작성하는지 알아봅시다.
어떻게 동작하게 만들 수 있는지 살펴보겠습니다.
## pytest.mark.anyio
## pytest.mark.anyio { #pytest-mark-anyio }
앞에서 작성한 테스트 함수에서 비동기 함수를 호출하고 싶다면, 테스트 코드도 비동기 함수여야합니다.
AnyIO는 특정 테스트 함수를 비동기 함수로 호출 할 수 있는 깔끔한 플러그인을 제공합니다.
테스트에서 비동기 함수를 호출하면, 테스트 함수도 비동기여야 합니다. AnyIO는 이를 위한 깔끔한 플러그인을 제공하며, 일부 테스트 함수를 비동기로 호출하도록 지정할 수 있습니다.
## HTTPX { #httpx }
## HTTPX
**FastAPI** 애플리케이션이 `async def` 대신 일반 `def` 함수를 사용하더라도, 내부적으로는 여전히 `async` 애플리케이션입니다.
**FastAPI** 애플리케이션`async def` 대신 `def` 키워드로 선언된 함수를 사용하더라도, 내부적으로는 여전히 `비동기` 애플리케이션입니다.
`TestClient`는 표준 pytest를 사용하여, 일반 `def` 테스트 함수 안에서 비동기 FastAPI 애플리케이션을 호출하도록 내부에서 마법 같은 처리를 합니다. 하지만 비동기 함수 안에서 이를 사용하면 그 마법은 더 이상 동작하지 않습니다. 테스트를 비동기로 실행하면, 테스트 함수 안에서 `TestClient`를 더 이상 사용할 수 없습니다.
`TestClient`pytest 표준을 사용하여 비동기 FastAPI 애플리케이션을 일반적인 `def` 테스트 함수 내에서 호출할 수 있도록 내부에서 마술을 부립니다. 하지만 이 마술은 비동기 함수 내부에서 사용할 때는 더 이상 작동하지 않습니다. 테스트를 비동기로 실행하면, 더 이상 테스트 함수 내부에서 `TestClient`를 사용할 수 습니다.
`TestClient`<a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>를 기반으로 하며, 다행히 HTTPX를 직접 사용해 API를 테스트할 수 습니다.
`TestClient`는 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>를 기반으로 하고 있으며, 다행히 이를 직접 사용하여 API를 테스트할 수 있습니다.
## 예시 { #example }
## 예시
간단한 예시를 위해 [더 큰 어플리케이션 만들기](../ko/tutorial/bigger-applications.md){.internal-link target=_blank} 와 [테스트](../ko/tutorial/testing.md){.internal-link target=_blank}:에서 다룬 파일 구조와 비슷한 형태를 확인해봅시다:
간단한 예시로, [더 큰 애플리케이션](../tutorial/bigger-applications.md){.internal-link target=_blank}과 [테스트](../tutorial/testing.md){.internal-link target=_blank}에서 설명한 것과 비슷한 파일 구조를 살펴보겠습니다:
```
.
@@ -35,17 +30,17 @@ AnyIO는 특정 테스트 함수를 비동기 함수로 호출 할 수 있는
│   └── test_main.py
```
`main.py`는 아래와 같아야 합니다:
`main.py` 파일은 다음과 같습니다:
{* ../../docs_src/async_tests/main.py *}
{* ../../docs_src/async_tests/app_a_py39/main.py *}
`test_main.py` 파일 `main.py`에 대한 테스트가 있을 텐데, 다음과 같 수 있습니다:
`test_main.py` 파일에는 `main.py`에 대한 테스트가 있으며, 이제 다음과 같이 보일 수 있습니다:
{* ../../docs_src/async_tests/test_main.py *}
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
## 실행하기
## 실행하기 { #run-it }
아래의 명령어로 테스트 코드를 실행니다:
다음과 같이 평소처럼 테스트를 실행할 수 있습니다:
<div class="termy">
@@ -57,52 +52,48 @@ $ pytest
</div>
## 자세히 보기
## 자세히 보기 { #in-detail }
`@pytest.mark.anyio` 마커는 pytest에게 이 테스트 함수가 비동기로 호출되어야 함을 알려줍니다:
`@pytest.mark.anyio` 마커는 pytest에게 이 테스트 함수가 비동기로 호출되어야 한다고 알려줍니다:
{* ../../docs_src/async_tests/test_main.py hl[7] *}
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
/// tip | 팁
테스트 함수가 이제 `TestClient`를 사용할 때처럼 단순히 `def`가 아니라 `async def`로 작성된 점에 주목해주세요.
`TestClient`를 사용할 때처럼 단순히 `def`가 아니라, 이제 테스트 함수가 `async def`라는 점에 주목세요.
///
그 다음 `AsyncClient` 로 앱을 만들고 비동기 요청을 `await` 키워드로 보낼 수 있습니다:
그 다음 앱으로 `AsyncClient`를 만들고, `await`를 사용해 비동기 요청을 보낼 수 있습니다.
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
위의 코드는:
이는 다음과 동등합니다:
```Python
response = client.get('/')
```
`TestClient` 요청을 보내던 것과 동일합니다.
`TestClient` 요청을 보내기 위해 사용하던 코드입니다.
/// tip | 팁
로운 `AsyncClient`를 사용할 때 async/await를 사용하고 있다는 점에 주목하세요. 요청은 비동기적으로 처리됩니다.
`AsyncClient`와 함께 async/await를 사용하고 있다는 점에 주목하세요. 요청은 비동기니다.
///
/// warning | 경고
만약의 어플리케이션이 Lifespan 이벤트에 의존성을 갖고 있다면 `AsyncClient` 이러한 이벤트를 실행시키지 않습니다.
`AsyncClient` 가 테스트를 실행시켰다는 것을 확인하기 위해
`LifespanManager` from <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a>.확인해주세요.
플리케이션이 lifespan 이벤트에 의존다면, `AsyncClient` 이러한 이벤트를 트리거하지 않습니다. 이벤트가 트리거되도록 하려면 <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a>의 `LifespanManager`를 사용하세요.
///
## 그 외의 비동기 함수 호출
## 기타 비동기 함수 호출 { #other-asynchronous-function-calls }
테스트 함수가 이제 비동기 함수이므로, FastAPI 애플리케이션에 요청을 보내는 것 외에도 다른 `async` 함수를 호출하고 `await` 키워드를 사용 할 수 있습니다.
테스트 함수가 이제 비동기이므로, 테스트에서 FastAPI 애플리케이션에 요청을 보내는 것 외에도 다른 `async` 함수를 코드의 다른 곳에서 호출하듯이 동일하게 호출하고 (`await`) 사용할 수 있습니다.
/// tip | 팁
테스트에 비동기 함수 호출을 통합할 때 (예: <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB의 MotorClient</a>를 사용할 때) `RuntimeError: Task attached to a different loop` 오류가 발생한다면, 이벤트 루프가 필요한 객체는 반드시 비동기 함수 에서만 인스턴스화해야 한다는 점을 주의하세요!
예를 들어 `@app.on_event("startup")` 콜백 내에서 인스턴스화하는 것이 좋습니다.
테스트에 비동기 함수 호출을 통합할 때(예: <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB의 MotorClient</a>를 사용할 때) `RuntimeError: Task attached to a different loop`를 마주친다면, 이벤트 루프가 필요한 객체는 async 함수 에서만 인스턴스화해야 한다는 점을 기억하세요. 예를 들어 `@app.on_event("startup")` 콜백에서 인스턴스화할 수 있습니다.
///

View File

@@ -1,4 +1,4 @@
# 사용자 정의 응답 - HTML, Stream, 파일, 기타
# 사용자 정의 응답 - HTML, Stream, 파일, 기타 { #custom-response-html-stream-file-others }
기본적으로, **FastAPI** 응답을 `JSONResponse`를 사용하여 반환합니다.
@@ -6,11 +6,11 @@
그러나 `Response` (또는 `JSONResponse`와 같은 하위 클래스)를 직접 반환하면, 데이터가 자동으로 변환되지 않으며 (심지어 `response_model`을 선언했더라도), 문서화가 자동으로 생성되지 않습니다(예를 들어, 생성된 OpenAPI의 일부로 HTTP 헤더 `Content-Type`에 특정 "미디어 타입"을 포함하는 경우).
하지만 *경로 작업 데코레이터*에서 `response_class` 매개변수를 사용하여 원하는 `Response`(예: 모든 `Response` 하위 클래스)를 선언할 수도 있습니다.
하지만 *경로 처리 데코레이터*에서 `response_class` 매개변수를 사용하여 원하는 `Response`(예: 모든 `Response` 하위 클래스)를 선언할 수도 있습니다.
*경로 작업 함수*에서 반환하는 내용은 해당 `Response`안에 포함됩니다.
*경로 처리 함수*에서 반환하는 내용은 해당 `Response`안에 포함됩니다.
그리고 만약 그 `Response``JSONResponse``UJSONResponse`의 경우 처럼 JSON 미디어 타입(`application/json`)을 가지고 있다면, *경로 작업 데코레이터*에서 선언한 Pydantic의 `response_model`을 사용해 자동으로 변환(및 필터링) 됩니다.
그리고 만약 그 `Response``JSONResponse``UJSONResponse`의 경우 처럼 JSON 미디어 타입(`application/json`)을 가지고 있다면, *경로 처리 데코레이터*에서 선언한 Pydantic의 `response_model`을 사용해 자동으로 변환(및 필터링) 됩니다.
/// note | 참고
@@ -18,11 +18,11 @@
///
## `ORJSONResponse` 사용하기
## `ORJSONResponse` 사용하기 { #use-orjsonresponse }
예를 들어, 성능을 극대화하려는 경우, <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">orjson</a>을 설치하여 사용하고 응답을 `ORJSONResponse`로 설정할 수 있습니다.
예를 들어, 성능을 극대화하려는 경우, <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>을 설치하여 사용하고 응답을 `ORJSONResponse`로 설정할 수 있습니다.
사용하고자 하는 `Response` 클래스(하위 클래스)를 임포트한 후, **경로 작업 데코레이터*에서 선언하세요.
사용하고자 하는 `Response` 클래스(하위 클래스)를 임포트한 후, *경로 처리 데코레이터*에서 선언하세요.
대규모 응답의 경우, 딕셔너리를 반환하는 것보다 `Response`를 반환하는 것이 훨씬 빠릅니다.
@@ -30,7 +30,7 @@
하지만 반환하는 내용이 **JSON으로 직렬화 가능**하다고 확신하는 경우, 해당 내용을 응답 클래스에 직접 전달할 수 있으며, FastAPI가 반환 내용을 `jsonable_encoder`를 통해 처리한 뒤 응답 클래스에 전달하는 오버헤드를 피할 수 있습니다.
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
/// info | 정보
@@ -48,14 +48,14 @@
///
## HTML 응답
## HTML 응답 { #html-response }
**FastAPI**에서 HTML 응답을 직접 반환하려면 `HTMLResponse`를 사용하세요.
* `HTMLResponse`를 임포트 합니다.
* *경로 작업 데코레이터*의 `response_class` 매개변수로 `HTMLResponse`를 전달합니다.
* *경로 처리 데코레이터*의 `response_class` 매개변수로 `HTMLResponse`를 전달합니다.
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
/// info | 정보
@@ -67,17 +67,17 @@
///
### `Response` 반환하기
### `Response` 반환하기 { #return-a-response }
[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 본 것 처럼, *경로 작업*에서 응답을 직접 반환하여 재정의할 수도 있습니다.
[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 본 것 처럼, *경로 처리*에서 응답을 직접 반환하여 재정의할 수도 있습니다.
위의 예제와 동일하게 `HTMLResponse`를 반환하는 코드는 다음과 같을 수 있습니다:
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
/// warning | 경고
*경로 작업 함수*에서 직접 반환된 `Response`는 OpenAPI에 문서화되지 않습니다(예를들어, `Content-Type`이 문서화되지 않음) 자동 대화형 문서에서도 표시되지 않습니다.
*경로 처리 함수*에서 직접 반환된 `Response`는 OpenAPI에 문서화되지 않습니다(예를들어, `Content-Type`이 문서화되지 않음) 자동 대화형 문서에서도 표시되지 않습니다.
///
@@ -87,27 +87,27 @@
///
### OpenAPI에 문서화하고 `Response` 재정의 하기
### OpenAPI에 문서화하고 `Response` 재정의 하기 { #document-in-openapi-and-override-response }
함수 내부에서 응답을 재정의하면서 동시에 OpenAPI에서 "미디어 타입"을 문서화하고 싶다면, `response_class` 매게변수를 사용하면서 `Response` 객체를 반환할 수 있습니다.
이 경우 `response_class`는 OpenAPI *경로 작업*을 문서화하는 데만 사용되고, 실제로는 여러분이 반환한 `Response`가 그대로 사용됩니다.
이 경우 `response_class`는 OpenAPI *경로 처리*를 문서화하는 데만 사용되고, 실제로는 여러분이 반환한 `Response`가 그대로 사용됩니다.
### `HTMLResponse`직접 반환하기
#### `HTMLResponse`직접 반환하기 { #return-an-htmlresponse-directly }
예를 들어, 다음과 같이 작성할 수 있습니다:
{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
이 예제에서, `generate_html_response()` 함수는 HTML을 `str`로 반환하는 대신 이미 `Response`를 생성하고 반환합니다.
`generate_html_response()`를 호출한 결과를 반환함으로써, 기본적인 **FastAPI** 기본 동작을 재정의 하는 `Response`를 이미 반환하고 있습니다.
하지만 `response_class``HTMLResponse`를 함께 전달했기 때문에, FastAPI는 이를 OpenAPI 및 대화형 문서에서 `text/html`로 HTML을 문서화 하는 방법을 알 수 있습니다.
하지만 `response_class``HTMLResponse`를 함께 전달했기 때문에, **FastAPI**는 이를 OpenAPI 및 대화형 문서에서 `text/html`로 HTML을 문서화 하는 방법을 알 수 있습니다.
<img src="/img/tutorial/custom-response/image01.png">
## 사용 가능한 응답들
## 사용 가능한 응답들 { #available-responses }
다음은 사용할 수 있는 몇가지 응답들 입니다.
@@ -121,7 +121,7 @@
///
### `Response`
### `Response` { #response }
기본 `Response` 클래스는 다른 모든 응답 클래스의 부모 클래스 입니다.
@@ -134,27 +134,27 @@
* `headers` - 문자열로 이루어진 `dict`.
* `media_type` - 미디어 타입을 나타내는 `str` 예: `"text/html"`.
FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포함시킵니다. 또한 `media_type`에 기반하여 `Content-Type` 헤더를 포함하며, 텍스트 타입의 경우 문자 집합을 추가 합니다.
FastAPI (실제로는 Starlette)가 자동으로 Content-Length 헤더를 포함시킵니다. 또한 `media_type`에 기반하여 Content-Type 헤더를 포함하며, 텍스트 타입의 경우 문자 집합을 추가 합니다.
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
### `HTMLResponse`
### `HTMLResponse` { #htmlresponse }
텍스트 또는 바이트를 받아 HTML 응답을 반환합니다. 위에서 설명한 내용과 같습니다.
### `PlainTextResponse`
### `PlainTextResponse` { #plaintextresponse }
텍스트 또는 바이트를 받아 일반 텍스트 응답을 반환합니다.
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
### `JSONResponse`
### `JSONResponse` { #jsonresponse }
데이터를 받아 `application/json`으로 인코딩된 응답을 반환합니다.
이는 위에서 설명했듯이 **FastAPI**에서 기본적으로 사용되는 응답 형식입니다.
### `ORJSONResponse`
### `ORJSONResponse` { #orjsonresponse }
<a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>을 사용하여 빠른 JSON 응답을 제공하는 대안입니다. 위에서 설명한 내용과 같습니다.
@@ -164,13 +164,13 @@ FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포
///
### `UJSONResponse`
### `UJSONResponse` { #ujsonresponse }
<a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>을 사용한 또 다른 JSON 응답 형식입니다.
/// info | 정보
이 응답을 사용하려면 `ujson`을 설치해야합니다. 예: 'pip install ujson`.
이 응답을 사용하려면 `ujson`을 설치해야합니다. 예: `pip install ujson`.
///
@@ -180,7 +180,7 @@ FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포
///
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
/// tip | 팁
@@ -188,22 +188,22 @@ FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포
///
### `RedirectResponse`
### `RedirectResponse` { #redirectresponse }
HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 307(임시 리디렉션)으로 설정됩니다.
`RedirectResponse`를 직접 반환할 수 있습니다.
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
---
또는 `response_class` 매개변수에서 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
이 경우, *경로 작업* 함수에서 URL을 직접 반환할 수 있습니다.
이 경우, *경로 처리* 함수에서 URL을 직접 반환할 수 있습니다.
이 경우, 사용되는 `status_code``RedirectResponse`의 기본값인 `307` 입니다.
@@ -211,15 +211,15 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
`status_code` 매개변수를 `response_class` 매개변수와 함께 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
### `StreamingResponse`
### `StreamingResponse` { #streamingresponse }
비동기 제너레이터 또는 일반 제너레이터/이터레이터를 받아 응답 본문을 스트리밍 합니다.
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
#### 파일과 같은 객체를 사용한 `StreamingResponse`
#### 파일과 같은 객체를 사용한 `StreamingResponse` { #using-streamingresponse-with-file-like-objects }
파일과 같은 객체(예: `open()`으로 반환된 객체)가 있는 경우, 해당 파일과 같은 객체를 반복(iterate)하는 제너레이터 함수를 만들 수 있습니다.
@@ -227,7 +227,7 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
이 방식은 클라우드 스토리지, 비디오 처리 등의 다양한 라이브러리와 함께 사용할 수 있습니다.
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
1. 이것이 제너레이터 함수입니다. `yield` 문을 포함하고 있으므로 "제너레이터 함수"입니다.
2. `with` 블록을 사용함으로써, 제너레이터 함수가 완료된 후 파일과 같은 객체가 닫히도록 합니다. 즉, 응답 전송이 끝난 후 닫힙니다.
@@ -235,15 +235,15 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
이렇게 하면 "생성(generating)" 작업을 내부적으로 다른 무언가에 위임하는 제너레이터 함수가 됩니다.
이 방식을 사용하면 `with` 블록 안에서 파일을 열 수 있어, 작업이 완료된 후 파일과 같은 객체가 닫히는 것을 보장할 수 있습니다.
이 방식을 사용하면 `with` 블록 안에서 파일을 열 수 있어, 작업이 완료된 후 파일과 같은 객체가 닫히는 것을 보장할 수 있습니다.
/// tip | 팁
여기서 표준 `open()`을 사용하고 있기 때문에 `async`와 `await`를 지원하지 않습니다. 따라서 경로 작업은 일반 `def`로 선언합니다.
여기서 표준 `open()`을 사용하고 있기 때문에 `async``await`를 지원하지 않습니다. 따라서 경로 처리는 일반 `def`로 선언합니다.
///
### `FileResponse`
### `FileResponse` { #fileresponse }
파일을 비동기로 스트리밍하여 응답합니다.
@@ -256,25 +256,25 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
파일 응답에는 적절한 `Content-Length`, `Last-Modified`, 및 `ETag` 헤더가 포함됩니다.
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
또한 `response_class` 매개변수를 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
이 경우, 경로 작업 함수에서 파일 경로를 직접 반환할 수 있습니다.
이 경우, 경로 처리 함수에서 파일 경로를 직접 반환할 수 있습니다.
## 사용자 정의 응답 클래스
## 사용자 정의 응답 클래스 { #custom-response-class }
`Response`를 상속받아 사용자 정의 응답 클래스를 생성하고 사용할 수 있습니다.
예를 들어, 포함된 `ORJSONResponse` 클래스에서 사용되지 않는 설정으로 <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">orjson</a>을 사용하고 싶다고 가정해봅시다.
예를 들어, 포함된 `ORJSONResponse` 클래스에서 사용되지 않는 설정으로 <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>을 사용하고 싶다고 가정해봅시다.
만약 들여쓰기 및 포맷된 JSON을 반환하고 싶다면, `orjson.OPT_INDENT_2` 옵션을 사용할 수 있습니다.
`CustomORJSONResponse`를 생성할 수 있습니다. 여기서 핵심은 `Response.render(content)` 메서드를 생성하여 내용을 `bytes`로 반환하는 것입니다:
{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
이제 다음 대신:
@@ -282,7 +282,7 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
{"message": "Hello World"}
```
이 응답은 이렇게 반환됩니다:
...이 응답은 이렇게 반환됩니다:
```json
{
@@ -292,22 +292,22 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
물론 JSON 포맷팅보다 더 유용하게 활용할 방법을 찾을 수 있을 것입니다. 😉
## 기본 응답 클래스
## 기본 응답 클래스 { #default-response-class }
**FastAPI** 클래스 객체 또는 `APIRouter`를 생성할 때 기본적으로 사용할 응답 클래스를 지정할 수 있습니다.
이를 정의하는 매개변수는 `default_response_class`입니다.
아래 예제에서 **FastAPI**는 모든 경로 작업에서 기본적으로 `JSONResponse` 대신 `ORJSONResponse`를 사용합니다.
아래 예제에서 **FastAPI**는 모든 *경로 처리*에서 기본적으로 `JSONResponse` 대신 `ORJSONResponse`를 사용합니다.
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
/// tip | 팁
여전히 이전처럼 *경로 작업*에서 `response_class`를 재정의할 수 있습니다.
여전히 이전처럼 *경로 처리*에서 `response_class`를 재정의할 수 있습니다.
///
## 추가 문서화
## 추가 문서화 { #additional-documentation }
OpenAPI에서 `responses`를 사용하여 미디어 타입 및 기타 세부 정보를 선언할 수도 있습니다: [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}.

View File

@@ -1,67 +1,66 @@
# Lifespan 이벤트
# Lifespan 이벤트 { #lifespan-events }
애플리케이션 **시작 전**에 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이는 이 코드가 **한 번**만 실행되며, **애플리케이션이 요청을 받기 시작하기 전**에 실행된다는 의미입니다.
애플리케이션 **시작**하기 전에 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이는 이 코드가 **한 번**만 실행되며, 애플리케이션이 **요청을 받기 시작하기 전**에 실행된다는 의미입니다.
마찬가지로, 애플리케이션이 **종료될 때** 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이 경우, 이 코드는 **한 번**만 실행되며, **여러 요청을 처리한 후**에 실행됩니다.
마찬가지로, 애플리케이션이 **종료**될 때 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이 경우, 이 코드는 **한 번**만 실행되며, **여러 요청을 처리한 후**에 실행됩니다.
이 코드 애플리케이션이 **요청을 받기 시작하기 전에** 실행되고, 요청 처리가 끝난 후 **종료 직전에** 실행되기 때문에 전체 애플리케이션의 **수명(Lifespan)**을 다룹니다. (잠시 후 "수명"이라는 단어가 중요해집니다 😉)
이 코드 애플리케이션이 요청을 받기 **시작**하기 전에 실행되고, 요청 처리를 **끝낸 직후** 실행되기 때문에 전체 애플리케이션의 **수명(lifespan)**을 다룹니다(잠시 후 "lifespan"이라는 단어가 중요해집니다 😉).
방법은 전체 애플리케이션에서 사용해야 하는 **자원**을 설정하거나 요청 간에 **공유되는** 자원을 설정하고, 또는 후에 **정리**하는 데 매우 유용할 수 있습니다. 예를 들어, 데이터베이스 연결 풀 또는 공유되는 머신러닝 모델을 로드하는 경우입니다.
는 전체 앱에서 사용해야 하는 **자원**을 설정하고, 요청 간에 **공유되는** 자원을 설정하고, 그리고/또는 후에 **정리**하는 데 매우 유용할 수 있습니다. 예를 들어, 데이터베이스 연결 풀 또는 공유 머신러닝 모델을 로드하는 경우입니다.
## 사용 사례 { #use-case }
## 사용 사례
먼저 **사용 사례** 예시로 시작한 다음, 이를 어떻게 해결할지 살펴보겠습니다.
먼저 **사용 사례**를 예로 들어보고, 이를 어떻게 해결할 수 있는지 살펴보겠습니다.
요청을 처리하는 데 사용하고 싶은 **머신러닝 모델**이 있다고 상상해 봅시다. 🤖
우리가 요청을 처리하기 위해 사용하고 싶은 **머신러닝 모델**이 있다고 상상해 봅시다. 🤖
동일한 모델이 요청 간에 공유되므로, 요청마다 모델이 하나씩 있거나 사용자마다 하나씩 있는 등의 방식이 아닙니다.
이 모델들은 요청 간에 공유되므로, 요청마다 모델이 하나씩 있는 것이 아니라, 여러 요청에서 동일한 모델을 사용합니다.
모델을 로드하는 데 **상당한 시간이 걸린다고 상상해 봅시다**, 왜냐하면 모델이 **디스크에서 많은 데이터를 읽어야** 하기 때문입니다. 그래서 모든 요청마다 이를 수행하고 싶지는 않습니다.
델을 로드하는 데 **상당한 시간이 걸린다고 상상해 봅시다**, 왜냐하면 모델이 **디스크에서 많은 데이터를 읽어야** 하기 때문입니다. 따라서 모든 요청에 대해 모델을 매번 로드하고 싶지 않습니다.
듈/파일의 최상위에서 로드할 수도 있지만, 그러면 단순한 자동화된 테스트를 실행하는 경우에도 **모델을 로드**하게 되고, 테스트가 코드의 독립적인 부분을 실행하기 전에 모델이 로드될 때까지 기다려야 하므로 **느려집니다**.
모듈/파일의 최상위에서 모델을 로드할 수도 있지만, 그러면 **모델을 로드하는데** 시간이 걸리기 때문에, 단순한 자동화된 테스트를 실행할 때도 모델이 로드될 때까지 기다려야 해서 **테스트 속도가 느려집니다**.
이것이 우리가 해결할 문제입니다. 요청을 처리하기 전에 모델을 로드하되, 코드가 로드되는 동안이 아니라 애플리케이션이 요청을 받기 시작하기 직전에만 로드하겠습니다.
이 문제를 해결하려고 하는 것입니다. 요청을 처리하기 전에 모델을 로드하되, 애플리케이션이 요청을 받기 시작하기 직전에만 로드하고, 코드가 로드되는 동안은 로드하지 않도록 하겠습니다.
## Lifespan { #lifespan }
## Lifespan
`FastAPI` 앱의 `lifespan` 매개변수와 "컨텍스트 매니저"를 사용하여 *시작*과 *종료* 로직을 정의할 수 있습니다(컨텍스트 매니저가 무엇인지 잠시 후에 보여드리겠습니다).
`FastAPI` 애플리케이션의 `lifespan` 매개변수와 "컨텍스트 매니저"를 사용하여 *시작*과 *종료* 로직을 정의할 수 있습니다. (컨텍스트 매니저가 무엇인지 잠시 후에 설명드리겠습니다.)
예제로 시작한 다음 자세히 살펴보겠습니다.
예제를 통해 시작하고, 그 후에 자세히 살펴보겠습니다.
`yield`를 사용해 비동기 함수 `lifespan()`을 다음과 같이 생성합니다:
우리는 `yield`를 사용하여 비동기 함수 `lifespan()`을 다음과 같이 생성합니다:
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
{* ../../docs_src/events/tutorial003.py hl[16,19] *}
여기서는 `yield` 이전에 (가짜) 모델 함수를 머신러닝 모델이 들어 있는 딕셔너리에 넣어 모델을 로드하는 비용이 큰 *시작* 작업을 시뮬레이션합니다. 이 코드는 애플리케이션이 **요청을 받기 시작하기 전**, *시작* 동안에 실행됩니다.
여기서 우리는 모델을 로드하는 비싼 *시작* 작업을 시뮬레이션하고 있습니다. `yield` 앞에서 (가짜) 모델 함수를 머신러닝 모델이 담긴 딕셔너리에 넣습니다. 이 코드는 **애플리케이션이 요청을 받기 시작하기 전**, *시작* 동안에 실행됩니다.
그리고 `yield` 직후에는 모델을 언로드합니다. 이 코드는 **애플리케이션이 요청 처리 완료 후**, *종료* 직전에 실행됩니다. 예를 들어, 메모리나 GPU와 같은 자원을 해제하는 작업을 할 수 있습니다.
그리고 `yield` 직후에는 모델을 언로드합니다. 이 코드는 애플리케이션이 **요청 처리를 마친 후**, *종료* 직전에 실행됩니다. 예를 들어 메모리나 GPU 같은 자원을 해제할 수 있습니다.
/// tip | 팁
`shutdown`은 애플리케이션을 **종료**할 때 발생합니다.
`shutdown`은 애플리케이션을 **중지**할 때 발생합니다.
로운 버전을 시작해야 하거나, 그냥 실행을 멈추고 싶을 수도 있습니다. 🤷
새 버전을 시작해야 할 수도 있고, 그냥 실행하는 게 지겨워졌을 수도 있습니다. 🤷
///
### Lifespan 함수
### Lifespan 함수 { #lifespan-function }
먼저 주목할 점은, `yield`를 사용하여 비동기 함수(async function)를 정의하고 있다는 것입니다. 이는 `yield`를 사용 의존성과 매우 유사합니다.
먼저 주목할 점은 `yield`를 사용하여 비동기 함수를 정의하고 있다는 것입니다. 이는 `yield`를 사용하는 의존성과 매우 유사합니다.
{* ../../docs_src/events/tutorial003.py hl[14:19] *}
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
함수의 첫 번째 부분, 즉 `yield` 이전의 코드는 애플리케이션이 시작되기 **전에** 실행됩니다.
그리고 `yield` 이후의 부분은 애플리케이션이 료된 **나중** 실행됩니다.
그리고 `yield` 이후의 부분은 애플리케이션이 료된 **** 실행됩니다.
### 비동기 컨텍스트 매니저
### 비동기 컨텍스트 매니저 { #async-context-manager }
함수를 확인해보면, `@asynccontextmanager`장식되어 있습니다.
확인해 보면, 함수는 `@asynccontextmanager`데코레이션되어 있습니다.
것은 함수를 "**비동기 컨텍스트 매니저**"라고 불리는 것으로 변환시킵니다.
함수를 "**비동기 컨텍스트 매니저**"라고 불리는 것으로 변환니다.
{* ../../docs_src/events/tutorial003.py hl[1,13] *}
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
파이썬에서 **컨텍스트 매니저**는 `with` 문에서 사용할 수 있는 것입니다. 예를 들어, `open()`은 컨텍스트 매니저로 사용할 수 있습니다:
@@ -69,97 +68,98 @@
with open("file.txt") as file:
file.read()
```
최근 버전의 파이썬에서는 **비동기 컨텍스트 매니저**도 있습니다. 이를 `async with`와 함께 사용합니다:
최근 버전의 파이썬에는 **비동기 컨텍스트 매니저**도 있습니다. 이를 `async with`와 함께 사용합니다:
```Python
async with lifespan(app):
await do_stuff()
```
컨텍스트 매니저나 위와 같은 비동기 컨텍스트 매니저를 만들면, `with` 블록에 들어가기 전에 `yield` 이전의 코드 실행고, `with` 블록을 벗어난 후에는 `yield` 이후의 코드 실행니다.
위와 같은 컨텍스트 매니저 또는 비동기 컨텍스트 매니저를 만들면, `with` 블록에 들어가기 전에 `yield` 이전의 코드 실행고, `with` 블록을 벗어난 후에는 `yield` 이후의 코드 실행니다.
위의 코드 예제에서는 직접 사용하지 않고, FastAPI에 전달하여 사용하도록 합니다.
위의 코드 예제에서는 직접 사용하지 않고, FastAPI에 전달하여 FastAPI가 이를 사용하도록 합니다.
`FastAPI` 애플리케이션`lifespan` 매개변수는 **비동기 컨텍스트 매니저**를 받기 때문에, 새로운 `lifespan` 비동기 컨텍스트 매니저를 FastAPI에 전달할 수 있습니다.
`FastAPI` `lifespan` 매개변수는 **비동기 컨텍스트 매니저**를 받으므로, 새 `lifespan` 비동기 컨텍스트 매니저를 전달할 수 있습니다.
{* ../../docs_src/events/tutorial003.py hl[22] *}
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
## 대체 이벤트 (사용 중단)
## 대체 이벤트(사용 중단) { #alternative-events-deprecated }
/// warning | 경고
*시작*과 *종료*를 처리하는 권장 방법은 위에서 설명한 대로 `FastAPI` 애플리케이션`lifespan` 매개변수를 사용하는 것입니다. `lifespan` 매개변수를 제공하면 `startup``shutdown` 이벤트 핸들러는 더 이상 호출되지 않습니다. `lifespan`을 사용할지, 모든 이벤트를 사용할지 선택해야 하며 둘 다 사용할 수는 없습니다.
*시작*과 *종료*를 처리하는 권장 방법은 위에서 설명한 대로 `FastAPI` `lifespan` 매개변수를 사용하는 것입니다. `lifespan` 매개변수를 제공하면 `startup``shutdown` 이벤트 핸들러는 더 이상 호출되지 않습니다. `lifespan`만 쓰거나 이벤트만 쓰거나 둘 중 하나이지, 둘 다는 아닙니다.
이 부분은 건너뛰셔도 좋습니다.
이 부분은 아마 건너뛰셔도 니다.
///
*시작*과 *종료* 동안 실행될 이 로직을 정의하는 대체 방법이 있습니다.
애플리케이션이 시작되기 전에 또는 종료될 때 실행야 하는 이벤트 핸들러(함수)를 정의할 수 있습니다.
애플리케이션이 시작되기 전에 또는 애플리케이션이 종료될 때 실행되어야 하는 이벤트 핸들러(함수)를 정의할 수 있습니다.
이 함수들은 `async def` 또는 일반 `def`로 선언할 수 있습니다.
### `startup` 이벤트
### `startup` 이벤트 { #startup-event }
애플리케이션이 시작되기 전에 실행되어야 하는 함수를 추가하려면, `"startup"` 이벤트로 선언합니다:
{* ../../docs_src/events/tutorial001.py hl[8] *}
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
이 경우, `startup` 이벤트 핸들러 함수는 "database"라는 항목(단지 `dict`)을 일부 값으로 초기화합니다.
이 경우, `startup` 이벤트 핸들러 함수는 "database"(그냥 `dict`) 항목을 일부 값으로 초기화합니다.
여러 개의 이벤트 핸들러 함수를 추가할 수 있습니다.
애플리케이션은 모든 `startup` 이벤트 핸들러가 완료될 때까지 요청을 받기 시작하지 않습니다.
그리고 모든 `startup` 이벤트 핸들러가 완료될 때까지 애플리케이션은 요청을 받기 시작하지 않습니다.
### `shutdown` 이벤트
### `shutdown` 이벤트 { #shutdown-event }
애플리케이션이 종료될 때 실행되어야 하는 함수를 추가하려면, `"shutdown"` 이벤트로 선언합니다:
{* ../../docs_src/events/tutorial002.py hl[6] *}
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
여기서, `shutdown` 이벤트 핸들러 함수는 `"Application shutdown"`이라는 텍스트를 `log.txt` 파일에 기록합니다.
여기서 `shutdown` 이벤트 핸들러 함수는 텍스트 한 줄 `"Application shutdown"` `log.txt` 파일에 기록합니다.
/// info | 정보
`open()` 함수에서 `mode="a"`는 "추가"를 의미하므로, 파일에 있는 기존 내용 덮어쓰지 않고 새로운 줄이 추가됩니다.
`open()` 함수에서 `mode="a"`는 "append"(추가)를 의미하므로, 기존 내용 덮어쓰지 않고 파일에 있던 내용 뒤에 줄이 추가됩니다.
///
/// tip | 팁
이 경우, 우리는 표준 파이썬 `open()` 함수를 사용하여 파일과 상호작용하고 있습니다.
이 경우에는 파일과 상호작용하는 표준 파이썬 `open()` 함수를 사용하고 있습니다.
따라서 I/O(입출력) 작업이 포함되어 있어 디스크에 기록되는 것을 "기다리는" 과정이 필요합니다.
따라서 I/O(input/output)가 포함되어 있어 디스크에 기록되는 것을 "기다리는" 과정이 필요합니다.
하지만 `open()``async``await`를 사용하지 않습니다.
그래서 우리는 이벤트 핸들러 함수 `async def` 대신 일반 `def`로 선언합니다.
그래서 이벤트 핸들러 함수 `async def` 대신 표준 `def`로 선언합니다.
///
### `startup`과 `shutdown`을 함께 사용
### `startup`과 `shutdown`을 함께 { #startup-and-shutdown-together }
*시작*과 *종료* 로직 연결 가능성이 높습니다. 예를 들어, 무언가를 시작한 후 끝내거나, 자원을 획득한 후 해제하는 등의 작업할 수 있습니다.
*시작*과 *종료* 로직 연결되어 있을 가능성이 높습니다. 무언가를 시작했다가 끝내거나, 자원을 획득했다가 해제하는 등의 작업이 필요할 수 있습니다.
이러한 작업을 별도의 함수로 처리하면 서로 로직이나 변수를 공유하지 않기 때문에 더 어려워집니다. 값들을 전역 변수에 저장하거나 비슷한 트릭을 사용해야 할 수 있습니다.
로직이나 변수를 함께 공유하지 않는 분리된 함수에서 이를 처리하면, 전역 변수에 값을 저장하거나 비슷한 트릭이 필요해져 더 어렵습니다.
렇기 때문에 위에서 설명한 대로 `lifespan`을 사용하는 것이 권장됩니다.
그 때문에, 이제는 위에서 설명한 대로 `lifespan`을 사용하는 것이 권장됩니다.
## 기술적 세부사항
## 기술적 세부사항 { #technical-details }
호기심 많은 분들을 위한 기술적인 세부사항입니다. 🤓
ASGI 기술 사양에 따르면, 이는 <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Lifespan Protocol</a>의 일부이며, `startup``shutdown`이라는 이벤트를 정의합니다.
내부적으로 ASGI 기술 사양에서는 이것이 <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Lifespan Protocol</a>의 일부이며, `startup``shutdown`이라는 이벤트를 정의합니다.
/// info | 정보
Starlette `lifespan` 핸들러에 대해 더 읽고 싶다면 <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">Starlette의 Lifespan 문서</a>에서 확인할 수 있습니다.
Starlette `lifespan` 핸들러에 대해서는 <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">Starlette의 Lifespan 문서</a>에서 더 읽어볼 수 있습니다.
이 문서에는 코드의 다른 영역에서 사용할 수 있는 lifespan 상태를 처리하는 방법도 포함되어 있습니다.
또한 코드의 다른 영역에서 사용할 수 있는 lifespan 상태를 처리하는 방법도 포함되어 있습니다.
///
## 서브 애플리케이션
## 서브 애플리케이션 { #sub-applications }
🚨 이 lifespan 이벤트(`startup``shutdown`)는 메인 애플리케이션에 대해서만 실행되며, [서브 애플리케이션 - Mounts](sub-applications.md){.internal-link target=_blank}에는 실행되지 않음을 유의하세요.
🚨 이 lifespan 이벤트(startupshutdown)는 메인 애플리케이션에 대해서만 실행되며, [서브 애플리케이션 - Mounts](sub-applications.md){.internal-link target=_blank}에는 실행되지 않음을 유의하세요.

View File

@@ -1,6 +1,6 @@
# 심화 사용자 안내서 - 도입부
# 심화 사용자 안내서 - 도입부 { #advanced-user-guide }
## 추가 기능
## 추가 기능 { #additional-features }
메인 [자습서 - 사용자 안내서](../tutorial/index.md){.internal-link target=_blank}는 여러분이 **FastAPI**의 모든 주요 기능을 둘러보시기에 충분할 것입니다.
@@ -14,14 +14,8 @@
///
## 자습서를 먼저 읽으십시오
## 자습서를 먼저 읽으십시오 { #read-the-tutorial-first }
여러분은 메인 [자습서 - 사용자 안내서](../tutorial/index.md){.internal-link target=_blank}의 지식으로 **FastAPI**의 대부분의 기능을 사용하실 수 있습니다.
이어지는 장들은 여러분이 메인 자습서 - 사용자 안내서를 이미 읽으셨으며 주요 아이디어를 알고 계신다고 가정합니다.
## TestDriven.io 강좌
여러분이 문서의 이 부분을 보완하시기 위해 심화-기초 강좌 수강을 희망하신다면 다음을 참고 하시기를 바랍니다: **TestDriven.io**의 <a href="https://testdriven.io/courses/tdd-fastapi/" class="external-link" target="_blank">FastAPI와 Docker를 사용한 테스트 주도 개발</a>.
그들은 현재 전체 수익의 10퍼센트를 **FastAPI** 개발에 기부하고 있습니다. 🎉 😄

View File

@@ -1,31 +1,31 @@
# 응답 - 상태 코드 변경
# 응답 - 상태 코드 변경 { #response-change-status-code }
기본 [응답 상태 코드 설정](../tutorial/response-status-code.md){.internal-link target=_blank}이 가능하다는 걸 이미 알고 계실 겁니다.
하지만 경우에 따라 기본 설정과 다른 상태 코드를 반환해야 할 때가 있습니다.
## 사용 예
## 사용 예 { #use-case }
예를 들어 기본적으로 HTTP 상태 코드 "OK" `200`을 반환하고 싶다고 가정해 봅시다.
하지만 데이터가 존재하지 않으면 이를 새로 생성하고, HTTP 상태 코드 "CREATED" `201`을 반환하고자 할 때가 있을 수 있습니다.
이때도 여전히 `response_model`을 사용하여 반환하는 데이터를 필터링하고 변환하고 싶을 수 있습니다.
하지만 여전히 `response_model`을 사용하여 반환하는 데이터를 필터링하고 변환할 수 있기를 원합니다.
이런 경우에는 `Response` 파라미터를 사용할 수 있습니다.
## `Response` 파라미터 사용하기
## `Response` 파라미터 사용하기 { #use-a-response-parameter }
*경로 작동 함수*에 `Response` 타입의 파라미터를 선언할 수 있습니다. (쿠키와 헤더에 대해 선언하는 것과 유사하게)
*경로 처리 함수*에 `Response` 타입의 파라미터를 선언할 수 있습니다. (쿠키와 헤더에 대해 선언하는 것과 유사하게)
그리고 이 *임시* 응답 객체에서 `status_code`를 설정할 수 있습니다.
{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
그리고 평소처럼 원하는 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
그리고 평소처럼 필요한 어떤 객체든 반환할 수 있습니다(`dict`, 데이터베이스 모델 등).
`response_model`을 선언했다면 반환된 객체는 여전히 필터링되고 변환됩니다.
**FastAPI**는 이 *임시* 응답 객체에서 상태 코드(쿠키와 헤더 포함)를 추출하여, `response_model`로 필터링된 반환 값을 최종 응답에 넣습니다.
**FastAPI**는 이 *임시* 응답 객체에서 상태 코드(쿠키와 헤더 포함)를 추출하여, `response_model`로 필터링된 반환 값을 포함하는 최종 응답에 넣습니다.
또한, 의존성에서도 `Response` 파라미터를 선언하고 그 안에서 상태 코드를 설정할 수 있습니다. 단, 마지막으로 설정된 상태 코드가 우선 적용된다는 점을 유의하세요.

View File

@@ -1,49 +1,51 @@
# 응답 쿠키
# 응답 쿠키 { #response-cookies }
## `Response` 매개변수 사용하기
## `Response` 매개변수 사용하기 { #use-a-response-parameter }
*경로 작동 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다.
*경로 처리 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다.
그런 다음 해당 *임시* 응답 객체에서 쿠키를 설정할 수 있습니다.
{* ../../docs_src/response_cookies/tutorial002.py hl[1,8:9] *}
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
그런 다음 필요한 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
그런 다음 일반적으로 하듯이 필요한 어떤 객체든 반환할 수 있습니다(`dict`, 데이터베이스 모델 등).
그리고 `response_model`을 선언했다면 반환한 객체를 거르고 변환하는 데 여전히 사용됩니다.
**FastAPI**는 그 *임시* 응답에서 쿠키(또한 헤더 및 상태 코드)를 추출하고, 반환 값이 포함된 최종 응답에 이를 넣습니다. 이 값은 `response_model`로 걸러지게 됩니다.
**FastAPI**는 그 *임시* 응답에서 쿠키(또한 헤더 및 상태 코드)를 추출하고, `response_model`로 필터링된 반환 값이 포함된 최종 응답에 이를 넣습니다.
또한 의존관계에서 `Response` 매개변수를 선언하고, 해당 의존성에서 쿠키(및 헤더)를 설정할 수도 있습니다.
## `Response`를 직접 반환하기
## `Response`를 직접 반환하기 { #return-a-response-directly }
코드에서 `Response`를 직접 반환할 때도 쿠키를 생성할 수 있습니다.
이를 위해 [Response를 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 설명한 대로 응답을 생성할 수 있습니다.
그런 다음 쿠키를 설정하고 반환하면 됩니다:
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
/// tip
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
/// tip | 팁
`Response` 매개변수를 사용하지 않고 응답을 직접 반환하는 경우, FastAPI는 이를 직접 반환한다는 점에 유의하세요.
따라서 데이터가 올바른 유형인지 확인해야 합니다. 예: `JSONResponse`를 반환하는 경우, JSON과 호환되는지 확인하세요.
또한 `response_model`걸러져야 할 데이터달되지 않도록 확인하세요.
또한 `response_model`필터링되어야 했던 데이터송하지 않도록 하세요.
///
### 추가 정보
### 추가 정보 { #more-info }
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette.responses import Response` 또는 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자의 편의를 위해 `fastapi.responses`로 동일한 `starlette.responses`를 제공합니다. 그러나 대부분의 응답은 Starlette에서 직접 제공됩니다.
**FastAPI**는 개발자의 편의를 위해 `fastapi.responses`로 동일한 `starlette.responses`를 제공합니다. 하지만 사용 가능한 대부분의 응답은 Starlette에서 직접 제공됩니다.
또한 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용되므로, **FastAPI**는 이를 `fastapi.Response`로도 제공합니다.
///
사용 가능한 모든 매개변수와 옵션은 <a href="https://www.starlette.dev/responses/#set-cookie" class="external-link" target="_blank">Starlette 문서</a>에서 확인할 수 있습니다.
사용 가능한 모든 매개변수와 옵션은 <a href="https://www.starlette.dev/responses/#set-cookie" class="external-link" target="_blank">Starlette 문서</a>에서 확인할 수 있습니다.

View File

@@ -1,20 +1,20 @@
# 응답을 직접 반환하기
# 응답을 직접 반환하기 { #return-a-response-directly }
**FastAPI**에서 *경로 작업(path operation)* 생성할 때, 일반적으로 `dict`, `list`, Pydantic 모델, 데이터베이스 모델 등의 데이터를 반환할 수 있습니다.
**FastAPI**에서 *경로 처리(path operation)* 생성할 때, 일반적으로 `dict`, `list`, Pydantic 모델, 데이터베이스 모델 등의 데이터를 반환할 수 있습니다.
기본적으로 **FastAPI**는 [JSON 호환 가능 인코더](../tutorial/encoder.md){.internal-link target=_blank}에 설명된 `jsonable_encoder`를 사용해 해당 반환 값을 자동으로 `JSON`으로 변환합니다.
기본적으로 **FastAPI**는 [JSON 호환 가능 인코더](../tutorial/encoder.md){.internal-link target=_blank}에 설명된 `jsonable_encoder`를 사용해 해당 반환 값을 자동으로 JSON으로 변환합니다.
그런 다음, JSON 호환 데이터(예: `dict`)를 `JSONResponse`에 넣어 사용자의 응답을 전송하는 방식으로 처리됩니다.
그런 다음, 내부적으로는 JSON 호환 데이터(예: `dict`)를 `JSONResponse`에 넣어 클라이언트로 응답을 전송하는 데 사용합니다.
그러나 *경로 작업*에서 `JSONResponse`를 직접 반환할 수도 있습니다.
하지만 *경로 처리*에서 `JSONResponse`를 직접 반환할 수도 있습니다.
예를 들어, 사용자 정의 헤더나 쿠키를 반환해야 하는 경우에 유용할 수 있습니다.
## `Response` 반환하기
## `Response` 반환하기 { #return-a-response }
사실, `Response` 또는 그 하위 클래스를 반환할 수 있습니다.
/// tip
/// tip | 팁
`JSONResponse` 자체도 `Response`의 하위 클래스입니다.
@@ -26,38 +26,40 @@ Pydantic 모델로 데이터 변환을 수행하지 않으며, 내용을 다른
이로 인해 많은 유연성을 얻을 수 있습니다. 어떤 데이터 유형이든 반환할 수 있고, 데이터 선언이나 유효성 검사를 재정의할 수 있습니다.
## `Response`에서 `jsonable_encoder` 사용하기
## `Response`에서 `jsonable_encoder` 사용하기 { #using-the-jsonable-encoder-in-a-response }
**FastAPI**는 반환하는 `Response`에 아무런 변환을 하지 않으므로, 그 내용이 준비되어 있야 합니다.
**FastAPI**는 반환하는 `Response`에 아무런 변경도 하지 않으므로, 그 내용이 준비되어 있는지 확인해야 합니다.
예를 들어, Pydantic 모델을 `dict`로 변환 `JSONResponse`에 넣지 않으면 JSON 호환 유형으로 변환된 데이터 유형(예: `datetime`, `UUID` 등)이 사용되지 않습니다.
예를 들어, Pydantic 모델을 먼저 `dict`로 변환하고 `datetime`, `UUID` 등의 모든 데이터 타입을 JSON 호환 타입으로 변환하지 않으면 Pydantic 모델을 `JSONResponse`에 넣을 수 없습니다.
이러한 경우, 데이터를 응답에 전달하기 전에 `jsonable_encoder`를 사용하여 변환할 수 있습니다:
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | 기술 세부 사항
/// note | 기술 세부사항
`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.responses``fastapi.responses`로 제공합니다. 그러나 대부분의 가능한 응답은 Starlette에서 직접 제공합니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.responses``fastapi.responses`로 제공합니다. 하지만 대부분의 사용 가능한 응답은 Starlette에서 직접 제공합니다.
///
## 사용자 정의 `Response` 반환하기
위 예제는 필요한 모든 부분을 보여주지만, 아직 유용하지는 않습니다. 사실 데이터를 직접 반환하면 **FastAPI**가 이를 `JSONResponse`에 넣고 `dict`로 변환하는 등 모든 작업을 자동으로 처리합니다.
## 사용자 정의 `Response` 반환하기 { #returning-a-custom-response }
이제, 사용자 정의 응답을 반환하는 방법을 알아보겠습니다.
위 예제는 필요한 모든 부분을 보여주지만, 아직은 그다지 유용하지 않습니다. `item`을 그냥 직접 반환했어도 **FastAPI**가 기본으로 이를 `JSONResponse`에 넣고 `dict`로 변환하는 등의 작업을 모두 수행해 주었을 것이기 때문입니다.
예를 들어 <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a> 응답을 반환하고 싶다고 가정해보겠습니다.
이제, 이를 사용해 사용자 정의 응답을 반환하는 방법을 알아보겠습니다.
예를 들어 <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a> 응답을 반환하고 싶다고 가정해 보겠습니다.
XML 내용을 문자열에 넣고, 이를 `Response`에 넣어 반환할 수 있습니다:
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
## 참고 사항 { #notes }
## 참고 사항
`Response`를 직접 반환할 때, 그 데이터는 자동으로 유효성 검사되거나, 변환(직렬화)되거나, 문서화되지 않습니다.
그러나 [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}에서 설명된 대로 문서화할 수 있습니다.
이후 단락에서 자동 데이터 변환, 문서화 등을 사용하면서 사용자 정의 `Response`를 선언하는 방법을 확인할 수 있습니다.
이후 섹션에서 자동 데이터 변환, 문서화 등을 계속 사용하면서 이러한 사용자 정의 `Response`사용하는/선언하는 방법을 확인할 수 있습니다.

View File

@@ -1,12 +1,12 @@
# 응답 헤더
# 응답 헤더 { #response-headers }
## `Response` 매개변수 사용하기
## `Response` 매개변수 사용하기 { #use-a-response-parameter }
여러분은 *경로 작동 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다 (쿠키와 같이 사용할 수 있습니다).
여러분은 *경로 처리 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다 (쿠키와 같이 사용할 수 있습니다).
그런 다음, 여러분은 해당 *임시* 응답 객체에서 헤더를 설정할 수 있습니다.
{* ../../docs_src/response_headers/tutorial002.py hl[1,7:8] *}
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
그 후, 일반적으로 사용하듯이 필요한 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
@@ -16,26 +16,26 @@
또한, 종속성에서 `Response` 매개변수를 선언하고 그 안에서 헤더(및 쿠키)를 설정할 수 있습니다.
## `Response` 직접 반환하기
## `Response` 직접 반환하기 { #return-a-response-directly }
`Response`를 직접 반환할 때에도 헤더를 추가할 수 있습니다.
[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 설명한 대로 응답을 생성하고, 헤더를 추가 매개변수로 전달하세요.
{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette.responses import Response``from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 `starlette.responses``fastapi.responses`로 개발자의 편의를 위해 직접 제공하지만, 대부분의 응답은 Starlette에서 직접 제공됩니다.
**FastAPI**는 해당 *임시* 응답에서 헤더(쿠키와 상태 코드도 포함)를 추출하여, 여러분이 반환한 값을 포함하는 최종 응답에 `response_model`로 필터링된 값을 넣습니다.
그리고 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용될 수 있으므로, **FastAPI**는 `fastapi.Response`로도 이를 제공합니다.
///
## 커스텀 헤더
## 커스텀 헤더 { #custom-headers }
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">X- 접두어를 사용하여</a> 커스텀 사설 헤더를 추가할 수 있습니다.
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">`X-` 접두어를 사용하여</a> 커스텀 사설 헤더를 추가할 수 있다는 점을 기억하세요.
하지만, 여러분이 브라우저에서 클라이언트가 볼 수 있기를 원하는 커스텀 헤더가 있는 경우, CORS 설정에 이를 추가해야 합니다([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}에서 자세히 알아보세요). `expose_headers` 매개변수를 사용하여 <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette의 CORS 설명서</a>에 문서화된 대로 설정할 수 있습니다.
하지만, 여러분이 브라우저에서 클라이언트가 볼 수 있기를 원하는 커스텀 헤더가 있는 경우, CORS 설정에 이를 추가해야 합니다([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}에서 자세히 알아보세요). <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette의 CORS 서</a>에 문서화된 `expose_headers` 매개변수를 사용하세요.

View File

@@ -1,67 +1,67 @@
# 하위 응용프로그램 - 마운트
# 하위 응용프로그램 - 마운트 { #sub-applications-mounts }
만약 각각의 독립적인 OpenAPI와 문서 UI를 갖는 두 개의 독립적인 FastAPI 응용프로그램이 필요하다면, 메인 어플리케이션에 하나 (또는 그 이상의) 하위-응용프로그램(들)마운트"해서 사용할 수 있습니다.
각각의 독립적인 OpenAPI와 문서 UI를 갖는 두 개의 독립적인 FastAPI 애플리케이션이 필요하다면, 메인 앱을 두고 하나(또는 그 이상)의 하위 응용프로그램을 "마운트"할 수 있습니다.
## **FastAPI** 응용프로그램 마운트
## **FastAPI** 애플리케이션 마운트 { #mounting-a-fastapi-application }
마운트"란 완전히 독립적인" 응용프로그램을 특정 경로에 추가하여 해당 하위 응용프로그램에 선언된 *경로 동작*을 통해 해당 경로 아래에 있는 모든 작업들을 처리할 수 있도록 하는 것을 의미합니다.
"마운트"란 완전히 "독립적인" 애플리케이션을 특정 경로에 추가하고, 그 하위 응용프로그램에 선언된 _경로 처리_로 해당 경로 아래 모든 을 처리도록 하는 것을 의미합니다.
### 최상단 응용프로그램
### 최상위 애플리케이션 { #top-level-application }
먼저, 메인, 최상단의 **FastAPI** 응용프로그램과 이것의 *경로 동작*을 생성합니다:
먼저, 메인 최상 **FastAPI** 애플리케이션과 그 *경로 처리*를 생성합니다:
{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
### 하위 응용프로그램
### 하위 응용프로그램 { #sub-application }
다음으로, 하위 응용프로그램과 이것의 *경로 동작*을 생성합니다:
다음, 하위 응용프로그램과 *경로 처리*를 생성합니다.
이 하위 응용프로그램은 또 다른 표준 FastAPI 응용프로그램입니다. 다만 이것은 “마운트입니다:
이 하위 응용프로그램은 또 다른 표준 FastAPI 애플리케이션이지만, "마운트"애플리케이션입니다:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
### 하위 응용프로그램 마운트
### 하위 응용프로그램 마운트 { #mount-the-sub-application }
최상단 응용프로그램, `app`에 하위 응용프로그램, `subapi`를 마운트합니다.
최상위 애플리케이션 `app` 하위 응용프로그램 `subapi`를 마운트합니다.
예시에서, 하위 응용프로그램션은 `/subapi` 경로에 마운트 될 것입니다:
경우 `/subapi` 경로에 마운트니다:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
### 자동으로 생성된 API 문서 확인
### 자동 API 문서 확인 { #check-the-automatic-api-docs }
이제, `uvicorn`으로 메인 응용프로그램을 실행하십시오. 당신의 파일이 `main.py`라면, 이렇게 실행합니다:
이제 파일과 함께 `fastapi` 명령을 실행하세요:
<div class="termy">
```console
$ uvicorn main:app --reload
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
그리고 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>에서 문서를 여십시오.
그리고 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>에서 문서를 여세요.
메인 응용프로그램의 *경로 동작*만을 포함하는, 메인 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다:
메인 앱의 자동 API 문서를 보게 될 것이며, 메인 앱 자체의 _경로 처리_만 포함됩니다:
<img src="https://fastapi.tiangolo.com//img/tutorial/sub-applications/image01.png">
<img src="/img/tutorial/sub-applications/image01.png">
다음으로, <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>에서 하위 응용프로그램의 문서를 여십시오.
다음, <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>에서 하위 응용프로그램의 문서를 여세요.
하위 경로 접두사 `/subapi` 아래에 선언된 *경로 동작* 을 포함하는, 하위 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다:
하위 응용프로그램의 자동 API 문서를 보게 될 것이며, 하위 경로 접두사 `/subapi` 아래에 올바르게 포함된 하위 응용프로그램 자체의 _경로 처리_만 포함됩니다:
<img src="https://fastapi.tiangolo.com//img/tutorial/sub-applications/image02.png">
<img src="/img/tutorial/sub-applications/image02.png">
두 사용자 인터페이스 중 어느 하나를 사용해야하는 경우, 브라우저 특정 응용프로그램 또는 하위 응용프로그램과 각각 통신할 수 있기 때문에 올바르게 동작할 것입니다.
두 사용자 인터페이스 중 어느 것과 상호작용을 시도하더라도 올바르게 동작할 것입니다. 브라우저가 각 특정 또는 하위 앱과 통신할 수 있기 때문입니다.
### 기술적 세부사항: `root_path`
### 기술적 세부사항: `root_path` { #technical-details-root-path }
위에 설명된 것과 같이 하위 응용프로그램을 마운트하는 경우, FastAPI는 `root_path`라고 하는 ASGI 명세의 매커니즘을 사용하여 하위 응용프로그램에 대한 마운트 경로 통신을 처리합니다.
위에 설명한 대로 하위 응용프로그램을 마운트하, FastAPI는 ASGI 명세의 메커니즘인 `root_path` 사용 하위 응용프로그램에 대한 마운트 경로를 전달하는 작업을 처리합니다.
를 통해, 하위 응용프로그램은 문서 UI를 위해 경로 접두사를 사용해야 한다는 사실을 인지합니다.
렇게 하면 하위 응용프로그램은 문서 UI를 위해 해당 경로 접두사를 사용해야 한다는 것을 알게 됩니다.
하위 응용프로그램역시 다른 하위 응용프로그램을 마운트하는 것이 가능하며 FastAPI가 모든 `root_path` 들을 자동으로 처리하기 때문에 모든 것 올바르게 동작할 것입니다.
또한 하위 응용프로그램도 자체적으로 하위 앱을 마운트할 수 있으며, FastAPI가 모든 `root_path` 자동으로 처리하기 때문에 모든 것 올바르게 동작니다.
`root_path`와 이것을 사용하는 방법에 대해서는 [프록시의 뒷단](./behind-a-proxy.md){.internal-link target=_blank} 섹션에서 배울 수 있습니다.
`root_path`와 이를 명시적으로 사용하는 방법에 대해서는 [프록시](behind-a-proxy.md){.internal-link target=_blank} 섹션에서 더 알아볼 수 있습니다.

View File

@@ -1,4 +1,4 @@
# 템플릿
# 템플릿 { #templates }
**FastAPI**와 함께 원하는 어떤 템플릿 엔진도 사용할 수 있습니다.
@@ -6,10 +6,9 @@
설정을 쉽게 할 수 있는 유틸리티가 있으며, 이를 **FastAPI** 애플리케이션에서 직접 사용할 수 있습니다(Starlette 제공).
## 의존성 설치
가상 환경을 생성하고(virtual environment{.internal-link target=_blank}), 활성화한 후 jinja2를 설치해야 합니다:
## 의존성 설치 { #install-dependencies }
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고, 활성화한 후 `jinja2`를 설치해야 합니다:
<div class="termy">
@@ -21,39 +20,38 @@ $ pip install jinja2
</div>
## 사용하기 `Jinja2Templates`
## `Jinja2Templates` 사용하기 { #using-jinja2templates }
* `Jinja2Templates`를 가져옵니다.
* 나중에 재사용할 수 있는 `templates` 객체를 생성합니다.
* 템플릿을 반환할 경로 작업`Request` 매개변수를 선언합니다.
* 템플릿을 반환할 *경로 처리*`Request` 매개변수를 선언합니다.
* 생성한 `templates`를 사용하여 `TemplateResponse`를 렌더링하고 반환합니다. 템플릿의 이름, 요청 객체 및 Jinja2 템플릿 내에서 사용될 키-값 쌍이 포함된 "컨텍스트" 딕셔너리도 전달합니다.
```Python hl_lines="4 11 15-18"
{!../../docs_src/templates/tutorial001.py!}
```
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
/// note | 참고
FastAPI 0.108.0 이전 Starlette 0.29.0에서는 `name`이 첫 번째 매개변수였습니다.
FastAPI 0.108.0 이전, Starlette 0.29.0에서는 `name`이 첫 번째 매개변수였습니다.
또한 이전 버전에서는 `request` 객체가 Jinja2의 컨텍스트에서 키-값 쌍의 일부로 전달되었습니다.
또한 이전 버전에서는 `request` 객체가 Jinja2의 컨텍스트에서 키-값 쌍의 일부로 전달되었습니다.
///
/// tip | 팁
`response_class=HTMLResponse`를 선언하면 문서 UI 응답이 HTML임을 알 수 있습니다.
`response_class=HTMLResponse`를 선언하면 문서 UI 응답이 HTML임을 알 수 있습니다.
///
/// note | 기술 세부 사항
/// note | 기술 세부사항
`from starlette.templating import Jinja2Templates`를 사용할 수도 있습니다.
**FastAPI**는 개발자를 위한 편리함으로 `fastapi.templating` 대신 `starlette.templating`을 제공합니다. 하지만 대부분의 사용 가능한 응답은 Starlette에서 직접 옵니다. `Request` 및 `StaticFiles`도 마찬가지입니다.
**FastAPI**는 개발자를 위한 편리함으로 `fastapi.templating`과 동일하게 `starlette.templating`을 제공합니다. 하지만 대부분의 사용 가능한 응답은 Starlette에서 직접 옵니다. `Request``StaticFiles`도 마찬가지입니다.
///
## 템플릿 작성하기
## 템플릿 작성하기 { #writing-templates }
그런 다음 `templates/item.html`에 템플릿을 작성할 수 있습니다. 예를 들면:
@@ -61,7 +59,7 @@ FastAPI 0.108.0 이전과 Starlette 0.29.0에서는 `name`이 첫 번째 매개
{!../../docs_src/templates/templates/item.html!}
```
### 템플릿 컨텍스트 값
### 템플릿 컨텍스트 값 { #template-context-values }
다음과 같은 HTML에서:
@@ -85,9 +83,9 @@ Item ID: {{ id }}
Item ID: 42
```
### 템플릿 `url_for` 인수
### 템플릿 `url_for` 인수 { #template-url-for-arguments }
템플릿 내에서 `url_for()`를 사용할 수도 있으며, 이는 *경로 작업 함수*에서 사용될 인수와 동일한 인수를 받습니다.
템플릿 내에서 `url_for()`를 사용할 수도 있으며, 이는 *경로 처리 함수*에서 사용될 인수와 동일한 인수를 받습니다.
따라서 다음과 같은 부분에서:
@@ -99,14 +97,15 @@ Item ID: 42
{% endraw %}
...이는 *경로 작업 함수* `read_item(id=id)`가 처리할 동일한 URL로 링크를 생성합니다.
...이는 *경로 처리 함수* `read_item(id=id)`가 처리할 동일한 URL로 링크를 생성합니다.
예를 들어, ID가 `42`일 경우, 이는 다음과 같이 렌더링됩니다:
```html
<a href="/items/42">
```
## 템플릿과 정적 파일
## 템플릿과 정적 파일 { #templates-and-static-files }
템플릿 내에서 `url_for()`를 사용할 수 있으며, 예를 들어 `name="static"`으로 마운트한 `StaticFiles`와 함께 사용할 수 있습니다.
@@ -114,7 +113,7 @@ Item ID: 42
{!../../docs_src/templates/templates/item.html!}
```
이 예제에서는 `static/styles.css`에 있는 CSS 파일에 연결될 것입니다:
이 예제에서는 다음을 통해 `static/styles.css`에 있는 CSS 파일에 링크합니다:
```CSS hl_lines="4"
{!../../docs_src/templates/static/styles.css!}
@@ -122,6 +121,6 @@ Item ID: 42
그리고 `StaticFiles`를 사용하고 있으므로, 해당 CSS 파일은 **FastAPI** 애플리케이션에서 `/static/styles.css` URL로 자동 제공됩니다.
## 더 많은 세부 사항
## 더 많은 세부 사항 { #more-details }
템플릿 테스트를 포함한 더 많은 세부 사항은 <a href="https://www.starlette.dev/templates/" class="external-link" target="_blank">Starlette의 템플릿 문서</a>를 확인하세요.

View File

@@ -1,14 +1,14 @@
# 테스트 의존성 오버라이드
# 오버라이드로 의존성 테스트하기 { #testing-dependencies-with-overrides }
## 테스트 중 의존성 오버라이드하기
## 테스트 중 의존성 오버라이드하기 { #overriding-dependencies-during-testing }
테스트를 진행하다 보면 의존성을 오버라이드해야 하는 경우가 있습니다.
테스트를 진행하다 보면 테스트 중에 의존성을 오버라이드해야 하는 경우가 있습니다.
원래 의존성을 실행하고 싶지 않을 수도 있습니다(또는 그 의존성이 가지고 있는 하위 의존성까지도 실행되지 않길 원할 수 있습니다).
대신, 테스트 동안(특정 테스트에서만) 사용될 다른 의존성을 제공하고, 원래 의존성이 사용되던 곳에서 사용할 수 있는 값을 제공하기를 원할 수 있습니다.
### 사용 사례: 외부 서비스
### 사용 사례: 외부 서비스 { #use-cases-external-service }
예를 들어, 외부 인증 제공자를 호출해야 하는 경우를 생각해봅시다.
@@ -18,11 +18,11 @@
외부 제공자를 한 번만 테스트하고 싶을 수도 있지만 테스트를 실행할 때마다 반드시 호출할 필요는 없습니다.
이 경우 해당 공급자를 호출하는 종속성을 오버라이드하고 테스트에 대해서만 모의 사용자를 반환하는 사용자 지정 종속성을 사용할 수 있습니다.
이 경우 해당 공급자를 호출하는 의존성을 오버라이드하고 테스트에 대해서만 모의 사용자를 반환하는 사용자 지정 의존성을 사용할 수 있습니다.
### `app.dependency_overrides` 속성 사용하기
### `app.dependency_overrides` 속성 사용하기 { #use-the-app-dependency-overrides-attribute }
이런 경우를 위해 **FastAPI** 응용 프로그램에는 `app.dependency_overrides`라는 속성이 있습니다. 이는 간단한 `dict`입니다.
이런 경우를 위해 **FastAPI** 애플리케이션에는 `app.dependency_overrides`라는 속성이 있습니다. 이는 간단한 `dict`입니다.
테스트를 위해 의존성을 오버라이드하려면, 원래 의존성(함수)을 키로 설정하고 오버라이드할 의존성(다른 함수)을 값으로 설정합니다.
@@ -34,7 +34,7 @@
**FastAPI** 애플리케이션 어디에서든 사용된 의존성에 대해 오버라이드를 설정할 수 있습니다.
원래 의존성은 *경로 동작 함수*, *경로 동작 데코레이터*(반환값을 사용하지 않는 경우), `.include_router()` 호출 등에서 사용될 수 있습니다.
원래 의존성은 *경로 처리 함수*, *경로 처리 데코레이터*(반환값을 사용하지 않는 경우), `.include_router()` 호출 등에서 사용될 수 있습니다.
FastAPI는 여전히 이를 오버라이드할 수 있습니다.
@@ -42,7 +42,7 @@ FastAPI는 여전히 이를 오버라이드할 수 있습니다.
그런 다음, `app.dependency_overrides`를 빈 `dict`로 설정하여 오버라이드를 재설정(제거)할 수 있습니다:
```python
```Python
app.dependency_overrides = {}
```

View File

@@ -1,5 +1,12 @@
# 이벤트 테스트: 시작 - 종료
# 이벤트 테스트: 라이프스팬 및 시작 - 종료 { #testing-events-lifespan-and-startup-shutdown }
테스트에서 이벤트 핸들러(`startup``shutdown`)를 실행해야 하는 경우, `with` 문과 함께 `TestClient`를 사용할 수 있습니다.
테스트에서 `lifespan` 실행해야 하는 경우, `with` 문과 함께 `TestClient`를 사용할 수 있습니다:
{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
["공식 Starlette 문서 사이트에서 테스트에서 라이프스팬 실행하기."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)에 대한 자세한 내용을 더 읽을 수 있습니다.
더 이상 권장되지 않는 `startup``shutdown` 이벤트의 경우, 다음과 같이 `TestClient`를 사용할 수 있습니다:
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}

View File

@@ -1,13 +1,13 @@
# WebSocket 테스트하기
# WebSocket 테스트하기 { #testing-websockets }
`TestClient`를 사용하여 WebSocket을 테스트할 수 있습니다.
같은 `TestClient`를 사용하여 WebSocket을 테스트할 수 있습니다.
이를 위해 `with` 문에서 `TestClient`를 사용하여 WebSocket에 연결합니다:
{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
/// note | 참고
자세한 내용은 Starlette의 <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank"> WebSocket 테스트</a>에 관한 설명서를 참고하시길 바랍니다.
자세한 내용은 Starlette의 <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">testing WebSockets</a> 문서를 확인하세요.
///

View File

@@ -1,10 +1,10 @@
# `Request` 직접 사용하기
# `Request` 직접 사용하기 { #using-the-request-directly }
지금까지 요청에서 필요한 부분을 각 타입으로 선언하여 사용해 왔습니다.
다음과 같은 곳에서 데이터를 가져왔습니다:
* 경로의 파라미터로부터.
* 경로를 매개변수로.
* 헤더.
* 쿠키.
* 기타 등등.
@@ -13,29 +13,29 @@
하지만 `Request` 객체에 직접 접근해야 하는 상황이 있을 수 있습니다.
## `Request` 객체에 대한 세부 사항
## `Request` 객체에 대한 세부 사항 { #details-about-the-request-object }
**FastAPI**는 실제로 내부에 **Starlette**을 사용하며, 그 위에 여러 도구를 덧붙인 구조입니다. 따라서 여러분이 필요할 때 Starlette의 <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request`</a> 객체를 직접 사용할 수 있습니다.
`Request` 객체에서 데이터를 직접 가져오는 경우(예: 본문을 읽기)에는 FastAPI가 해당 데이터를 검증하거나 변환하지 않으며, 문서화(OpenAPI를 통한 문서 자동화(로 생성된) API 사용자 인터페이스)도 되지 않니다.
또한 이는 `Request` 객체에서 데이터를 직접 가져오는 경우(예: 본문을 읽기) FastAPI가 해당 데이터를 검증하거나 변환하지 않으며, 문서화(OpenAPI를 통한 자동 API 사용자 인터페이스)도 되지 않는다는 의미이기도 합니다.
그러나 다른 매개변수(예: Pydantic 모델을 사용한 본문)는 여전히 검증, 변환, 주석 추가 등이 이루어집니다.
하지만 특정한 경우에는 `Request` 객체에 직접 접근하는 것이 유용할 수 있습니다.
하지만 특정한 경우에는 `Request` 객체를 가져오는 것이 유용할 수 있습니다.
## `Request` 객체를 직접 사용하기
## `Request` 객체를 직접 사용하기 { #use-the-request-object-directly }
여러분이 클라이언트의 IP 주소/호스트 정보를 *경로 작동 함수* 내부에서 가져와야 한다고 가정해 보겠습니다.
여러분이 클라이언트의 IP 주소/호스트 정보를 *경로 처리 함수* 내부에서 가져와야 한다고 가정해 보겠습니다.
이를 위해서는 요청에 직접 접근해야 합니다.
{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
*경로 작동 함수* 매개변수를 `Request` 타입으로 선언하면 **FastAPI**가 해당 매개변수에 `Request` 객체를 전달하는 것을 알게 됩니다.
*경로 처리 함수* 매개변수를 `Request` 타입으로 선언하면 **FastAPI**가 해당 매개변수에 `Request`를 전달하는 것을 알게 됩니다.
/// tip | 팁
이 경우, 요청 매개변수와 함께 경로 매개변수를 선언한 것을 볼 수 있습니다.
이 경우, 요청 매개변수 옆에 경로 매개변수를 선언하고 있다는 점을 참고하세요.
따라서, 경로 매개변수는 추출되고 검증되며 지정된 타입으로 변환되고 OpenAPI로 주석이 추가됩니다.
@@ -43,14 +43,14 @@
///
## `Request` 설명서
## `Request` 설명서 { #request-documentation }
여러분은 `Request` 객체에 대한 더 자세한 내용을 <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">공식 Starlette 설명서 사이트</a>에서 읽어볼 수 있습니다.
여러분은 <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">공식 Starlette 설명서 사이트`Request` 객체</a>에 대한 더 자세한 내용을 읽어볼 수 있습니다.
/// note | 기술 세부사항
`from starlette.requests import Request`를 사용할 수도 있습니다.
**FastAPI**는 여러분(개발자)를 위한 편의를 위해 이를 직접 제공하지만, 실제로는 Starlette에서 가져온 것입니다.
**FastAPI**는 여러분(개발자)를 위한 편의를 위해 이를 직접 제공하지만, Starlette에서 직접 가져온 것입니다.
///

View File

@@ -1,10 +1,10 @@
# WebSockets
# WebSockets { #websockets }
여러분은 **FastAPI**에서 <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a>를 사용할 수 있습니다.
## `WebSockets` 설치
## `websockets` 설치 { #install-websockets }
[가상 환경](../virtual-environments.md){.internal-link target=_blank)를 생성하고 활성화한 다음, `websockets`를 설치하세요:
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, `websockets`("WebSocket" 프로토콜을 쉽게 사용할 수 있게 해주는 Python 라이브러리)를 설치하세요:
<div class="termy">
@@ -16,13 +16,13 @@ $ pip install websockets
</div>
## WebSockets 클라이언트
## WebSockets 클라이언트 { #websockets-client }
### 프로덕션 환경에서
### 프로덕션 환경에서 { #in-production }
여러분의 프로덕션 시스템에서는 React, Vue.js 또는 Angular와 같은 최신 프레임워크로 생성된 프런트엔드를 사용하고 있을 가능성이 높습니다.
백엔드와 WebSockets을 사용해 통신하려면 아마도 프런트엔드의 유틸리티를 사용할 것입니다.
그리고 백엔드와 WebSockets을 사용해 통신하려면 아마도 프런트엔드의 유틸리티를 사용할 것입니다.
또는 네이티브 코드로 WebSocket 백엔드와 직접 통신하는 네이티브 모바일 응용 프로그램을 가질 수도 있습니다.
@@ -30,23 +30,23 @@ $ pip install websockets
---
하지만 이번 예제에서는 일부 자바스크립트를 포함한 간단한 HTML 문서를 사용하겠습니다. 모든 것을 긴 문자열 안에 넣습니다.
하지만 이번 예제에서는 일부 자바스크립트를 포함한 매우 간단한 HTML 문서를 사용하겠습니다. 모든 것을 긴 문자열 안에 넣습니다.
물론, 이는 최적의 방법이 아니며 프로덕션 환경에서는 사용하지 않을 것입니다.
프로덕션 환경에서는 위에서 설명한 옵션 중 하나를 사용하는 것이 좋습니다.
프로덕션 환경에서는 위에서 설명한 옵션 중 하나를 사용할 것입니다.
그러나 이는 WebSockets의 서버 측에 집중하고 동작하는 예제를 제공하는 가장 간단한 방법입니다:
{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
## `websocket` 생성하기
## `websocket` 생성하기 { #create-a-websocket }
**FastAPI** 응용 프로그램에서 `websocket`을 생성합니다:
{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette.websockets import WebSocket`을 사용할 수도 있습니다.
@@ -54,17 +54,17 @@ $ pip install websockets
///
## 메시지를 대기하고 전송하기
## 메시지를 대기하고 전송하기 { #await-for-messages-and-send-messages }
WebSocket 경로에서 메시지를 대기(`await`)하고 전송할 수 있습니다.
{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
여러분은 이진 데이터, 텍스트, JSON 데이터를 받을 수 있고 전송할 수 있습니다.
## 시도해보기
## 시도해보기 { #try-it }
파일 이름이 `main.py`라고 가정하고 응용 프로그램을 실행합니다:
파일 이름이 `main.py`라고 가정하고 다음으로 응용 프로그램을 실행합니다:
<div class="termy">
@@ -76,7 +76,7 @@ $ fastapi dev main.py
</div>
브라우저에서 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>을 열어보세요.
브라우저에서 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>을 세요.
간단한 페이지가 나타날 것입니다:
@@ -86,7 +86,7 @@ $ fastapi dev main.py
<img src="/img/tutorial/websockets/image02.png">
**FastAPI** WebSocket 응용 프로그램이 응답을 돌려줄 것입니다:
그리고 WebSockets가 포함된 **FastAPI** 응용 프로그램이 응답을 돌려줄 것입니다:
<img src="/img/tutorial/websockets/image03.png">
@@ -94,9 +94,9 @@ $ fastapi dev main.py
<img src="/img/tutorial/websockets/image04.png">
모든 메시지는 동일한 WebSocket 연결을 사용합니다.
그리고 모든 메시지는 동일한 WebSocket 연결을 사용합니다.
## `Depends` 및 기타 사용하기
## `Depends` 및 기타 사용하기 { #using-depends-and-others }
WebSocket 엔드포인트에서 `fastapi`에서 다음을 가져와 사용할 수 있습니다:
@@ -107,21 +107,21 @@ WebSocket 엔드포인트에서 `fastapi`에서 다음을 가져와 사용할
* `Path`
* `Query`
이들은 다른 FastAPI 엔드포인트/*경로 작동*과 동일하게 동작합니다:
이들은 다른 FastAPI 엔드포인트/*경로 처리*와 동일하게 동작합니다:
{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *}
/// info | 정보
WebSocket에서는 `HTTPException`을 발생시키는 것하지 않습니다. 대신 `WebSocketException`을 발생시킵니다.
WebSocket이기 때문`HTTPException`을 발생시키는 것하지 않습니다. 대신 `WebSocketException`을 발생시킵니다.
명세서에 정의된 <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">유효한 코드</a>를 사용하여 종료 코드를 설정할 수 있습니다.
///
### 종속성을 가진 WebSockets 테스트
### 종속성을 가진 WebSockets 시도해보기 { #try-the-websockets-with-dependencies }
파일 이름이 `main.py`라고 가정하고 응용 프로그램을 실행합니다:
파일 이름이 `main.py`라고 가정하고 다음으로 응용 프로그램을 실행합니다:
<div class="termy">
@@ -133,9 +133,9 @@ $ fastapi dev main.py
</div>
브라우저에서 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>을 열어보세요.
브라우저에서 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>을 세요.
다음과 같은 값을 설정할 수 있습니다:
여기에서 다음을 설정할 수 있습니다:
* 경로에 사용된 "Item ID".
* 쿼리 매개변수로 사용된 "Token".
@@ -146,13 +146,13 @@ $ fastapi dev main.py
///
WebSocket에 연결하고 메시지를 전송 및 수신할 수 있습니다:
렇게 하면 WebSocket에 연결하고 메시지를 전송 및 수신할 수 있습니다:
<img src="/img/tutorial/websockets/image05.png">
## 연결 해제 및 다중 클라이언트 처리
## 연결 해제 및 다중 클라이언트 처리 { #handling-disconnections-and-multiple-clients }
WebSocket 연결이 닫히면, `await websocket.receive_text()``WebSocketDisconnect` 예외를 발생시킵니다. 이를 잡아 처리할 수 있습니다:
WebSocket 연결이 닫히면, `await websocket.receive_text()``WebSocketDisconnect` 예외를 발생시킵니다. 그러면 이 예제처럼 이를 잡아 처리할 수 있습니다.
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
@@ -160,7 +160,7 @@ WebSocket 연결이 닫히면, `await websocket.receive_text()`가 `WebSocketDis
* 여러 브라우저 탭에서 앱을 엽니다.
* 각 탭에서 메시지를 작성합니다.
* 한 탭을 닫아보세요.
* 그런 다음 탭 중 하나를 닫아보세요.
`WebSocketDisconnect` 예외가 발생하며, 다른 모든 클라이언트가 다음과 같은 메시지를 수신합니다:
@@ -170,17 +170,17 @@ Client #1596980209979 left the chat
/// tip | 팁
응용 프로그램은 여러 WebSocket 연결에 메시지를 브로드캐스트하는 방법을 보여주는 간단한 예제입니다.
은 여러 WebSocket 연결에 메시지를 처리하고 브로드캐스트하는 방법을 보여주는 최소한의 간단한 예제입니다.
그러나 모든 것을 메모리의 단일 리스트로 처리하므로, 프로세스가 실행 중인 동안만 동작하며 단일 프로세스에서만 작동합니다.
하지만 모든 것을 메모리의 단일 리스트로 처리하므로, 프로세스가 실행 중인 동안만 동작하며 단일 프로세스에서만 작동한다는 점을 기억하세요.
FastAPI와 쉽게 통합할 수 있으면서 더 견고하고 Redis, PostgreSQL 등을 지원하는 도구를 찾고 있다면, <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>를 확인하세요.
FastAPI와 쉽게 통합할 수 있으면서 더 견고하고 Redis, PostgreSQL 등을 지원하는 도구가 필요하다면, <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>를 확인하세요.
///
## 추가 정보
## 추가 정보 { #more-info }
다음 옵션에 대한 자세한 내용을 보려면 Starlette의 문서를 확인하세요:
다음 옵션에 대해 더 알아보려면 Starlette의 문서를 확인하세요:
* <a href="https://www.starlette.dev/websockets/" class="external-link" target="_blank">`WebSocket` 클래스</a>.
* <a href="https://www.starlette.dev/endpoints/#websocketendpoint" class="external-link" target="_blank">클래스 기반 WebSocket 처리</a>.

View File

@@ -1,10 +1,10 @@
# WSGI 포함하기 - Flask, Django 그 외
# WSGI 포함하기 - Flask, Django 그 외 { #including-wsgi-flask-django-others }
[서브 응용 프로그램 - 마운트](sub-applications.md){.internal-link target=_blank}, [프록시 뒤편에서](behind-a-proxy.md){.internal-link target=_blank}에서 보았듯이 WSGI 응용 프로그램들을 다음과 같이 마운트 할 수 있습니다.
[서브 응용 프로그램 - 마운트](sub-applications.md){.internal-link target=_blank}, [프록시 뒤편에서](behind-a-proxy.md){.internal-link target=_blank}에서 보았듯이 WSGI 응용 프로그램들을 마운트 할 수 있습니다.
`WSGIMiddleware`를 사용하여 WSGI 응용 프로그램(예: Flask, Django 등)을 감쌀 수 있습니다.
이를 위해 `WSGIMiddleware`를 사용 WSGI 응용 프로그램(예: Flask, Django 등)을 감쌀 수 있습니다.
## `WSGIMiddleware` 사용하기
## `WSGIMiddleware` 사용하기 { #using-wsgimiddleware }
`WSGIMiddleware`를 불러와야 합니다.
@@ -12,9 +12,9 @@
그 후, 해당 경로에 마운트합니다.
{* ../../docs_src/wsgi/tutorial001.py hl[2:3,23] *}
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
## 확인하기
## 확인하기 { #check-it }
이제 `/v1/` 경로에 있는 모든 요청은 Flask 응용 프로그램에서 처리됩니다.
@@ -26,7 +26,7 @@
Hello, World from Flask!
```
그리고 다음으로 이동하면 <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a> Flask의 응답을 볼 수 있습니다:
그리고 다음으로 이동하면 <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a> **FastAPI**의 응답을 볼 수 있습니다:
```JSON
{

View File

@@ -1,10 +1,10 @@
# 벤치마크
# 벤치마크 { #benchmarks }
독립적인 TechEmpower 벤치마크에 따르면 **FastAPI** 애플리케이션이 Uvicorn을 사용하여 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">가장 빠른 Python 프레임워크 중 하나</a>로 실행되며, Starlette와 Uvicorn 자체(내부적으로 FastAPI가 사용하는 도구)보다 조금 아래에 위치합니다.
독립적인 TechEmpower 벤치마크에 따르면 **FastAPI** 애플리케이션이 Uvicorn을 사용하여 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">사용 가능한 가장 빠른 Python 프레임워크 중 하나</a>로 실행되며, Starlette와 Uvicorn 자체(내부적으로 FastAPI가 사용하는 도구)보다 조금 아래에 위치합니다.
그러나 벤치마크와 비교를 확인할 때 다음 사항을 염두에 두어야 합니다.
## 벤치마크와 속도
## 벤치마크와 속도 { #benchmarks-and-speed }
벤치마크를 확인할 때, 일반적으로 여러 가지 유형의 도구가 동등한 것으로 비교되는 것을 볼 수 있습니다.
@@ -16,7 +16,7 @@
* **Uvicorn**: ASGI 서버
* **Starlette**: (Uvicorn 사용) 웹 마이크로 프레임워크
* **FastAPI**: (Starlette 사용) API 구축 위한 데이터 검증 등 여러 추가 기능이 포함된 API 마이크로 프레임워크
* **FastAPI**: (Starlette 사용) 데이터 검증 등 API 구축하기 위한 여러 추가 기능이 포함된 API 마이크로 프레임워크
* **Uvicorn**:
* 서버 자체 외에는 많은 추가 코드가 없기 때문에 최고의 성능을 발휘합니다.
@@ -29,6 +29,6 @@
* **FastAPI**:
* Starlette가 Uvicorn을 사용하므로 Uvicorn보다 빨라질 수 없는 것과 마찬가지로, **FastAPI**는 Starlette를 사용하므로 더 빠를 수 없습니다.
* FastAPI는 Starlette에 추가적으로 더 많은 기능을 제공합니다. API를 구축할 때 거의 항상 필요한 데이터 검증 및 직렬화와 같은 기능들이 포함되어 있습니다. 그리고 이를 사용하면 문서 자동화 기능도 제공됩니다(문서 자동화는 응용 프로그램 실행 시 오버헤드를 추가하지 않고 시작 시 생성됩니다).
* FastAPI를 사용하지 않고 직접 Starlette(또는 Sanic, Flask, Responder 등)를 사용했다면 데이터 검증 및 직렬화를 직접 구현해야 합니다. 따라서 최종 응용 프로그램은 FastAPI를 사용한 것과 동일한 오버헤드를 가지게 될 것입니다. 많은 경우 데이터 검증 및 직렬화가 응용 프로그램에서 작성된 코드 중 가장 많은 부분을 차지합니다.
* 따라서 FastAPI를 사용함으로써 개발 시간, 버그, 코드 라인을 줄일 수 있으며, FastAPI를 사용하지 않았을 때와 동일하거나 더 나은 성능을 얻을 수 있니다(코드에서 모두 구현해야 하기 때문에).
* FastAPI를 비교할 때는 Flask-apispec, NestJS, Molten 등 데이터 검증, 직렬화 및 문서화가 통합된 자동 데이터 검증, 직렬화 및 문서화를 제공하는 웹 응용 프로그램 프레임워크(또는 도구 집합)와 비교하세요.
* FastAPI를 사용하지 않고 직접 Starlette(또는 다른 도구, 예: Sanic, Flask, Responder 등)를 사용했다면 데이터 검증 및 직렬화를 직접 구현해야 합니다. 따라서 최종 응용 프로그램은 FastAPI를 사용한 것과 동일한 오버헤드를 가지게 될 것입니다. 많은 경우 데이터 검증 및 직렬화가 응용 프로그램에서 작성된 코드 중 가장 많은 부분을 차지합니다.
* 따라서 FastAPI를 사용함으로써 개발 시간, 버그, 코드 라인을 줄일 수 있으며, FastAPI를 사용하지 않았을 때와 동일한 성능(또는 더 나은 성능)을 얻을 수 있을 것입니다(코드에서 모두 구현해야 하기 때문에).
* FastAPI를 비교할 때는 Flask-apispec, NestJS, Molten 등 데이터 검증, 직렬화 및 문서화를 제공하는 웹 애플리케이션 프레임워크(또는 도구 집합)와 비교하세요. 통합된 자동 데이터 검증, 직렬화 및 문서화를 제공하는 프레임워크입니다.

View File

@@ -1,13 +1,24 @@
# FastAPI를 클라우드 제공업체에서 배포하기
# 클라우드 제공업체에서 FastAPI 배포하기 { #deploy-fastapi-on-cloud-providers }
사실상 거의 **모든 클라우드 제공업체**를 사용하여 여러분의 FastAPI 애플리케이션을 배포할 수 있습니다.
대부분의 경우, 주요 클라우드 제공업체에서는 FastAPI를 배포할 수 있도록 가이드를 제공합니다.
## 클라우드 제공업체 - 후원자들
## FastAPI Cloud { #fastapi-cloud }
몇몇 클라우드 제공업체들은 [**FastAPI를 후원하며**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, 이를 통해 FastAPI와 FastAPI **생태계**가 지속적이고 건전한 **발전**을 할 수 있습니다.
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>**는 **FastAPI**를 만든 동일한 작성자와 팀이 구축했습니다.
이는 FastAPI **커뮤니티** (여러분)에 대한 진정한 헌신을 보여줍니다. 그들은 여러분에게 **좋은 서비스**를 제공할 뿐 만이 아니라 여러분이 **훌륭하고 건강한 프레임워크인** FastAPI 를 사용하길 원하기 때문입니다. 🙇
최소한의 노력으로 API **구축**, **배포**, **접근**하는 과정을 간소화합니다.
아래와 같은 서비스를 사용해보고 각 서비스의 가이드를 따를 수도 있습니다.
FastAPI로 앱을 빌드할 때의 동일한 **개발자 경험**을 클라우드에 **배포**하는 데에도 제공합니다. 🎉
FastAPI Cloud는 *FastAPI and friends* 오픈 소스 프로젝트의 주요 후원자이자 자금 제공자입니다. ✨
## 클라우드 제공업체 - 후원자들 { #cloud-providers-sponsors }
다른 몇몇 클라우드 제공업체들도 ✨ [**FastAPI를 후원합니다**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨. 🙇
가이드를 따라 하고 서비스를 사용해보기 위해 이들도 고려해볼 수 있습니다:
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>
* <a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" class="external-link" target="_blank">Railway</a>

View File

@@ -1,17 +1,17 @@
# 컨테이너의 FastAPI - 도커
# 컨테이너의 FastAPI - 도커 { #fastapi-in-containers-docker }
FastAPI 플리케이션을 배포할 때 일반적인 접근 방법은 **리눅스 컨테이너 이미지**를 생성하는 것입니다. 이 방법은 주로 <a href="https://www.docker.com/" class="external-link" target="_blank">**도커**</a>를 사용해 이루어집니다. 그런 다음 해당 컨테이너 이미지를 몇가지 방법으로 배포할 수 있습니다.
FastAPI 플리케이션을 배포할 때 일반적인 접근 방법은 **리눅스 컨테이너 이미지**를 빌드하는 것입니다. 보통 <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>를 사용해 수행합니다. 그런 다음 해당 컨테이너 이미지를 몇 가지 가능한 방법 중 하나로 배포할 수 있습니다.
리눅스 컨테이너를 사용하는 데에는 **보안**, **반복 가능성**, **단순함** 장점이 있습니다.
리눅스 컨테이너를 사용하 **보안**, **재현 가능성**, **단순함** 여러 장점이 있습니다.
/// tip | 팁
시간에 쫓기고 있고 이미 이런들을 알고 있다면 [`Dockerfile`👇](#build-a-docker-image-for-fastapi)로 점프할 수 있습니다.
시간이 없고 이미 이런 내용들을 알고 계신가요? 아래의 [`Dockerfile` 👇](#build-a-docker-image-for-fastapi)로 이동하세요.
///
<details>
<summary>도커파일 미리보기 👀</summary>
<summary>Dockerfile Preview 👀</summary>
```Dockerfile
FROM python:3.9
@@ -24,128 +24,125 @@ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# If running behind a proxy like Nginx or Traefik add --proxy-headers
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"]
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
```
</details>
## 컨테이너란
## 컨테이너란 { #what-is-a-container }
컨테이너(주로 리눅스 컨테이너)는 어플리케이션의 의존성과 필요한 파일모두 패키징하는 매우 **가벼운** 방법입니다. 컨테이너는 같은 시스템에 있는 다른 컨테이너(다른 플리케이션이나 요소들)와 독립적으로 유지됩니다.
컨테이너(주로 리눅스 컨테이너)는 모든 의존성과 필요한 파일을 포함해 애플리케이션을 패키징하면서, 같은 시스템 다른 컨테이너(다른 플리케이션이나 컴포넌트)와는 분리된 상태로 유지할 수 있는 매우 **가벼운** 방법입니다.
리눅스 컨테이너는 호스트(머신, 가상 머신, 클라우드 서버 등)와 같은 리눅스 커널을 사용해 실행됩니다. 이말은 리눅스 컨테이너가 (전체 운영체제를 모방하는 다른 가상 머신교했을 때) 매우 가볍다는 것을 의미합니다.
리눅스 컨테이너는 호스트(머신, 가상 머신, 클라우드 서버 등)와 같은 리눅스 커널을 사용해 실행됩니다. 즉, 전체 운영체제를 에뮬레이션하는 완전한 가상 머신해 매우 가볍습니다.
이 방법을 통해, 컨테이너는 직접 프로세스를 실행하는 것과 비슷한 정도의 **적은 자원**을 소비합니다 (가상 머신은 훨씬 많은 자원을 소비할 것입니다).
이 방식으로 컨테이너는 프로세스를 직접 실행하는 것과 비슷한 수준의 **적은 자원**을 소비합니다(가상 머신은 훨씬 많은 자원을 소비니다).
컨테이너는 또한 그들만의 **독립** 실행 프로세스 (일반적으로 하나의 프로세스로 충분합니다), 파일 시스템, 그리고 네트워크를 가지므로 배포, 보안, 개발 및 기타 과정을 단순화 합니다.
또한 컨테이너는 자체적인 **격리** 실행 프로세스(보통 하나의 프로세스), 파일 시스템, 네트워크를 가지므로 배포, 보안, 개발 을 단순화합니다.
## 컨테이너 이미지란
## 컨테이너 이미지란 { #what-is-a-container-image }
**컨테이너**는 **컨테이너 이미지** 실행한 것 입니다.
**컨테이너**는 **컨테이너 이미지**에서 실행니다.
컨테이너 이미지 컨테이너에 필요한 모든 파일, 환경 변수 그리고 디폴트 명령/프로그램의 **정적** 버전입니다. 여기서 **정적**이란 말은 컨테이너 **이미지**가 작동되거나 행되지 않으며, 단지 패키 파일과 메타 데이터라는 것을 의미합니다.
컨테이너 이미지 컨테이너에 있어야 하는 모든 파일, 환경 변수, 기본 명령/프로그램의 **정적** 버전입니다. 여기서 **정적**이라는 것은 컨테이너 **이미지**가 실행 중이거나 행되는 것이 아니라, 패키징된 파일과 메타데이터일 뿐이라는 뜻입니다.
저장된 정적 텐츠인 **컨테이너 이미지**와 대조되게, **컨테이너** 보통 실행될 수 있는 작동 인스턴스를 의미합니다.
저장된 정적 텐츠인 "**컨테이너 이미지**"달리, "**컨테이너**"는 보통 실행 중인 인스턴스, 즉 **실행되는** 대상을 의미합니다.
**컨테이너**가 (**컨테이너 이미지**로 부터) 시작되고 실행되면, 컨테이너는 파일이나 환경 변수 생성하거나 변경할 수 있습니다. 이러한 변화는 오직 컨테이너에만 존재하며, 기반이 되는 컨테이너 이미지에는 지속되지 않습니다 (즉 디스크에 저장되지 않습니다).
**컨테이너**가 시작되어 실행 중이면(**컨테이너 이미지**로부터 시작됨) 파일, 환경 변수 등을 생성하거나 변경할 수 있습니다. 이러한 변경은 해당 컨테이너에만 존재하며, 기반이 되는 컨테이너 이미지에는 지속되지 않습니다(디스크에 저장되지 않습니다).
컨테이너 이미지는 **프로그램** 파일과 텐츠, `python`과 어떤 파일 `main.py`에 비할 수 있습니다.
컨테이너 이미지는 **프로그램** 파일과 그 콘텐츠, 예를 들어 `python`과 어떤 파일 `main.py`에 비할 수 있습니다.
그리고 (**컨테이너 이미지**와 대비해서) **컨테이너**는 이미지의 실제 실행 인스턴스로 **프로세스**에 비할 수 있습니다. 사실, 컨테이너는 **프로세스 러닝** 있을 때만 실행됩니다 (그리고 보통 하나의 프로세스 입니다). 컨테이너 내부에 실행되는 프로세스가 없으면 종료됩니다.
그리고 **컨테이너** 자체는(**컨테이너 이미지**와 달리) 이미지의 실제 실행 인스턴스로 **프로세스**에 비할 수 있습니다. 실제로 컨테이너는 **실행 중인 프로세스** 있을 때만 실행됩니다(보통 단일 프로세스입니다). 컨테이너 내부에 실행 중인 프로세스가 없으면 컨테이너는 중지됩니다.
## 컨테이너 이미지
## 컨테이너 이미지 { #container-images }
도커는 **컨테이너 이미지**와 **컨테이너**를 생성하고 관리하는 주요 도구 중 하나가 되어왔습니다.
Docker는 **컨테이너 이미지**와 **컨테이너**를 생성하고 관리하는 주요 도구 중 하나니다.
그리고 <a href="https://hub.docker.com/" class="external-link" target="_blank">도커 허브</a>에 다양한 도구, 환경, 데이터베이스, 그리고 어플리케이션에 대해 미리 만들어진 **공식 컨테이너 이미지**가 공개되어 있습니다.
또한 <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a>에 다양한 도구, 환경, 데이터베이스, 플리케이션을 위한 미리 만들어진 **공식 컨테이너 이미지**가 공개되어 있습니다.
예를 들어, 공식 <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">파이썬 이미지</a>가 있습니다.
예를 들어, 공식 <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">Python Image</a>가 있습니다.
또한 다른 대상, 예를 들면 데이터베이스를 위한 이미지도 있습니다:
그리고 데이터베이스 등 다양한 용도의 다른 이미지도 많이 있습니다. 예를 들면:
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a> 등
미리 만들어진 컨테이너 이미지를 사용하면 서로 다른 도구들을 **결합**하기 쉽습니다. 대부분의 경우에, **공식 이미지** 사용하고 환경 변수를 통해 설정할 수 있습니다.
미리 만들어진 컨테이너 이미지를 사용하면 서로 다른 도구 **결합**하고 사용하기가 매우 쉽습니다. 예를 들어 새로운 데이터베이스를 시험해 볼 때도 그렇습니다. 대부분의 경우 **공식 이미지** 사용하고, 환경 변수로 설정만 하면 됩니다.
런 방법으로 대부분의 경우 컨테이너와 도커에 대해 배울 수 있으며 다양한 도구와 요소들에 대한 지식을 재사용할 수 있습니다.
렇게 하면 많은 경우 컨테이너와 Docker를 학습하고, 그 지식을 여러 다른 도구와 컴포넌트에 재사용할 수 있습니다.
따라서, 서로 다른 **다중 컨테이너**를 생성한 다음 이들을 연결할 수 있습니다. 예를 들어 데이터베이스, 파이썬 어플리케이션, 리액트 프론트엔드 플리케이션을 사용하는 웹 서버에 대한 컨테이너를 만들어 이들의 내부 네트워크로 각 컨테이너를 연결할 수 있습니다.
따라서 데이터베이스, Python 애플리케이션, React 프론트엔드 플리케이션이 있는 웹 서버 등 서로 다른 것들을 담은 **여러 컨테이너**를 실행하고 내부 네트워크를 통해 연결할 수 있습니다.
모든 컨테이너 관리 시스템(도커나 쿠버네티스)은 이러한 네트워킹 특성을 포함하고 있습니다.
Docker나 Kubernetes 같은 모든 컨테이너 관리 시스템에는 이러한 네트워킹 기능이 통합되어 있습니다.
## 컨테이너와 프로세스
## 컨테이너와 프로세스 { #containers-and-processes }
**컨테이너 이미지**는 보통 **컨테이너** 시작하기 위해 필요한 메타데이터와 디폴트 커맨드/프로그램과 그 프로그램에 전달하기 위한 파라미터들을 포함합니다. 이는 커맨드 라인에서 프로그램을 실행할 때 필요한 값들과 유사합니다.
**컨테이너 이미지**는 보통 **컨테이너** 시작될 때 실행되어야 하는 기본 프로그램/명령과 해당 프로그램에 전달할 매개변수를 메타데이터에 포함합니다. 커맨드 라인에서 실행할 때와 매우 유사합니다.
**컨테이너**가 시작되면, 해당 커맨드/프로그램 실행니다 (그러나 다른 커맨드/프로그램을 실행하도록 오버라이드 할 수 있습니다).
**컨테이너**가 시작되면 해당 명령/프로그램 실행니다(다만 오버라이드하여 다른 명령/프로그램을 실행하 할 수 있습니다).
컨테이너는 **메인 프로세스**(커맨드 또는 프로그램) 실행되는 동안 실행됩니다.
컨테이너는 **메인 프로세스**(명령 또는 프로그램) 실행되는 동안 실행됩니다.
컨테이너는 일반적으로 **단일 프로세스**를 가지고 있지만, 메인 프로세스 서브 프로세스를 시작하는 것도 가능하며, 이 방법으로 하나의 컨테이너에 **다중 프로세스**를 가질 수 있습니다.
컨테이너는 보통 **단일 프로세스**를 가지지만, 메인 프로세스에서 서브프로세스를 시작할 수도 있으며, 그러면 같은 컨테이너에 **여러 프로세스**가 존재하게 됩니다.
그러나 **최소 하나의 실행중인 프로세스**를 가지지 않고서는 실행중인 컨테이너를 가질 수 없습니다. 만약 메인 프로세스가 중되면, 컨테이너도 중됩니다.
하지만 **최소 하나의 실행 중인 프로세스** 없이 실행 중인 컨테이너를 가질 수 없습니다. 메인 프로세스가 중되면 컨테이너도 중됩니다.
## FastAPI를 위한 도커 이미지 빌드하기
## FastAPI를 위한 도커 이미지 빌드하기 { #build-a-docker-image-for-fastapi }
이제 무언가를 만들어 봅시다! 🚀
좋습니다, 이제 무언가를 만들어 봅시다! 🚀
**공식 파이썬** 이미지에 기반하여, FastAPI를 위한 **도커 이미지**를 **처음부터** 생성하는 방법을 보겠습니다.
**공식 Python** 이미지에 기반하여 FastAPI**Docker 이미지**를 **처음부터** 빌드하는 방법을 보여드리겠습니다.
**대부분의 경우**에 다음과 같은 것들을 하게 됩니다. 예를 들면:
이는 **대부분의 경우**에 하고 싶은 방식입니다. 예를 들면:
* **쿠버네티스** 또는 유사한 도구 사용하기
* **라즈베리 파이**로 실행하기
* 컨테이너 이미지를 실행할 클라우드 서비스 사용하기
* **Kubernetes** 또는 유사한 도구 사용할 때
* **Raspberry Pi**에서 실행할 때
* 컨테이너 이미지를 대신 실행해주는 클라우드 서비스 사용할 때
### 요구 패키지
### 패키지 요구사항 { #package-requirements }
일반적으로는 어플리케이션의 특정 파일을 위한 **패키지 요구 조건**이 있을 것입니다.
보통 애플리케이션의 **패키지 요구사항**을 어떤 파일에 적어 둡니다.
그 요구 조건을 **설치**하는 방법은 여러분이 사용하는 도구에 따라 다를 것입니다.
이는 주로 그 요구사항을 **설치**하는 사용하는 도구에 따라 달라집니다.
가장 일반적인 방법은 패키지 이름과 버전이 줄 별로 기록된 `requirements.txt` 파일을 만드는 것입니다.
가장 일반적인 방법은 패키지 이름과 버전을 한 줄에 하나씩 적어 둔 `requirements.txt` 파일을 사용하는 것입니다.
버전 범위를 설정하기 위해서는 [FastAPI 버전들에 대하여](versions.md){.internal-link target=_blank}에 쓰여진 것과 같은 아이디어를 사용니다.
버전 범위를 설정할 때는 [FastAPI 버전들에 대하여](versions.md){.internal-link target=_blank}에서 읽은 것과 같은 아이디어를 사용하면 됩니다.
예를 들어, `requirements.txt` 파일은 다음과 같을 수 있습니다:
예를 들어 `requirements.txt` 다음과 같을 수 있습니다:
```
fastapi>=0.68.0,<0.69.0
pydantic>=1.8.0,<2.0.0
uvicorn>=0.15.0,<0.16.0
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
```
그리고 일반적으로 패키지 종속성은 `pip`로 설치합니다. 예를 들:
그리고 보통 `pip` 패키지 의존성을 설치합니다. 예를 들:
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic uvicorn
Successfully installed fastapi pydantic
```
</div>
/// info | 정보
패키지 종속성을 정의하고 설치하기 위한 방법과 도구는 다양합니다.
나중에 아래 세션에서 Poetry를 사용한 예시를 보이겠습니다. 👇
패키지 의존성을 정의하고 설치하는 다른 형식과 도구도 있습니다.
///
### **FastAPI** 코드 생성하기
### **FastAPI** 코드 생성하기 { #create-the-fastapi-code }
* `app` 디렉터리를 생성하고 이동합니다.
* 빈 파일 `__init__.py`을 생성합니다.
* 다음과 같은 `main.py`을 생성합니다:
* `app` 디렉터리를 만들고 들어갑니다.
* 빈 파일 `__init__.py`를 만듭니다.
* 다음 내용으로 `main.py` 파일을 만듭니다:
```Python
from typing import Union
@@ -165,79 +162,109 @@ def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
### 도커파일
### Dockerfile { #dockerfile }
이제 같은 프로젝트 디렉터리에 다음과 같은 파일 `Dockerfile`을 생성합니다:
이제 같은 프로젝트 디렉터리에 다음 내용으로 `Dockerfile` 파일을 만듭니다:
```{ .dockerfile .annotate }
# (1)
# (1)!
FROM python:3.9
# (2)
# (2)!
WORKDIR /code
# (3)
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (5)
# (5)!
COPY ./app /code/app
# (6)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
# (6)!
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. 공식 파이썬 베이스 이미지에서 시작합니다.
1. 공식 Python 베이스 이미지에서 시작합니다.
2. 현재 워킹 디렉터리를 `/code`로 설정합니다.
2. 현재 작업 디렉터리를 `/code`로 설정합니다.
여기에 `requirements.txt` 파일과 `app` 디렉터리를 위치시킬 것입니다.
여기에 `requirements.txt` 파일과 `app` 디렉터리를 것입니다.
3. 요구 조건과 파일을 `/code` 디렉터리로 복사합니다.
3. 요구사항 파일을 `/code` 디렉터리로 복사합니다.
처음에는 **오직** 요구 조건이 필요한 파일만 복사하고, 이외의 코드는 그대로 둡니다.
처음에는 요구사항 파일만 **단독으로** 복사하고, 나머지 코드는 복사하지 않습니다.
이 파일 **자주 바뀌지 않기 때문에**, 도커는 파일을 탐지하여 이 단계 **캐시**를 사용하 다음 단계에서도 캐시를 사용할 수 있도록 합니다.
이 파일 **자주 바뀌지 않기** 때문에 Docker는 이를 감지하여 이 단계에서 **캐시**를 사용하고, 다음 단계에서도 캐시를 사용할 수 있게 해줍니다.
4. 요구 조건 파일에 있는 패키지 종속성을 설치합니다.
4. 요구사항 파일에 있는 패키지 의존성을 설치합니다.
`--no-cache-dir` 옵션은 `pip`에게 다운로드한 패키지들을 로컬 환경에 저장하지 않도록 전달합니다. 이는 마치 같은 패키지를 설치하기 위해 오직 `pip`만 다시 실행하면 될 것 같지만, 컨테이너 작업하는 경우 그렇지니다.
`--no-cache-dir` 옵션은 `pip` 다운로드한 패키지 로컬에 저장하지 않도록 합니다. 이는 `pip`가 같은 패키지를 설치하기 위해 다시 실행될 때만 의미가 있지만, 컨테이너 작업에서는 그렇지 않기 때문입니다.
/// note | 노트
/// note | 참고
`--no-cache-dir` 는 오직 `pip` 관련되어 있으며, 도커나 컨테이너와는 무관합니다.
`--no-cache-dir` `pip`에만 관련되어 있으며 Docker나 컨테이너와는 관련이 없습니다.
///
`--upgrade` 옵션은 `pip`에게 설치된 패키지들을 업데이트하도록 합니다.
`--upgrade` 옵션은 이미 설치된 패키지가 있다면 `pip`가 이를 업그레이드하도록 합니다.
이전 단계에서 파일을 복사한 것이 **도커 캐시**에 의해 탐지되기 때문에, 이 단계에서도 가능한 한 **도커 캐시**를 사용하게 됩니다.
이전 단계에서 파일을 복사한 것이 **Docker 캐시**에 의해 감지될 수 있으므로, 이 단계에서도 가능하면 **Docker 캐시를 사용**합니다.
이 단계에서 캐시를 사용하면 **매번** 모든 종속성을 다운로드고 설치할 필요가 없어, 개발 과정에서 이미지를 지속적으로 생성하는 데에 드는 **시간**을 많이 **절약**할 수 있습니다.
이 단계에서 캐시를 사용하면 개발 중에 이미지를 반복해서 빌드할 때, 의존성을 **매번 다운로드고 설치하는** 대신 많은 **시간**을 **절약**할 수 있습니다.
5. `/code` 디렉터리 `./app` 디렉터리 복사합니다.
5. `./app` 디렉터리 `/code` 디렉터리 안으로 복사합니다.
**자주 변경되는** 모든 코드를 포함하고 있기 때문에, 도커 **캐시**는 이 단계나 **이후 단계에서** 잘 사용되지 않습니다.
이 디렉터리에는 **가장 자주 변경되는** 코드가 모두 포함되어 있으므로, Docker **캐시**는 이 단계나 **이후 단계들**에서는 쉽게 사용되지 않습니다.
그러므로 컨테이너 이미지 빌드 시간을 최적화하기 위해 `Dockerfile`의 **거의 끝 부분**에 입력하는 것이 중요합니다.
따라서 컨테이너 이미지 빌드 시간을 최적화하려면 `Dockerfile`의 **끝부분 근처**에 는 것이 중요합니다.
6. `uvicorn` 서버를 실행하기 위해 **커맨드** 설정합니다.
6. 내부적으로 Uvicorn을 사용하는 `fastapi run`을 사용하도록 **명령** 설정합니다.
`CMD`는 문자열 리스트를 입력받고, 각 문자열은 커맨드 라인의 각 줄에 입력할 문자열입니다.
`CMD`는 문자열 리스트를 받으며, 각 문자열은 커맨드 라인에서 공백으로 구분해 입력하는 항목들입니다.
커맨드는 **현재 워킹 디렉터리**에서 실행되며, 이는 위에서 `WORKDIR /code`로 설정한 `/code` 디렉터리와 같습니다.
프로그램이 `/code`에서 시작하고 그 속에 `./app` 디렉터리가 여러분의 코드와 함께 들어있기 때문에, **Uvicorn**은 이를 보고 `app`을 `app.main`으로부터 **불러 올** 것입니다.
명령은 **현재 작업 디렉터리**에서 실행되며, 이는 위에서 `WORKDIR /code`로 설정한 `/code` 디렉터리와 같습니다.
/// tip | 팁
각 코드 라인을 코드의 숫자 버블을 클릭하여 리뷰할 수 있습니다. 👆
코드의 숫자 버블을 클릭해 각 줄이 하는 일을 확인하세요. 👆
///
이제 여러분은 다음과 같은 디렉터리 구조를 가지고 있을 것입니다:
/// warning | 경고
아래에서 설명하는 것처럼 `CMD` 지시어는 **항상** **exec form**을 사용해야 합니다.
///
#### `CMD` 사용하기 - Exec Form { #use-cmd-exec-form }
Docker 지시어 <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a>는 두 가지 형식으로 작성할 수 있습니다:
✅ **Exec** form:
```Dockerfile
# ✅ Do this
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Shell** form:
```Dockerfile
# ⛔️ Don't do this
CMD fastapi run app/main.py --port 80
```
FastAPI가 정상적으로 종료(graceful shutdown)되고 [lifespan 이벤트](../advanced/events.md){.internal-link target=_blank}가 트리거되도록 하려면, 항상 **exec** form을 사용하세요.
자세한 내용은 <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">shell and exec form에 대한 Docker 문서</a>를 참고하세요.
이는 `docker compose`를 사용할 때 꽤 눈에 띌 수 있습니다. 좀 더 기술적인 상세 내용은 Docker Compose FAQ 섹션을 참고하세요: <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">Why do my services take 10 seconds to recreate or stop?</a>.
#### 디렉터리 구조 { #directory-structure }
이제 다음과 같은 디렉터리 구조가 되어야 합니다:
```
.
@@ -248,51 +275,51 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
└── requirements.txt
```
#### TLS 종료 프록시의 배후
#### TLS 종료 프록시의 배후 { #behind-a-tls-termination-proxy }
만약 여러분이 컨테이너를 Nginx 또는 Traefik 같은 TLS 종료 프록시 (로드 밸런서) 뒤에서 실행하고 있다면, `--proxy-headers` 옵션을 더하는 것이 좋습니다. 이 옵션은 Uvicorn에게 어플리케이션이 HTTPS 등의 뒤에서 실행되고 있으므로 프록시에서 전송된 헤더를 신뢰할 수 있다고 알립니다.
Nginx Traefik 같은 TLS 종료 프록시(로드 밸런서) 뒤에서 컨테이너를 실행하고 있다면 `--proxy-headers` 옵션을 추가하세요. 이 옵션은 (FastAPI CLI를 통해) Uvicorn에게 해당 프록시가 보낸 헤더를 신뢰하도록 하여, 애플리케이션이 HTTPS 뒤에서 실행 중임을 알게 합니다.
```Dockerfile
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### 도커 캐시
#### 도커 캐시 { #docker-cache }
이 `Dockerfile`에는 중요한 트릭이 있는데, 처음에는 **의존성이 있는 파일만** 복사하고, 나머지 코드는 그대로 둡니다. 왜 이런 방법을 써야하는지 설명하겠습니다.
이 `Dockerfile`에는 중요한 트릭이 있습니다. 먼저 **의존성 파일만** 복사하고, 나머지 코드는 복사하지 않는 것입니다. 왜 그런지 설명하겠습니다.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
도커와 다른 도구들은 컨테이너 이미지를 **증가하는 방식으로 빌드**합니다. `Dockerfile`의 맨 윗 부분부터 시작해, 레이어 위에 새로운 레이어를 더하는 방식으로, `Dockerfile`의 각 지시 사항으로 부터 생성된 어떤 파일이든 더해갑니다.
Docker와 다른 도구들은 `Dockerfile`의 위에서부터 시작해, 각 지시어가 만든 파일을 포함하며 **레이어를 하나씩 위에 쌓는 방식으로** 컨테이너 이미지를 **점진적으로** 빌드합니다.
도커 그리고 이와 유사한 도구들은 이미지 생성 시에 **내부 캐시** 사용합니다. 만약 어떤 파일이 마지막으로 컨테이너 이미지를 빌드부터 바뀌지 않았다면, 파일을 다시 복사하로운 레이어를 처음부터 생성하는 것이 아니라, 마지막에 생성했던 **같은 레이어를 재사용**합니다.
Docker와 유사한 도구들은 이미지를 빌드할 때 **내부 캐시** 사용합니다. 어떤 파일이 마지막으로 컨테이너 이미지를 빌드했을 때부터 바뀌지 않았다면, 파일을 다시 복사하 새 레이어를 처음부터 만드는 대신, 이전에 만든 **같은 레이어를 재사용**합니다.
단지 파일 복사를 지양하는 것으로 효율이 많이 향상되는 것은 아니지만, 단계에서 캐시를 사용했기 때문에, **다음 단계에서도 마찬가지로 캐시를 사용**할 수 있습니다. 예를 들어, 다음과 같 의존성을 설치하는 지시 사항을 위한 캐시를 사용할 수 있습니다:
파일 복사를 하는 것으로 큰 개선이 생기지는 않을 수 있지만, 해당 단계에서 캐시를 사용했기 때문에 **다음 단계에서도 캐시를 사용할 수** 있습니다. 예를 들어 다음과 같 의존성을 설치하는 지시어에서 캐시를 사용할 수 있습니다:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
패키지를 포함하는 파일은 **자주 변경되지 않습니다**. 따라서 해당 파일만 복사하므로서, 도커는 그 단계 **캐시를 사용**할 수 있습니다.
패키지 요구사항 파일은 **자주 변경되지 않습니다**. 따라서 파일만 복사하면 Docker는 그 단계에서 **캐시를 사용할 수** 있습니다.
다음으로, 도커는 **다음 단계에서** 의존성을 다운로드하고 설치하는 **캐시를 사용**할 수 있게 됩니다. 바로 이 과정에서 우리는 **많은 시간을 절약**하게 됩니다. ✨ ...그리고 기다리 지루도 피할 수 있습니다. 😪😆
리고 Docker는 그 다음 단계에서 의존성을 다운로드하고 설치할 때도 **캐시를 사용할 수** 있습니다. 바로 여기에서 **많은 시간을 절약**하게 됩니다. ✨ ...그리고 기다리 지루해지는 것도 피할 수 있습니다. 😪😆
패키지 의존성을 다운로드고 설치하는 데는 ** 분이 걸릴 수 있지만**, **캐시**를 사용하면 최대 **만에** 끝낼 수 있습니다.
패키지 의존성을 다운로드고 설치하는 데는 ****이 걸릴 수 있지만, **캐시**를 사용하면 많아야 ** 초**니다.
또한 여러분이 개발 과정에서 코드 변경 사항이 반영되었는지 확인하기 위해 컨테이너 이미지를 계속해서 빌드하면, 절약된 시간은 적되어 더욱 커질 것입니다.
또한 개발 중에 코드 변경 사항이 동작하는지 확인하기 위해 컨테이너 이미지를 계속 빌드하게 되므로, 이렇게 절약되는 시간은 적되어 상당히 커집니다.
리고 나서 `Dockerfile`의 거의 끝 부분에서, 모든 코드를 복사합니다. 이것이 **가장 빈번하게 변경**되는 부분이며, 대부분의 경우에 이 다음 단계에서는 캐시를 사용할 수 없기 때문에 가장 마지막에 둡니다.
다음 `Dockerfile`의 끝부분 근처에서 모든 코드를 복사합니다. 이 부분은 **가장 자주 변경되는** 부분이므로, 거의 항상 이 단계 이후에는 캐시를 사용할 수 없기 때문에 끝부분에 둡니다.
```Dockerfile
COPY ./app /code/app
```
### 도커 이미지 생성하기
### 도커 이미지 생성하기 { #build-the-docker-image }
이제 모든 파일이 제자리에 있으니, 컨테이너 이미지를 빌드합니다.
이제 모든 파일이 제자리에 있으니 컨테이너 이미지를 빌드해봅시다.
* (여러분의 `Dockerfile` `app` 디렉터리가 위치한) 프로젝트 디렉터리로 이동합니다.
* 프로젝트 디렉터리로 이동합니다(`Dockerfile`이 있고 `app` 디렉터리를 포함하는 위치).
* FastAPI 이미지를 빌드합니다:
<div class="termy">
@@ -307,13 +334,13 @@ $ docker build -t myimage .
/// tip | 팁
끝에 있는 `.` 에 주목합시다. 이는 `./`와 동하며, 도커에게 컨테이너 이미지를 빌드하기 위한 디렉터리를 알려줍니다.
끝에 있는 `.`에 주목하세요. 이는 `./`와 동하며, Docker에게 컨테이너 이미지를 빌드할 때 사용할 디렉터리를 알려줍니다.
이 경우에는 현재 디렉터리(`.`)와 같습니다.
이 경우 현재 디렉터리(`.`)니다.
///
### 도커 컨테이너 시작하기
### 도커 컨테이너 시작하기 { #start-the-docker-container }
* 여러분의 이미지에 기반하여 컨테이너를 실행합니다:
@@ -325,35 +352,35 @@ $ docker run -d --name mycontainer -p 80:80 myimage
</div>
## 체크하기
## 확인하기 { #check-it }
여러분의 도커 컨테이너 URL에서 실행 사항을 체크할 수 있니다. 예를 들어: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> 또는 <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (또는 동일하게, 여러분의 도커 호스트를 용해서 체크할 수 있습니다).
Docker 컨테이너 URL에서 확인할 수 있어야 합니다. 예를 들어: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> 또는 <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a>(또는 Docker 호스트를 용해 동등하게 확인할 수 있습니다).
아래와 비슷한 것을 보게 될 것입니다:
아래와 같은 것을 보게 될 것입니다:
```JSON
{"item_id": 5, "q": "somequery"}
```
## 인터랙티브 API 문서
## 인터랙티브 API 문서 { #interactive-api-docs }
이제 여러분은 <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> 또는 <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a>로 이동할 수 있습니다(또는, 여러분의 도커 호스트를 이용할 수 있습니다).
이제 <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> 또는 <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a>(또는 Docker 호스트를 사용해 동등하게 접근)로 이동할 수 있습니다.
여러분은 자동으로 생성된 인터랙티브 API(<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>에서 제공)를 볼 수 있습니다:
자동으로 생성된 인터랙티브 API 문서(<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> 제공)를 볼 수 있습니다:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
## 대안 API 문서
## 대안 API 문서 { #alternative-api-docs }
또한 여러분은 <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> 또는 <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a>으로 이동할 수 있습니다(또는, 여러분의 도커 호스트를 이용할 수 있습니다).
또한 <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> 또는 <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a>(또는 Docker 호스트를 사용해 동등하게 접근)로 이동할 수 있습니다.
여러분은 자동으로 생성된 대안 문서(<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>에서 제공)를 볼 수 있습니다:
대안 자동 문서(<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> 제공)를 볼 수 있습니다:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## 단일 파일 FastAPI로 도커 이미지 생성하기
## 단일 파일 FastAPI로 도커 이미지 빌드하기 { #build-a-docker-image-with-a-single-file-fastapi }
만약 여러분의 FastAPI가 하나의 파일이라면, 예를 들어 `./app` 디렉터리 없이 `main.py` 파일만으로 이루어져 있다면, 파일 구조는 다음과 유사할 것입니다:
FastAPI가 단일 파일(예: `./app` 디렉터리 없이 `main.py`만 있는 경우)이라면, 파일 구조는 다음과 같을 수 있습니다:
```
.
@@ -362,7 +389,7 @@ $ docker run -d --name mycontainer -p 80:80 myimage
└── requirements.txt
```
러면 여러분들은 `Dockerfile` 내에 있는 파일을 복사하기 위해 그저 상응하는 경로를 바꾸기만 하면 됩니다:
런 다음 `Dockerfile`에서 해당 파일을 복사하도록 경로만 맞게 변경하면 됩니다:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.9
@@ -373,359 +400,221 @@ COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)
# (1)!
COPY ./main.py /code/
# (2)
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
# (2)!
CMD ["fastapi", "run", "main.py", "--port", "80"]
```
1. `main.py` 파일을 `/code` 디렉터리로 곧바로 복사합니다(`./app` 디렉터리는 고려하지 않습니다).
1. `main.py` 파일을 `/code` 디렉터리로 직접 복사합니다(`./app` 디렉터리 없이).
2. Uvicorn을 실행해 `app` 객체를 (`app.main` 대신) `main`으로 부터 불러오도록 합니다.
2. 단일 파일 `main.py`에 있는 애플리케이션을 제공(serve)하기 위해 `fastapi run`을 사용합니다.
그 다음 Uvicorn 커맨드를 조정해서 FastAPI 객체를 불러오는데 `app.main` 대신에 새로운 모듈 `main`을 사용하도록 합니다.
`fastapi run`에 파일을 전달하면, 이것이 패키지의 일부가 아닌 단일 파일이라는 것을 자동으로 감지하고, 어떻게 임포트해서 FastAPI 앱을 제공할지 알아냅니다. 😎
## 배포 개념
## 배포 개념 { #deployment-concepts }
이제 컨테이너의 측면에서 [배포 개념](concepts.md){.internal-link target=_blank}에서 다루었던 것과 같은 배포 개념에 대해 이야기해 보겠습니다.
컨테이너 관점에서 같은 [배포 개념](concepts.md){.internal-link target=_blank}들을 다시 이야기해 봅시다.
컨테이너는 주로 플리케이션을 빌드하고 배포하기 위한 과정을 단순화하는 도구이지만, **배포 개념**에 대한 특정 접근을 강하지 않기 때문에 가능한 배포 전략에는 여러가지가 있습니다.
컨테이너는 주로 플리케이션의 **빌드 및 배포** 과정을 단순화하는 도구이지만, 이러한 **배포 개념**을 처리하는 특정 접근 방식을 강하지으며, 가능한 전략은 여러 가지니다.
**좋은 소식**은 서로 다른 전략들을 포괄하는 배포 개념이 있다는 점입니다. 🎉
**좋은 소식**은 각 전략마다 모든 배포 개념을 다룰 수 있는 방법이 있다는 점입니다. 🎉
컨테이너 측면에서 **배포 개념**을 리뷰해 보겠습니다:
컨테이너 관점에서 **배포 개념**살펴봅시다:
* HTTPS
* 구동하기
* 시작 시 자동 실행
* 재시작
* 복제 (실행 중인 프로세스 수)
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작하기 전 단계
* 시작 전 사전 단계
## HTTPS
## HTTPS { #https }
만약 우리가 FastAPI 플리케이션을 위한 **컨테이너 이미지**에만 집중한다면 (그리고 나중에 실행 **컨테이너**), HTTPS는 일반적으로 다른 도구에 의해 **외부적으로** 다루어질 것 입니다.
FastAPI 플리케이션 **컨테이너 이미지**(그리고 나중에 실행 중인 **컨테이너**)에만 집중한다면, HTTPS는 보통 다른 도구에 의해 **외부적으로** 처리됩니다.
**HTTPS**와 **인증서**의 **자동** 취득을 다루는 것은 다른 컨테이너가 될 수 있는데, 예를 들어 <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>을 사용하는 것입니다.
예를 들어 <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>을 사용하는 다른 컨테이너가 **HTTPS**와 **인증서**의 **자동** 획득을 처리할 수 있습니다.
/// tip | 팁
Traefik은 도커, 쿠버네티스, 그리고 다른 도구와 통합되어 있어 여러분의 컨테이너를 포함하는 HTTPS를 셋업하고 설정하는 것이 매우 쉽습니다.
Traefik은 Docker, Kubernetes 등과 통합되어 있어, 이를 사용해 컨테이너에 HTTPS를 설정하고 구성하기가 매우 쉽습니다.
///
대안적으로, HTTPS 클라우드 제공자에 의해 서비스의 일환으로 다루어질 수도 있습니다 (이때도 어플리케이션은 여전히 컨테이너에서 실행될 것입니다).
또는 HTTPS 클라우드 제공자 서비스의 일부로 처리할 수도 있습니다(애플리케이션은 여전히 컨테이너에서 실행니다).
## 구동과 재시작
## 시작 시 자동 실행과 재시작 { #running-on-startup-and-restarts }
여러분의 컨테이너를 **시작하고 실행하는** 데에 일반적으로 사용되는 도구는 따로 있습니다.
보통 컨테이너를 **시작하고 실행**하는 역할을 담당하는 다른 도구가 있습니다.
이는 **도커** 자체일 수도 있고, **도커 컴포즈**, **쿠버네티스**, **클라우드 서비스** 등이 될 수 있습니다.
직접 **Docker**일 수도 있고, **Docker Compose**, **Kubernetes**, **클라우드 서비스** 등 있습니다.
대부분 (또는 전체) 경우, 컨테이너를 구동하거나 고장시에 재시작하도록 하는 간단한 옵션이 있습니다. 예를 들어, 도커에서는, 커맨드 라인 옵션 `--restart` 입니다.
대부분(또는 전부)의 경우, 시작 시 컨테이너를 실행하고 실패 시 재시작을 활성화하는 간단한 옵션이 있습니다. 예를 들어 Docker에서는 커맨드 라인 옵션 `--restart`입니다.
컨테이너를 사용하지 않고서는, 어플리케이션을 구동하고 재시작하는 것이 매우 번거롭고 어려울 수 있습니다. 하지만 **컨테이너를 사용한다면** 대부분의 경우에 이런 기능 기본으로 포함되어 있습니다. ✨
컨테이너를 사용하지 않으면 애플리케이션을 시작 시 자동 실행하고 재시작까지 구성하는 것이 번거롭고 어습니다. 하지만 **컨테이너로 작업할 때** 대부분의 경우 기능 기본으로 포함되어 있습니다. ✨
## 복제 - 프로세스 개수
## 복제 - 프로세스 개수 { #replication-number-of-processes }
만약 여러분이 **쿠버네티스**와 머신 <abbr title="A group of machines that are configured to be connected and work together in some way.">클러스터</abbr>, 도커 스왐 모드, 노마드, 또는 다른 여러 머신 위에 분산 컨테이너를 관리하는 복잡한 시스템을 다루고 있다면, 여러분은 각 컨테이너에서 (워커와 함께 사용하는 Gunicorn 같은) **프로세스 매니저** 대신 **클러스터 레벨**에서 **복제를 다루**고 싶을 것입니다.
**Kubernetes**, Docker Swarm Mode, Nomad 등의 복잡한 시스템으로 여러 머신에 분산된 컨테이너를 관리하는 <abbr title="A group of machines that are configured to be connected and work together in some way.">cluster</abbr>를 사용한다면, 각 컨테이너에서(**워커를 사용하는 Uvicorn** 같은) **프로세스 매니저**를 쓰는 대신, **클러스터 레벨**에서 **복제를 처리**고 싶을 가능성이 큽니다.
쿠버네티스와 같은 분산 컨테이너 관리 시스템 중 일부는 일반적으로 들어오는 요청에 대한 **로드 밸런싱**을 지원하면서 **컨테이너 복제**를 다루는 통합된 방법을 가지고 있습니다. 모두 **클러스터 레벨**에서 말이죠.
Kubernetes 같은 분산 컨테이너 관리 시스템은 보통 들어오는 요청에 대한 **로드 밸런싱**을 지원하면서도, **컨테이너 복제**를 처리하는 통합된 방법을 가지고 있습니다. 모두 **클러스터 레벨**에서.
런 경우에, 여러분은 [위에서 묘사된 것](#dockerfile)처럼 **처음부터 도커 이미지를** 빌드해서, 의존성을 설치하고, Uvicorn 워커를 관리하는 Gunicorn 대신 **단일 Uvicorn 프로세스**를 실행하고 싶을 것입니다.
런 경우에는 [위에서 설명한 대로](#dockerfile) 의존성을 설치하고, 여러 Uvicorn 워커를 사용하는 대신 **단일 Uvicorn 프로세스**를 실행하는 **처음부터 만든 Docker 이미지**를 사용하는 것이 좋을 것입니다.
### 로드 밸런서
### 로드 밸런서 { #load-balancer }
컨테이너로 작업할 때, 여러분은 일반적으로 **메인 포트의 상황을 감지하는** 요소를 가지고 있을 것입니다. 이는 **HTTPS**를 다루는 **TLS 종료 프록시**와 같은 다른 컨테이너일 수도 있고, 유사한 다른 도구일 수도 있습니다.
컨테이너를 사용할 때는 보통 **메인 포트에서 대기(listening)하는** 컴포넌트가 있습니다. **HTTPS**를 처리하기 위한 **TLS 종료 프록시** 역할을 하는 다른 컨테이너일 수도 있고, 유사한 도구일 수도 있습니다.
요소가 요청의 **로드**를 읽어들이고 각 워커에게 (바라건대) **균형적으로** 분한다면, 이 요소는 일반적으로 **로드 밸런서**라고 불립니다.
컴포넌트가 요청의 **부하(load)**를 받아 워커들에 (가능하면) **균형 있게** 분한다면, 보통 **로드 밸런서**라고 부릅니다.
/// tip | 팁
HTTPS를 위해 사용된 **TLS 종료 프록시** 요소 또한 **로드 밸런서**가 될 수 있습니다.
HTTPS에 사용되는 동일한 **TLS 종료 프록시** 컴포넌트가 **로드 밸런서**이기도 한 경우가 많습니다.
///
또한 컨테이너로 작업할 때, 컨테이너를 시작하고 관리하기 위해 사용한 것과 동일한 시스템은 이미 해당 **로드 밸런서**로 부터 여러분의 앱에 해당하는 컨테이너로 **네트워크 통신**(예를 들어, HTTP 요청)을 전하는 내부적인 도구를 가지고 있을 것입니다 (여기서도 로드 밸런서는 **TLS 종료 프록시**일 수 있습니다).
또한 컨테이너로 작업할 때, 를 시작하고 관리하 시스템은 이미 해당 **로드 밸런서**(또는 **TLS 종료 프록시**)에서 여러분의 앱이 있는 컨테이너로 **네트워크 통신**(예: HTTP 요청)을 전하는 내부 도구를 가지고 있습니다.
### 하나의 로드 밸런서 - 다중 워커 컨테이너
### 하나의 로드 밸런서 - 여러 워커 컨테이너 { #one-load-balancer-multiple-worker-containers }
**쿠버네티스**나 또는 다른 분산 컨테이너 관리 시스템으로 작업할 때, 시스템 내부 네트워킹 메커니즘을 이용함으로써 메인 **포트**를 감지하고 있는 단일 **로드 밸런서** 여러분의 앱에서 실행되고 있는 **여러개의 컨테이너** 통신(요청)을 전할 수 있게 됩니다.
**Kubernetes** 같은 분산 컨테이너 관리 시스템에서는 내부 네트워킹 메커니즘을 통해, 메인 **포트**에서 대기하는 단일 **로드 밸런서** 여러분의 앱 실행는 **여러 컨테이너** 통신(요청)을 전할 수 있니다.
여러분의 앱에서 실행되고 있는 각각의 컨테이너는 일반적으로 **하나의 프로세스**질 것입니다 (예를 들어, FastAPI 플리케이션에서 실행되는 하나의 Uvicorn 프로세스처럼). 이 컨테이너들은 모두 같은 것을 실행하는 점에서 **동일한 컨테이너**이지만, 프로세스, 메모리 등은 공유하지 않습니다. 이 방식으로 여러분은 CPU의 **서로 다른 코어** 또는 **서로 다른 머신** **병렬화**하는 이점을 얻을 수 있습니다.
앱을 실행하는 각 컨테이너는 보통 **프로세스 하나만** 가니다(예: FastAPI 플리케이션 실행하는 Uvicorn 프로세스). 모두 같은 것을 실행하는 **동일한 컨테이너**이지만, 각자 고유한 프로세스, 메모리 등을 가집니다. 이렇게 하면 CPU의 **서로 다른 코어** 또는 **서로 다른 머신**에서 **병렬화** 이점을 얻을 수 있습니다.
또한 **로드 밸런서**가 있는 분산 컨테이너 시스템은 여러분의 앱에 있는 컨테이너 각각에 **차례대로 요청을 분산**시킬 것 입니다. 따라서 각 요청은 여러분의 앱에서 실행는 여러개의 **복제된 컨테이너** 중 하나에 의해 다루어질 것 입니다.
그리고 **로드 밸런서**가 있는 분산 컨테이너 시스템은 여러분의 앱을 실행하는 각 컨테이너에 **번갈아가며** 요청을 **분산**니다. 따라서 각 요청은 여러분의 앱 실행는 여러 **복제된 컨테이너** 중 하나에서 처리될 수 있습니다.
그리고 일반적으로 **로드 밸런서**는 여러분의 클러스터에 있는 *다른* 앱으로 가는 요청들도 다룰 수 있으며 (예를 들어, 다른 도메인으로 가거나 다른 URL 경로 접두사경우), 통신을 클러스터에 있는 *바로 그 다른* 플리케이션으로 제대로 전할 수 있습니다.
또한 보통 이 **로드 밸런서**는 클러스터 내 *다른* 앱으로 가는 요청(예: 다른 도메인, 또는 다른 URL 경로 접두사 아래로 가는 요청)도 처리할 수 있으며, 통신을 클러스터에서 실행 중인 *그 다른* 플리케이션의 올바른 컨테이너로 전할 수 있습니다.
### 단일 프로세스를 가지는 컨테이너
### 컨테이너당 하나의 프로세스 { #one-process-per-container }
이 시나리오의 경우, 여러분은 이미 클러스터 레벨에서 복제를 다루고 있을 것이므로 **컨테이너 당 단일 (Uvicorn) 프로세스**를 가지고자 할 것입니다.
이 시나리오에서는 이미 클러스터 레벨에서 복제를 처리하고 있으므로, **컨테이너당 단일 (Uvicorn) 프로세스**를 두는 것이 좋을 가능성이 큽니다.
따라서, 여러분은 Gunicorn 이나 Uvicorn 워커, 또는 Uvicorn 워커를 사용하는 Uvicorn 매니저와 같은 프로세스 매니저를 가지고 싶어하지 **않을** 것입니다. 여러분은 컨테이너 당 **단일 Uvicorn 프로세스**를 가지고 싶어할 것입니다 (그러나 아마도 다중 컨테이너를 가질 것입니다).
따라서 이 경우 컨테이너에서 `--workers` 커맨드 라인 옵션 같은 방식으로 여러 워커를 두고 싶지는 **않을** 것입니다. 컨테이너당 **단일 Uvicorn 프로세스**만 두고(하지만 컨테이너는 여러 개일 수 있습니다) 싶을 것입니다.
이미 여러분이 클러스터 시스템을 관리하고 있으므로, (Uvicorn 워커를 관리하는 Gunicorn 이나 Uvicorn 처럼) 컨테이너 내에 다른 프로세스 매니저를 가지는 것은 **불필요한 복잡성**만 더하게 될 것입니다.
컨테이너 내부에 (여러 워커를 위한) 또 다른 프로세스 매니저를 는 것은, 이미 클러스터 시스템에서 처리하고 있는 **불필요한 복잡성**만 추가할 가능성이 큽니다.
### 다중 프로세스를 가지는 컨테이너와 특수한 경우
### 여러 프로세스를 가 컨테이너와 특수한 경우 { #containers-with-multiple-processes-and-special-cases }
당연한 말이지만, 여러분이 내부적으로 **Uvicorn 워커 프로세스**를 시작하는 **Gunicorn 프로세스 매니저**를 가지는 단일 컨테이너를 원하는 **특수한 경우**도 있을 것입니다.
물론 컨테이너 하나에 여러 **Uvicorn 워커 프로세스**를 두고 싶을 수 있는 **특수한 경우**도 있니다.
그런 경우에, 여러분들은 **Gunicorn**을 프로세스 매니저로 포함하는 **공식 도커 이미지**를 사용할 수 있습니다. 이 프로세스 매니저는 다중 **Uvicorn 워커 프로세스들**을 실행하며, 디폴트 세팅으로 현재 CPU 코어에 기반하여 자동으로 워커 수를 조정합니다. 이 사항에 대해서는 아래의 [Gunicorn과 함께하는 공식 도커 이미지 - Uvicorn](#official-docker-image-with-gunicorn-uvicorn)에서 더 다루겠습니다.
그런 경우에는 `--workers` 커맨드 라인 옵션을 사용해 실행할 워커 수를 설정할 수 있습니다:
이런 경우에 해당하는 몇가지 예시가 있습니다:
```{ .dockerfile .annotate }
FROM python:3.9
#### 단순한 앱
WORKDIR /code
만약 여러분의 어플리케이션이 **충분히 단순**해서 (적어도 아직은) 프로세스 개수를 파인-튠 할 필요가 없거나 클러스터가 아닌 **단일 서버**에서 실행하고 있다면, 여러분은 컨테이너 내에 프로세스 매니저를 사용하거나 (공식 도커 이미지에서) 자동으로 설정되는 디폴트 값을 사용할 수 있습니다.
COPY ./requirements.txt /code/requirements.txt
#### 도커 구성
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
여러분은 **도커 컴포즈**로 (클러스터가 아닌) **단일 서버로** 배포할 수 있으며, 이 경우에 공유된 네트워크와 **로드 밸런싱**을 포함하는 (도커 컴포즈로) 컨테이너의 복제를 관리하는 단순한 방법이 없을 수도 있습니다.
COPY ./app /code/app
그렇다면 여러분은 **프로세스 매니저**와 함께 내부에 **몇개의 워커 프로세스들**을 시작하는 **단일 컨테이너**를 필요로 할 수 있습니다.
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
#### Prometheus와 다른 이유들
1. 여기서는 `--workers` 커맨드 라인 옵션으로 워커 수를 4로 설정합니다.
여러분은 **단일 프로세스**를 가지는 **다중 컨테이너** 대신 **다중 프로세스**를 가지는 **단일 컨테이너**를 채택하는 **다른 이유**가 있을 수 있습니다.
이런 방식이 의미가 있을 수 있는 예시는 다음과 같습니다:
예를 들어 (여러분의 장치 설정에 따라) Prometheus 익스포터와 같이 같은 컨테이너에 들어오는 **각 요청에 대해** 접근권한을 가지는 도구를 사용할 수 있습니다.
#### 단순한 앱 { #a-simple-app }
이 경우에 여러분이 **여러개의 컨테이너들**을 가지고 있다면, Prometheus가 **메트릭을 읽어 들일 때**, 디폴트로 **매번 하나의 컨테이너**(특정 리퀘스트를 관리하는 바로 그 컨테이너)로 부터 읽어들일 것입니다. 이는 모든 복제된 컨테이너에 대해 **축적된 메트릭들**을 읽어들이는 것과 대비됩니다.
애플리케이션이 **충분히 단순**해서 클러스터가 아닌 **단일 서버**에서 실행할 수 있다면, 컨테이너에 프로세스 매니저를 두고 싶을 수 있습니다.
그렇다면 이 경우에는 **다중 프로세스**를 가지는 **하나의 컨테이너**를 두어서 같은 컨테이너에서 모든 내부 프로세스에 대한 Prometheus 메트릭을 수집하는 로컬 도구(예를 들어 Prometheus 익스포터 같은)를 두어서 이 메그릭들을 하나의 컨테이너에 내에서 공유하는 방법이 더 단순할 것입니다.
#### Docker Compose { #docker-compose }
**Docker Compose**로 클러스터가 아닌 **단일 서버**에 배포하는 경우, 공유 네트워크와 **로드 밸런싱**을 유지하면서(Docker Compose로) 컨테이너 복제를 관리하는 쉬운 방법이 없을 수 있습니다.
그렇다면 **프로세스 매니저**가 컨테이너 내부에서 **여러 워커 프로세스**를 시작하는 **단일 컨테이너**를 원할 수 있습니다.
---
요점은, 이 중 **어느것도** 여러분들이 반드시 따라야하는 **확정된 사실** 아니라는 것입니다. 여러분은 이 아이디어들을 **여러분의 고유한 이용 사례를 평가**하는데 사용하고, 여러분의 시스템에 가장 적합한 접근법이 어떤 것인지 결정하며, 다음 개념관리하는 방법을 확인할 수 있습니다:
핵심은, 이것들 중 **어느 것도** 무조건 따라야 하는 **절대적인 규칙** 아니라는 것입니다. 이 아이디어들을 사용해 **여러분의 용 사례를 평가**하고, 여러분의 시스템에 가장 적합한 접근 방식을 결정하면서 다음 개념을 어떻게 관리할지 확인할 수 있습니다:
* 보안 - HTTPS
* 구동하기
* 시작 시 자동 실행
* 재시작
* 복제 (실행 중인 프로세스 수)
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작하기 전 단계
* 시작 전 사전 단계
## 메모리
## 메모리 { #memory }
만약 여러분이 **컨테이너 당 단일 프로세스**를 실행한다면, 여러분은 각 컨테이너(복제된 경우에는 여러개의 컨테이너들)에 대해 잘 정의되고, 안정적이며, 제한된 용량의 메모리 소비량을 가지고 있을 것입니다.
**컨테이너당 단일 프로세스**를 실행하면, 각 컨테이너(복제된 경우 여러 개)마다 소비하는 메모리 양이 대체로 잘 정의되고 안정적이며 제한된 값이 됩니다.
러면 여러분의 컨테이너 관리 시스템(예를 들어 **쿠버네티스**) 설정에서 앞서 정의된 것과 같은 메모리 제한과 요구사항을 설정할 수 있습니다. 이런 방법으로 **가용 머신**이 필요로하는 메모리와 클러스터에 있는 가용 머신들을 염두에 두고 **컨테이너를 복제**할 수 있습니다.
런 다음 컨테이너 관리 시스템(예: **Kubernetes**) 설정에서 동일하게 메모리 제한과 요구사항을 설정할 수 있습니다. 그러면 클러스터에서 사용 가능한 머신에 있는 메모리와 컨테이너가 필요로 하는 메모리 양을 고려해 **컨테이너를 복제**할 수 있습니다.
만약 여러분의 어플리케이션이 **단순**하다면,것은 **문제가 되지 않을** 것이고, 고정된 메모리 제한을 구체화할 필요 없을 것입니다. 하지만 여러분의 어플리케이션이 (예를 들어 **머신 러닝** 모델같이) **많은 메모리를 소요한다면**, 어플리케이션이 얼마나 많은 양의 메모리를 사용하는지 확인하고 **각 머신에서** 사용하는 **컨테이너 수**를 조정할 필요가 있습니다 (그리고 필요에 따라 여러분의 클러스터에 머신을 추가할 수 있습니다).
플리케이션이 **단순**하다면 이는 아마도 **문제가 되지 않을** 것이고, 엄격한 메모리 제한을 지정할 필요 없을 수도 있습니다. 하지만 **많은 메모리를 사용한다면**(예: **머신 러닝** 모델), 얼마나 많은 메모리를 소비하는지 확인하고, **각 머신**에서 실행되는 **컨테이너 수**를 조정해야 합니다(필요하다면 클러스터에 머신을 추가할 수 있습니다).
만약 여러분이 **컨테이너 당 여러개의 프로세스**를 실행한다면 (예를 들어 공식 도커 이미지 처럼), 여러분은 시작된 프로세스 수가 가용한 것 보다 **더 많은 메모리를 소비**하지 않는지 확인해야 합니다.
**컨테이너당 여러 프로세스**를 실행한다면, 시작되는 프로세스 수가 사용 가능한 것보다 **더 많은 메모리를 소비하지** 않는지 확인해야 합니다.
## 시작하기 전 단계들과 컨테이너
## 시작 전 단계 컨테이너 { #previous-steps-before-starting-and-containers }
만약 여러분이 컨테이너(예를 들어 도커, 쿠버네티스)를 사용한다면, 여러분이 접근할 수 있는 주요 방법은 크게 두가지가 있습니다.
컨테이너(예: Docker, Kubernetes)를 사용한다면, 사용할 수 있는 주요 접근 방식은 두 가지입니다.
### 다중 컨테이너
### 여러 컨테이너 { #multiple-containers }
만약 여러분이 **여러개의 컨테이너**를 가지고 있다면, 아마도 각각의 컨테이너는 **하나의 프로세스**를 가지고 있을 것입니다(예를 들어, **쿠버네티스** 클러스터에서). 그러면 여러분은 복제된 워커 컨테이너를 실행하기 **전에**, 하나의 컨테이너에 있는 **이전의 단계들을** 수행하는 단일 프로세스를 가지는 **별도의 컨테이너**을 가지고 싶을 것입니다.
**여러 컨테이너**가 있고 각 컨테이너가 보통 **단일 프로세스**를 실행한다면(예: **Kubernetes** 클러스터), 복제된 워커 컨테이너를 실행하기 **전에**, 단일 컨테이너에서 단일 프로세스로 **시작 전 사전 단계** 수행하는 **별도의 컨테이너**를 두고 싶을 가능성이 큽니다.
/// info | 정보
만약 여러분이 쿠버네티스를 사용하고 있다면, 아마도 이는 <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>일 것입니다.
Kubernetes를 사용한다면, 이는 아마도 <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>일 것입니다.
///
만약 여러분의 이용 사례에서 전 단계들을 **병렬적으로 여러번** 수행하는데에 문제가 없다면 (예를 들어 데이터베이스 이전을 실행하지 않고 데이터베이스가 준비되었는지 확인만 하는 경우), 메인 프로세스를 시작하기 전에 이 단계들을 각 컨테이너에 넣을 수 있습니다.
용 사례에서 시작 전 사전 단계 **여러 번 병렬로 실행**해도 문제가 없다면(예: 데이터베이스 마이그레이션을 실행하는 것이 아니라, 데이터베이스가 준비되었는지 확인만 하는 경우), 메인 프로세스를 시작하기 전에 각 컨테이너에 그 단계를 넣을 수 있습니다.
### 단일 컨테이너
### 단일 컨테이너 { #single-container }
만약 여러분의 셋업이 **다중 프로세스**(또는 하나의 프로세스)를 시작하는 **하나의 컨테이너**를 가지는 단순한 셋업이라면, 사전 단계들을 앱을 포함하는 프로세스를 시작하기 직전에 같은 컨테이너에서 실행할 수 있습니다. 공식 도커 이미지는 이를 내부적으로 지원합니다.
**단일 컨테이너**에서 여러 **워커 프로세스**(또는 단일 프로세스)를 시작하는 단순한 셋업이라면, 앱이 있는 프로세스를 시작하기 직전에 같은 컨테이너에서 시작 전 사전 단계를 실행할 수 있습니다.
## Gunicorn과 함께하는 공식 도커 이미지 - Uvicorn
### 베이스 도커 이미지 { #base-docker-image }
앞 챕터에서 자세하게 설명된 것 처럼, Uvicorn 워커와 같이 실행되는 Gunicorn을 포함하는 공식 도커 이미지가 있습니다: [서버 워커 - Uvicorn과 함께하는 Gunicorn](server-workers.md){.internal-link target=_blank}.
과거에는 공식 FastAPI Docker 이미지가 있습니다: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. 하지만 이제는 deprecated되었습니다. ⛔️
이 이미지는 주로 위에서 설명된 상황에서 유용할 것입니다: [다중 프로세스를 가지는 컨테이너와 특수한 경우들](#containers-with-multiple-processes-and-special-cases).
아마도 이 베이스 도커 이미지(또는 유사한 다른 이미지)는 **사용하지 않는** 것이 좋습니다.
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
**Kubernetes**(또는 다른 도구)를 사용하고, 클러스터 레벨에서 여러 **컨테이너**로 **복제**를 이미 설정해 둔 경우라면, 위에서 설명한 대로 **처음부터 이미지를 빌드하는 것**이 더 낫습니다: [FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi).
/// warning | 경고
그리고 여러 워커가 필요하다면, `--workers` 커맨드 라인 옵션을 간단히 사용하면 됩니다.
여러분이 이 베이스 이미지 또는 다른 유사한 이미지를 필요로 하지 **않을** 높은 가능성이 있으며, [위에서 설명된 것처럼: FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi) 처음부터 이미지를 빌드하는 것이 더 나을 수 있습니다.
/// note Technical Details | 기술 세부사항
이 Docker 이미지는 Uvicorn이 죽은 워커를 관리하고 재시작하는 기능을 지원하지 않던 시기에 만들어졌습니다. 그래서 Gunicorn과 Uvicorn을 함께 사용해야 했고, Gunicorn이 Uvicorn 워커 프로세스를 관리하고 재시작하도록 하기 위해 상당한 복잡성이 추가되었습니다.
하지만 이제 Uvicorn(그리고 `fastapi` 명령)은 `--workers`를 지원하므로, 베이스 도커 이미지를 사용하는 대신 직접 이미지를 빌드하지 않을 이유가 없습니다(코드 양도 사실상 거의 같습니다 😅).
///
이 이미지는 가능한 CPU 코어에 기반한 **몇개의 워커 프로세스**를 설정하는 **자동-튜닝** 메커니즘을 포함하고 있습니다.
## 컨테이너 이미지 배포하기 { #deploy-the-container-image }
이 이미지는 **민감한 디폴트** 값을 가지고 있지만, 여러분들은 여전히 **환경 변수** 또는 설정 파일을 통해 설정값을 수정하고 업데이트 할 수 있습니다.
또한 스크립트를 통해 <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**시작하기 전 사전 단계**</a>를 실행하는 것을 지원합니다.
/// tip | 팁
모든 설정과 옵션을 보려면, 도커 이미지 페이지로 이동합니다: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
///
### 공식 도커 이미지에 있는 프로세스 개수
이 이미지에 있는 **프로세스 개수**는 가용한 CPU **코어들**로 부터 **자동으로 계산**됩니다.
이것이 의미하는 바는 이미지가 CPU로부터 **최대한의 성능**을 **쥐어짜낸다**는 것입니다.
여러분은 이 설정 값을 **환경 변수**나 기타 방법들로 조정할 수 있습니다.
그러나 프로세스의 개수가 컨테이너가 실행되고 있는 CPU에 의존한다는 것은 또한 **소요되는 메모리의 크기** 또한 이에 의존한다는 것을 의미합니다.
그렇기 때문에, 만약 여러분의 어플리케이션이 많은 메모리를 요구하고 (예를 들어 머신러닝 모델처럼), 여러분의 서버가 CPU 코어 수는 많지만 **적은 메모리**를 가지고 있다면, 여러분의 컨테이너는 가용한 메모리보다 많은 메모리를 사용하려고 시도할 수 있으며, 결국 퍼포먼스를 크게 떨어뜨릴 수 있습니다(심지어 고장이 날 수도 있습니다). 🚨
### `Dockerfile` 생성하기
이 이미지에 기반해 `Dockerfile`을 생성하는 방법은 다음과 같습니다:
```Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
```
### 더 큰 어플리케이션
만약 여러분이 [다중 파일을 가지는 더 큰 어플리케이션](../tutorial/bigger-applications.md){.internal-link target=_blank}을 생성하는 섹션을 따랐다면, 여러분의 `Dockerfile`은 대신 이렇게 생겼을 것입니다:
```Dockerfile hl_lines="7"
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app/app
```
### 언제 사용할까
여러분들이 **쿠버네티스**(또는 유사한 다른 도구) 사용하거나 클러스터 레벨에서 다중 컨테이너를 이용해 이미 **사본**을 설정하고 있다면, 공식 베이스 이미지(또는 유사한 다른 이미지)를 사용하지 **않는** 것 좋습니다. 그런 경우에 여러분은 다음에 설명된 것 처럼 **처음부터 이미지를 빌드하는 것**이 더 낫습니다: [FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi).
이 이미지는 위의 [다중 프로세스를 가지는 컨테이너와 특수한 경우들](#containers-with-multiple-processes-and-special-cases)에서 설명된 특수한 경우에 대해서만 주로 유용할 것입니다. 예를 들어, 만약 여러분의 어플리케이션이 **충분히 단순**해서 CPU에 기반한 디폴트 프로세스 개수를 설정하는 것이 잘 작동한다면, 클러스터 레벨에서 수동으로 사본을 설정할 필요가 없을 것이고, 여러분의 앱에서 하나 이상의 컨테이너를 실행하지도 않을 것입니다. 또는 만약에 여러분이 **도커 컴포즈**로 배포하거나, 단일 서버에서 실행하거나 하는 경우에도 마찬가지입니다.
## 컨테이너 이미지 배포하기
컨테이너 (도커) 이미지를 완성한 뒤에 이를 배포하는 방법에는 여러가지 방법이 있습니다.
컨테이너(Docker) 이미지를 만든 후에는 이를 배포하는 여러 방법이 있습니다.
예를 들어:
* 단일 서버에서 **도커 컴포즈**로 배포하기
* **쿠버네티스** 클러스터로 배포하기
* 도커 스왐 모드 클러스터로 배포하기
* 노마드 같은 다른 도구로 배포하기
* 여러분의 컨테이너 이미지를 배포해주는 클라우드 서비스로 배포하기
* 단일 서버에서 **Docker Compose**로
* **Kubernetes** 클러스터로
* Docker Swarm Mode 클러스터로
* Nomad 같은 다른 도구로
* 컨테이너 이미지를 받아 배포해주는 클라우드 서비스로
## Poetry의 도커 이미지
## `uv`를 사용하는 도커 이미지 { #docker-image-with-uv }
만약 여러분들이 프로젝트 의존성을 관리하기 위해 <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a>를 사용한다면, 도커의 멀티-스테이지 빌딩을 사용할 수 있습니다:
프로젝트를 설치하고 관리하기 위해 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>를 사용한다면, <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">uv Docker guide</a>를 따를 수 있습니다.
```{ .dockerfile .annotate }
# (1)
FROM python:3.9 as requirements-stage
## 요약 { #recap }
# (2)
WORKDIR /tmp
# (3)
RUN pip install poetry
# (4)
COPY ./pyproject.toml ./poetry.lock* /tmp/
# (5)
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
# (6)
FROM python:3.9
# (7)
WORKDIR /code
# (8)
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt
# (9)
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (10)
COPY ./app /code/app
# (11)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
```
1. 첫 스테이지로, `requirements-stage`라고 이름 붙였습니다.
2. `/tmp`를 현재의 워킹 디렉터리로 설정합니다.
이 위치에 우리는 `requirements.txt` 파일을 생성할 것입니다.
3. 이 도커 스테이지에서 Poetry를 설치합니다.
4. 파일 `pyproject.toml`와 `poetry.lock`를 `/tmp` 디렉터리로 복사합니다.
`./poetry.lock*` (`*`로 끝나는) 파일을 사용하기 때문에, 파일이 아직 사용가능하지 않더라도 고장나지 않을 것입니다.
5. `requirements.txt` 파일을 생성합니다.
6. 이것이 마지막 스테이지로, 여기에 위치한 모든 것이 마지막 컨테이너 이미지에 포함될 것입니다.
7. 현재의 워킹 디렉터리를 `/code`로 설정합니다.
8. 파일 `requirements.txt`를 `/code` 디렉터리로 복사합니다.
이 파일은 오직 이전의 도커 스테이지에만 존재하며, 때문에 복사하기 위해서 `--from-requirements-stage` 옵션이 필요합니다.
9. 생성된 `requirements.txt` 파일에 패키지 의존성을 설치합니다.
10. `app` 디렉터리를 `/code` 디렉터리로 복사합니다.
11. `uvicorn` 커맨드를 실행하여, `app.main`에서 불러온 `app` 객체를 사용하도록 합니다.
/// tip | 팁
버블 숫자를 클릭해 각 줄이 하는 일을 알아볼 수 있습니다.
///
**도커 스테이지**란 `Dockefile`의 일부로서 나중에 사용하기 위한 파일들을 생성하기 위한 **일시적인 컨테이너 이미지**로 작동합니다.
첫 스테이지는 오직 **Poetry를 설치**하고 Poetry의 `pyproject.toml` 파일로부터 프로젝트 의존성을 위한 **`requirements.txt`를 생성**하기 위해 사용됩니다.
이 `requirements.txt` 파일은 **다음 스테이지**에서 `pip`로 사용될 것입니다.
마지막 컨테이너 이미지에는 **오직 마지막 스테이지만** 보존됩니다. 이전 스테이지(들)은 버려집니다.
Poetry를 사용할 때 **도커 멀티-스테이지 빌드**를 사용하는 것이 좋은데, 여러분들의 프로젝트 의존성을 설치하기 위해 마지막 컨테이너 이미지에 **오직** `requirements.txt` 파일만 필요하지, Poetry와 그 의존성은 있을 필요가 없기 때문입니다.
이 다음 (또한 마지막) 스테이지에서 여러분들은 이전에 설명된 것과 비슷한 방식으로 방식으로 이미지를 빌드할 수 있습니다.
### TLS 종료 프록시의 배후 - Poetry
이전에 언급한 것과 같이, 만약 여러분이 컨테이너를 Nginx 또는 Traefik과 같은 TLS 종료 프록시 (로드 밸런서) 뒤에서 실행하고 있다면, 커맨드에 `--proxy-headers` 옵션을 추가합니다:
```Dockerfile
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
```
## 요약
컨테이너 시스템(예를 들어 **도커**나 **쿠버네티스**)을 사용하여 모든 **배포 개념**을 다루는 것은 꽤 간단합니다:
컨테이너 시스템(예: **Docker**, **Kubernetes**)을 사용하면 모든 **배포 개념**을 다루는 것이 상당히 단순해집니다:
* HTTPS
* 구동하기
* 시작 시 자동 실행
* 재시작
* 복제 (실행 중인 프로세스 수)
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작하기 전 단계
* 시작 전 사전 단계
대부분의 경우에서 여러분은 어떤 베이스 이미지 사용하지 않고 공식 파이썬 도커 이미지에 기반해 **처음부터 컨테이너 이미지를 빌드**할 것입니다.
대부분의 경우 베이스 이미지 사용하지 않고, 공식 Python Docker 이미지에 기반해 **처음부터 컨테이너 이미지를 빌드**하는 것이 좋습니다.
`Dockerfile`에 있는 지시 사항을 **순서대로** 다루고 **도커 캐시**를 사용하는 것으로 여러분은 **빌드 시간을 최소화**할 수 있으며, 이로써 생산성을 최대화할 수 있습니다 (그리고 지루함 피할 수 있죠) 😎
특별한 경우에는, FastAPI를 위한 공식 도커 이미지를 사용할 수도 있습니다. 🤓
`Dockerfile`에서 지시어의 **순서**와 **Docker 캐시**를 신경 쓰면 **빌드 시간을 최소화** 생산성을 최대화할 수 있습니다(그리고 지루함 피할 수 있습니다). 😎

View File

@@ -1,21 +1,23 @@
# 배포하기 - 들어가면서
# 배포 { #deployment }
**FastAPI**을 배포하는 것은 비교적 쉽습니다.
**FastAPI** 애플리케이션을 배포하는 것은 비교적 쉽습니다.
## 배포의 의미
## 배포의 의미 { #what-does-deployment-mean }
**배포**란 애플리케이션을 **사용자가 사용**할 수 있도록 하는 데 필요한 단계를 수행하는 것을 의미합니다.
애플리케이션을 **배포**한다는 것은 **사용자가 사용**할 수 있도록 하는 데 필요한 단계를 수행하는 것을 의미합니다.
**웹 API**의 경우, 일반적으로 **사용자**가 중단이나 오류 없이 애플리케이션에 효율적으로 **접근**할 수 있도록 좋은 성능, 안정성 등을 제공하는 **서버 프로그램** 함께 **원격 시스템**에 이를 설치하는 작업을 의미합니다.
**웹 API**의 경우, 일반적으로 **원격 머신**에 이를 설치하고, 좋은 성능, 안정성 등을 제공하는 **서버 프로그램** 함께 구성하여 **사용자**가 중단이나 문제 없이 애플리케이션에 효율적으로 **접근**할 수 있게 하는 것을 포함합니다.
이는 지속적으로 코드를 변경하고, 지우고, 수정하고, 개발 서버를 중지했다가 다시 시작하는 등의 **개발** 단계와 대조됩니다.
이는 지속적으로 코드를 변경하고, 망가뜨리고 고치고, 개발 서버를 중지했다가 다시 시작하는 등의 **개발** 단계와 대조됩니다.
## 배포 전략
## 배포 전략 { #deployment-strategies }
사용하는 도구나 특정 사례에 따라 여러 가지 방법이 있습니다.
구체적인 사용 사례와 사용하는 도구에 따라 여러 가지 방법이 있습니다.
배포도구들을 사용하여 직접 **서버 배포**하거나, 배포작업의 일부를 수행하는 **클라우드 서비스** 또는 다른 방법을 사용할 수도 있습니다.
여러 도구를 조합해 직접 **서버 배포**할 수도 있고, 작업의 일부를 대신해 주는 **클라우드 서비스**를 사용할 수도 있으며, 다른 가능한 선택지도 있습니다.
**FastAPI** 애플리케이션을 배포할 때 선택할 수 있는 몇 가지 주요 방법을 보여 드리겠습니다 (대부분 다른 유형의 웹 애플리케이션에도 적용됩니다).
예를 들어, FastAPI 뒤에 있는 저희 팀은 FastAPI로 작업하는 것과 같은 개발자 경험을 유지하면서, FastAPI 앱을 클라우드에 가능한 한 간소화된 방식으로 배포할 수 있도록 <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a>를 만들었습니다.
다음 차례에 자세한 내용과 이를 위한 몇 가지 기술을 볼 수 있습니다. ✨
**FastAPI** 애플리케이션을 배포할 때 아마 염두에 두어야 할 몇 가지 주요 개념을 보여드리겠습니다(대부분은 다른 유형의 웹 애플리케이션에도 적용됩니다).
다음 섹션에서 염두에 둘 더 많은 세부사항과 이를 위한 몇 가지 기술을 볼 수 있습니다. ✨

View File

@@ -1,130 +1,87 @@
# 서버 워커 - 구니콘과 유비콘
# 서버 워커 - 워커와 함께 사용하는 Uvicorn { #server-workers-uvicorn-with-workers }
단계에서의 배포 개념들을 다시 확인해보겠습니다:
전의 배포 개념들을 다시 확인해보겠습니다:
* 보안 - HTTPS
* 서버 시작과 동시에 실행하기
* 서버 시작 실행
* 재시작
* **복제(실행 중인 프로세스의 숫자)**
* **복제(실행 중인 프로세스)**
* 메모리
* 시작하기 전의 여러 단계
* 시작하기 전의 이전 단계
지금까지 문서의 모든 튜토리얼을 참고하여 **단일 프로세스**로 Uvicorn과 같은 **서버 프로그램**을 실행했을 것입니다.
지금까지 문서의 모든 튜토리얼을 참고하면서, `fastapi` 명령처럼 Uvicorn을 실행하는 **서버 프로그램**을 사용해 **단일 프로세스**로 실행해 왔을 가능성이 큽니다.
애플리케이션을 배포할 때 **다중 코어**를 활용하고 더 많은 요청을 처리할 수 있도록 **프로세스 복제**이 필요합니다.
애플리케이션을 배포할 때 **다중 코어**를 활용하고 더 많은 요청을 처리할 수 있도록 **프로세스 복제**를 하고 싶을 가능성이 큽니다.
과정이었던 [배포 개념들](concepts.md){.internal-link target=_blank}에서 본 것처럼 여러가지 방법이 존재합니다.
장의 [배포 개념들](concepts.md){.internal-link target=_blank}에서 본 것처럼, 사용할 수 있는 전략이 여러 가지 있습니다.
지금부터 <a href="https://gunicorn.org/" class="external-link" target="_blank">**구니콘**</a>을 **유비콘 워커 프로세스**와 함께 사용하는 방법을 알려드리겠습니다.
여기서는 `fastapi` 명령을 사용하거나 `uvicorn` 명령을 직접 사용해서, **워커 프로세스**와 함께 **Uvicorn**을 사용하는 방법을 보여드리겠습니다.
/// info | 정보
만약 도커와 쿠버네티스 같은 컨테이너를 사용하고 있다면 다음 챕터 [FastAPI와 컨테이너 - 도커](docker.md){.internal-link target=_blank}에서 더 많은 정보를 얻을 수 있습니다.
Docker나 Kubernetes 같은 컨테이너를 사용하고 있다면, 다음 장인 [컨테이너에서의 FastAPI - 도커](docker.md){.internal-link target=_blank}에서 더 자세히 설명하겠습니다.
특히, 쿠버네티스에서 실행할 때는 구니콘을 사용하지 않고 대신 컨테이너당 하나의 유비콘 프로세스를 실행하는 것이 좋습니다. 이 장의 뒷부분에서 설명하겠습니다.
특히 **Kubernetes**에서 실행할 때는 워커를 사용하기보다는, 대신 **컨테이너당 단일 Uvicorn 프로세스 하나**를 실행하고 싶을 가능성이 크지만, 해당 내용은 그 장의 에서 설명하겠습니다.
///
## 구니콘과 유비콘 워커
## 여러 워커 { #multiple-workers }
**Gunicorn**은 **WSGI 표준**을 주로 사용하는 애플리케이션 서버입니다. 이것은 구니콘이 플라스크와 쟝고와 같은 애플리케이션을 제공할 수 있다는 것을 의미합니다. 구니콘 자체는 최신 **<a href="https://asgi.readthedocs.io/en/latest/" class="external-link" target="_blank">ASGI 표준</a>**을 사용하기 때문에 FastAPI와 호환되지 않습니다.
`--workers` 커맨드라인 옵션으로 여러 워커를 시작할 수 있습니다:
하지만 구니콘은 **프로세스 관리자**역할을 하고 사용자에게 특정 **워커 프로세스 클래스**를 알려줍니다. 그런 다음 구니콘은 해당 클래스를 사용하여 하나 이상의 **워커 프로세스**를 시작합니다.
//// tab | `fastapi`
그리고 **유비콘**은 **구니콘과 호환되는 워커 클래스**가 있습니다.
이 조합을 사용하여 구니콘은 **프로세스 관리자** 역할을 하며 **포트**와 **IP**를 관찰하고, **유비콘 클래스**를 실행하는 워커 프로세스로 통신 정보를 **전송**합니다.
그리고 나서 구니콘과 호환되는 **유비콘 워커** 클래스는 구니콘이 보낸 데이터를 FastAPI에서 사용하기 위한 ASGI 표준으로 변환하는 일을 담당합니다.
## 구니콘과 유비콘 설치하기
`fastapi` 명령을 사용한다면:
<div class="termy">
```console
$ pip install "uvicorn[standard]" gunicorn
$ <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:solid">main.py</u>
---> 100%
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started parent process <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27367</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
이 명령어는 유비콘 `standard` 추가 패키지(좋은 성능을 위한)와 구니콘을 설치할 것입니다.
////
## 구니콘을 유비콘 워커와 함께 실행하기
//// tab | `uvicorn`
설치 후 구니콘 실행하기:
<div class="termy">
```console
$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
[19499] [INFO] Starting gunicorn 20.1.0
[19499] [INFO] Listening at: http://0.0.0.0:80 (19499)
[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker
[19511] [INFO] Booting worker with pid: 19511
[19513] [INFO] Booting worker with pid: 19513
[19514] [INFO] Booting worker with pid: 19514
[19515] [INFO] Booting worker with pid: 19515
[19511] [INFO] Started server process [19511]
[19511] [INFO] Waiting for application startup.
[19511] [INFO] Application startup complete.
[19513] [INFO] Started server process [19513]
[19513] [INFO] Waiting for application startup.
[19513] [INFO] Application startup complete.
[19514] [INFO] Started server process [19514]
[19514] [INFO] Waiting for application startup.
[19514] [INFO] Application startup complete.
[19515] [INFO] Started server process [19515]
[19515] [INFO] Waiting for application startup.
[19515] [INFO] Application startup complete.
```
</div>
각 옵션이 무엇을 의미하는지 살펴봅시다:
* 이것은 유비콘과 똑같은 문법입니다. `main`은 파이썬 모듈 네임 "`main`"을 의미하므로 `main.py`파일을 뜻합니다. 그리고 `app`**FastAPI** 어플리케이션이 들어 있는 변수의 이름입니다.
* `main:app`이 파이썬의 `import` 문법과 흡사한 면이 있다는 걸 알 수 있습니다:
```Python
from main import app
```
* 곧, `main:app`안에 있는 콜론의 의미는 파이썬에서 `from main import app`에서의 `import`와 같습니다.
* `--workers`: 사용할 워커 프로세스의 개수이며 숫자만큼의 유비콘 워커를 실행합니다. 이 예제에서는 4개의 워커를 실행합니다.
* `--worker-class`: 워커 프로세스에서 사용하기 위한 구니콘과 호환되는 워커클래스.
* 이런식으로 구니콘이 import하여 사용할 수 있는 클래스를 전달해줍니다:
```Python
import uvicorn.workers.UvicornWorker
```
* `--bind`: 구니콘이 관찰할 IP와 포트를 의미합니다. 콜론 (`:`)을 사용하여 IP와 포트를 구분합니다.
* 만약에 `--bind 0.0.0.0:80` (구니콘 옵션) 대신 유비콘을 직접 실행하고 싶다면 `--host 0.0.0.0`과 `--port 80`을 사용해야 합니다.
출력에서 각 프로세스에 대한 **PID** (process ID)를 확인할 수 있습니다. (단순한 숫자입니다)
출력 내용:
* 구니콘 **프로세스 매니저**는 PID `19499`로 실행됩니다. (직접 실행할 경우 숫자가 다를 수 있습니다)
* 다음으로 `Listening at: http://0.0.0.0:80`을 시작합니다.
* 그런 다음 사용해야할 `uvicorn.workers.UvicornWorker`의 워커클래스를 탐지합니다.
* 그리고 PID `19511`, `19513`, `19514`, 그리고 `19515`를 가진 **4개의 워커**를 실행합니다.
또한 구니콘은 워커의 수를 유지하기 위해 **죽은 프로세스**를 관리하고 **재시작**하는 작업을 책임집니다. 이것은 이번 장 상단 목록의 **재시작** 개념을 부분적으로 도와주는 것입니다.
그럼에도 불구하고 필요할 경우 외부에서 **구니콘을 재시작**하고, 혹은 **서버를 시작할 때 실행**할 수 있도록 하고 싶어할 것입니다.
## 유비콘과 워커
유비콘은 몇 개의 **워커 프로세스**와 함께 실행할 수 있는 선택지가 있습니다.
그럼에도 불구하고, 유비콘은 워커 프로세스를 다루는 데에 있어서 구니콘보다 더 제한적입니다. 따라서 이 수준(파이썬 수준)의 프로세스 관리자를 사용하려면 구니콘을 프로세스 관리자로 사용하는 것이 좋습니다.
보통 이렇게 실행할 수 있습니다:
`uvicorn` 명령을 직접 사용하는 편이 좋다면:
<div class="termy">
@@ -148,36 +105,35 @@ $ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
</div>
새로운 옵션인 `--workers`은 유비콘에게 4개의 워커 프로세스를 사용한다고 알려줍니다.
////
각 프로세스의 **PID**를 확인할 수 있습니다. `27365`는 상위 프로세스(**프로세스 매니저**), 그리고 각각의 워커프로세스는 `27368`, `27369`, `27370`, 그리고 `27367`입니다.
여기서 새로운 옵션은 `--workers`뿐이며, Uvicorn에게 워커 프로세스 4개를 시작하라고 알려줍니다.
## 배포 개념들
또한 각 프로세스의 **PID**도 확인할 수 있는데, 상위 프로세스(이것이 **프로세스 관리자**)의 PID는 `27365`이고, 각 워커 프로세스의 PID는 `27368`, `27369`, `27370`, `27367`입니다.
여기에서는 **유비콘 워커 프로세스**를 관리하는 **구니콘**(또는 유비콘)을 사용하여 애플리케이션을 **병렬화**하고, CPU **멀티 코어**의 장점을 활용하고, **더 많은 요청**을 처리할 수 있는 방법을 살펴보았습니다.
## 배포 개념들 { #deployment-concepts }
워커를 사용하는 것은 배포 개념 목록에서 주로 **복제본** 부분과 **재시작**에 약간 도움이 되지만 다른 배포 개념들도 다루어야 합니다:
여기서는 여러 **워커**를 사용해 애플리케이션 실행을 **병렬화**하고, CPU의 **다중 코어**를 활용하며, **더 많은 요청**을 제공할 수 있는 방법을 살펴봤습니다.
위의 배포 개념 목록에서 워커를 사용하는 것은 주로 **복제** 부분에 도움이 되고, **재시작**에도 약간 도움이 되지만, 나머지 항목들도 여전히 신경 써야 합니다:
* **보안 - HTTPS**
* **서버 시작과 동시에 실행하기**
* **서버 시작 실행**
* ***재시작***
* 복제(실행 중인 프로세스의 숫자)
* 복제(실행 중인 프로세스)
* **메모리**
* **시작하기 전의 여러 단계**
* **시작하기 전의 이전 단계**
## 컨테이너와 도커 { #containers-and-docker }
## 컨테이너와 도커
다음 장인 [컨테이너에서의 FastAPI - 도커](docker.md){.internal-link target=_blank}에서는 다른 **배포 개념들**을 처리하기 위해 사용할 수 있는 몇 가지 전략을 설명하겠습니다.
다음 장인 [FastAPI와 컨테이너 - 도커](docker.md){.internal-link target=_blank}에서 다른 **배포 개념들**을 다루는 전략들을 알려드리겠습니다.
단일 Uvicorn 프로세스를 실행하기 위해, **처음부터 여러분만의 이미지를 직접 빌드**하는 방법을 보여드리겠습니다. 이는 간단한 과정이며, **Kubernetes** 같은 분산 컨테이너 관리 시스템을 사용할 때 아마도 이렇게 하고 싶을 것입니다.
또한 간단한 케이스에서 사용할 수 있는, **구니콘과 유비콘 워커**가 포함돼 있는 **공식 도커 이미지**와 함께 몇 가지 기본 구성을 보여드리겠습니다.
## 요약 { #recap }
그리고 단일 유비콘 프로세스(구니콘 없이)를 실행할 수 있도록 **사용자 자신의 이미지를 처음부터 구축**하는 방법도 보여드리겠습니다. 이는 간단한 과정이며, **쿠버네티스**와 같은 분산 컨테이너 관리 시스템을 사용할 때 수행할 작업입니다.
`fastapi` 또는 `uvicorn` 명령에서 `--workers` CLI 옵션을 사용해 여러 워커 프로세스를 실행하면, **멀티 코어 CPU**를 활용해 **여러 프로세스를 병렬로 실행**할 수 있습니다.
## 요약
다른 배포 개념들을 직접 처리하면서 **자체 배포 시스템**을 구축하는 경우, 이러한 도구와 아이디어를 활용할 수 있습니다.
당신은 **구니콘**(또는 유비콘)을 유비콘 워커와 함께 프로세스 관리자로 사용하여 **멀티-코어 CPU**를 활용하는 **멀티 프로세스를 병렬로 실행**할 수 있습니다.
다른 배포 개념을 직접 다루면서 **자신만의 배포 시스템**을 구성하는 경우 이러한 도구와 개념들을 활용할 수 있습니다.
다음 장에서 컨테이너(예: 도커 및 쿠버네티스)와 함께하는 **FastAPI**에 대해 배워보세요. 이러한 툴에는 다른 **배포 개념**들을 간단히 해결할 수 있는 방법이 있습니다. ✨
다음 장에서 컨테이너(예: Docker 및 Kubernetes)와 함께 사용하는 **FastAPI**에 대해 알아보세요. 해당 도구들이 다른 **배포 개념들**도 간단히 해결하는 방법이 있다는 것을 확인할 수 있습니다.

View File

@@ -1,94 +1,93 @@
# FastAPI 버전들에 대하여
# FastAPI 버전들에 대하여 { #about-fastapi-versions }
**FastAPI** 는 이미 많은 응용 프로그램과 시스템들을 만드는데 사용되고 있습니다. 그리고 100%의 테스트 정확성을 가지고 있습니다. 하지만 이것은 아직까지도 빠르게 발전하고 있습니다.
**FastAPI**는 이미 많은 애플리케이션과 시스템에서 프로덕션으로 사용되고 있습니다. 그리고 테스트 커버리지는 100%로 유지됩니다. 하지만 개발은 여전히 빠르게 진행되고 있습니다.
새로운 특징들이 빈번하게 추가되고, 오류들이 지속적으로 수정되고 있습니다. 그리고 코드 계속적으로 향상되고 있습니다.
새로운 기능이 자주 추가되고, 버그가 규칙적으로 수정되며, 코드 계속해서 지속적으로 개선되고 있습니다.
이것이 아직도 최신 버전이 `0.x.x`이유입니다. 이것은 각각의 버전이 잠재적으로 변할 수 있다는 것을 보여줍니다. 이는 <a href="https://semver.org/" class="external-link" target="_blank">유의적 버전</a> 관습을 따릅니다.
그래서 현재 버전이 아직 `0.x.x`입니다. 이는 각 버전이 잠재적으로 하위 호환성이 깨지는 변경을 포함할 수 있음을 반영합니다. 이는 <a href="https://semver.org/" class="external-link" target="_blank">Semantic Versioning</a> 관례를 따릅니다.
지금 바로 **FastAPI**로 응용 프로그램을 만들 수 있습니다. 이때 (아마 지금까지 그래 왔던 것처럼), 사용하는 버전이 코드와 잘 맞는지 확인해야합니다.
지금 바로 **FastAPI**로 프로덕션 애플리케이션을 만들 수 있습니다(그리고 아마도 한동안 그렇게 해오셨을 것입니다). 다만 나머지 코드와 함께 올바르게 동작하는 버전을 사용하고 있는지 확인하기만 하면 됩니다.
## `fastapi` 버전을 표시
## `fastapi` 버전을 고정하기 { #pin-your-fastapi-version }
가장 먼저 해야할 응용 프로그램이 잘 작동하는 가장 최신의 구체적인 **FastAPI** 버전을 표시하는 것입니다.
가장 먼저 해야 여러분의 애플리케이션에서 올바르게 동작하는 것으로 알고 있는 **FastAPI**의 최신 구체 버전에 맞춰 사용 중인 버전을 "고정(pin)"하는 것입니다.
예를 들어, 응용 프로그램에 `0.45.0` 버전을 사용다고 가정니다.
예를 들어, 앱에서 `0.112.0` 버전을 사용하고 있다고 가정해 보겠습니다.
만약에 `requirements.txt` 파일을 사용다면, 다음과 같이 버전을 명세할 수 있습니다:
`requirements.txt` 파일을 사용다면 다음과 같이 버전을 지정할 수 있습니다:
```txt
fastapi==0.45.0
fastapi[standard]==0.112.0
```
것은 `0.45.0` 버전을 사용다는 것을 의미니다.
는 정확히 `0.112.0` 버전을 사용다는 의미니다.
또는 다음과 같이 표시할 수 있습니다:
또는 다음과 같이 고정할 수 있습니다:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
이는 `0.112.0` 이상이면서 `0.113.0` 미만의 버전을 사용한다는 의미입니다. 예를 들어 `0.112.2` 버전도 허용됩니다.
`uv`, Poetry, Pipenv 등 다른 도구로 설치를 관리한다면, 모두 패키지의 특정 버전을 정의할 수 있는 방법을 제공합니다.
## 이용 가능한 버전들 { #available-versions }
사용 가능한 버전(예: 현재 최신 버전이 무엇인지 확인하기 위해)은 [Release Notes](../release-notes.md){.internal-link target=_blank}에서 확인할 수 있습니다.
## 버전들에 대해 { #about-versions }
Semantic Versioning 관례에 따르면, `1.0.0` 미만의 어떤 버전이든 잠재적으로 하위 호환성이 깨지는 변경을 추가할 수 있습니다.
FastAPI는 또한 "PATCH" 버전 변경은 버그 수정과 하위 호환성이 깨지지 않는 변경을 위한 것이라는 관례를 따릅니다.
/// tip | 팁
"PATCH"는 마지막 숫자입니다. 예를 들어 `0.2.3`에서 PATCH 버전은 `3`입니다.
///
따라서 다음과 같이 버전을 고정할 수 있어야 합니다:
```txt
fastapi>=0.45.0,<0.46.0
```
이것은 `0.45.0` 버전과 같거나 높으면서 `0.46.0` 버전 보다는 낮은 버전을 사용했다는 것을 의미합니다. 예를 들어, `0.45.2` 버전과 같은 경우는 해당 조건을 만족합니다.
만약에 Poetry, Pipenv, 또는 그밖의 다양한 설치 도구를 사용한다면, 패키지에 구체적인 버전을 정의할 수 있는 방법을 가지고 있을 것입니다.
## 이용가능한 버전들
[Release Notes](../release-notes.md){.internal-link target=_blank}를 통해 사용할 수 있는 버전들을 확인할 수 있습니다.(예를 들어, 가장 최신의 버전을 확인할 수 있습니다.)
## 버전들에 대해
유의적 버전 관습을 따라서, `1.0.0` 이하의 모든 버전들은 잠재적으로 급변할 수 있습니다.
FastAPI는 오류를 수정하고, 일반적인 변경사항을 위해 "패치"버전의 관습을 따릅니다.
하위 호환성이 깨지는 변경과 새로운 기능은 "MINOR" 버전에 추가됩니다.
/// tip | 팁
여기서 말하는 "패치"란 버전의 마지막 숫자로, 예를 들어 `0.2.3` 버전에서 "패치"는 `3`을 의미합니다.
"MINOR"는 가운데 숫자입니다. 예를 들어 `0.2.3`에서 MINOR 버전은 `2`니다.
///
따라서 다음과 같이 버전을 표시할 수 있습니다:
## FastAPI 버전 업그레이드하기 { #upgrading-the-fastapi-versions }
앱에 테스트를 추가해야 합니다.
**FastAPI**에서는 매우 쉽습니다(Starlette 덕분에). 문서를 확인해 보세요: [Testing](../tutorial/testing.md){.internal-link target=_blank}
테스트를 갖춘 뒤에는 **FastAPI** 버전을 더 최신 버전으로 업그레이드하고, 테스트를 실행하여 모든 코드가 올바르게 동작하는지 확인하세요.
모든 것이 동작하거나 필요한 변경을 한 뒤 모든 테스트가 통과한다면, `fastapi`를 그 새로운 최신 버전으로 고정할 수 있습니다.
## Starlette에 대해 { #about-starlette }
`starlette`의 버전은 고정하지 않는 것이 좋습니다.
서로 다른 **FastAPI** 버전은 Starlette의 특정한 더 새로운 버전을 사용하게 됩니다.
따라서 **FastAPI**가 올바른 Starlette 버전을 사용하도록 그냥 두면 됩니다.
## Pydantic에 대해 { #about-pydantic }
Pydantic은 자체 테스트에 **FastAPI**에 대한 테스트도 포함하고 있으므로, Pydantic의 새 버전(`1.0.0` 초과)은 항상 FastAPI와 호환됩니다.
여러분에게 맞는 `1.0.0` 초과의 어떤 Pydantic 버전으로든 고정할 수 있습니다.
예를 들어:
```txt
fastapi>=0.45.0,<0.46.0
```
수정된 사항과 새로운 요소들이 "마이너" 버전에 추가되었습니다.
/// tip | 팁
"마이너"란 버전 넘버의 가운데 숫자로, 예를 들어서 `0.2.3`의 "마이너" 버전은 `2`입니다.
///
## FastAPI 버전의 업그레이드
응용 프로그램을 검사해야합니다.
(Starlette 덕분에), **FastAPI** 를 이용하여 굉장히 쉽게 할 수 있습니다. [Testing](../tutorial/testing.md){.internal-link target=_blank}문서를 확인해 보십시오:
검사를 해보고 난 후에, **FastAPI** 버전을 더 최신으로 업그레이드 할 수 있습니다. 그리고 코드들이 테스트에 정상적으로 작동하는지 확인을 해야합니다.
만약에 모든 것이 정상 작동하거나 필요한 부분을 변경하고, 모든 검사를 통과한다면, 새로운 버전의 `fastapi`를 표시할 수 있습니다.
## Starlette에 대해
`starlette`의 버전은 표시할 수 없습니다.
서로다른 버전의 **FastAPI**가 구체적이고 새로운 버전의 Starlette을 사용할 것입니다.
그러므로 **FastAPI**가 알맞은 Starlette 버전을 사용하도록 하십시오.
## Pydantic에 대해
Pydantic은 **FastAPI** 를 위한 검사를 포함하고 있습니다. 따라서, 새로운 버전의 Pydantic(`1.0.0`이상)은 항상 FastAPI와 호환됩니다.
작업을 하고 있는 `1.0.0` 이상의 모든 버전과 `2.0.0` 이하의 Pydantic 버전을 표시할 수 있습니다.
예를 들어 다음과 같습니다:
```txt
pydantic>=1.2.0,<2.0.0
pydantic>=2.7.0,<3.0.0
```

View File

@@ -1,4 +1,4 @@
# 환경 변수
# 환경 변수 { #environment-variables }
/// tip | 팁
@@ -6,11 +6,11 @@
///
환경 변수는 파이썬 코드의 **바깥**인, **운영 체제**에 존재하는 변수입니다. 파이썬 코드 다른 프로그램에서 읽을 수 있습니다.
환경 변수(또는 "**env var**"라고도 합니다)는 파이썬 코드의 **바깥**인, **운영 체제**에 존재하는 변수이며, 파이썬 코드(또는 다른 프로그램에서도)에서 읽을 수 있습니다.
환경 변수는 애플리케이션 **설정**을 처리하거나, 파이썬의 **설치** 과정의 일부로 유용니다.
환경 변수는 애플리케이션 **설정**을 처리하거나, 파이썬의 **설치** 과정의 일부로 유용할 수 있습니다.
## 환경 변수를 만들고 사용하기
## 환경 변수를 만들고 사용하기 { #create-and-use-env-vars }
파이썬 없이도, **셸 (터미널)** 에서 환경 변수를 **생성** 하고 사용할 수 있습니다.
@@ -50,9 +50,9 @@ Hello Wade Wilson
////
## 파이썬에서 환경 변수 읽기
## 파이썬에서 env var 읽기 { #read-env-vars-in-python }
파이썬 **바깥**인 터미널에서(다른 도구로도 가능) 환경 변수를 생성도 할 수도 있고, 이를 **파이썬에서 읽을 수 있습니다.**
파이썬 **바깥**인 터미널에서(또는 다른 어떤 방법으로든) 환경 변수를 만들고, 그런 다음 **파이썬에서 읽을 수 있습니다**.
예를 들어 다음과 같은 `main.py` 파일이 있다고 합시다:
@@ -67,7 +67,7 @@ print(f"Hello {name} from Python")
<a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> 의 두 번째 인자는 반환할 기본값입니다.
여기서는 `"World"`를 넣었기에 기본값으로써 사용됩니다. 넣지 않으면 `None` 이 기본값으로 사용됩니다.
제공하지 않으면 기본값은 `None`이며, 여기서는 사용할 기본값으로 `"World"`를 제공합니다.
///
@@ -129,7 +129,7 @@ Hello Wade Wilson from Python
환경변수는 코드 바깥에서 설정될 수 있지만, 코드에서 읽을 수 있고, 나머지 파일과 함께 저장(`git`에 커밋)할 필요가 없으므로, 구성이나 **설정** 에 사용하는 것이 일반적입니다.
**특정 프로그램 호출**에 대해서만 사용할 수 있는 환경 변수를 만들 수도 있습니다. 해당 프로그램에서만 사용할 수 있고, 해당 프로그램이 실행되는 동안만 사용할 수 있습니다.
또한 **특정 프로그램 호출**에 대해서만 사용할 수 있는 환경 변수를 만들 수도 있는데, 해당 프로그램에서만 사용할 수 있고, 해당 프로그램이 실행되는 동안만 사용할 수 있습니다.
그렇게 하려면 프로그램 바로 앞, 같은 줄에 환경 변수를 만들어야 합니다:
@@ -157,17 +157,17 @@ Hello World from Python
///
## 타입과 검증
## 타입과 검증 { #types-and-validation }
이 환경변수들은 오직 **텍스트 문자열**로만 처리할 수 있습니다. 텍스트 문자열은 파이썬 외부에 있으며 다른 프로그램 및 나머지 시스템(Linux, Windows, macOS 다른 운영 체제)과 호환되어야 합니다.
이 환경변수들은 오직 **텍스트 문자열**로만 처리할 수 있습니다. 텍스트 문자열은 파이썬 외부에 있으며 다른 프로그램 및 나머지 시스템(그리고 Linux, Windows, macOS 같은 서로 다른 운영 체제에서도)과 호환되어야 합니다.
즉, 파이썬에서 환경 변수로부터 읽은 **모든 값**은 **`str`**이 되고, 다른 타입으로의 변환이나 검증은 코드에서 수행해야 합니다.
**애플리케이션 설정**을 처리하기 위한 환경 변수 사용에 대한 자세한 내용은 [고급 사용자 가이드 - 설정 및 환경 변수](./advanced/settings.md){.internal-link target=\_blank} 에서 확인할 수 있습니다.
**애플리케이션 설정**을 처리하기 위한 환경 변수 사용에 대한 자세한 내용은 [고급 사용자 가이드 - 설정 및 환경 변수](./advanced/settings.md){.internal-link target=_blank} 에서 확인할 수 있습니다.
## `PATH` 환경 변수
## `PATH` 환경 변수 { #path-environment-variable }
**`PATH`**라고 불리는, **특별한** 환경변수가 있습니다. 운영체제(Linux, Windows, macOS 등)에서 실행할 프로그램을 찾기위해 사용됩니다.
**`PATH`**라고 불리는, **특별한** 환경변수가 있습니다. 운영체제(Linux, macOS, Windows)에서 실행할 프로그램을 찾기위해 사용됩니다.
변수 `PATH`의 값은 Linux와 macOS에서는 콜론 `:`, Windows에서는 세미콜론 `;`으로 구분된 디렉토리로 구성된 긴 문자열입니다.
@@ -181,11 +181,11 @@ Hello World from Python
이는 시스템이 다음 디렉토리에서 프로그램을 찾아야 함을 의미합니다:
- `/usr/local/bin`
- `/usr/bin`
- `/bin`
- `/usr/sbin`
- `/sbin`
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
@@ -197,9 +197,9 @@ C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System3
이는 시스템이 다음 디렉토리에서 프로그램을 찾아야 함을 의미합니다:
- `C:\Program Files\Python312\Scripts`
- `C:\Program Files\Python312`
- `C:\Windows\System32`
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
@@ -209,7 +209,7 @@ C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System3
찾으면 **사용합니다**. 그렇지 않으면 **다른 디렉토리**에서 계속 찾습니다.
### 파이썬 설치와 `PATH` 업데이트
### 파이썬 설치와 `PATH` 업데이트 { #installing-python-and-updating-the-path }
파이썬을 설치할 때, 아마 `PATH` 환경 변수를 업데이트 할 것이냐고 물어봤을 겁니다.
@@ -285,13 +285,13 @@ $ C:\opt\custompython\bin\python
////
이 정보는 [가상 환경](virtual-environments.md){.internal-link target=\_blank} 에 대해 알아볼 때 유용할 것입니다.
이 정보는 [가상 환경](virtual-environments.md){.internal-link target=_blank} 에 대해 알아볼 때 유용할 것입니다.
## 결론
## 결론 { #conclusion }
이 문서를 읽고 **환경 변수**가 무엇이고 파이썬에서 어떻게 사용하는지 기본적으로 이해하셨을 겁니다.
이 문서를 통해 **환경 변수**가 무엇이고 파이썬에서 어떻게 사용하는지 기본적으로 이해하셨을 겁니다.
또한 <a href="https://ko.wikipedia.org/wiki/환경_변수" class="external-link" target="_blank">환경 변수에 대한 위키피디아(한국어)</a>에서 이에 대해 자세히 알아볼 수 있습니다.
또한 <a href="https://en.wikipedia.org/wiki/Environment_variable" class="external-link" target="_blank">환경 변수에 대한 위키피디아</a>에서 이에 대해 자세히 알아볼 수 있습니다.
많은 경우에서, 환경 변수가 어떻게 유용하고 적용 가능한지 바로 명확하게 알 수는 없습니다. 하지만 개발할 때 다양한 시나리오에서 계속 나타나므로 이에 대해 아는 것이 좋습니다.

View File

@@ -1,46 +1,41 @@
# 조건부적인 OpenAPI
# 조건부 OpenAPI { #conditional-openapi }
필요한 경우, 설정 및 환경 변수를 사용하여 환경에 따라 조건부로 OpenAPI를 구성하고 완전히 비활성화할 수도 있습니다.
필요한 경우, 설정 및 환경 변수를 사용하여 환경에 따라 OpenAPI를 조건부로 구성하고 완전히 비활성화할 수도 있습니다.
## 보안, API 및 docs에 대해서
## 보안, API 및 docs에 대해서 { #about-security-apis-and-docs }
프로덕션에서, 문서화된 사용자 인터페이스(UI)를 숨기는 것이 API를 보호하는 방법이 *되어서는 안 됩니다*.
이는 API에 추가적인 보안을 제공하지 않으며, *경로 작업*은 여전히 동일한 위치에서 사용 할 수 있습니다.
이는 API에 추가적인 보안을 제공하지 않으며, *경로 처리*는 여전히 동일한 위치에서 사용 할 수 있습니다.
코드에 보안 결함이 있다면, 그 결함은 여전히 존재할 것입니다.
문서를 숨기는 것은 API와 상호작용하는 방법을 이해하기 어렵게 만들며, 프로덕션에서 디버깅을 더 어렵게 만들 수 있습니다. 이는 단순히 <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">'모호성에 의한 보안'</a>의 한 형태로 간주될 수 있습니다.
문서를 숨기는 것은 API와 상호작용하는 방법을 이해하기 어렵게 만들며, 프로덕션에서 디버깅을 더 어렵게 만들 수 있습니다. 이는 단순히 <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">Security through obscurity</a>의 한 형태로 간주될 수 있습니다.
API를 보호하고 싶다면, 예를 들어 다음과 같은 더 나은 방법들이 있습니다:
* 요청 본문과 응답에 대해 잘 정의된 Pydantic 모델을 사용하도록 하세요.
* 요청 본문과 응답에 대해 잘 정의된 Pydantic 모델이 있는지 확인하세요.
* 종속성을 사용하여 필요한 권한과 역할을 구성하세요.
* 평문 비밀번호를 절대 저장하지 말고, 오직 암호화된 비밀번호만 저장하세요.
* Passlib과 JWT 토큰과 같은 잘 알려진 암호화 도구들을 구현하고 사용하세요.
* 평문 비밀번호를 절대 저장하지 말고, 비밀번호 해시만 저장하세요.
* pwdlib와 JWT 토큰 등과 같은 잘 알려진 암호화 도구들을 구현하고 사용하세요.
* 필요한 곳에 OAuth2 범위를 사용하여 더 세분화된 권한 제어를 추가하세요.
* ...등등.
* 등등....
그럼에도 불구하고, 특정 환경(예: 프로덕션)에서 또는 환경 변수의 설정에 따라 API docs를 비활성화해야 하는 매우 특정한 사용 사례가 있을 수 있습니다.
그럼에도 불구하고, 특정 환경(예: 프로덕션)에서 또는 환경 변수의 설정에 따라 API 문서를 비활성화해야 하는 매우 특정한 사용 사례가 있을 수 있습니다.
## 설정 및 환경변수의 조건부 OpenAPI { #conditional-openapi-from-settings-and-env-vars }
## 설정 및 환경변수의 조건부 OpenAPI
동일한 Pydantic 설정을 사용하여 생성된 OpenAPI 및 문서 UI를 쉽게 구성할 수 있습니다.
동일한 Pydantic 설정을 사용하여 생성된 OpenAPI 및 docs UI를 쉽게 구성할 수 있습니다.
예를 들어:
{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
여기서 `openapi_url` 설정을 기본값인 `"/openapi.json"`으로 선언합니다.
그런 뒤, 우리는 `FastAPI` 앱을 만들 때 그것을 사용합니다.
환경 변수 `OPENAPI_URL`을 빈 문자열로 설정하여 OpenAPI(문서 UI 포함)를 비활성화할 수도 있습니다. 예를 들어:
그런 다음 환경 변수 `OPENAPI_URL`을 빈 문자열로 설정하여 OpenAPI(UI docs 포함)를 비활성화할 수도 있습니다. 예를 들어:
<div class="termy">

View File

@@ -1,4 +1,4 @@
# Swagger UI 구성
# Swagger UI 구성 { #configure-swagger-ui }
추가적인 <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">Swagger UI 매개변수</a>를 구성할 수 있습니다.
@@ -8,7 +8,7 @@
FastAPI는 이 구성을 **JSON** 형식으로 변환하여 JavaScript와 호환되도록 합니다. 이는 Swagger UI에서 필요로 하는 형식입니다.
## 구문 강조 비활성화
## 구문 강조 비활성화 { #disable-syntax-highlighting }
예를 들어, Swagger UI에서 구문 강조 기능을 비활성화할 수 있습니다.
@@ -18,41 +18,41 @@ FastAPI는 이 구성을 **JSON** 형식으로 변환하여 JavaScript와 호환
그러나 `syntaxHighlight``False`로 설정하여 구문 강조 기능을 비활성화할 수 있습니다:
{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
...그럼 Swagger UI에서 더 이상 구문 강조 기능이 표시되지 않습니다:
<img src="/img/tutorial/extending-openapi/image03.png">
## 테마 변경
## 테마 변경 { #change-the-theme }
동일한 방식으로 `"syntaxHighlight.theme"` 키를 사용하여 구문 강조 테마를 설정할 수 있습니다 (중간에 점이 포함된 것을 참고하십시오).
{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
이 설정은 구문 강조 색상 테마를 변경합니다:
<img src="/img/tutorial/extending-openapi/image04.png">
## 기본 Swagger UI 매개변수 변경
## 기본 Swagger UI 매개변수 변경 { #change-default-swagger-ui-parameters }
FastAPI는 대부분의 사용 사례에 적합한 몇 가지 기본 구성 매개변수를 포함하고 있습니다.
기본 구성에는 다음이 포함됩니다:
{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
`swagger_ui_parameters` 인수에 다른 값을 설정하여 이러한 기본값 중 일부를 재정의할 수 있습니다.
예를 들어, `deepLinking`을 비활성화하려면 `swagger_ui_parameters`에 다음 설정을 전달할 수 있습니다:
{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
## 기타 Swagger UI 매개변수
## 기타 Swagger UI 매개변수 { #other-swagger-ui-parameters }
사용할 수 있는 다른 모든 구성 옵션을 확인하려면, Swagger UI 매개변수에 대한 공식 <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">문서</a>를 참조하십시오.
사용할 수 있는 다른 모든 구성 옵션을 확인하려면, Swagger UI 매개변수에 대한 공식 <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">Swagger UI 매개변수 문서</a>를 참조하십시오.
## JavaScript 전용 설정
## JavaScript 전용 설정 { #javascript-only-settings }
Swagger UI는 **JavaScript 전용** 객체(예: JavaScript 함수)로 다른 구성을 허용하기도 합니다.
@@ -67,4 +67,4 @@ presets: [
이들은 문자열이 아닌 **JavaScript** 객체이므로 Python 코드에서 직접 전달할 수 없습니다.
이와 같은 JavaScript 전용 구성을 사용해야 하는 경우, 위의 방법 중 하나를 사용하여 모든 Swagger UI 경로 작업을 재정의하고 필요한 JavaScript를 수동으로 작성할 수 있습니다.
이와 같은 JavaScript 전용 구성을 사용해야 하는 경우, 위의 방법 중 하나를 사용할 수 있습니다. Swagger UI *경로 처리*를 모두 재정의하고 필요한 JavaScript를 수동으로 작성하세요.

View File

@@ -1,4 +1,4 @@
# FastAPI
# FastAPI { #fastapi }
<style>
.md-content .md-typeset h1 { display: none; }
@@ -37,36 +37,41 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
주요 특징으로:
* **빠름**: (Starlette과 Pydantic 덕분에) **NodeJS** 및 **Go**와 대등할 정도로 매우 높은 성능. [사용 가능한 가장 빠른 파이썬 프레임워크 중 하나](#_11).
* **빠름**: (Starlette과 Pydantic 덕분에) **NodeJS** 및 **Go**와 대등할 정도로 매우 높은 성능. [사용 가능한 가장 빠른 파이썬 프레임워크 중 하나](#performance).
* **빠른 코드 작성**: 약 200%에서 300%까지 기능 개발 속도 증가. *
* **적은 버그**: 사람(개발자)에 의한 에러 약 40% 감소. *
* **직관적**: 훌륭한 편집기 지원. 모든 곳에서 <abbr title="also known as auto-complete, autocompletion, IntelliSense">자동완성</abbr>. 적은 디버깅 시간.
* **쉬움**: 쉽게 사용하고 배우도록 설계. 적은 문서 읽기 시간.
* **짧음**: 코드 중복 최소화. 각 매개변수 선언의 여러 기능. 적은 버그.
* **견고함**: 준비된 프로덕션 용 코드를 얻으십시오. 자동 대화형 문서와 함께.
* **표준 기반**: API에 대한 (완전히 호환되는) 개방형 표준 기반: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (이전에 Swagger로 알려졌던) 및 <a href="http://json-schema.org/" class="external-link" target="_blank">JSON 스키마</a>.
* **표준 기반**: API에 대한 (완전히 호환되는) 개방형 표준 기반: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (이전에 Swagger로 알려졌던) 및 <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* 내부 개발팀의 프로덕션 애플리케이션을 빌드한 테스트에 근거한 측정</small>
## 골드 스폰서
## 스폰서 { #sponsors }
<!-- sponsors -->
{% if sponsors %}
### 키스톤 스폰서 { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
### 골드 및 실버 스폰서 { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">다른 스폰서</a>
## 의견들
## 의견들 { #opinions }
"_[...] 저는 요즘 **FastAPI**를 많이 사용하고 있습니다. [...] 사실 우리 팀의 **마이크로소프트 ML 서비스** 전부를 바꿀 계획입니다. 그중 일부는 핵심 **Windows**와 몇몇의 **Office** 제품들이 통합되고 있습니다._"
@@ -94,7 +99,7 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
"_솔직히, 당신이 만든 것은 매우 견고하고 세련되어 보입니다. 여러 면에서 **Hug**가 이렇게 되었으면 합니다 - 그걸 만든 누군가를 보는 것은 많은 영감을 줍니다._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> 제작자</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">Hug</a> 제작자</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
@@ -106,50 +111,48 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
---
## **Typer**, FastAPI의 CLI
"_프로덕션 Python API를 만들고자 한다면, 저는 **FastAPI**를 강력히 추천합니다. **아름답게 설계**되었고, **사용이 간단**하며, **확장성이 매우 뛰어나**고, 우리의 API 우선 개발 전략에서 **핵심 구성 요소**가 되었으며 Virtual TAC Engineer 같은 많은 자동화와 서비스를 이끌고 있습니다._"
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, CLI를 위한 FastAPI { #typer-the-fastapi-of-clis }
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
웹 API 대신 터미널에서 사용할 <abbr title="Command Line Interface">CLI</abbr> 앱을 만들고 있다면, <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>를 확인해 보십시오.
**Typer**는 FastAPI의 동생입니다. 그리고 **FastAPI의 CLI**가 되기 위해 생겼습니다. ⌨️ 🚀
**Typer**는 FastAPI의 동생입니다. 그리고 **CLI를 위한 FastAPI**가 되기 위해 생겼습니다. ⌨️ 🚀
## 요구사항
## 요구사항 { #requirements }
FastAPI는 거인들의 어깨 위에 서 있습니다:
* 웹 부분을 위한 <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a>.
* 데이터 부분을 위한 <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>.
## 설치
## 설치 { #installation }
<a href="https://fastapi.tiangolo.com/ko/virtual-environments/" class="external-link" target="_blank">가상 환경</a>을 생성하고 활성화한 다음 FastAPI를 설치하세요:
<div class="termy">
```console
$ pip install fastapi
$ pip install "fastapi[standard]"
---> 100%
```
</div>
프로덕션을 위해 <a href="http://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a> 또는 <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>과 같은 ASGI 서버도 필요할 겁니다.
**Note**: 모든 터미널에서 동작하도록 `"fastapi[standard]"`를 따옴표로 감싸 넣었는지 확인하세요.
<div class="termy">
## 예제 { #example }
```console
$ pip install "uvicorn[standard]"
### 만들기 { #create-it }
---> 100%
```
</div>
## 예제
### 만들기
* `main.py` 파일을 만드십시오:
다음 내용으로 `main.py` 파일을 만드십시오:
```Python
from typing import Union
@@ -172,9 +175,9 @@ def read_item(item_id: int, q: Union[str, None] = None):
<details markdown="1">
<summary>또는 <code>async def</code> 사용하기...</summary>
여러분의 코드가 `async` / `await`을 사용한다면, `async def`를 사용하십시오.
여러분의 코드가 `async` / `await`을 사용한다면, `async def`를 사용하십시오:
```Python hl_lines="9 14"
```Python hl_lines="9 14"
from typing import Union
from fastapi import FastAPI
@@ -194,22 +197,35 @@ async def read_item(item_id: int, q: Union[str, None] = None):
**Note**:
잘 모르겠다면, <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">문서에서 `async`와 `await`</a>에 관한 _"급하세요?"_ 섹션을 확인해 보십시오.
잘 모르겠다면, <a href="https://fastapi.tiangolo.com/ko/async/#in-a-hurry" target="_blank">문서에서 `async`와 `await`</a>에 관한 _"급하세요?"_ 섹션을 확인해 보십시오.
</details>
### 실행하기
### 실행하기 { #run-it }
서버를 실행하십시오:
다음 명령으로 서버를 실행하십시오:
<div class="termy">
```console
$ uvicorn main:app --reload
$ fastapi dev main.py
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
@@ -217,17 +233,17 @@ INFO: Application startup complete.
</div>
<details markdown="1">
<summary><code>uvicorn main:app --reload</code> 명령에 관하여...</summary>
<summary><code>fastapi dev main.py</code> 명령에 관하여...</summary>
명령 `uvicorn main:app`은 다음을 나타냅니다:
`fastapi dev` 명령 `main.py` 파일을 읽고, 그 안의 **FastAPI** 앱을 감지한 다음, <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>을 사용해 서버를 시작합니다.
* `main`: `main.py` 파일 (파이썬 "모듈").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: 코드가 변경된 후 서버 재시작하기. 개발환경에서만 사용하십시오.
기본적으로 `fastapi dev`는 로컬 개발을 위해 auto-reload가 활성화된 상태로 시작됩니다.
자세한 내용은 <a href="https://fastapi.tiangolo.com/ko/fastapi-cli/" target="_blank">FastAPI CLI 문서</a>에서 확인할 수 있습니다.
</details>
### 확인하기
### 확인하기 { #check-it }
브라우저로 <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>를 열어보십시오.
@@ -241,10 +257,10 @@ INFO: Application startup complete.
* _경로_ `/` 및 `/items/{item_id}`에서 HTTP 요청 받기.
* 두 _경로_ 모두 `GET` <em>연산</em>(HTTP _메소드_ 로 알려진)을 받습니다.
* _경로_ `/items/{item_id}`는 _경로 매개변수_ `int`형 이어야 하는 `item_id`를 가지고 있습니다.
* _경로_ `/items/{item_id}`는 선택적인 `str`형 이어야 하는 _경로 매개변수_ `q`를 가지고 있습니다.
* _경로_ `/items/{item_id}`는 `int`형 이어야 하는 _경로 매개변수_ `item_id`를 가지고 있습니다.
* _경로_ `/items/{item_id}`는 선택적인 `str`형 _쿼리 매개변수_ `q`를 가지고 있습니다.
### 대화형 API 문서
### 대화형 API 문서 { #interactive-api-docs }
이제 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>로 가보십시오.
@@ -252,7 +268,7 @@ INFO: Application startup complete.
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### 대안 API 문서
### 대안 API 문서 { #alternative-api-docs }
그리고 이제 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>로 가봅시다.
@@ -260,13 +276,13 @@ INFO: Application startup complete.
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## 예제 심화
## 예제 업그레이드 { #example-upgrade }
이제 `PUT` 요청에 있는 본문(Body)을 받기 위해 `main.py` 수정해봅시다.
이제 `PUT` 요청에서 본문을 받기 위해 `main.py` 파일을 수정해봅시다.
Pydantic을 이용해 파이썬 표준 타입으로 본문을 선언합니다.
Pydantic 덕분에 표준 Python 타입을 사용해 본문을 선언합니다.
```Python hl_lines="4 9 10 11 12 25 26 27"
```Python hl_lines="4 9-12 25-27"
from typing import Union
from fastapi import FastAPI
@@ -296,25 +312,25 @@ def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
서버 자동으로 리로딩 할 수 있어야 합니다 (위에서 `uvicorn` 명령에 `--reload`을 추가 했기 때문입니다).
`fastapi dev` 서버 자동으로 리로딩되어야 합니다.
### 대화형 API 문서 업그레이드
### 대화형 API 문서 업그레이드 { #interactive-api-docs-upgrade }
이제 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>로 이동합니다.
* 대화형 API 문서 새 본문과 함께 자동으로 업데이트니다:
* 대화형 API 문서 새 본문을 포함해 자동으로 업데이트니다:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* "Try it out" 버튼을 클릭하면, 매개변수를 채울 수 있게 해주고 직접 API와 상호작용 할 수 있습니다:
* "Try it out" 버튼을 클릭하면, 매개변수를 채우고 API와 직접 상호작용할 수 있습니다:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* 그러고 나서 "Execute" 버튼을 누르면, 사용자 인터페이스 API와 통신하고 매개변수를 전송하며 그 결과를 가져와서 화면에 표시합니다:
* 그런 다음 "Execute" 버튼을 클릭하면, 사용자 인터페이스 API와 통신하고 매개변수를 전송한 뒤 결과를 받아 화면에 표시합니다:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### 대안 API 문서 업그레이드
### 대안 API 문서 업그레이드 { #alternative-api-docs-upgrade }
그리고 이제, <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>로 이동합니다.
@@ -322,7 +338,7 @@ def update_item(item_id: int, item: Item):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### 요약
### 요약 { #recap }
요약하면, 여러분은 매개변수의 타입, 본문 등을 함수 매개변수로서 **한번에** 선언했습니다.
@@ -351,8 +367,8 @@ item: Item
* 타입 검사.
* 데이터 검증:
* 데이터가 유효하지 않을 때 자동으로 생성하는 명확한 에러.
* 중첩된 JSON 객체에 대한 유효성 검사.
* 입력 데이터 <abbr title="다음으로 알려진: 직렬화, 파싱, 마샬링">변환</abbr>: 네트워크에서 파이썬 데이터 및 타입으로 전송. 읽을 수 있는 것들:
* 깊이 중첩된 JSON 객체에 대한 유효성 검사.
* 입력 데이터 <abbr title="also known as: serialization, parsing, marshalling">변환</abbr>: 네트워크에서 파이썬 데이터 및 타입으로 전송. 읽을 수 있는 것들:
* JSON.
* 경로 매개변수.
* 쿼리 매개변수.
@@ -360,7 +376,7 @@ item: Item
* 헤더.
* 폼(Forms).
* 파일.
* 출력 데이터 <abbr title="다음으로 알려진: 직렬화, 파싱, 마샬링">변환</abbr>: 파이썬 데이터 및 타입을 네트워크 데이터로 전환(JSON 형식으로):
* 출력 데이터 <abbr title="also known as: serialization, parsing, marshalling">변환</abbr>: 파이썬 데이터 및 타입을 네트워크 데이터로 전환(JSON 형식으로):
* 파이썬 타입 변환 (`str`, `int`, `float`, `bool`, `list`, 등).
* `datetime` 객체.
* `UUID` 객체.
@@ -377,13 +393,13 @@ item: Item
* `GET` 및 `PUT` 요청에 `item_id`가 경로에 있는지 검증.
* `GET` 및 `PUT` 요청에 `item_id`가 `int` 타입인지 검증.
* 그렇지 않다면 클라이언트는 유용하고 명확한 에러를 볼 수 있습니다.
* `GET` 요청에 `q`라는 선택적인 쿼리 매개변수가 검사(`http://127.0.0.1:8000/items/foo?q=somequery`처럼).
* `GET` 요청에 `q`라는 선택적인 쿼리 매개변수가 있는지 검사(`http://127.0.0.1:8000/items/foo?q=somequery`처럼).
* `q` 매개변수는 `= None`으로 선언되었기 때문에 선택사항입니다.
* `None`이 없다면 필수사항입니다(`PUT`의 경우와 마찬가지로).
* `/items/{item_id}`으로의 `PUT` 요청은 본문을 JSON으로 읽음:
* `name`을 필수 속성으로 갖고 `str` 형인지 검사.
* `price` 필수 속성으로 갖고 `float` 형지 검사.
* 만약 주어진다면, `is_offer`를 선택 속성으로 갖고 `bool` 형지 검사.
* `price` 필수 속성으로 갖고 `float` 형이어야 하는지 검사.
* 만약 주어진다면, `is_offer`를 선택 속성으로 갖고 `bool` 형이어야 하는지 검사.
* 이 모든 것은 깊이 중첩된 JSON 객체에도 적용됩니다.
* JSON을 변환하거나 JSON으로 변환하는 것을 자동화.
* 다음에서 사용할 수 있는 모든 것을 OpenAPI로 문서화:
@@ -417,30 +433,88 @@ item: Item
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
더 많은 기능을 포함한 보다 완전한 예제의 경우, <a href="https://fastapi.tiangolo.com/tutorial/">튜토리얼 - 사용자 가이드</a>를 보십시오.
더 많은 기능을 포함한 보다 완전한 예제의 경우, <a href="https://fastapi.tiangolo.com/ko/tutorial/">튜토리얼 - 사용자 가이드</a>를 보십시오.
**스포일러 주의**: 튜토리얼 - 사용자 가이드는:
* 서로 다른 장소에서 **매개변수** 선언: **헤더**, **쿠키**, **폼 필드** 그리고 **파일**.
* `maximum_length` 또는 `regex`처럼 **유효성 제약**하는 방법.
* 강력하고 사용하기 쉬운 **<abbr title="컴포넌트, 리소스, 제공자, 서비스, injectables라 알려진">의존성 주입</abbr>** 시스템.
* 강력하고 사용하기 쉬운 **<abbr title="also known as components, resources, providers, services, injectables">의존성 주입</abbr>** 시스템.
* **OAuth2** 지원을 포함한 **JWT tokens** 및 **HTTP Basic**을 갖는 보안과 인증.
* (Pydantic 덕분에) **깊은 중첩 JSON 모델**을 선언하는데 더 진보한 (하지만 마찬가지로 쉬운) 기술.
* <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> 및 기타 라이브러리와의 **GraphQL** 통합.
* (Starlette 덕분에) 많은 추가 기능:
* **웹 소켓**
* **GraphQL**
* HTTPX 및 `pytest`에 기반한 극히 쉬운 테스트
* **CORS**
* **쿠키 세션**
* ...기타 등등.
## 성능
### 앱 배포하기(선택 사항) { #deploy-your-app-optional }
독립된 TechEmpower 벤치마크에서 Uvicorn에서 작동하는 FastAPI 어플리케이션이 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">사용 가능한 가장 빠른 프레임워크 중 하나</a>로 Starlette와 Uvicorn(FastAPI에서 내부적으로 사용)에만 밑돌고 있습니다. (*)
선택적으로 FastAPI 앱을 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>에 배포할 수 있습니다. 아직이라면 대기자 명단에 등록해 보세요. 🚀
자세한 내용은 <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">벤치마크</a> 섹션을 보십시오.
이미 **FastAPI Cloud** 계정이 있다면(대기자 명단에서 초대해 드렸습니다 😉), 한 번의 명령으로 애플리케이션을 배포할 수 있습니다.
## 선택가능한 의존성
배포하기 전에, 로그인되어 있는지 확인하세요:
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
그런 다음 앱을 배포하세요:
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
이게 전부입니다! 이제 해당 URL에서 앱에 접근할 수 있습니다. ✨
#### FastAPI Cloud 소개 { #about-fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>**는 **FastAPI** 뒤에 있는 동일한 작성자와 팀이 만들었습니다.
최소한의 노력으로 API를 **빌드**, **배포**, **접근**하는 과정을 간소화합니다.
FastAPI로 앱을 빌드할 때의 동일한 **개발자 경험**을 클라우드에 **배포**하는 데까지 확장해 줍니다. 🎉
FastAPI Cloud는 *FastAPI and friends* 오픈 소스 프로젝트의 주요 스폰서이자 자금 제공자입니다. ✨
#### 다른 클라우드 제공자에 배포하기 { #deploy-to-other-cloud-providers }
FastAPI는 오픈 소스이며 표준을 기반으로 합니다. 선택한 어떤 클라우드 제공자에도 FastAPI 앱을 배포할 수 있습니다.
클라우드 제공자의 가이드를 따라 FastAPI 앱을 배포하세요. 🤓
## 성능 { #performance }
독립된 TechEmpower 벤치마크에서 Uvicorn에서 작동하는 FastAPI 애플리케이션이 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">사용 가능한 가장 빠른 Python 프레임워크 중 하나</a>로 Starlette와 Uvicorn(FastAPI에서 내부적으로 사용)에만 밑돌고 있습니다. (*)
자세한 내용은 <a href="https://fastapi.tiangolo.com/ko/benchmarks/" class="internal-link" target="_blank">벤치마크</a> 섹션을 보십시오.
## 의존성 { #dependencies }
FastAPI는 Pydantic과 Starlette에 의존합니다.
### `standard` 의존성 { #standard-dependencies }
FastAPI를 `pip install "fastapi[standard]"`로 설치하면 `standard` 그룹의 선택적 의존성이 함께 설치됩니다.
Pydantic이 사용하는:
@@ -448,21 +522,38 @@ Pydantic이 사용하는:
Starlette이 사용하는:
* <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - `TestClient`를 사용하려면 필요.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - 기본 템플릿 설정을 사용하려면 필요.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - `request.form()`과 함께 <abbr title="HTTP 요청에서 파이썬 데이터로 가는 문자열 변환">"parsing"</abbr> 지원을 원하면 필요.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` 지원을 위해 필요.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Starlette의 `SchemaGenerator` 지원을 위해 필요 (FastAPI와 쓸때는 필요 없을 것입니다).
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - `GraphQLApp` 지원을 위해 필요.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - `TestClient`를 사용하려면 필요.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - 기본 템플릿 설정을 사용하려면 필요.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - `request.form()`과 함께 form <abbr title="HTTP 요청에서 파이썬 데이터로 가는 문자열 변환">"parsing"</abbr> 지원을 원하면 필요.
FastAPI / Starlette이 사용하는:
FastAPI 사용하는:
* <a href="http://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - 애플리케이션을 로드하고 제공하는 서버.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - `ORJSONResponse`을 사용하려면 필요.
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - 애플리케이션을 로드하고 제공하는 서버를 위한 것입니다. 여기에는 고성능 서빙에 필요한 일부 의존성(예: `uvloop`)이 포함된 `uvicorn[standard]`가 포함됩니다.
* `fastapi-cli[standard]` - `fastapi` 명령을 제공하기 위한 것입니다.
* 여기에는 FastAPI 애플리케이션을 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>에 배포할 수 있게 해주는 `fastapi-cloud-cli`가 포함됩니다.
### `standard` 의존성 없이 { #without-standard-dependencies }
`standard` 선택적 의존성을 포함하고 싶지 않다면, `pip install "fastapi[standard]"` 대신 `pip install fastapi`로 설치할 수 있습니다.
### `fastapi-cloud-cli` 없이 { #without-fastapi-cloud-cli }
표준 의존성과 함께 FastAPI를 설치하되 `fastapi-cloud-cli` 없이 설치하고 싶다면, `pip install "fastapi[standard-no-fastapi-cloud-cli]"`로 설치할 수 있습니다.
### 추가 선택적 의존성 { #additional-optional-dependencies }
추가로 설치하고 싶을 수 있는 의존성도 있습니다.
추가 선택적 Pydantic 의존성:
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - 설정 관리를 위한 것입니다.
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - Pydantic에서 사용할 추가 타입을 위한 것입니다.
추가 선택적 FastAPI 의존성:
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - `ORJSONResponse`를 사용하려면 필요.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - `UJSONResponse`를 사용하려면 필요.
`pip install fastapi[all]`를 통해 이 모두를 설치 할 수 있습니다.
## 라이센스
## 라이센스 { #license }
이 프로젝트는 MIT 라이센스 조약에 따라 라이센스가 부여됩니다.

View File

@@ -1,5 +1,5 @@
# 배우기
# 배우기 { #learn }
여기 **FastAPI**를 배우기 위한 입문 자료와 자습서가 있습니다.
여기 **FastAPI**를 배우기 위한 입문 섹션과 자습서가 있습니다.
여러분은 FastAPI를 배우기 위 **책**, **강의**, **공식 자료** 그리고 추천받은 방법을 고려할 수 있습니다. 😎
여러분은 이것을 FastAPI를 배우기 위 **책**, **강의**, **공식**이자 권장되는 방법으로 생각할 수 있습니다. 😎

View File

@@ -1,4 +1,4 @@
# Full Stack FastAPI 템플릿
# Full Stack FastAPI 템플릿 { #full-stack-fastapi-template }
템플릿은 일반적으로 특정 설정과 함께 제공되지만, 유연하고 커스터마이징이 가능하게 디자인 되었습니다. 이 특성들은 여러분이 프로젝트의 요구사항에 맞춰 수정, 적용을 할 수 있게 해주고, 템플릿이 완벽한 시작점이 되게 해줍니다. 🏁
@@ -6,23 +6,23 @@
GitHub 저장소: <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Full Stack FastAPI 템플릿</a>
## Full Stack FastAPI 템플릿 - 기술 스택과 기능들
## Full Stack FastAPI 템플릿 - 기술 스택과 기능들 { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com): Python 백엔드 API.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com): Python SQL 데이터 상호작용을 위한 (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev): FastAPI에 의해 사용되는, 데이터 검증과 설정관리.
- 💾 [PostgreSQL](https://www.postgresql.org): SQL 데이터베이스.
- 🚀 [React](https://react.dev): 프론트엔드.
- 💃 TypeScript, hooks, [Vite](https://vitejs.dev) 및 기타 현대적인 프론트엔드 스택을 사용.
- 🎨 [Chakra UI](https://chakra-ui.com): 프론트엔드 컴포넌트.
- Python 백엔드 API를 위한 [**FastAPI**](https://fastapi.tiangolo.com).
- 🧰 Python SQL 데이터베이스 상호작용을 위한 [SQLModel](https://sqlmodel.tiangolo.com) (ORM).
- 🔍 FastAPI에 의해 사용되는, 데이터 검증과 설정 관리를 위한 [Pydantic](https://docs.pydantic.dev).
- 💾 SQL 데이터베이스로서의 [PostgreSQL](https://www.postgresql.org).
- 🚀 프론트엔드를 위한 [React](https://react.dev).
- 💃 TypeScript, hooks, Vite 및 기타 현대적인 프론트엔드 스택을 사용.
- 🎨 프론트엔드 컴포넌트를 위한 [Tailwind CSS](https://tailwindcss.com) 및 [shadcn/ui](https://ui.shadcn.com).
- 🤖 자동으로 생성된 프론트엔드 클라이언트.
- 🧪 E2E 테스트를 위한 [Playwright](https://playwright.dev).
- 🧪 End-to-End 테스트를 위한 [Playwright](https://playwright.dev).
- 🦇 다크 모드 지원.
- 🐋 [Docker Compose](https://www.docker.com): 개발 환경과 프로덕션(운영).
- 🐋 개발 환경과 프로덕션(운영)을 위한 [Docker Compose](https://www.docker.com).
- 🔒 기본으로 지원되는 안전한 비밀번호 해싱.
- 🔑 JWT 토큰 인증.
- 🔑 JWT (JSON Web Token) 인증.
- 📫 이메일 기반 비밀번호 복구.
- ✅ [Pytest]를 이용한 테스트(https://pytest.org).
- 📞 [Traefik](https://traefik.io): 리버스 프록시 / 로드 밸런서.
- ✅ [Pytest](https://pytest.org)를 이용한 테스트.
- 📞 리버스 프록시 / 로드 밸런서로서의 [Traefik](https://traefik.io).
- 🚢 Docker Compose를 이용한 배포 지침: 자동 HTTPS 인증서를 처리하기 위한 프론트엔드 Traefik 프록시 설정 방법을 포함.
- 🏭 GitHub Actions를 기반으로 CI (지속적인 통합) 및 CD (지속적인 배포).

View File

@@ -1,313 +1,466 @@
# 파이썬 타입 소개
# 파이썬 타입 소개 { #python-types-intro }
파이썬은 선택적으로 "타입 힌트(type hints)"를 지원합니다.
파이썬은 선택적으로 "타입 힌트(type hints)"(“type annotations”라고도 함)를 지원합니다.
이러한 **타입 힌트**은 변수의 <abbr title="예를 들면: str, int, float, bool">타입</abbr>을 선언할 수 있게 해주는 특수한 구문입니다.
이러한 **"타입 힌트"** 또는 애너테이션은 변수의 <abbr title="for example: str, int, float, bool">타입</abbr>을 선언할 수 있게 해주는 특수한 구문입니다.
변수의 타입을 지정하면 에디터와 툴이도움을 줄 수 있게 됩니다.
변수의 타입을 선언하면 에디터와 도구가지원을 제공할 수 있니다.
이 문서는 파이썬 타입 힌트에 대한 **빠른 자습서 / 내용환기** 수준의 문서입니다. 여기서는 **FastAPI**를 쓰기 위한 최소한의 내용만을 다룹니다.
이 문서는 파이썬 타입 힌트에 대한 **빠른 자습서 / 내용 환기**입니다. **FastAPI**와 함께 사용하기 위해 필요한 최소한만 다룹니다... 실제로는 아주 조금만 있으면 됩니다.
**FastAPI**는 타입 힌트에 기반을 두고 있으며, 이는 많은 장점과 이익이 있습니다.
**FastAPI**는 모두 이러한 타입 힌트에 기반을 두고 있으며, 이는 많은 장점과 이점을 제공합니다.
비록 **FastAPI**를 쓰지 않는다고 하더라도, 조금이라도 알아두면 도움이 될 것입니다.
하지만 **FastAPI**를 전혀 사용하지 않더라도, 타입 힌트를 조금만 배워도 도움이 니다.
/// note | 참고
파이썬에 능숙하셔서 타입 힌트에 대해 모두 아신다면, 다음 챕터로 건너뛰세요.
파이썬에 능숙하 타입 힌트에 대해 이미 모두 알고 있다면, 다음 장으로 건너뛰세요.
///
## 동기 부여
## 동기 부여 { #motivation }
간단한 예제부터 시작해봅시다:
간단한 예제 시작해봅시다:
{* ../../docs_src/python_types/tutorial001.py *}
{* ../../docs_src/python_types/tutorial001_py39.py *}
이 프로그램을 실행한 결과값:
이 프로그램을 호출하면 다음이 출력됩니다:
```
John Doe
```
함수는 아래와 같이 실행됩니다:
함수는 다음을 수행합니다:
* `first_name``last_name`를 받습니다.
* `title()`로 각 첫 자를 대문자로 변환시킵니다.
* 두 단어를 중간에 공백을 두고 <abbr title="두 개를 하나로 차례차례 이어지게 하다">연결</abbr>합니다.
* `title()`로 각각의자를 대문자로 변환니다.
* 가운데에 공백을 두고 <abbr title="Puts them together, as one. With the contents of one after the other.">연결</abbr>합니다.
{* ../../docs_src/python_types/tutorial001.py hl[2] *}
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
### 수정하기 { #edit-it }
### 코드 수정
매우 간단한 프로그램입니다.
이건 매우 간단한 프로그램입니다.
하지만 이제, 이것을 처음부터 작성한다고 상상해봅시다.
그런데 처음부터 작성한다고 생각을 해봅시다.
어느 시점엔 함수를 정의하기 시작했고, 매개변수도 준비해두었을 겁니다...
여러분은 매개변수를 준비했고, 함수를 정의하기 시작했을 겁니다.
그런데 "첫 글자를 대문자로 변환하는 그 메서드"를 호출해야 합니다.
이때 "첫 글자를 대문자로 바꾸는 함수"를 호출해야 합니다.
`upper`였나요? `uppercase`였나요? `first_uppercase`? `capitalize`?
`upper`였나? 아니면 `uppercase`? `first_uppercase`? `capitalize`?
그 다음, 개발자들의 오랜 친구인 에디터 자동완성을 시도합니다.
그때 개발자들의 오랜 친구, 에디터 자동완성을 시도해봅니다.
함수의 첫 번째 매개변수인 `first_name`을 입력하고, 점(`.`)을 찍은 다음, 완성을 트리거하기 위해 `Ctrl+Space`를 누릅니다.
당신은 `first_name`를 입력한 뒤 점(`.`)을 입력하고 자동완성을 켜기 위해서 `Ctrl+Space`를 눌렀습니다.
하지만 슬프게도 아무런 도움이 되지 않습니다:
하지만, 슬프게도 쓸만한 게 아무것도 없습니다:
<img src="/img/python-types/image01.png">
### 타입 추가하기
### 타입 추가하기 { #add-types }
이전 버전에서 한 줄만 수정해봅시다.
저희는 이 함수의 매개변수 부분:
함수의 매개변수인 정확히 이 부분:
```Python
first_name, last_name
```
을 아래와 같이 바꿀 겁니다:
에서:
```Python
first_name: str, last_name: str
```
로 바꾸겠습니다.
이게 다입니다.
"타입 힌트"입니다:
것들이 "타입 힌트"입니다:
{* ../../docs_src/python_types/tutorial002.py hl[1] *}
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
타입힌트는 다음과 같이 기본 값을 선언하는 것과는 다릅니다:
이것은 다음처럼 기본값을 선언하는 것과는 다릅니다:
```Python
first_name="john", last_name="doe"
```
이는 다른 것입니다.
다른 것입니다.
등호(`=`) 대신 콜론(`:`)을 쓰고 있습니다.
등호(`=`)가 아니라 콜론(`:`)을 사용합니다.
일반적으로 타입힌트를 추가한다고 해서 특별하게 어떤 일이 일어나지도 않습니다.
그리고 보통 타입 힌트를 추가해도, 타입 힌트 없이 일어나는 일과 비교해 특별히 달라지는 것은 없습니다.
그렇지만 이제, 다시 함수를 만드는 중이라고 생각해봅시다. 다만 이번엔 타입 힌트가 있습니다.
지만 이제, 타입 힌트를 포함해 그 함수를 다시 만드는 중이라고 상상해봅시다.
같은 상황에서 `Ctrl+Space`로 자동완성을 작동시키면,
같은 지점에서 `Ctrl+Space`로 자동완성을 트리거하면 다음이 보입니다:
<img src="/img/python-types/image02.png">
아래와 같이 "그렇지!"하는 옵션이 나올때까지 스크롤을 내려서 볼 수 있습니다:
그러면 스크롤하며 옵션을 보다가, "기억나는" 것을 찾을 수 있습니다:
<img src="/img/python-types/image03.png">
## 더 큰 동기부여
## 더 큰 동기부여 { #more-motivation }
아래 함수를 보면, 이미 타입 힌트가 적용되어 있는 걸 볼 수 있습니다:
함수를 확인해보세요. 이미 타입 힌트가 있습니다:
{* ../../docs_src/python_types/tutorial003.py hl[1] *}
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
편집기가 변수의 타입을 알고 있기 때문에, 자동완성 뿐 아니라 에러도 확인할 수 있습니다:
에디터가 변수의 타입을 알고 있기 때문에, 자동완성만 되는 게 아니라 오류 검사도 할 수 있습니다:
<img src="/img/python-types/image04.png">
이제 고쳐야하는 걸 알기 때문에, `age``str(age)`과 같이 문자열로 바꾸게 됩니다:
이제 고쳐야 한다는 것을 알고, `age``str(age)` 문자열로 바니다:
{* ../../docs_src/python_types/tutorial004.py hl[2] *}
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
## 타입 선언 { #declaring-types }
## 타입 선언
방금 타입 힌트를 선언하는 주요 위치를 보았습니다. 함수 매개변수입니다.
방금 함수의 매개변수로써 타입 힌트를 선언하는 주요 장소를 보았습니다.
이것은 **FastAPI**와 함께 사용할 때도 주요 위치입니다.
이 위치는 여러분이 **FastAPI**와 함께 이를 사용하는 주요 장소입니다.
### Simple 타입
### Simple 타입 { #simple-types }
`str`뿐 아니라 모든 파이썬 표준 타입을 선언할 수 있습니다.
예를 들:
예를 들어 다음을 사용할 수 있습니다:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005.py hl[1] *}
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
### 타입 매개변수가 있는 Generic(제네릭) 타입 { #generic-types-with-type-parameters }
### 타입 매개변수를 활용한 Generic(제네릭) 타입
`dict`, `list`, `set`, `tuple`처럼 다른 값을 담을 수 있는 데이터 구조가 있습니다. 그리고 내부 값에도 각자의 타입이 있을 수 있습니다.
`dict`, `list`, `set`, `tuple`과 같은 값을 저장할 수 있는 데이터 구조가 있고, 내부의 값은 각자의 타입을 가질 수도 있습니다.
이렇게 내부 타입을 가지는 타입을 "**generic**" 타입이라고 부릅니다. 그리고 내부 타입까지 포함해 선언할 수도 있습니다.
타입과 내부 타입을 선언하기 위해서는 파이썬 표준 모듈 `typing`이용해야 합니다.
이런 타입과 내부 타입을 선언하려면 표준 파이썬 모듈 `typing`사용할 수 있습니다. 이 모듈은 이러한 타입 힌트를 지원하기 위해 존재합니다.
구체적으로는 아래 타입 힌트를 지원합니다.
#### 더 최신 버전의 Python { #newer-versions-of-python }
#### `List`
`typing`을 사용하는 문법은 Python 3.6부터 최신 버전까지, Python 3.9, Python 3.10 등을 포함한 모든 버전과 **호환**됩니다.
예를 들면, `str``list`인 변수를 정의해봅시다.
파이썬이 발전함에 따라 **더 최신 버전**에서는 이러한 타입 애너테이션 지원이 개선되며, 많은 경우 타입 애너테이션을 선언하기 위해 `typing` 모듈을 import해서 사용할 필요조차 없게 됩니다.
`typing`에서 `List`(대문자 `L`)를 import 합니다.
프로젝트에서 더 최신 버전의 파이썬을 선택할 수 있다면, 그 추가적인 단순함을 활용할 수 있습니다.
{* ../../docs_src/python_types/tutorial006.py hl[1] *}
이 문서 전체에는 각 파이썬 버전과 호환되는 예제가 있습니다(차이가 있을 때).
예를 들어 "**Python 3.6+**"는 Python 3.6 이상(3.7, 3.8, 3.9, 3.10 등 포함)과 호환된다는 뜻입니다. 그리고 "**Python 3.9+**"는 Python 3.9 이상(3.10 등 포함)과 호환된다는 뜻입니다.
콜론(`:`) 문법을 이용하여 변수를 선언합니다.
**최신 버전의 Python**을 사용할 수 있다면, 최신 버전용 예제를 사용하세요. 예를 들어 "**Python 3.10+**"처럼, 가장 **좋고 가장 단순한 문법**을 갖게 됩니다.
타입으로는 `List`를 넣어줍니다.
#### List { #list }
이때 배열은 내부 타입을 포함하는 타입이기 때문에 대괄호 안에 넣어줍니다.
예를 들어, `str``list`인 변수를 정의해봅시다.
{* ../../docs_src/python_types/tutorial006.py hl[4] *}
같은 콜론(`:`) 문법으로 변수를 선언합니다.
타입으로 `list`를 넣습니다.
/// tip | 팁
대괄호 안의 내부 타입은 "타입 매개변수(type paramters)"라고 합니다.
이번 예제에서는 `str``List`에 들어간 타입 매개변수 입니다.
///
이는 "`items``list`인데, 배열에 들어있는 아이템 각각은 `str`이다"라는 뜻입니다.
이렇게 함으로써, 에디터는 배열에 들어있는 아이템을 처리할때도 도움을 줄 수 있게 됩니다:
<img src="/img/python-types/image05.png">
타입이 없으면 이건 거의 불가능이나 다름 없습니다.
변수 `item``items`의 개별 요소라는 사실을 알아두세요.
그리고 에디터는 계속 `str`라는 사실을 알고 도와줍니다.
#### `Tuple`과 `Set`
`tuple``set`도 동일하게 선언할 수 있습니다.
{* ../../docs_src/python_types/tutorial007.py hl[1,4] *}
이 뜻은 아래와 같습니다:
* 변수 `items_t`는, 차례대로 `int`, `int`, `str``tuple`이다.
* 변수 `items_s`는, 각 아이템이 `bytes``set`이다.
#### `Dict`
`dict`를 선언하려면 컴마로 구분된 2개의 파라미터가 필요합니다.
첫 번째 매개변수는 `dict`의 키(key)이고,
두 번째 매개변수는 `dict`의 값(value)입니다.
{* ../../docs_src/python_types/tutorial008.py hl[1,4] *}
이 뜻은 아래와 같습니다:
* 변수 `prices``dict`이다:
* `dict`의 키(key)는 `str`타입이다. (각 아이템의 이름(name))
* `dict`의 값(value)는 `float`타입이다. (각 아이템의 가격(price))
#### `Optional`
`str`과 같이 타입을 선언할 때 `Optional`을 쓸 수도 있는데, "선택적(Optional)"이기때문에 `None`도 될 수 있습니다:
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009.py!}
```
`Optional[str]`을 `str` 대신 쓰게 되면, 특정 값이 실제로는 `None`이 될 수도 있는데 항상 `str`이라고 가정하는 상황에서 에디터가 에러를 찾게 도와줄 수 있습니다.
#### Generic(제네릭) 타입
이 타입은 대괄호 안에 매개변수를 가지며, 종류는:
* `List`
* `Tuple`
* `Set`
* `Dict`
* `Optional`
* ...등등
위와 같은 타입은 **Generic(제네릭) 타입** 혹은 **Generics(제네릭스)**라고 불립니다.
### 타입으로서의 클래스
변수의 타입으로 클래스를 선언할 수도 있습니다.
이름(name)을 가진 `Person` 클래스가 있다고 해봅시다.
{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
그렇게 하면 변수를 `Person`이라고 선언할 수 있게 됩니다.
{* ../../docs_src/python_types/tutorial010.py hl[6] *}
그리고 역시나 모든 에디터 도움을 받게 되겠죠.
<img src="/img/python-types/image06.png">
## Pydantic 모델
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>은 데이터 검증(Validation)을 위한 파이썬 라이브러리입니다.
당신은 속성들을 포함한 클래스 형태로 "모양(shape)"을 선언할 수 있습니다.
그리고 각 속성은 타입을 가지고 있습니다.
이 클래스를 활용하여서 값을 가지고 있는 인스턴스를 만들게 되면, 필요한 경우에는 적당한 타입으로 변환까지 시키기도 하여 데이터가 포함된 객체를 반환합니다.
그리고 결과 객체에 대해서는 에디터의 도움을 받을 수 있게 됩니다.
Pydantic 공식 문서 예시:
{* ../../docs_src/python_types/tutorial011.py *}
`list`는 내부 타입을 포함하는 타입이므로, 그 타입들을 대괄호 안에 넣습니다:
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
/// info | 정보
Pydantic<에 대해 더 배우고 싶다면 <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">공식 문서</a>를 참고하세요.</a>
대괄호 안의 내부 타입은 "type parameters"라고 부릅니다.
이 경우 `str``list`에 전달된 타입 매개변수입니다.
///
**FastAPI**는 모두 Pydantic을 기반으로 되어 있습니다.
이는 "변수 `items``list`이고, 이 `list`의 각 아이템은 `str`이다"라는 뜻입니다.
이 모든 것이 실제로 어떻게 사용되는지에 대해서는 [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank} 에서 더 많이 확인하실 수 있습니다.
이렇게 하면, 에디터는 리스트의 아이템을 처리하는 동안에도 지원을 제공할 수 있습니다:
## **FastAPI**에서의 타입 힌트
<img src="/img/python-types/image05.png">
**FastAPI**는 여러 부분에서 타입 힌트의 장점을 취하고 있습니다.
타입이 없으면, 이는 거의 불가능합니다.
**FastAPI**에서 타입 힌트와 함께 매개변수를 선언하면 장점은:
변수 `item`이 리스트 `items`의 요소 중 하나라는 점에 주목하세요.
그리고 에디터는 여전히 이것이 `str`임을 알고, 그에 대한 지원을 제공합니다.
#### Tuple과 Set { #tuple-and-set }
`tuple``set`도 동일하게 선언할 수 있습니다:
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
이는 다음을 의미합니다:
* 변수 `items_t`는 3개의 아이템을 가진 `tuple`이며, `int`, 또 다른 `int`, 그리고 `str`입니다.
* 변수 `items_s``set`이며, 각 아이템의 타입은 `bytes`입니다.
#### Dict { #dict }
`dict`를 정의하려면, 쉼표로 구분된 2개의 타입 매개변수를 전달합니다.
첫 번째 타입 매개변수는 `dict`의 키를 위한 것입니다.
두 번째 타입 매개변수는 `dict`의 값을 위한 것입니다:
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
이는 다음을 의미합니다:
* 변수 `prices``dict`입니다:
*`dict`의 키는 `str` 타입입니다(예: 각 아이템의 이름).
*`dict`의 값은 `float` 타입입니다(예: 각 아이템의 가격).
#### Union { #union }
변수가 **여러 타입 중 어떤 것이든** 될 수 있다고 선언할 수 있습니다. 예를 들어 `int` 또는 `str`입니다.
Python 3.6 이상(3.10 포함)에서는 `typing``Union` 타입을 사용하고, 대괄호 안에 허용할 수 있는 타입들을 넣을 수 있습니다.
Python 3.10에는 가능한 타입들을 <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>세로 막대(`|`)</abbr>로 구분해 넣을 수 있는 **새 문법**도 있습니다.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
두 경우 모두 이는 `item`이 `int` 또는 `str`일 수 있다는 뜻입니다.
#### `None`일 수도 있음 { #possibly-none }
값이 `str` 같은 타입일 수도 있지만, `None`일 수도 있다고 선언할 수 있습니다.
Python 3.6 이상(3.10 포함)에서는 `typing` 모듈에서 `Optional`을 import해서 사용하여 선언할 수 있습니다.
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009_py39.py!}
```
그냥 `str` 대신 `Optional[str]`을 사용하면, 값이 항상 `str`이라고 가정하고 있지만 실제로는 `None`일 수도 있는 상황에서 에디터가 오류를 감지하도록 도와줍니다.
`Optional[Something]`은 사실 `Union[Something, None]`의 축약이며, 서로 동등합니다.
또한 이는 Python 3.10에서 `Something | None`을 사용할 수 있다는 의미이기도 합니다:
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
//// tab | Python 3.9+ alternative
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
#### `Union` 또는 `Optional` 사용하기 { #using-union-or-optional }
Python 3.10 미만 버전을 사용한다면, 아주 **주관적인** 관점에서의 팁입니다:
* 🚨 `Optional[SomeType]` 사용을 피하세요
* 대신 ✨ **`Union[SomeType, None]`을 사용하세요** ✨.
둘은 동등하고 내부적으로는 같은 것이지만, `Optional`이라는 단어가 값이 선택 사항인 것처럼 보일 수 있기 때문에 `Optional` 대신 `Union`을 권장합니다. 실제 의미는 값이 선택 사항이라는 뜻이 아니라, "값이 `None`일 수 있다"는 뜻이기 때문입니다. 선택 사항이 아니고 여전히 필수인 경우에도요.
`Union[SomeType, None]`이 의미를 더 명확하게 드러낸다고 생각합니다.
이건 단지 단어와 이름의 문제입니다. 하지만 그런 단어들이 여러분과 팀원이 코드에 대해 생각하는 방식에 영향을 줄 수 있습니다.
예로, 이 함수를 봅시다:
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
매개변수 `name`은 `Optional[str]`로 정의되어 있지만, **선택 사항이 아닙니다**. 매개변수 없이 함수를 호출할 수 없습니다:
```Python
say_hi() # Oh, no, this throws an error! 😱
```
기본값이 없기 때문에 `name` 매개변수는 **여전히 필수입니다**(*optional*이 아님). 그럼에도 `name`은 값으로 `None`을 허용합니다:
```Python
say_hi(name=None) # This works, None is valid 🎉
```
좋은 소식은 Python 3.10을 사용하면, 타입의 유니온을 정의하기 위해 간단히 `|`를 사용할 수 있어서 이런 걱정을 할 필요가 없다는 점입니다:
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
그러면 `Optional`이나 `Union` 같은 이름에 대해 걱정할 필요도 없습니다. 😎
#### Generic(제네릭) 타입 { #generic-types }
대괄호 안에 타입 매개변수를 받는 이러한 타입들은 **Generic types** 또는 **Generics**라고 부릅니다. 예를 들면:
//// tab | Python 3.10+
대괄호와 내부 타입을 사용해, 동일한 내장 타입들을 제네릭으로 사용할 수 있습니다:
* `list`
* `tuple`
* `set`
* `dict`
그리고 이전 파이썬 버전과 마찬가지로 `typing` 모듈의 다음도 사용할 수 있습니다:
* `Union`
* `Optional`
* ...그 밖의 것들.
Python 3.10에서는 제네릭 `Union`과 `Optional`을 사용하는 대안으로, 타입 유니온을 선언하기 위해 <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>세로 막대(`|`)</abbr>를 사용할 수 있는데, 훨씬 더 좋고 단순합니다.
////
//// tab | Python 3.9+
대괄호와 내부 타입을 사용해, 동일한 내장 타입들을 제네릭으로 사용할 수 있습니다:
* `list`
* `tuple`
* `set`
* `dict`
그리고 `typing` 모듈의 제네릭들:
* `Union`
* `Optional`
* ...그 밖의 것들.
////
### 타입으로서의 클래스 { #classes-as-types }
변수의 타입으로 클래스를 선언할 수도 있습니다.
이름을 가진 `Person` 클래스가 있다고 해봅시다:
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
그러면 `Person` 타입의 변수를 선언할 수 있습니다:
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
그리고 다시, 에디터의 모든 지원을 받을 수 있습니다:
<img src="/img/python-types/image06.png">
이는 "`one_person`은 `Person` 클래스의 **인스턴스**"라는 뜻입니다.
"`one_person`은 `Person`이라는 **클래스**다"라는 뜻이 아닙니다.
## Pydantic 모델 { #pydantic-models }
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>은 데이터 검증을 수행하는 파이썬 라이브러리입니다.
속성을 가진 클래스 형태로 데이터의 "모양(shape)"을 선언합니다.
그리고 각 속성은 타입을 가집니다.
그 다음 그 클래스의 인스턴스를 몇 가지 값으로 생성하면, 값들을 검증하고, (그런 경우라면) 적절한 타입으로 변환한 뒤, 모든 데이터를 가진 객체를 제공합니다.
그리고 그 결과 객체에 대해 에디터의 모든 지원을 받을 수 있습니다.
Pydantic 공식 문서의 예시:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | 정보
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic에 대해 더 알아보려면 문서를 확인하세요</a>.
///
**FastAPI**는 모두 Pydantic에 기반을 두고 있습니다.
이 모든 것은 [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank}에서 실제로 많이 보게 될 것입니다.
/// tip | 팁
Pydantic은 기본값 없이 `Optional` 또는 `Union[Something, None]`을 사용할 때 특별한 동작이 있습니다. 이에 대해서는 Pydantic 문서의 <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Required Optional fields</a>에서 더 읽을 수 있습니다.
///
## 메타데이터 애너테이션이 있는 타입 힌트 { #type-hints-with-metadata-annotations }
파이썬에는 `Annotated`를 사용해 이러한 타입 힌트에 **추가 <abbr title="Data about the data, in this case, information about the type, e.g. a description.">메타데이터</abbr>**를 넣을 수 있는 기능도 있습니다.
Python 3.9부터 `Annotated`는 표준 라이브러리의 일부이므로, `typing`에서 import할 수 있습니다.
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
파이썬 자체는 이 `Annotated`로 아무것도 하지 않습니다. 그리고 에디터와 다른 도구들에게는 타입이 여전히 `str`입니다.
하지만 `Annotated`의 이 공간을 사용해, 애플리케이션이 어떻게 동작하길 원하는지에 대한 추가 메타데이터를 **FastAPI**에 제공할 수 있습니다.
기억해야 할 중요한 점은 `Annotated`에 전달하는 **첫 번째 *타입 매개변수***가 **실제 타입**이라는 것입니다. 나머지는 다른 도구를 위한 메타데이터일 뿐입니다.
지금은 `Annotated`가 존재하며, 표준 파이썬이라는 것만 알면 됩니다. 😎
나중에 이것이 얼마나 **강력**할 수 있는지 보게 될 것입니다.
/// tip | 팁
이것이 **표준 파이썬**이라는 사실은, 에디터에서 가능한 **최고의 개발자 경험**을 계속 얻을 수 있다는 뜻이기도 합니다. 사용하는 도구로 코드를 분석하고 리팩터링하는 등에서도요. ✨
또한 코드가 많은 다른 파이썬 도구 및 라이브러리와 매우 호환된다는 뜻이기도 합니다. 🚀
///
## **FastAPI**에서의 타입 힌트 { #type-hints-in-fastapi }
**FastAPI**는 이러한 타입 힌트를 활용해 여러 가지를 합니다.
**FastAPI**에서는 타입 힌트로 매개변수를 선언하면 다음을 얻습니다:
* **에디터 도움**.
* **타입 확인**.
...그리고 **FastAPI**는 같은 정의를 아래에도 용합니다:
...그리고 **FastAPI**는 같은 선언을 다음에도 용합니다:
* **요구사항 정의**: 요청 경로 매개변수, 쿼리 매개변수, 헤더, 바디, 의존성 등.
* **데이터 변환**: 요청에서 요한 타입으로.
* **데이터 검증**: 각 요청마다:
* 데이터가 유효하지 않은 경우에는 **자동으로 에러**를 생합니다.
* OpenAPI를 활용한 **API 문서화**:
* 자동으로 상호작용하는 유저 인터페이스에 쓰이게 됩니다.
* **요구사항 정의**: 요청 경로 매개변수, 쿼리 매개변수, 헤더, 바디, 의존성 등에서.
* **데이터 변환**: 요청에서 요한 타입으로.
* **데이터 검증**: 각 요청에서:
* 데이터가 유효하지 않을 때 클라이언트에 반환되는 **자동 오류**를 생합니다.
* OpenAPI를 사용해 API를 **문서화**:
* 자동 상호작용 문서 UI에서 사용됩니다.
위 내용이 다소 추상적일 수도 있지만, 걱정마세요. [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank}에서 전부 확인 가능합니다.
이 모든 것이 다소 추상적으로 들릴 수도 있습니다. 걱정하지 마세요. [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank}에서 실제로 확인하게 될 것입니다.
가장 중요한 건, 표준 파이썬 타입을 한 곳에서(클래스를 더하거나, 데코레이터 사용하는 대신) 사용함으로써 **FastAPI**가 당신을 위해 많은 일을 해준다는 사실이죠.
가장 중요한 점은 표준 파이썬 타입을 한 곳에서 사용함으로써(더 많은 클래스, 데코레이터 등을 추가하는 대신) **FastAPI**가 여러분을 위해 많은 일을 해준다는 사실입니다.
/// info | 정보
만약 모든 자습서를 다 보았음에도 타입에 대해보고자 방문한 경우에는 <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">`mypy`에서 제공하는 "cheat sheet"</a>이 좋은 자료가 될 겁니다.
자습서를 모두 끝내고 타입에 대해 더 알아보기 위해 다시 돌아왔다면, 좋은 자료로 <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">`mypy` "cheat sheet"</a>가 있습니다.
///

View File

@@ -1,3 +1,3 @@
# 리소스
# 리소스 { #resources }
추가 리소스, 외부 링크, 기사 등. ✈️
추가 리소스, 외부 링크 등. ✈️

View File

@@ -1,84 +1,86 @@
# 백그라운드 작업
# 백그라운드 작업 { #background-tasks }
FastAPI에서는 응답을 반환한 후에 실행할 백그라운드 작업을 정의할 수 있습니다.
FastAPI에서는 응답을 반환한 *후에* 실행할 백그라운드 작업을 정의할 수 있습니다.
백그라운드 작업은 클라이언트가 응답을 받기 위해 작업이 완료될 때까지 기다릴 필요가 없기 때문에 요청 후에 발생해야하는 작업에 매우 유용합니다.
백그라운드 작업은 요청 후에 발생해야 하지만, 클라이언트가 응답을 받기 전에 작업이 완료될 때까지 기다릴 필요가 없는 작업에 유용합니다.
이러한 작업에는 다음이 포함됩니다.
예를 들면 다음과 같습니다.
* 작업을 수행한 후 전송되는 이메일 알림
* 이메일 서버에 연결하고 이메일을 전송하는 것은 (몇 초 정도) "느린" 경향이 있으므로, 응답은 즉시 반환하고 이메일 알림은 백그라운드에서 전송하는 게 가능합니다.
* 작업을 수행한 후 전송되는 이메일 알림:
* 이메일 서버에 연결하고 이메일을 전송하는 것은 (몇 초 정도) "느린" 경향이 있으므로, 응답은 즉시 반환하고 이메일 알림은 백그라운드에서 전송할 수 있습니다.
* 데이터 처리:
* 예를 들어 처리에 오랜 시간이 걸리는 데이터를 받았을 때 "Accepted" (HTTP 202)을 반환하고, 백그라운드에서 데이터를 처리할 수 있습니다.
* 예를 들어 처리에 오랜 시간이 걸리는 프로세스를 거쳐야 하는 파일을 받았다면, "Accepted"(HTTP 202) 응답을 반환하고 백그라운드에서 파일을 처리할 수 있습니다.
## `백그라운드 작업` 사용
## `BackgroundTasks` 사용 { #using-backgroundtasks }
먼저 아래와 같이 `BackgroundTasks`를 임포트하고, `BackgroundTasks`_경로 작동 함수_ 에서 매개변수로 가져오고 정의합니다.
먼저 `BackgroundTasks`를 임포트하고, `BackgroundTasks` 타입 선언으로 *경로 처리 함수*에 매개변수를 정의합니다:
{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
**FastAPI** `BackgroundTasks` 체를 생성하고, 매개 변수로 전달합니다.
**FastAPI**가 `BackgroundTasks` 타입의 객체를 생성하고 해당 매개변수로 전달합니다.
## 작업 함수 생성
## 작업 함수 생성 { #create-a-task-function }
백그라운드 작업으로 실행할 함수를 정의합니다.
백그라운드 작업으로 실행할 함수를 생성합니다.
것은 단순히 매개변수를 받을 수 있는 표준 함수일 뿐입니다.
매개변수를 받을 수 있는 표준 함수일 뿐입니다.
**FastAPI**는 이것이 `async def` 함수이든, 일반 `def` 함수이든 내부적으로 이를 올바르게 처리합니다.
`async def` 함수일 수도, 일반 `def` 함수일 수도 있으며, **FastAPI**가 이를 올바르게 처리하는 방법을 알고 있습니다.
이 경우, 아래 작업은 파일에 쓰는 함수입니다. (이메일 보내기레이션)
이 경우 작업 함수는 파일에 쓰기를 수행합니다(이메일 전송을레이션).
그리고 작업은 `async``await`를 사용하지 않으므로 일반 `def` 함수로 선언합니다.
그리고 쓰기 작업은 `async``await`를 사용하지 않으므로, 일반 `def` 함수를 정의합니다:
{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
## 백그라운드 작업 추가
## 백그라운드 작업 추가 { #add-the-background-task }
_경로 작동 함수_ 내에서 작업 함수를 `.add_task()` 함수 통해 _백그라운드 작업_ 체에 전달합니다.
*경로 처리 함수*에서 `.add_task()` 메서드로 작업 함수를 *백그라운드 작업* 체에 전달합니다:
{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
`.add_task()` 함수는 다음과 같은 인자를 받습니다 :
`.add_task()`는 다음 인자를 받습니다:
- 백그라운드에서 실행되는 작업 함수 (`write_notification`).
- 작업 함수에 순서대로 전달되어야 하는 일련의 인자 (`email`).
- 작업 함수에 전달되어야하는 모든 키워드 인자 (`message="some notification"`).
* 백그라운드에서 실행 작업 함수(`write_notification`).
* 작업 함수에 순서대로 전달되어야 하는 인자 시퀀스(`email`).
* 작업 함수에 전달되어야 하는 키워드 인자(`message="some notification"`).
## 의존성 주입
## 의존성 주입 { #dependency-injection }
`BackgroundTasks` 의존성 주입 시스템과 함께 사용하면 _경로 작동 함수_, 종속성, 하위 종속성 등 여러 수준에서 BackgroundTasks 유형의 매개변수를 선언할 수 있습니다.
`BackgroundTasks` 의존성 주입 시스템에서도 동작하며, *경로 처리 함수*, 의존성(dependable), 하위 의존성 등 여러 수준에서 `BackgroundTasks` 타입의 매개변수를 선언할 수 있습니다.
**FastAPI**는 각 경우에 수행할 작업과 동일한 체를 내부적으로 재사용하기에, 모든 백그라운드 작업이 함께 병합되고 나중에 백그라운드에서 실행됩니다.
**FastAPI**는 각 경우에 무엇을 해야 하는지와 동일한 체를 어떻게 재사용해야 하는지를 알고 있으므로, 모든 백그라운드 작업이 함께 병합되어 이후 백그라운드에서 실행됩니다:
{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
이 예제에서는 응답이 반환된 후에 `log.txt` 파일에 메시지가 기록됩니다.
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
요청에 쿼리가 있는 경우 백그라운드 작업의 로그에 기록됩니다.
그리고 _경로 작동 함수_ 에서 생성된 또 다른 백그라운드 작업은 경로 매개 변수를 활용하여 사용하여 메시지를 작성니다.
이 예제에서는 응답이 전송된 *후에* 메시지가 `log.txt` 파일에 작성니다.
## 기술적 세부사항
요청에 쿼리가 있었다면, 백그라운드 작업으로 로그에 작성됩니다.
그 다음 *경로 처리 함수*에서 생성된 또 다른 백그라운드 작업이 `email` 경로 매개변수를 사용해 메시지를 작성합니다.
## 기술적 세부사항 { #technical-details }
`BackgroundTasks` 클래스는 <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`starlette.background`</a>에서 직접 가져옵니다.
`BackgroundTasks` 클래스는 FastAPI에 직접 임포트하거나 포함하기 때문에 실수로 `BackgroundTask` (끝에 `s`가 없음)을 임포트하더라도 starlette.background에서 `BackgroundTask`를 가져오는 것을 방지할 수 있습니다.
FastAPI에 직접 임포트/포함되어 있으므로 `fastapi`에서 임포트할 수 있고, 실수로 `starlette.background`에서 대안인 `BackgroundTask`(끝에 `s`가 없음)를 임포트하는 것을 할 수 있습니다.
(`BackgroundTask`가 아닌) `BackgroundTasks` 사용하면, _경로 작동 함수_ 매개변수로 사용할 수 있게 되고 나머지는 **FastAPI**가 대신 처리하도록 할 수 있습니다. 이것은 `Request` 객체를 직접 사용하는 것과 같은 방식입니다.
`BackgroundTask`가 아닌 `BackgroundTasks` 사용하면, 이를 *경로 처리 함수*의 매개변수로 사용할 수 있고 나머지는 **FastAPI**가 `Request` 객체를 직접 사용할 때처럼 대신 처리해 줍니다.
FastAPI에서 `BackgroundTask` 단독으로 사용하는 것은 여전히 가능합니다. 하지만 객체를 코드에서 생성하고, 객체를 포함하는 Starlette `Response`를 반환해야 합니다.
FastAPI에서 `BackgroundTask` 단독으로 사용하는 것도 가능하지만, 코드에서 객체를 생성하고 이를 포함하는 Starlette `Response`를 반환해야 합니다.
<a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`Starlette의 공식 문서`</a>에서 백그라운드 작업에 대한 자세한 내용을 확인할 수 있습니다.
더 자세한 내용은 <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">Starlette의 Background Tasks 공식 문서</a>에서 확인할 수 있습니다.
## 경고
## 주의사항 { #caveat }
만약 무거운 백그라운드 작업을 수행해야하고 동일한 프로세스에서 실행할 필요가 없는 경우 (예: 메모리, 변수 등을 공유할 필요가 없음) <a href="https://docs.celeryq.dev" class="external-link" target="_blank">`Celery`</a> 같은 큰 도구를 사용하 도움이 될 수 있습니다.
무거운 백그라운드 계산을 수행해야 하고, 반드시 동일한 프로세스에서 실행할 필요가 없다면(예: 메모리, 변수 등을 공유할 필요가 없음) <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a> 같은 큰 도구를 사용하는 것이 도움이 될 수 있습니다.
RabbitMQ 또는 Redis 같은 메시지/작업 큐 시스템 보다 복잡한 구성이 필요한 경향이 있지만, 여러 작업 프로세스 특히 여러 서버 백그라운드에서 실행할 수 있습니다.
이들은 RabbitMQ Redis 같은 메시지/작업 큐 관리자 등 더 복잡한 설정을 필요로 하는 경향이 있지만, 여러 프로세스에서, 특히 여러 서버에서 백그라운드 작업을 실행할 수 있습니다.
그러나 동일한 FastAPI 앱에서 변수 및 개체에 접근해야햐는 작은 백그라운드 수행이 필요한 경우 (예 : 알림 이메일 보내기) 간단하게 `BackgroundTasks`사용해보세요.
하지만 동일한 **FastAPI** 앱의 변수와 객체에 접근해야 하거나(또는 이메일 알림 전송처럼) 작은 백그라운드 작업을 수행해야 한다면, `BackgroundTasks`간단히 사용하면 됩니다.
## 요약
## 요약 { #recap }
백그라운드 작업을 추가하기 위해 _경로 작동 함수_ 매개변수로 `BackgroundTasks`가져오고 사용합니다.
*경로 처리 함수*와 의존성에서 매개변수로 `BackgroundTasks`임포트해 사용하여 백그라운드 작업을 추가합니다.

View File

@@ -1,8 +1,8 @@
# 본문 - 필드
# 본문 - 필드 { #body-fields }
`Query`, `Path``Body`를 사용해 *경로 작동 함수* 매개변수 내에서 추가적인 검증이나 메타데이터를 선언한 것처럼 Pydantic의 `Field`를 사용하여 모델 내에서 검증과 메타데이터를 선언할 수 있습니다.
`Query`, `Path``Body`를 사용해 *경로 처리 함수* 매개변수 내에서 추가적인 검증이나 메타데이터를 선언한 것처럼 Pydantic의 `Field`를 사용하여 모델 내에서 검증과 메타데이터를 선언할 수 있습니다.
## `Field` 임포트
## `Field` 임포트 { #import-field }
먼저 이를 임포트해야 합니다:
@@ -14,7 +14,7 @@
///
## 모델 어트리뷰트 선언
## 모델 어트리뷰트 선언 { #declare-model-attributes }
그 다음 모델 어트리뷰트와 함께 `Field`를 사용할 수 있습니다:
@@ -22,7 +22,7 @@
`Field``Query`, `Path``Body`와 같은 방식으로 동작하며, 모두 같은 매개변수들 등을 가집니다.
/// note | 기술 세부사항
/// note | 기술 세부사항
실제로 `Query`, `Path`등, 여러분이 앞으로 볼 다른 것들은 공통 클래스인 `Param` 클래스의 서브클래스 객체를 만드는데, 그 자체로 Pydantic의 `FieldInfo` 클래스의 서브클래스입니다.
@@ -36,11 +36,11 @@
/// tip | 팁
주목할 점은 타입, 기본 값 및 `Field`로 이루어진 각 모델 어트리뷰트가 `Path`, `Query``Body`대신 `Field`를 사용하는 *경로 작동 함수*의 매개변수와 같은 구조를 가진다는 점 입니다.
주목할 점은 타입, 기본 값 및 `Field`로 이루어진 각 모델 어트리뷰트가 `Path`, `Query``Body`대신 `Field`를 사용하는 *경로 처리 함수*의 매개변수와 같은 구조를 가진다는 점 입니다.
///
## 별도 정보 추가
## 별도 정보 추가 { #add-extra-information }
`Field`, `Query`, `Body`, 그 외 안에 별도 정보를 선언할 수 있습니다. 이는 생성된 JSON 스키마에 포함됩니다.
@@ -53,7 +53,7 @@
///
## 요약
## 요약 { #recap }
모델 어트리뷰트를 위한 추가 검증과 메타데이터 선언하기 위해 Pydantic의 `Field` 를 사용할 수 있습니다.

View File

@@ -1,26 +1,24 @@
# 본문 - 다중 매개변수
# 본문 - 다중 매개변수 { #body-multiple-parameters }
지금부터 `Path``Query`를 어떻게 사용하는지 확인겠습니다.
이제 `Path``Query`를 어떻게 사용하는지 확인했으니, 요청 본문 선언에 대한 더 고급 사용법을 살펴보겠습니다.
요청 본문 선언에 대한 심화 사용법을 알아보겠습니다.
## `Path`, `Query` 및 본문 매개변수 혼합 { #mix-path-query-and-body-parameters }
## `Path`, `Query` 및 본문 매개변수 혼합
먼저, 물론 `Path`, `Query` 요청 본문 매개변수 선언을 자유롭게 혼합해서 사용할 수 있고, **FastAPI**는 어떤 동작을 할지 압니다.
당연하게 `Path`, `Query` 및 요청 본문 매개변수 선언을 자유롭게 혼합해서 사용할 수 있고, **FastAPI**는 어떤 동작을 할지 압니다.
또한 기본 값을 `None`으로 설정해 본문 매개변수택사항으로 선언할 수 있습니다:
또한, 기본 값을 `None`으로 설정해 본문 매개변수를 선택사항으로 선언할 수 있습니다.
{* ../../docs_src/body_multiple_params/tutorial001.py hl[19:21] *}
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | 참고
이 경우에는 본문으로 부터 가져 ` item`은 기본값이 `None`이기 때문에, 선택사항이라는 점을 유의해야 합니다.
이 경우에는 본문에서 가져 `item`이 선택사항이라는 점을 유의하세요. 기본값이 `None`이기 때문입니다.
///
## 다중 본문 매개변수
## 다중 본문 매개변수 { #multiple-body-parameters }
이전 예제에서 보듯이, *경로 작동*은 아래와 같이 `Item` 속성을 가진 JSON 본문을 예상합니다:
이전 예제에서, *경로 처리*는 아래처럼 `Item` 속성을 가진 JSON 본문을 예상합니다:
```JSON
{
@@ -33,11 +31,12 @@
하지만, 다중 본문 매개변수 역시 선언할 수 있습니다. 예. `item``user`:
{* ../../docs_src/body_multiple_params/tutorial002.py hl[22] *}
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
이 경우에, **FastAPI**는 이 함수 안에 한 개 이상의 본문 매개변수(Pydantic 모델인 두 매개변수)가 있다고 알 것입니다.
그래서, 본문 매개변수 이름을 키(필드 명)로 사용할 수 있고, 다음과 같은 본문을 예측합니다:
이 경우에, **FastAPI**는 이 함수에 본문 매개변수가 1개보다 많다는 것을 알아챌 것입니다(두 매개변수가 Pydantic 모델입니다).
그래서, 본문에서 매개변수 이름을 키(필드 이름)로 사용하고, 다음과 같은 본문을 예상합니다:
```JSON
{
@@ -56,29 +55,28 @@
/// note | 참고
이전과 같이 `item`이 선언 되었더라도, 본문 내의 `item` 있을 것이라고 예측합니다.
`item` 이전과 같은 방식으로 선언되었더라도, 이제는 본문에서 `item` 안에 있을 것으로 예상된다는 점을 유의하세요.
///
FastAPI는 요청 자동으로 변환, 매개변수 `item``user`를 특별한 내용으로 받도록 할 것입니다.
**FastAPI**는 요청에서 자동으로 변환을 수행하여, 매개변수 `item`이 해당하는 내용을 받고 `user`도 마찬가지로 받도록 니다.
복합 데이터의 검증을 수행하고 OpenAPI 스키마 및 자동 문서 문서화합니다.
복합 데이터의 검증을 수행하고, OpenAPI 스키마 및 자동 문서에도 그에 맞게 문서화합니다.
## 본문 내의 단일 값
## 본문 내의 단일 값 { #singular-values-in-body }
쿼리 및 경로 매개변수에 대한 추가 데이터를 정의하는 `Query``Path`와 같이, **FastAPI**는 동등한 `Body`를 제공합니다.
쿼리 및 경로 매개변수에 대한 추가 데이터를 정의하는 `Query``Path`가 있는 것과 같은 방식으로, **FastAPI**는 동등한 `Body`를 제공합니다.
예를 들어 이전 모델을 확장하면, `item``user`와 동일한 본문에 또 다른 `importance`라는 키를 갖도록 할 수있습니다.
예를 들어 이전 모델을 확장해서, `item``user` 외에도 같은 본문에 `importance`라는 다른 키를 두고 싶을 있습니다.
단일 값 그대로 선언한다면, **FastAPI**는 쿼리 매개변수 가정할 것입니다.
단일 값이므로 그대로 선언면, **FastAPI**는 이를 쿼리 매개변수라고 가정할 것입니다.
하지만, **FastAPI**의 `Body`를 사용 다른 본문 키로 처리하도록 제어할 수 있습니다:
하지만 `Body`를 사용하여 다른 본문 키로 처리하도록 **FastAPI**에 지시할 수 있습니다:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
{* ../../docs_src/body_multiple_params/tutorial003.py hl[23] *}
이 경우에는 **FastAPI**는 본문을 이와 같이 예측할 것입니다:
이 경우에는 **FastAPI**가 다음과 같은 본문을 예상할 것입니다:
```JSON
{
@@ -96,58 +94,55 @@ FastAPI는 요청을 자동으로 변환해, 매개변수의 `item`과 `user`를
}
```
다시 말해, 데이터 타입, 검증, 문서 등을 변환합니다.
다시 말해, 데이터 타입을 변환하고, 검증하고, 문서화하는 등의 작업을 수행합니다.
## 다중 본문 매개변수와 쿼리
## 다중 본문 매개변수와 쿼리 { #multiple-body-params-and-query }
당연히, 필요할 때마다 추가적인 쿼리 매개변수 선언할 수 있고, 이는 본문 매개변수에 추가됩니다.
물론, 필요할 때마다 어떤 본문 매개변수에 추가로 쿼리 매개변수 선언할 수 있니다.
기본적으로 단일 값은 쿼리 매개변수로 해석되므로, 명시적으로 `Query`를 추가할 필요고, 아래처럼 할 수 있습니다:
{* ../../docs_src/body_multiple_params/tutorial004.py hl[27] *}
이렇게:
기본적으로 단일 값은 쿼리 매개변수로 해석되므로, 명시적으로 `Query`를 추가할 필요 없이 이렇게 하면 됩니다:
```Python
q: Optional[str] = None
q: Union[str, None] = None
```
또는 Python 3.10 이상에서는:
```Python
q: str | None = None
```
예를 들어:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | 정보
`Body` 또한 `Query`, `Path` 그리고 이후에 볼 다른 것들처럼 동일한 추가 검증과 메타데이터 매개변수를 갖고 있습니다.
`Body` 또한 `Query`, `Path` 그리고 이후에 볼 다른 것들과 마찬가지로 동일한 추가 검증과 메타데이터 매개변수를 모두 갖고 있습니다.
///
## 단일 본문 매개변수 삽입하기
## 단일 본문 매개변수 삽입하기 { #embed-a-single-body-parameter }
Pydantic 모델 `Item` `item` 본문 매개변수로 오직 한개만 갖고있다고 하겠습니다.
Pydantic 모델 `Item`에서 가져온 단일 `item` 본문 매개변수있다고 하겠습니다.
기본적으로 **FastAPI**는 직접 본문으로 예측할 것입니다.
기본적으로 **FastAPI**는 본문을 직접 예상합니다.
하지만, 만약 모델 내용에 `item `키를 가진 JSON으로 예측하길 원한다면, 추가적인 본문 매개변수를 선언한 것처럼 `Body`의 특별한 매개변수 `embed`를 사용할 수 있습니다:
{* ../../docs_src/body_multiple_params/tutorial005.py hl[17] *}
아래 처럼:
하지만 추가 본문 매개변수를 선언할 때처럼, `item` 키를 가지고 그 안에 모델 내용이 들어 있는 JSON을 예상하게 하려면, `Body`의 특별한 매개변수 `embed`를 사용할 수 있습니다:
```Python
item: Item = Body(..., embed=True)
item: Item = Body(embed=True)
```
이 경우에 **FastAPI**는 본문을 아래 대신에:
다음과 같이요:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
이 경우 **FastAPI**는 다음과 같은 본문을 예상합니다:
```JSON hl_lines="2"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
아래 처럼 예측할 것 입니다:
```JSON
{
"item": {
"name": "Foo",
@@ -158,12 +153,23 @@ item: Item = Body(..., embed=True)
}
```
## 정리
다음 대신에:
요청이 단 한개의 본문을 가지고 있더라도, *경로 작동 함수*로 다중 본문 매개변수를 추가할 수 있습니다.
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
하지만, **FastAPI**는 이를 처리하고, 함수에 올바른 데이터를 제공하며, *경로 작동*으로 올바른 스키마를 검증하고 문서화 합니다.
## 정리 { #recap }
또한, 단일 값을 본문의 일부로 받도록 선언할 수 있습니다.
요청은 본문을 하나만 가질 수 있지만, *경로 처리 함수*에 다중 본문 매개변수를 추가할 수 있습니다.
그리고 **FastAPI**는 단 한개의 매개변수가 선언 되더라도, 본문 내의 키로 삽입 시킬 수 있습니다.
하지만 **FastAPI**는 이를 처리하고, 함수에 올바른 데이터를 제공하며, *경로 처리*에서 올바른 스키마를 검증하고 문서화합니다.
또한 단일 값을 본문의 일부로 받도록 선언할 수 있습니다.
그리고 단 하나의 매개변수만 선언되어 있더라도, **FastAPI**에 본문을 키 안에 삽입하도록 지시할 수 있습니다.

View File

@@ -1,35 +1,26 @@
# 본문 - 중첩 모델
# 본문 - 중첩 모델 { #body-nested-models }
**FastAPI**를 용하면 (Pydantic 덕분에) 단독으로 깊 중첩된 모델을 정의, 검증, 문서화하 사용할 수 있습니다.
## 리스트 필드
**FastAPI**를 용하면 (Pydantic 덕분에) 임의로 깊 중첩된 모델을 정의, 검증, 문서화하 사용할 수 있습니다.
## 리스트 필드 { #list-fields }
어트리뷰트를 서브타입으로 정의할 수 있습니다. 예를 들어 파이썬 `list`는:
{* ../../docs_src/body_nested_models/tutorial001.py hl[14] *}
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
이는 `tags` 항목 리스트로 만듭니다. 각 항목의 타입을 선언하지 않더라도요.
이는 `tags`를 리스트로 만들지만, 리스트 요소의 타입을 선언하지습니다.
## 타입 매개변수가 있는 리스트 필드
## 타입 매개변수가 있는 리스트 필드 { #list-fields-with-type-parameter }
하지만 파이썬 내부 타입이나 "타입 매개변수"를 선언할 수 있는 특정 방법이 있습니다:
하지만 파이썬에는 내부 타입, 즉 "타입 매개변수"를 사용해 리스트를 선언하는 특정 방법이 있습니다:
### typing의 `List` 임포트
### 타입 매개변수로 `list` 선언 { #declare-a-list-with-a-type-parameter }
먼저, 파이썬 표준 `typing` 모듈에서 `List`를 임포트합니다:
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
### 타입 매개변수로 `List` 선언
`list`, `dict`, `tuple`과 같은 타입 매개변수(내부 타입)를 갖는 타입을 선언하려면:
* `typing` 모듈에서 임포트
* 대괄호를 사용하여 "타입 매개변수"로 내부 타입 전달: `[``]`
`list`, `dict`, `tuple`처럼 타입 매개변수(내부 타입)를 갖는 타입을 선언하려면,
대괄호 `[``]`를 사용해 내부 타입(들)을 "타입 매개변수"로 전달하세요.
```Python
from typing import List
my_list: List[str]
my_list: list[str]
```
이 모든 것은 타입 선언을 위한 표준 파이썬 문법입니다.
@@ -38,45 +29,45 @@ my_list: List[str]
마찬가지로 예제에서 `tags`를 구체적으로 "문자열의 리스트"로 만들 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial002.py hl[14] *}
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## 집합 타입
## 집합 타입 { #set-types }
그런데 생각해보니 태그는 반복되면 안 되고, 고유한(Unique) 문자열이어야 할 것 같습니다.
그런데 생각해보니 태그는 반복되면 안 되고, 아마 고유한 문자열이어야 할 것니다.
그리고 파이썬 집합을 위한 특별한 데이터 타입 `set`이 있습니다.
그리고 파이썬에는 고유한 항목들의 집합을 위한 특별한 데이터 타입 `set`이 있습니다.
그렇다면 `Set`을 임포트 하고 `tags``str``set`으로 선언할 수 있습니다:
그렇다면 `tags`를 문자열의 집합으로 선언할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial003.py hl[1,14] *}
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
덕분에 중복 데이터가 있는 요청을 수신하더라도 고유한 항목들의 집합으로 변환됩니다.
이렇게 하면 중복 데이터가 있는 요청을 더라도 고유한 항목들의 집합으로 변환됩니다.
그리고 해당 데이터를 출력 할 때마다 소스에 중복이 있더라도 고유한 항목들의 집합으로 출력됩니다.
그리고 해당 데이터를 출력할 때마다, 소스에 중복이 있더라도 고유한 항목들의 집합으로 출력됩니다.
또한 그에 따라 주석이 생기고 문서화됩니다.
## 중첩 모델
## 중첩 모델 { #nested-models }
Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
그런데 해당 타입 자체 또다른 Pydantic 모델의 타입이 될 수 있습니다.
그런데 타입 자체 다른 Pydantic 모델 수 있습니다.
그러므로 특정한 어트리뷰트 이름, 타입, 검증을 사용하여 깊게 중첩된 JSON "객체"를 선언할 수 있습니다.
따라서 특정한 어트리뷰트 이름, 타입, 검증을 사용하여 깊게 중첩된 JSON "객체"를 선언할 수 있습니다.
모든 것이 단독으로 중첩됩니다.
모든 것이 임의의 깊이로 중첩됩니다.
### 서브모델 정의
### 서브모델 정의 { #define-a-submodel }
예를 들어, `Image` 모델을 선언할 수 있습니다:
예를 들어, `Image` 모델을 정의할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial004.py hl[9:11] *}
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### 서브모을 타입으로 사용
### 서브모을 타입으로 사용 { #use-the-submodel-as-a-type }
그리고 어트리뷰트의 타입으로 사용할 수 있습니다:
그리고 이를 어트리뷰트의 타입으로 사용할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial004.py hl[20] *}
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
이는 **FastAPI**가 다음과 유사한 본문을 기대한다는 것을 의미합니다:
@@ -94,32 +85,32 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
}
```
다시 한번, **FastAPI**를 사용하여 해당 선언을 함으로써 얻는 것은:
다시 한번, **FastAPI**로 그 선언만 해도 얻는 것은:
* 중첩 모델도 편집기 지원(자동완성 등)
* 데이터 변환
* 데이터 검증
* 자동 문서화
## 특별한 타입과 검증
## 특별한 타입과 검증 { #special-types-and-validation }
`str`, `int`, `float` 등과 같은 단일 타입과는 별개로, `str`을 상속하는 더 복잡한 단일 타입을 사용할 수 있습니다.
`str`, `int`, `float` 등과 같은 일반적인 단일 타입과는 별개로, `str`을 상속하는 더 복잡한 단일 타입을 사용할 수 있습니다.
모든 옵션을 보려면, <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">Pydantic's exotic types</a> 문서를 확인하세요. 다음 장에서 몇가지 예제를 볼 수 있습니다.
사용할 수 있는 모든 옵션을 보려면 <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">Pydantic의 Type Overview</a>를 확인하세요. 다음 장에서 몇 가지 예제를 볼 수 있습니다.
예를 들어 `Image` 모델`url` 필드를 `str` 대신 Pydantic의 `HttpUrl`로 선언할 수 있습니다:
예를 들어 `Image` 모델에 `url` 필드가 있으므로, 이`str` 대신 Pydantic의 `HttpUrl` 인스턴스로 선언할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial005.py hl[4,10] *}
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
이 문자열 유효한 URL인지 검사하고 JSON 스키마/OpenAPI로 문서화 됩니다.
이 문자열 유효한 URL인지 검사되며, JSON Schema / OpenAPI에도 그에 맞게 문서화됩니다.
## 서브모델 리스트를 갖는 어트리뷰트
## 서브모델 리스트를 갖는 어트리뷰트 { #attributes-with-lists-of-submodels }
`list`, `set` 등의 서브타입으로 Pydantic 모델을 사용할 수도 있습니다:
{* ../../docs_src/body_nested_models/tutorial006.py hl[20] *}
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
아래와 같은 JSON 본문으로 예상(변환, 검증, 문서화 등)합니다:
아래와 같은 JSON 본문 예상(변환, 검증, 문서화 등)합니다:
```JSON hl_lines="11"
{
@@ -147,84 +138,84 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
/// info | 정보
`images` 키가 어떻게 이미지 객체 리스트를 갖는지 주목하세요.
`images` 키가 이제 이미지 객체 리스트를 갖는지 주목하세요.
///
## 깊게 중첩된 모델
## 깊게 중첩된 모델 { #deeply-nested-models }
단독으로 깊게 중첩된 모델을 정의할 수 있습니다:
임의로 깊게 중첩된 모델을 정의할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial007.py hl[9,14,20,23,27] *}
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | 정보
`Offer`가 선택사항 `Image` 리스트를 차례로 갖는 `Item` 리스트를 어떻게 가지고 있는지 주목하세요
`Offer`가 `Item` 리스트를 가지고, 그 `Item`이 다시 선택 사항인 `Image` 리스트를 갖는지 주목하세요
///
## 순수 리스트의 본문
## 순수 리스트의 본문 { #bodies-of-pure-lists }
예상되는 JSON 본문의 최상위 값이 JSON `array`(파이썬 `list`)면, Pydantic 모델에서와 마찬가지로 함수의 매개변수에서 타입을 선언할 수 있습니다:
예상되는 JSON 본문의 최상위 값이 JSON `array`(파이썬 `list`)면, Pydantic 모델에서와 마찬가지로 함수의 매개변수에서 타입을 선언할 수 있습니다:
```Python
images: List[Image]
images: list[Image]
```
이를 아래처럼:
{* ../../docs_src/body_nested_models/tutorial008.py hl[15] *}
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
## 어디서나 편집기 지원
## 어디서나 편집기 지원 { #editor-support-everywhere }
그리고 어디서나 편집기 지원을 받을수 있습니다.
그리고 어디서나 편집기 지원을 받을 수 있습니다.
리스트 내부 항목의 경우에도:
<img src="/img/tutorial/body-nested-models/image01.png">
Pydantic 모델 대신 `dict` 직접 사용하여 작업할 경우, 이러한 편집기 지원을 받을수 없습니다.
Pydantic 모델 대신 `dict` 직접 작업한다면 이런 종류의 편집기 지원을 받을 수 없습니다.
하지만 수신한 딕셔너리가 자동으로 변환되고 출력도 자동으로 JSON으로 변환되므로 걱정할 필요는 없습니다.
하지만 그 부분에 대해서도 걱정할 필요는 없습니다. 들어오는 dict는 자동으로 변환되고, 출력도 자동으로 JSON으로 변환니다.
## 단독 `dict` 본문
## 임의의 `dict` 본문 { #bodies-of-arbitrary-dicts }
일부 타입의 키와 다른 타입의 값을 사용하여 `dict`로 본문을 선언할 수 있습니다.
또한 키는 어떤 타입이고 값은 다른 타입인 `dict`로 본문을 선언할 수 있습니다.
(Pydantic을 사용 경우처럼) 유효한 필드/어트리뷰트 이름이 무엇인지 알 필요가 없습니다.
이렇게 하면 (Pydantic 모델을 사용하는 경우처럼) 유효한 필드/어트리뷰트 이름이 무엇인지 미리 알 필요가 없습니다.
아직 모르는 키를 받으려는 경우 유용합니다.
아직 모르는 키를 받으려는 경우 유용합니다.
---
다른 유용한 경우는 다른 타입의 키를 가질 때입니다. 예. `int`.
다른 유용한 경우는 다른 타입(예: `int`)의 키를 갖고 싶을 때입니다.
여기서 그 경우를 볼 것입니다.
이 경우, `float` 값을 가진 `int` 키가 있는 모든 `dict` 받아들입니다:
이 경우, `int` 키와 `float` 값을 가진 한 어떤 `dict` 받아들입니다:
{* ../../docs_src/body_nested_models/tutorial009.py hl[15] *}
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
/// tip | 팁
JSON은 오직 `str`형 키만 지원한다는 것을 염두에 두세요.
JSON은 키로 `str`만 지원한다는 것을 염두에 두세요.
하지만 Pydantic은 자동 데이터 변환이 있습니다.
하지만 Pydantic은 자동 데이터 변환 기능이 있습니다.
즉, API 클라이언트가 문자열을 키로 보내더라도 해당 문자열이 순수한 정수를 포함하는한 Pydantic 이를 변환하고 검증합니다.
즉, API 클라이언트는 키로 문자열만 보낼 수 있더라도, 해당 문자열이 순수한 정수를 포함하기만 하면 Pydantic 이를 변환하고 검증합니다.
러므로 `weights`로 받 `dict`는 실제로 `int` 키와 `float` 값을 가집니다.
리고 `weights`로 받 `dict`는 실제로 `int` 키와 `float` 값을 갖게 됩니다.
///
## 요약
## 요약 { #recap }
**FastAPI**를 사용하면 Pydantic 모델이 제공하는 최대 유연성을 확보하면서 코드를 간단하고 짧게, 그리고 우아하게 유지할 수 있습니다.
**FastAPI**를 사용하면 Pydantic 모델이 제공하는 최대 유연성을 확보하면서 코드를 간단하고 짧고 우아하게 유지할 수 있습니다.
물론 아래의 이점도 있습니다:
하지만 아래의 모든 이점도 있습니다:
* 편집기 지원 (자동완성이 어디서나!)
* 데이터 변환 (일명 파싱/직렬화)
* 편집기 지원(어디서나 자동완성!)
* 데이터 변환(일명 파싱/직렬화)
* 데이터 검증
* 스키마 문서화
* 자동 문서

View File

@@ -1,10 +1,10 @@
# 요청 본문
# 요청 본문 { #request-body }
클라이언트(브라우저라고 해봅시다)로부터 여러분의 API로 데이터를 보내야 할 때, **요청 본문**으로 보냅니다.
**요청** 본문은 클라이언트에서 API로 보내지는 데이터입니다. **응답** 본문은 API가 클라이언트로 보내는 데이터입니다.
여러분의 API는 대부분의 경우 **응답** 본문을 보내야 합니다. 하지만 클라이언트는 **요청** 본문을 매 번 보낼 필요 없습니다.
여러분의 API는 대부분의 경우 **응답** 본문을 보내야 합니다. 하지만 클라이언트는 항상 **요청 본문**을 보낼 필요고, 때로는 (쿼리 매개변수와 함께) 어떤 경로만 요청하고 본문은 보내지 않을 수도 있습니다.
**요청** 본문을 선언하기 위해서 모든 강력함과 이점을 갖춘 <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> 모델을 사용합니다.
@@ -18,13 +18,13 @@
///
## Pydantic의 `BaseModel` 임포트
## Pydantic의 `BaseModel` 임포트 { #import-pydantics-basemodel }
먼저 `pydantic`에서 `BaseModel`를 임포트해야 합니다:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## 여러분의 데이터 모델 만들기
## 여러분의 데이터 모델 만들기 { #create-your-data-model }
`BaseModel`를 상속받은 클래스로 여러분의 데이터 모델을 선언합니다.
@@ -32,6 +32,7 @@
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
쿼리 매개변수를 선언할 때와 같이, 모델 어트리뷰트가 기본 값을 가지고 있어도 이는 필수가 아닙니다. 그외에는 필수입니다. 그저 `None`을 사용하여 선택적으로 만들 수 있습니다.
예를 들면, 위의 이 모델은 JSON "`object`" (혹은 파이썬 `dict`)을 다음과 같이 선언합니다:
@@ -39,7 +40,7 @@
```JSON
{
"name": "Foo",
"description": "선택적인 설명란",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
@@ -54,15 +55,15 @@
}
```
## 매개변수로서 선언하기
## 매개변수로서 선언하기 { #declare-it-as-a-parameter }
여러분의 *경로 작동*에 추가하기 위해, 경로 매개변수 그리고 쿼리 매개변수에서 선언했던 것과 같은 방식으로 선언하면 됩니다.
여러분의 *경로 처리*에 추가하기 위해, 경로 매개변수 그리고 쿼리 매개변수에서 선언했던 것과 같은 방식으로 선언하면 됩니다.
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...그리고 만들어낸 모델인 `Item`으로 타입을 선언합니다.
## 결과
## 결과 { #results }
위에서의 단순한 파이썬 타입 선언으로, **FastAPI**는 다음과 같이 동작합니다:
@@ -72,20 +73,20 @@
* 만약 데이터가 유효하지 않다면, 정확히 어떤 것이 그리고 어디에서 데이터가 잘 못 되었는지 지시하는 친절하고 명료한 에러를 반환할 것입니다.
* 매개변수 `item`에 포함된 수신 데이터를 제공합니다.
* 함수 내에서 매개변수를 `Item` 타입으로 선언했기 때문에, 모든 어트리뷰트와 그에 대한 타입에 대한 편집기 지원(완성 등)을 또한 받을 수 있습니다.
* 여러분의 모델을 위한 <a href="https://json-schema.org" class="external-link" target="_blank">JSON 스키마</a> 정의를 생성합니다. 여러분의 프로젝트에 적합하다면 여러분이 사용하고 싶은 곳 어디에서나 사용할 수 있습니다.
* 이러한 스키마는, 생성된 OpenAPI 스키마 일부가 될 것이며, 자동 문서화 <abbr title="사용자 인터페이스">UI</abbr>에 사용됩니다.
* 여러분의 모델을 위한 <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> 정의를 생성합니다. 여러분의 프로젝트에 적합하다면 여러분이 사용하고 싶은 곳 어디에서나 사용할 수 있습니다.
* 이러한 스키마는, 생성된 OpenAPI 스키마 일부가 될 것이며, 자동 문서화 <abbr title="User Interfaces 사용자 인터페이스">UIs</abbr>에 사용됩니다.
## 자동 문서화
## 자동 문서화 { #automatic-docs }
모델의 JSON 스키마는 생성된 OpenAPI 스키마에 포함되며 대화형 API 문서에 표시됩니다:
<img src="/img/tutorial/body/image01.png">
이를 필요로 하는 각각의 *경로 작동*내부의 API 문서에도 사용됩니다:
이를 필요로 하는 각각의 *경로 처리* 내부의 API 문서에도 사용됩니다:
<img src="/img/tutorial/body/image02.png">
## 편집기 지원
## 편집기 지원 { #editor-support }
편집기에서, 함수 내에서 타입 힌트와 완성을 어디서나 (만약 Pydantic model 대신에 `dict`을 받을 경우 나타나지 않을 수 있습니다) 받을 수 있습니다:
@@ -97,13 +98,13 @@
단순한 우연이 아닙니다. 프레임워크 전체가 이러한 디자인을 중심으로 설계되었습니다.
그 어떤 실행 전에, 모든 편집기에서 작동할 수 있도록 보장하기 위해 설계 단계에서 혹독하게 테스트되었습니다.
그 어떤 구현 전에, 모든 편집기에서 작동할 수 있도록 보장하기 위해 설계 단계에서 혹독하게 테스트되었습니다.
이를 지원하기 위해 Pydantic 자체에서 몇몇 변경점이 있었습니다.
이전 스크린샷은 <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>를 찍은 것입니다.
하지만 똑같은 편집기 지원을 <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>에서 받을 수 있거나, 대부분의 다른 편집기에서도 받을 수 있습니다:
하지만 똑같은 편집기 지원을 <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> 대부분의 다른 파이썬 편집기에서도 받을 수 있습니다:
<img src="/img/tutorial/body/image05.png">
@@ -113,21 +114,21 @@
다음 사항을 포함해 Pydantic 모델에 대한 편집기 지원을 향상시킵니다:
* 자동 완성
* 타입 확인
* 리팩토링
* 검색
* 점검
* auto-completion
* type checks
* refactoring
* searching
* inspections
///
## 모델 사용하기
## 모델 사용하기 { #use-the-model }
함수 안에서 모델 객체의 모든 어트리뷰트에 직접 접근 가능합니다:
{* ../../docs_src/body/tutorial002_py310.py hl[19] *}
{* ../../docs_src/body/tutorial002_py310.py *}
## 요청 본문 + 경로 매개변수
## 요청 본문 + 경로 매개변수 { #request-body-path-parameters }
경로 매개변수와 요청 본문을 동시에 선언할 수 있습니다.
@@ -135,7 +136,8 @@
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## 요청 본문 + 경로 + 쿼리 매개변수
## 요청 본문 + 경로 + 쿼리 매개변수 { #request-body-path-query-parameters }
**본문**, **경로** 그리고 **쿼리** 매개변수 모두 동시에 선언할 수도 있습니다.
@@ -153,10 +155,12 @@
FastAPI는 `q`의 값이 필요없음을 알게 될 것입니다. 기본 값이 `= None`이기 때문입니다.
`Union[str, None]`에 있는 `Union`은 FastAPI에 의해 사용된 것이 아니지만, 편집기로 하여금 더 나은 지원과 에러 탐지를 지원할 것입니다.
Python 3.10+의 `str | None` 또는 Python 3.9+의 `Union[str, None]`에 있는 `Union`은 FastAPI가 `q` 값이 필수가 아님을 판단하기 위해 사용하지 않습니다. 기본 값이 `= None`이기 때문에 필수가 아님을 알게 됩니다.
하지만 타입 어노테이션을 추가하면 편집기가 더 나은 지원을 제공하고 오류를 감지할 수 있습니다.
///
## Pydantic없이
## Pydantic없이 { #without-pydantic }
만약 Pydantic 모델을 사용하고 싶지 않다면, **Body** 매개변수를 사용할 수도 있습니다. [Body - 다중 매개변수: 본문에 있는 유일한 값](body-multiple-params.md#_2){.internal-link target=_blank} 문서를 확인하세요.
만약 Pydantic 모델을 사용하고 싶지 않다면, **Body** 매개변수를 사용할 수도 있습니다. [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank} 문서를 확인하세요.

View File

@@ -1,8 +1,8 @@
# 쿠키 매개변수 모델
# 쿠키 매개변수 모델 { #cookie-parameter-models }
관련있는 **쿠키**들의 그룹이 있는 경우, **Pydantic 모델**을 생성하여 선언할 수 있습니다. 🍪
이를 통해 **여러 위치**에서 **모델을 재사용** 할 수 있고 모든 매개변수에 대한 유효성 검사 및 메타데이터를 한 번에 선언할 수도 있습니다. 😍
이를 통해 **여러 위치**에서 **모델을 재사용** 할 수 있고 모든 매개변수에 대한 유효성 검사 및 메타데이터를 한 번에 선언할 수도 있습니다. 😎
/// note | 참고
@@ -16,7 +16,7 @@
///
## Pydantic 모델을 사용한 쿠키
## Pydantic 모델을 사용한 쿠키 { #cookies-with-a-pydantic-model }
**Pydantic 모델**에 필요한 **쿠키** 매개변수를 선언한 다음, 해당 매개변수를 `Cookie`로 선언합니다:
@@ -24,7 +24,7 @@
**FastAPI**는 요청에서 받은 **쿠키**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기
## 문서 확인하기 { #check-the-docs }
문서 UI `/docs`에서 정의한 쿠키를 볼 수 있습니다:
@@ -36,27 +36,27 @@
명심하세요, 내부적으로 **브라우저는 쿠키를 특별한 방식으로 처리**하기 때문에 **자바스크립트**가 쉽게 쿠키를 건드릴 수 **없습니다**.
`/docs`에서 **API 문서 UI**로 이동하면 *경로 작업*에 대한 쿠키의 **문서**를 볼 수 있습니다.
`/docs`에서 **API 문서 UI**로 이동하면 *경로 처리*에 대한 쿠키의 **문서**를 볼 수 있습니다.
하지만 아무리 **데이터를 입력**하고 "실행(Execute)"을 클릭해도, 문서 UI는 **자바스크립트**로 작동하기 때문에 쿠키는 전송되지 않고, 아무 값도 쓰지 않은 것처럼 **오류** 메시지를 보게 됩니다.
///
## 추가 쿠키 금지하기
## 추가 쿠키 금지하기 { #forbid-extra-cookies }
일부 특별한 사용 사례(흔하지는 않겠지만)에서는 수신하려는 쿠키를 **제한**할 수 있습니다.
이제 API는 자신의 <abbr title="농담입니다, 혹시나 해서요. 쿠키 동의와 관련해서 할 수 있는 것은 없지만, 이제 API조차도 잘못된 쿠키를 거부할 수 있다는 점이 재밌습니다. 쿠키 드세요. 🍪">쿠키 동의</abbr>를 제어할 수 있는 권한을 갖게 되었습니다. 🤪🍪
이제 API는 자신의 <abbr title="This is a joke, just in case. It has nothing to do with cookie consents, but it's funny that even the API can now reject the poor cookies. Have a cookie. 🍪">cookie consent</abbr>를 제어할 수 있는 권한을 갖게 되었습니다. 🤪🍪
Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forbid`)할 수 있습니다:
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *}
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
클라이언트가 **추가 쿠키**를 보내려고 시도하면, **오류** 응답을 받게 됩니다.
<abbr title="이건 또 다른 농담입니다. 제 말에 귀 기울이지 마세요. 커피랑 쿠키 좀 드세요. ☕">API가 거부</abbr>하는데도 동의를 얻기 위해 애쓰는 불쌍한 쿠키 배너(팝업)들. 🍪
동의를 얻기 위해 애쓰는 불쌍한 쿠키 배너(팝업)들, <abbr title="This is another joke. Don't pay attention to me. Have some coffee for your cookie. ☕">API가 거부</abbr>하는데도. 🍪
예를 들어, 클라이언트가 `good-list-please` 값으로 `santa_tracker` 쿠키를 보내려고 하면 클라이언트는 `santa_tracker` <abbr title="산타는 쿠키가 부족한 것을 못마땅해합니다. 🎅 알겠습니다, 쿠키 농담은 이제 없습니다.">쿠키가 허용되지 않는다</abbr>는 **오류** 응답을 받게 됩니다:
예를 들어, 클라이언트가 `good-list-please` 값으로 `santa_tracker` 쿠키를 보내려고 하면 클라이언트는 `santa_tracker` <abbr title="Santa disapproves the lack of cookies. 🎅 Okay, no more cookie jokes.">쿠키가 허용되지 않는다</abbr>는 **오류** 응답을 받게 됩니다:
```json
{
@@ -71,6 +71,6 @@ Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forb
}
```
## 요약
## 요약 { #summary }
**Pydantic 모델**을 사용하여 **FastAPI**에서 <abbr title="가기 전에 마지막 쿠키를 드세요. 🍪">**쿠키**</abbr>를 선언할 수 있습니다. 😍
**Pydantic 모델**을 사용하여 **FastAPI**에서 <abbr title="Have a last cookie before you go. 🍪">**쿠키**</abbr>를 선언할 수 있습니다. 😎

View File

@@ -1,14 +1,14 @@
# 쿠키 매개변수
# 쿠키 매개변수 { #cookie-parameters }
쿠키 매개변수를 `Query``Path` 매개변수들과 같은 방식으로 정의할 수 있습니다.
## `Cookie` 임포트
## `Cookie` 임포트 { #import-cookie }
먼저 `Cookie`를 임포트합니다:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## `Cookie` 매개변수 선언
## `Cookie` 매개변수 선언 { #declare-cookie-parameters }
그런 다음, `Path``Query`처럼 동일한 구조를 사용하는 쿠키 매개변수를 선언합니다.
@@ -20,7 +20,7 @@
`Cookie``Path``Query`의 "자매"클래스입니다. 이 역시 동일한 공통 `Param` 클래스를 상속합니다.
`Query`, `Path`, `Cookie` 그리고 다른 것들`fastapi`에서 임포트 할 때, 실제로는 특별한 클래스를 반환하는 함수임을 기억하세요.
하지만 `fastapi`에서 `Query`, `Path`, `Cookie` 그리고 다른 것들 임포트할 때, 실제로는 특별한 클래스를 반환하는 함수임을 기억하세요.
///
@@ -30,6 +30,16 @@
///
## 요약
/// info | 정보
`Cookie``Query`, `Path`와 동일한 패턴을 사용하여 선언합니다.
**브라우저는 쿠키를** 내부적으로 특별한 방식으로 처리하기 때문에, **JavaScript**가 쉽게 쿠키를 다루도록 허용하지 않는다는 점을 염두에 두세요.
`/docs`의 **API docs UI**로 이동하면 *경로 처리*에 대한 쿠키 **문서**를 확인할 수 있습니다.
하지만 **데이터를 채우고** "Execute"를 클릭하더라도, docs UI는 **JavaScript**로 동작하기 때문에 쿠키가 전송되지 않고, 아무 값도 입력하지 않은 것처럼 **오류** 메시지를 보게 될 것입니다.
///
## 요약 { #recap }
`Query``Path`에서 사용하는 것과 동일한 공통 패턴으로, `Cookie`를 사용해 쿠키를 선언합니다.

View File

@@ -1,10 +1,10 @@
# 교차 출처 리소스 공유
# CORS (교차-출처 리소스 공유) { #cors-cross-origin-resource-sharing }
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">CORS 또는 "교차-출처 리소스 공유"</a>란, 브라우저에서 동작하는 프론트엔드가 자바스크립트로 코드로 백엔드와 통신하고, 백엔드는 해당 프론트엔드와 다른 "출처"에 존재하는 상황을 의미합니다.
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">CORS 또는 "Cross-Origin Resource Sharing"</a>란, 브라우저에서 실행되는 프론트엔드에 백엔드와 통신하는 JavaScript 코드가 있고, 백엔드 프론트엔드와 다른 "출처(origin)"에 있는 상황을 의미합니다.
## 출처
## 출처 { #origin }
출처란 프로토콜(`http` , `https`), 도메인(`myapp.com`, `localhost`, `localhost.tiangolo.com` ), 그리고 포트(`80`, `443`, `8080` )의 조합을 의미합니다.
출처란 프로토콜(`http`, `https`), 도메인(`myapp.com`, `localhost`, `localhost.tiangolo.com`), 그리고 포트(`80`, `443`, `8080`)의 조합을 의미합니다.
따라서, 아래는 모두 상이한 출처입니다:
@@ -12,74 +12,78 @@
* `https://localhost`
* `http://localhost:8080`
모두 `localhost` 에 있지만, 서로 다른 프로토콜 포트를 사용하고 있으므로 다른 "출처"입니다.
모두 `localhost`에 있더라도, 서로 다른 프로토콜이나 포트를 사용하므로 서로 다른 "출처"입니다.
## 단계
## 단계 { #steps }
브라우저 `http://localhost:8080`에서 동작하는 프론트엔드가 있고, 자바스크립트는 `http://localhost`를 통해 백엔드와 통신한다고 가정해봅시다(포트를 명시하지 않는 경우, 브라우저는 `80` 기본 포트로 간주합니다).
그러면 브라우저에서 `http://localhost:8080`으로 실행되는 프론트엔드가 있고, 그 JavaScript가 `http://localhost`에서 실행되는 백엔드와 통신하려고 한다고 해봅시다(포트를 명시하지 않았기 때문에, 브라우저는 기본 포트 `80`가정합니다).
그러면 브라우저는 백엔드에 HTTP `OPTIONS` 요청을 보내고, 백엔드에서 이 다른 출처(`http://localhost:8080`)의 통신을 허가하는 적절한 헤더를 보내면, 브라우저는 프론트엔드의 자바스크립트가 백엔드에 요청을 보낼 수 있도록 합니다.
그러면 브라우저는 `:80`-백엔드에 HTTP `OPTIONS` 요청을 보내고, 백엔드 이 다른 출처(`http://localhost:8080`)로부터의 통신을 허가하는 적절한 헤더를 보내면, `:8080`-브라우저는 프론트엔드의 JavaScript가 `:80`-백엔드에 요청을 보낼 수 있도록 합니다.
이를 위해, 백엔드는 "허용된 출처(allowed origins)" 목록을 가지고 있어야 합니다.
이를 위해, `:80`-백엔드는 "허용된 출처(allowed origins)" 목록을 가지고 있어야 합니다.
이 경우, 프론트엔드가 제대로 동작하기 위해 `http://localhost:8080`을 목록에 포함야 합니다.
이 경우, `:8080`-프론트엔드가 올바르게 동작하려면 목록에 `http://localhost:8080` 포함되어야 합니다.
## 와일드카드
## 와일드카드 { #wildcards }
모든 출처를 허용하기 위해 목록을 `"*"` ("와일드카드")로 선언하는 것도 가능합니다.
또한 목록을 `"*"`("와일드카드")로 선언해 모두 허용된다고 말할 수도 있습니다.
하지만 이것은 특정 유형의 통신만 허용하며, 쿠키 및 액세스 토큰과 사용되는 인증 헤더(Authoriztion header) 등이 포함된 경우와 같이 자격 증명(credentials)이 포함된 통신은 허용되지 않습니다.
하지만 그러면 자격 증명(credentials)이 포함된 모든 것을 제외하고 특정 유형의 통신만 허용하게 됩니다. 예: 쿠키, Bearer Token에 사용되는 것과 같은 Authorization 헤더 등.
따라서 모든 작업을 의도한대로 실행하기 위해, 허용되는 출처를 명시적으로 지정하는 것이 좋습니다.
따라서 모든 것이 올바르게 동작하게 하려면, 허용 출처를 명시적으로 지정하는 것이 좋습니다.
## `CORSMiddleware` 사용
## `CORSMiddleware` 사용 { #use-corsmiddleware }
`CORSMiddleware` 사용하여 **FastAPI** 응용 프로그램의 교차 출처 리소스 공유 환경을 설정할 수 있습니다.
`CORSMiddleware` 사용하여 **FastAPI** 애플리케이션에서 이를 설정할 수 있습니다.
* `CORSMiddleware` 임포트.
* 허용되는 출처(문자열 형식)의 리스트 생성.
* FastAPI 응용 프로그램에 "미들웨어(middleware)"로 추가.
* `CORSMiddleware` 임포트합니다.
* 허용 출처(문자열)의 리스트 생성합니다.
* **FastAPI** 애플리케이션에 "미들웨어(middleware)"로 추가합니다.
백엔드에서 다음의 사항을 허용할지에 대해 설정할 수 있습니다:
또한 백엔드 다음을 허용할지 여부도 지정할 수 있습니다:
* 자격증명 (인증 헤더, 쿠키 등).
* 특정 HTTP 메드(`POST`, `PUT`) 또는 와일드카드 `"*"` 를 사용한 모든 HTTP 메소드.
* 특정 HTTP 헤더 또는 와일드카드 `"*"` 를 사용한 모든 HTTP 헤더.
* 자격 증명(Authorization 헤더, 쿠키 등).
* 특정 HTTP 메드(`POST`, `PUT`) 또는 와일드카드 `"*"`를 사용한 모든 메서드.
* 특정 HTTP 헤더 또는 와일드카드 `"*"`를 사용한 모든 헤더.
{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
`CORSMiddleware` 에서 사용하는 기본 매개변수는 제한적이므로, 브라우저가 교차-도메인 상황에서 특정한 출처, 메소드, 헤더 등을 사용할 수 있도록 하려면 이들을 명시적으로 허용해야 합니다.
다음의 인자들이 지원됩니다:
`CORSMiddleware` 구현에서 사용하는 기본 매개변수는 기본적으로 제한적이므로, 브라우저가 Cross-Domain 컨텍스트에서 특정 출처, 메서드 또는 헤더를 사용할 수 있도록 하려면 이를 명시적으로 활성화해야 합니다.
* `allow_origins` - 교차-출처 요청을 보낼 수 있는 출처의 리스트입니다. 예) `['https://example.org', 'https://www.example.org']`. 모든 출처를 허용하기 위해 `['*']` 를 사용할 수 있습니다.
* `allow_origin_regex` - 교차-출처 요청을 보낼 수 있는 출처를 정규표현식 문자열로 나타냅니다. `'https://.*\.example\.org'`.
* `allow_methods` - 교차-출처 요청을 허용하는 HTTP 메소드의 리스트입니다. 기본값은 `['GET']` 입니다. `['*']` 을 사용하여 모든 표준 메소드들을 허용할 수 있습니다.
* `allow_headers` - 교차-출처를 지원하는 HTTP 요청 헤더의 리스트입니다. 기본값은 `[]` 입니다. 모든 헤더들을 허용하기 위해 `['*']` 를 사용할 수 있습니다. `Accept`, `Accept-Language`, `Content-Language` 그리고 `Content-Type` 헤더는 CORS 요청시 언제나 허용됩니다.
* `allow_credentials` - 교차-출처 요청시 쿠키 지원 여부를 설정합니다. 기본값은 `False` 입니다. 또한 해당 항목을 허용할 경우 `allow_origins``['*']` 로 설정할 수 없으며, 출처를 반드시 특정해야 합니다.
* `expose_headers` - 브라우저에 접근할 수 있어야 하는 모든 응답 헤더를 가리킵니다. 기본값은 `[]` 입니다.
* `max_age` - 브라우저가 CORS 응답을 캐시에 저장하는 최대 시간을 초 단위로 설정합니다. 기본값은 `600` 입니다.
다음 인자들이 지원됩니다:
미들웨어는 두가지 특정한 종류의 HTTP 요청에 응답합니다...
* `allow_origins` - 교차-출처 요청을 보낼 수 있도록 허용해야 하는 출처의 리스트입니다. 예: `['https://example.org', 'https://www.example.org']`. 모든 출처를 허용하려면 `['*']`를 사용할 수 있습니다.
* `allow_origin_regex` - 교차-출처 요청을 보낼 수 있도록 허용해야 하는 출처와 매칭할 정규표현식 문자열입니다. 예: `'https://.*\.example\.org'`.
* `allow_methods` - 교차-출처 요청에 허용되어야 하는 HTTP 메서드의 리스트입니다. 기본값은 `['GET']`입니다. 모든 표준 메서드를 허용하려면 `['*']`를 사용할 수 있습니다.
* `allow_headers` - 교차-출처 요청에 대해 지원되어야 하는 HTTP 요청 헤더의 리스트입니다. 기본값은 `[]`입니다. 모든 헤더를 허용하려면 `['*']`를 사용할 수 있습니다. `Accept`, `Accept-Language`, `Content-Language`, `Content-Type` 헤더는 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">단순 CORS 요청</a>에 대해 항상 허용됩니다.
* `allow_credentials` - 교차-출처 요청에 대해 쿠키를 지원해야 함을 나타냅니다. 기본값은 `False`입니다.
### CORS 사전 요청
`allow_credentials``True`로 설정된 경우 `allow_origins`, `allow_methods`, `allow_headers` 중 어느 것도 `['*']`로 설정할 수 없습니다. 모두 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards" class="external-link" rel="noopener" target="_blank">명시적으로 지정</a>되어야 합니다.
`Origin``Access-Control-Request-Method` 헤더와 함께 전송하는 모든 `OPTIONS` 요청입니다.
* `expose_headers` - 브라우저에서 접근 가능해야 하는 모든 응답 헤더를 나타냅니다. 기본값은 `[]`입니다.
* `max_age` - 브라우저가 CORS 응답을 캐시하는 최대 시간을 초 단위로 설정합니다. 기본값은 `600`입니다.
이 경우 미들웨어는 들어오는 요청을 가로채 적절한 CORS 헤더와, 정보 제공을 위한 `200` 또는 `400` 응답으로 응답합니다.
미들웨어는 두 가지 특정한 종류의 HTTP 요청에 응답합니다...
### 단순한 요청
### CORS 사전 요청 { #cors-preflight-requests }
`Origin` 헤더를 가진 모든 요청. 이 경우 미들웨어는 요청을 정상적으로 전달하지만, 적절한 CORS 헤더를 응답에 포함시킵니다.
`Origin` `Access-Control-Request-Method` 헤더가 있는 모든 `OPTIONS` 요청입니다.
## 더 많은 정보
이 경우 미들웨어는 들어오는 요청을 가로채 적절한 CORS 헤더와 함께, 정보 제공 목적으로 `200` 또는 `400` 응답을 반환합니다.
<abbr title="교차-출처 리소스 공유">CORS</abbr>에 대한 더 많은 정보를 알고싶다면, <a href="https://developer.mozilla.org/ko/docs/Web/HTTP/CORS" class="external-link" target="_blank">Mozilla CORS 문서</a>를 참고하기 바랍니다.
### 단순한 요청 { #simple-requests }
/// note | 기술적 세부 사항
`Origin` 헤더가 있는 모든 요청입니다. 이 경우 미들웨어는 요청을 정상적으로 통과시키지만, 응답에 적절한 CORS 헤더를 포함합니다.
`from starlette.middleware.cors import CORSMiddleware` 역시 사용할 수 있습니다.
## 더 많은 정보 { #more-info }
**FastAPI**는 개발자인 당신의 편의를 위해 `fastapi.middleware` 에서 몇가지의 미들웨어를 제공합니다. 하지만 대부분의 미들웨어가 Stralette으로부터 직접 제공됩니다.
<abbr title="Cross-Origin Resource Sharing 교차-출처 리소스 공유">CORS</abbr>에 대한 더 많은 정보는 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">Mozilla CORS 문서</a>를 참고하세요.
/// note | 기술 세부사항
`from starlette.middleware.cors import CORSMiddleware`도 사용할 수 있습니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 `fastapi.middleware`에 여러 미들웨어를 제공합니다. 하지만 사용 가능한 미들웨어 대부분은 Starlette에서 직접 제공됩니다.
///

View File

@@ -1,14 +1,14 @@
# 디버깅
# 디버깅 { #debugging }
예를 들면 Visual Studio Code 또는 PyCharm을 사용하여 편집기에서 디버거를 연결할 수 있습니다.
## `uvicorn` 호출
## `uvicorn` 호출 { #call-uvicorn }
FastAPI 애플리케이션에서 `uvicorn`을 직접 임포트하여 실행합니다
FastAPI 애플리케이션에서 `uvicorn`을 직접 임포트하여 실행합니다:
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
### `__name__ == "__main__"` 에 대하여
### `__name__ == "__main__"` 에 대하여 { #about-name-main }
`__name__ == "__main__"`의 주요 목적은 다음과 같이 파일이 호출될 때 실행되는 일부 코드를 갖는 것입니다.
@@ -26,7 +26,7 @@ $ python myapp.py
from myapp import app
```
#### 추가 세부사항
#### 추가 세부사항 { #more-details }
파일 이름이 `myapp.py`라고 가정해 보겠습니다.
@@ -62,7 +62,7 @@ from myapp import app
# Some more code
```
이 경우 `myapp.py` 내부의 자동 변수에는 값이 `"__main__"`인 변수 `__name__`습니다.
이 경우 `myapp.py` 내부의 자동 변수 `__name__`에는 값이 `"__main__"`들어가지 않습니다.
따라서 다음 행
@@ -74,11 +74,11 @@ from myapp import app
/// info | 정보
자세한 내용은 <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">공식 Python 문서</a>를 확인하세요
자세한 내용은 <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">공식 Python 문서</a>를 확인하세요.
///
## 디버거로 코드 실행
## 디버거로 코드 실행 { #run-your-code-with-your-debugger }
코드에서 직접 Uvicorn 서버를 실행하고 있기 때문에 디버거에서 직접 Python 프로그램(FastAPI 애플리케이션)을 호출할 수 있습니다.
@@ -101,7 +101,7 @@ from myapp import app
Pycharm을 사용하는 경우 다음을 수행할 수 있습니다
* "Run" 메뉴를 엽니다
* "Run" 메뉴를 엽니다.
* "Debug..." 옵션을 선택합니다.
* 그러면 상황에 맞는 메뉴가 나타납니다.
* 디버그할 파일을 선택합니다(이 경우 `main.py`).

View File

@@ -1,30 +1,30 @@
# 의존성으로서의 클래스
# 의존성으로서의 클래스 { #classes-as-dependencies }
**의존성 주입** 시스템에 대해 자세히 살펴보기 전에 이전 예제를 업그레이드 해보겠습니다.
**의존성 주입** 시스템에 대해 더 깊이 살펴보기 전에, 이전 예제를 업그레이드해 보겠습니다.
## 이전 예제의 `딕셔너리`
## 이전 예제의 `dict` { #a-dict-from-the-previous-example }
이전 예제에서, 우리는 의존성(의존 가능한) 함수에서 `딕셔너리`객체를 반환하고 있었습니다:
이전 예제에서는 의존성("dependable")에서 `dict`를 반환하고 있었습니다:
{* ../../docs_src/dependencies/tutorial001.py hl[9] *}
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
우리는 *경로 작동 함수*의 매개변수 `commons`에서 `딕셔너리` 객체를 얻습니다.
하지만 그러면 *경로 처리 함수*의 매개변수 `commons`에서 `dict`를 받게 됩니다.
그리고 우리는 에디터들이 `딕셔너리` 객체의 키나 밸류의 자료형을 알 수 없기 때문에 자동 완성과 같은 기능을 제공해 줄 수 없다는 것을 알고 있습니다.
그리고 에디터 `dict`의 키와 값 타입을 알 수 없기 때문에 `dict`에 대해서는 (완성 기능 같은) 많은 지원을 제공 수 없다는 것을 알고 있습니다.
더 나은 방법이 있을 것 같습니다...
더 나은 방법이 있습니다...
## 의존성으로 사용 가능한 것
## 의존성이 되기 위한 조건 { #what-makes-a-dependency }
지금까지 함수로 선언된 의존성을 봐왔습니다.
지금까지 함수로 선언된 의존성을 습니다.
아마도 더 일반적이기는 하겠지만 의존성을 선언하는 유일한 방법은 아닙니다.
하지만 그것만이 의존성을 선언하는 유일한 방법은 아닙니다(아마도 더 일반적이긴 하겠지만요).
핵심 요소는 의존성이 "호출 가능"해야 한다는 것입니다
핵심 요소는 의존성이 "호출 가능(callable)"해야 한다는 것입니다.
파이썬에서 "**호출 가능**" 파이썬이 함수처럼 "호출"할 수 있는 모든 것입니다.
파이썬에서 "**호출 가능(callable)**"이란 파이썬이 함수처럼 "호출"할 수 있는 모든 것입니다.
따라서, 만약 당신이 `something`(함수가 아닐 수도 있음) 객체를 가지고 있고,
따라서 `something`(함수가 _아닐_ 수도 있습니다)이라는 객체가 있고, 다음처럼 "호출"(실행)할 수 있다면:
```Python
something()
@@ -36,11 +36,11 @@ something()
something(some_argument, some_keyword_argument="foo")
```
상기와 같은 방식으로 "호출(실행)" 할 수 있다면 "호출 가능"이 됩니다.
그것은 "호출 가능(callable)"입니다.
## 의존성으로서의 클래스
## 의존성으로서의 클래스 { #classes-as-dependencies_1 }
파이썬 클래스의 인스턴스를 생성하기 위해 사용하는 것과 동일한 문법을 사용한다는 알 수 있니다.
파이썬 클래스의 인스턴스를 만들 때도 같은 문법을 사용한다는 것을 알 수 있을 겁니다.
예를 들어:
@@ -53,125 +53,236 @@ class Cat:
fluffy = Cat(name="Mr Fluffy")
```
이 경우 `fluffy`는 클래스 `Cat`의 인스턴스입니다. 그리고 우리는 `fluffy`를 만들기 위해서 `Cat`을 "호출"했습니다.
이 경우 `fluffy`는 클래스 `Cat`의 인스턴스입니다.
따라서, 파이썬 클래스는 **호출 가능**합니다.
그리고 `fluffy`를 만들기 위해 `Cat`을 "호출"하고 있습니다.
그래서 **FastAPI**에서는 파이썬 클래스를 의존성으로 사용할 수 있습니다.
따라서 파이썬 클래스도 **호출 가능(callable)**합니다.
FastAPI가 실질적으로 확인하는 것은 "호출 가능성"(함수, 클래스 또는 다른 모든 것)과 정의된 매개변수들입니다.
그러면 **FastAPI**에서는 파이썬 클래스를 의존성으로 사용할 수 있습니다.
"호출 가능"한 것을 의존성으로서 **FastAPI**에 전달하면, 그 "호출 가능"한 것의 매개변수들을 분석한 후 이를 *경로 작동 함수*를 위한 매개변수와 동일한 방식으로 처리합니다. 하위-의존성 또한 같은 방식으로 처리합니다.
FastAPI가 실제로 확인하는 것은 그것이 "호출 가능(callable)"(함수, 클래스, 또는 다른 무엇이든)한지와 정의된 매개변수들입니다.
매개변수가 없는 "호출 가능"한 것 역시 매개변수가 없는 *경로 작동 함수*와 동일한 방식으로 적용됩니다.
**FastAPI**에서 "호출 가능(callable)"한 것을 의존성으로 넘기면, 그 "호출 가능(callable)"한 것 매개변수들을 분석하고 *경로 처리 함수*의 매개변수와 동일한 방식으로 처리합니다. 하위 의존성도 포함해서요.
그래서, 우리는 위 예제에서의 `common_paramenters` 의존성을 클래스 `CommonQueryParams`로 바꿀 수 있습니다.
이것은 매개변수가 전혀 없는 callable에도 적용됩니다. 매개변수가 없는 *경로 처리 함수*에 적용되는 것과 동일합니다.
{* ../../docs_src/dependencies/tutorial002.py hl[11:15] *}
그러면 위의 의존성("dependable") `common_parameters`를 클래스 `CommonQueryParams`로 바꿀 수 있습니다:
클래스의 인스턴스를 생성하는 데 사용되는 `__init__` 메서드에 주목하기 바랍니다:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
{* ../../docs_src/dependencies/tutorial002.py hl[12] *}
클래스의 인스턴스를 만들 때 사용하는 `__init__` 메서드에 주목하세요:
...이전 `common_parameters`와 동일한 매개변수를 가집니다:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
{* ../../docs_src/dependencies/tutorial001.py hl[9] *}
...이전의 `common_parameters`와 동일한 매개변수를 가지고 있습니다:
이 매개변수들은 **FastAPI**가 의존성을 "해결"하기 위해 사용할 것입니다
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
함수와 클래스 두 가지 방식 모두, 아래 요소를 갖습니다:
이 매개변수들이 **FastAPI**가 의존성을 "해결"하는 데 사용할 것들입니다.
* `문자열`이면서 선택사항인 쿼리 매개변수 `q`.
* 기본값이 `0`이면서 `정수형`인 쿼리 매개변수 `skip`
* 기본값이 `100`이면서 `정수형`인 쿼리 매개변수 `limit`
두 경우 모두 다음을 갖게 됩니다:
두 가지 방식 모두, 데이터는 변환, 검증되고 OpenAPI 스키마에 문서화됩니다.
* `str`인 선택적 쿼리 매개변수 `q`.
* 기본값이 `0``int` 쿼리 매개변수 `skip`.
* 기본값이 `100``int` 쿼리 매개변수 `limit`.
## 사용해봅시다!
두 경우 모두 데이터는 변환되고, 검증되며, OpenAPI 스키마에 문서화되는 등 여러 처리가 적용됩니다.
이제 아래의 클래스를 이용해서 의존성을 정의할 수 있습니다.
## 사용하기 { #use-it }
{* ../../docs_src/dependencies/tutorial002.py hl[19] *}
이제 이 클래스를 사용해 의존성을 선언할 수 있습니다.
**FastAPI**는 `CommonQueryParams` 클래스를 호출합니다. 이것은 해당 클래스의 "인스턴스"를 생성하고 그 인스턴스는 함수의 매개변수 `commons`로 전달됩니다.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
## 타입 힌팅 vs `Depends`
**FastAPI**는 `CommonQueryParams` 클래스를 호출합니다. 그러면 해당 클래스의 "인스턴스"가 생성되고, 그 인스턴스가 함수의 매개변수 `commons`로 전달됩니다.
위 코드에서 `CommonQueryParams`를 두 번 작성한 방식에 주목하십시오:
## 타입 어노테이션 vs `Depends` { #type-annotation-vs-depends }
위 코드에서 `CommonQueryParams`를 두 번 작성하는 방식에 주목하세요:
//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.9+ non-Annotated
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
마지막 `CommonQueryParams` 변수를 보면:
////
마지막 `CommonQueryParams`는, 다음에서:
```Python
... = Depends(CommonQueryParams)
... Depends(CommonQueryParams)
```
... **FastAPI**가 실제로 어떤 것이 의존성인지 알기 위해 사용하는 방법입니다.
FastAPI는 선언된 매개변수들을 추출할 것이고 실제로 이 변수들을 호출할 것입니다.
...**FastAPI**가 실제로 무엇이 의존성인지 알기 위해 사용하는 입니다.
FastAPI는 여기에서 선언된 매개변수들을 추출하고, 실제로 이것을 호출합니다.
---
이 경우에, 첫번째 `CommonQueryParams` 변수를 보면:
이 경우 첫 번째 `CommonQueryParams`는 다음에서:
//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.9+ non-Annotated
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams ...
```
... **FastAPI**는 `CommonQueryParams` 변수에 어떠한 특별한 의미도 부여하지 않습니다. FastAPI는 이 변수를 데이터 변환, 검증 등에 활용하지 않습니다. (활용하려면 `= Depends(CommonQueryParams)`를 사용해야 합니다.)
////
사실 아래와 같이 작성해도 무관합니다:
...**FastAPI**에 특별한 의미가 없습니다. FastAPI는 이를 데이터 변환, 검증 등에 사용하지 않습니다(그런 용도로는 `Depends(CommonQueryParams)`를 사용하고 있기 때문입니다).
실제로는 이렇게만 작성해도 됩니다:
//// tab | Python 3.9+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.9+ non-Annotated
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons = Depends(CommonQueryParams)
```
..전체적인 코드는 아래와 같습니다:
////
{* ../../docs_src/dependencies/tutorial003.py hl[19] *}
...다음과 같이요:
그러나 자료형을 선언하면 에디터가 매개변수 `commons`로 전달될 것이 무엇인지 알게 되고, 이를 통해 코드 완성, 자료형 확인 등에 도움이 될 수 있으므로 권장됩니다.
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
<!-- <img src="/img/tutorial/dependencies/image02.png"> -->
하지만 타입을 선언하는 것을 권장합니다. 그러면 에디터가 매개변수 `commons`에 무엇이 전달되는지 알 수 있고, 코드 완성, 타입 체크 등에서 도움을 받을 수 있습니다:
## 코드 단축
<img src="/img/tutorial/dependencies/image02.png">
그러나 여기 `CommonQueryParams`를 두 번이나 작성하는, 코드 반복이 있다는 것을 알 수 있습니다:
## 단축 { #shortcut }
하지만 `CommonQueryParams`를 두 번 작성하는 코드 반복이 있다는 것을 볼 수 있습니다:
//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.9+ non-Annotated
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
**FastAPI**는 *특히* 의존성이 **FastAPI**가 클래스 자체의 인스턴스를 생성하기 위해 "호출"하는 클래스인 경우, 조금 더 쉬운 방법을 제공합니다.
////
이러한 특정한 경우에는 아래처럼 사용할 수 있습니다:
**FastAPI**는 이런 경우를 위한 단축 방법을 제공합니다. 이때 의존성은 *특히* **FastAPI**가 "호출"해서 클래스 자체의 인스턴스를 만들도록 하는 클래스입니다.
렇게 쓰는 것 대신:
특정한 경우에는 다음과 같이 할 수 있습니다:
다음처럼 작성하는 대신:
//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.9+ non-Annotated
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
...이렇게 쓸 수 있습니다.:
////
...이렇게 작성합니다:
//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.9+ non-Annotated
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams = Depends()
```
의존성을 매개변수의 타입으로 선언하는 경우 `Depends(CommonQueryParams)`처럼 클래스 이름 전체를 *다시* 작성하는 대신, 매개변수를 넣지 않은 `Depends()`의 형태로 사용할 수 있습니다.
////
아래에 같은 예제가 있습니다:
의존성을 매개변수의 타입으로 선언하고, `Depends(CommonQueryParams)` 안에 클래스 전체를 *다시* 작성하는 대신 매개변수 없이 `Depends()`를 사용합니다.
{* ../../docs_src/dependencies/tutorial004.py hl[19] *}
그러면 같은 예제는 다음처럼 보일 겁니다:
...이렇게 코드를 단축하여도 **FastAPI**는 무엇을 해야하는지 알고 있습니다.
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...그리고 **FastAPI**는 무엇을 해야 하는지 알게 됩니다.
/// tip | 팁
만약 이것이 도움이 되기보다 더 헷갈리게 만든다면, 잊어버리십시오. 이것이 반드시 필요한 것은 아닙니다.
도움이 되기보다 더 헷갈린다면, 무시하세요. 이건 *필수*가 아닙니다.
이것은 단지 손쉬운 방법일 뿐입니다. 왜냐하면 **FastAPI**는 코드 반복을 최소화할 수 있는 방법을 고민하기 때문입니다.
그저 단축 방법일 뿐입니다. **FastAPI**는 코드 반복을 최소화하도록 도와주는 것을 중요하게 생각하기 때문입니다.
///

View File

@@ -1,4 +1,4 @@
# 경로 작동 데코레이터에서의 의존성
# 경로 작동 데코레이터에서의 의존성 { #dependencies-in-path-operation-decorators }
몇몇 경우에는, *경로 작동 함수* 안에서 의존성의 반환 값이 필요하지 않습니다.
@@ -8,7 +8,7 @@
그런 경우에, `Depends`를 사용하여 *경로 작동 함수*의 매개변수로 선언하는 것보다 *경로 작동 데코레이터*에 `dependencies``list`를 추가할 수 있습니다.
## *경로 작동 데코레이터*에 `dependencies` 추가하기
## *경로 작동 데코레이터*에 `dependencies` 추가하기 { #add-dependencies-to-the-path-operation-decorator }
*경로 작동 데코레이터*는 `dependencies`라는 선택적인 인자를 받습니다.
@@ -22,7 +22,7 @@
일부 편집기에서는 사용되지 않는 함수 매개변수를 검사하고 오류로 표시합니다.
*경로 작동 데코레이터*에서 `dependencies`를 사용하면 편집기/도구 오류를 피하 실행되도록 할 수 있습니다.
*경로 작동 데코레이터*에서 이러한 `dependencies`를 사용하면 편집기/도구 오류를 피하면서도 실행되도록 할 수 있습니다.
또한 코드에서 사용되지 않는 매개변수를 보고 불필요하다고 생각할 수 있는 새로운 개발자의 혼란을 방지하는데 도움이 될 수 있습니다.
@@ -36,23 +36,23 @@
///
## 의존성 오류와 값 반환하기
## 의존성 오류와 값 반환하기 { #dependencies-errors-and-return-values }
평소에 사용하던대로 같은 의존성 *함수*를 사용할 수 있습니다.
### 의존성 요구사항
### 의존성 요구사항 { #dependency-requirements }
(헤더같은) 요청 요구사항이나 하위-의존성을 선언할 수 있습니다:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
### 오류 발생시키기
### 오류 발생시키기 { #raise-exceptions }
다음 의존성은 기존 의존성과 동일하게 예외를 `raise`를 일으킬 수 있습니다:
다음 의존성은 기존 의존성과 동일하게 예외를 `raise` 수 있습니다:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
### 값 반환하기
### 값 반환하기 { #return-values }
값을 반환하거나, 그러지 않을 수 있으며 값은 사용되지 않습니다.
@@ -60,10 +60,10 @@
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
## *경로 작동* 모음에 대한 의존성
## *경로 작동* 모음에 대한 의존성 { #dependencies-for-a-group-of-path-operations }
나중에 여러 파일을 가지고 있을 수 있는 더 큰 애플리케이션을 구조화하는 법([더 큰 애플리케이션 - 여러 파일들](../../tutorial/bigger-applications.md){.internal-link target=_blank})을 읽을 때, *경로 작동* 모음에 대한 단일 `dependencies` 매개변수를 선언하는 법에 대해서 배우게 될 것입니다.
## 전역 의존성
## 전역 의존성 { #global-dependencies }
다음으로 각 *경로 작동*에 적용되도록 `FastAPI` 애플리케이션 전체에 의존성을 추가하는 법을 볼 것입니다.

View File

@@ -1,6 +1,6 @@
# yield를 사용하는 의존성
# `yield`를 사용하는 의존성 { #dependencies-with-yield }
FastAPI는 <abbr title='때로는 "종료 코드", "정리 코드", "종료 처리 코드", "닫기 코드", "컨텍스트 관리자 종료 코드" 등으로도 불립니다'>작업 완료 후 추가 단계를 수행하는</abbr> 의존성을 지원합니다.
FastAPI는 <abbr title='sometimes also called "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code", etc. 때로는 "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code" 등으로도 불립니다'>작업 완료 후 추가 단계를 수행하는</abbr> 의존성을 지원합니다.
이를 구현하려면 `return` 대신 `yield`를 사용하고, 추가로 실행할 단계 (코드)를 그 뒤에 작성하세요.
@@ -23,21 +23,21 @@ FastAPI는 <abbr title='때로는 "종료 코드", "정리 코드", "종료 처
///
## `yield`를 사용하는 데이터베이스 의존성
## `yield`를 사용하는 데이터베이스 의존성 { #a-database-dependency-with-yield }
예를 들어, 이 기능을 사용하면 데이터베이스 세션을 생성하고 작업이 끝난 후에 세션을 종료할 수 있습니다.
응답을 생성하기 전에는 `yield`문을 포함하여 그 이전의 코드만이 실행됩니다:
{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
yield된 값은 *경로 작업* 및 다른 의존성들에 주입되는 값 입니다:
yield된 값은 *경로 처리* 및 다른 의존성들에 주입되는 값 입니다:
{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
`yield`문 다음의 코드는 응답을 생성한 후 보내기 전에 실행됩니다:
`yield`문 다음의 코드는 응답을 생성한 후 실행됩니다:
{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
/// tip | 팁
@@ -47,19 +47,19 @@ yield된 값은 *경로 작업* 및 다른 의존성들에 주입되는 값 입
///
## `yield`와 `try`를 사용하는 의존성
## `yield`와 `try`를 사용하는 의존성 { #a-dependency-with-yield-and-try }
`yield`를 사용하는 의존성에서 `try` 블록을 사용한다면, 의존성을 사용하는 도중 발생한 모든 예외를 받을 수 있습니다.
예를 들어, 다른 의존성이나 *경로 작업*의 중간에 데이터베이스 트랜잭션 "롤백"이 발생하거나 다른 오류가 발생한다면, 해당 예외를 의존성에서 받을 수 있습니다.
예를 들어, 다른 의존성이나 *경로 처리*의 중간에 데이터베이스 트랜잭션 "롤백"이 발생하거나 다른 오류가 발생한다면, 해당 예외를 의존성에서 받을 수 있습니다.
따라서, 의존성 내에서 `except SomeException`을 사용하여 특정 예외를 처리할 수 있습니다.
마찬가지로, `finally`를 사용하여 예외 발생 여부와 관계 없이 종료 단계까 실행되도록 할 수 있습니다.
{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
## `yield`를 사용하는 하위 의존성
## `yield`를 사용하는 하위 의존성 { #sub-dependencies-with-yield }
모든 크기와 형태의 하위 의존성과 하위 의존성의 "트리"도 가질 수 있으며, 이들 모두가 `yield`를 사용할 수 있습니다.
@@ -87,21 +87,23 @@ yield된 값은 *경로 작업* 및 다른 의존성들에 주입되는 값 입
/// note | 기술 세부사항
파이썬의 <a href=https://docs.python.org/3/library/contextlib.html class=external-link target=_blank>Context Managers</a> 덕분에 이 기능이 작동합니다.
파이썬의 <a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">Context Managers</a> 덕분에 이 기능이 작동합니다.
**FastAPI**는 이를 내부적으로 컨텍스트 관리자를 사용하여 구현합니다.
**FastAPI**는 이를 내부적으로 사용하여 이를 달성합니다.
///
## `yield`와 `HTTPException`를 사용하는 의존성
## `yield`와 `HTTPException`를 사용하는 의존성 { #dependencies-with-yield-and-httpexception }
`yield``try` 블록이 있는 의존성을 사용하여 예외를 처리할 수 있다는 것을 알게 되었습니다.
`yield`를 사용하는 의존성에서 `try` 블록을 사용해 코드를 실행하고, 그 다음 `finally` 뒤에 종료 코드를 실행할 수 있다는 것을 보았습니다.
같은 방식으로, `yield` 이후의 종료 코드에서 `HTTPException`이나 유사한 예외를 발생시킬 수 있습니다.
또한 `except`를 사용해 발생한 예외를 잡고 그에 대해 무언가를 할 있습니다.
예를 들어, `HTTPException` 같은 다른 예외를 발생시킬 수 있습니다.
/// tip | 팁
이는 다소 고급 기술이며, 대부분의 경우 경로 연산 함수 등 나머지 애플리케이션 코드 내부에서 예외 (`HTTPException` 포함)를 발생시킬 수 있으므로 실제로는 필요하지 않을 것입니다.
이는 다소 고급 기술이며, 대부분의 경우 실제로는 필요하지 않을 것입니다. 예를 들어, *경로 처리 함수* 등 나머지 애플리케이션 코드 내부에서 예외 (`HTTPException` 포함)를 발생시킬 수 있기 때문입니다.
하지만 필요한 경우 사용할 수 있습니다. 🤓
@@ -109,27 +111,27 @@ yield된 값은 *경로 작업* 및 다른 의존성들에 주입되는 값 입
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
예외를 처리하고(또는 추가로 다른 `HTTPException`을 발생시키기 위해) 사용할 수 있는 또 다른 방법은 [사용자 정의 예외 처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}를 생성하는 것 입니다.
예외를 잡고 그에 기반해 사용자 정의 응답을 생성하려면, [사용자 정의 예외 처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}를 생성하세요.
## `yield`와 `except`를 사용하는 의존성
## `yield`와 `except`를 사용하는 의존성 { #dependencies-with-yield-and-except }
`yield`를 사용하는 의존성에서 `except`를 사용하여 예외를 포착하고 예외를 다시 발생시키지 않거나 (또는 새 예외를 발생시키지 않으면), FastAPI는 해당 예외가 발생했는지 알 수 없습니다. 이는 일반적인 Python 방식과 동일합니다:
`yield`를 사용하는 의존성에서 `except`를 사용하여 예외를 포착하고 예외를 다시 발생시키지 않거나 (또는 새 예외를 발생시키지 않으면), FastAPI는 일반적인 Python에서와 마찬가지로 예외가 있었다는 것을 알아차릴 수 없습니다:
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
이 경우, `HTTPException`이나 유사한 예외를 발생시키지 않기 때문에 클라이언트는 HTTP 500 Internal Server Error 응답을 보게 되지만, 서버는 어떤 오류가 발생했는지에 대한 **로그**나 다른 표시전혀 가지지 않게 됩니다. 😱
이 경우, `HTTPException`이나 유사한 예외를 발생시키지 않기 때문에 클라이언트는 마땅히 *HTTP 500 Internal Server Error* 응답을 보게 되지만, 서버는 어떤 오류는지에 대한 **로그**나 다른 표시가 **전혀 지 않게 됩니다**. 😱
### `yield`와 `except`를 사용하는 의존성에서 항상 `raise` 하기
### `yield`와 `except`를 사용하는 의존성에서 항상 `raise` 하기 { #always-raise-in-dependencies-with-yield-and-except }
`yield`가 있는 의존성에서 예외를 잡았을 때 `HTTPException`이나 유사한 예외를 새로 발생시키지 않는 한, 반드시 원래 예외를 다시 발생시켜야 합니다.
`yield`가 있는 의존성에서 예외를 잡았을 때, 다른 `HTTPException`이나 유사한 예외를 발생시키는 것이 아니라면, **원래 예외를 다시 발생시켜야 합니다**.
`raise`를 사용하여 동일한 예외를 다시 발생시킬 수 있습니다:
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
이제 클라이언트는 동일한 *HTTP 500 Internal Server Error* 오류 응답을 받게 되지만, 서버 로그에는 사용자 정의 예외인 `InternalError"가 기록됩니다. 😎
이제 클라이언트는 동일한 *HTTP 500 Internal Server Error* 응답을 받게 되지만, 서버 로그에는 사용자 정의 `InternalError`가 기록됩니다. 😎
## `yield`를 사용하는 의존성의 실행 순서
## `yield`를 사용하는 의존성의 실행 순서 { #execution-of-dependencies-with-yield }
실행 순서는 아래 다이어그램과 거의 비슷합니다. 시간은 위에서 아래로 흐릅니다. 그리고 각 열은 상호 작용하거나 코드를 실행하는 부분 중 하나입니다.
@@ -170,7 +172,7 @@ participant tasks as Background tasks
/// info | 정보
클라이언트에 **하나의 응답** 만 전송됩니다. 이는 오류 응답 중 하나일 수도 있고,*경로 작업*에서 생성된 응답일 수도 있습니다.
클라이언트에 **하나의 응답**만 전송됩니다. 이는 오류 응답 중 하나일 수도 있고, *경로 처리*에서 생성된 응답일 수도 있습니다.
이러한 응답 중 하나가 전송된 후에는 다른 응답을 보낼 수 없습니다.
@@ -178,55 +180,67 @@ participant tasks as Background tasks
/// tip | 팁
이 다이어그램은 `HTTPException`을 보여주지만, `yield`를 사용하는 의존성에서 처리한 예외나 [사용자 정의 예외처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.를 사용하여 처리한 다른 예외도 발생시킬 수 있습니다.
어떤 예외가 발생하든, `HTTPException`을 포함하여 yield를 사용하는 의존성으로 전달됩니다. 대부분의 경우 예외를 다시 발생시키거나 새로운 예외를 발생시켜야 합니다.
*경로 처리 함수*의 코드에서 어떤 예외를 발생시키면 `HTTPException`을 포함해 `yield`를 사용하는 의존성으로 전달됩니다. 대부분의 경우 해당 예외(또는 새 예외)를 `yield`를 사용하는 의존성에서 다시 발생시켜, 제대로 처리되도록 해야 합니다.
///
## `yield`, `HTTPException`, `except` 및 백그라운드 작업을 사용하는 의존성
## 조기 종료와 `scope` { #early-exit-and-scope }
/// warning | 경고
일반적으로 `yield`를 사용하는 의존성의 종료 코드는 클라이언트로 **응답이 전송된 후에** 실행됩니다.
이러한 기술적 세부 사항은 대부분 필요하지 않으므로 이 섹션을 건너뛰고 아래에서 계속 진행해도 됩니다.
하지만 *경로 처리 함수*에서 반환한 뒤에는 더 이상 해당 의존성이 필요 없다는 것을 알고 있다면, `Depends(scope="function")`을 사용하여 FastAPI에 *경로 처리 함수*가 반환된 후, 하지만 **응답이 전송되기 전에** 의존성을 종료(닫기)해야 한다고 알려줄 수 있습니다.
이러한 세부 정보는 주로 FastAPI 0.106.0 이전 버전에서 `yield`가 있는 의존성의 리소스를 백그라운드 작업에서 사용했던 경우메 유용합니다.
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
///
`Depends()`는 다음이 될 수 있는 `scope` 매개변수를 받습니다:
### `yield`와 `except`를 사용하는 의존성, 기술 세부사항
* `"function"`: 요청을 처리하는 *경로 처리 함수* 전에 의존성을 시작하고, *경로 처리 함수*가 끝난 후, 하지만 응답이 클라이언트로 전송되기 **전에** 의존성을 종료합니다. 즉, 의존성 함수는 *경로 처리 **함수***를 **둘러싸며** 실행됩니다.
* `"request"`: 요청을 처리하는 *경로 처리 함수* 전에 의존성을 시작하고(`"function"`을 사용할 때와 유사), 응답이 클라이언트로 전송된 **후에** 종료합니다. 즉, 의존성 함수는 **요청**과 응답 사이클을 **둘러싸며** 실행됩니다.
FastAPI 0.110.0 이전에는 `yield`가 포함된 의존성을 사용한 후 해당 의존성에서 `except`가 포함된 예외를 캡처하고 다시 예외를 발생시키지 않으면 예외가 자동으로 예외 핸들러 또는 내부 서버 오류 핸들러로 발생/전달되었습니다.
지정하지 않고 의존성이 `yield`를 사용한다면, 기본 `scope``"request"`니다.
이는 처리기 없이 전달된 예외(내부 서버 오류)에서 처리되지 않은 메모리 소비를 수정하고 일반 파이썬 코드의 동작과 일치하도록 하기 위해 0.110.0 버전에서 변경되었습니다.
### 하위 의존성을 위한 `scope` { #scope-for-sub-dependencies }
### 백그라운드 작업과 `yield`를 사용하는 의존성, 기술 세부사항
`scope="request"`(기본값)로 의존성을 선언하면, 모든 하위 의존성도 `scope``"request"`여야 합니다.
FastAPI 0.106.0 이전에는 `yield` 이후에 예외를 발생시키는 것이 불가능했습니다. `yield`가 있는 의존성 종료 코드는 응답이 전송된 이후에 실행되었기 때문에, [예외 처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}가 이미 실행된 상태였습니다.
하지만 `scope``"function"`인 의존성은 `scope``"function"`인 의존성과 `"request"`인 의존성을 모두 의존성으로 가질 수 있습니다.
이는 주로 백그라운드 작업 내에서 의존성에서 "yield된" 동일한 객체를 사용할 수 있도록 하기 위해 이런 방식으로 설계되었습니다. 종료 코드는 백그라운드 작업이 완료된 후에 실행되었기 때문입니다
이는 어떤 의존성이든, 종료 코드에서 하위 의존성을 계속 사용해야 할 수으므로, 하위 의존성보다 먼저 종료 코드를 실행할 수 있어야 하기 때문입니다.
하지만 이렇게 하면 리소스를 불필요하게 양보한 의존성(예: 데이터베이스 연결)에서 보유하면서 응답이 네트워크를 통해 이동할 때까지 기다리는 것을 의미하기 때문에 FastAPI 0.106.0에서 변경되었습니다.
```mermaid
sequenceDiagram
/// tip | 팁
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
또한 백그라운드 작업은 일반적으로 자체 리소스(예: 자체 데이터베이스 연결)를 사용하여 별도로 처리해야 하는 독립적인 로직 집합입니다.
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
```
따라서 이렇게 하면 코드가 더 깔끔해집니다.
## `yield`, `HTTPException`, `except` 및 백그라운드 작업을 사용하는 의존성 { #dependencies-with-yield-httpexception-except-and-background-tasks }
///
`yield`를 사용하는 의존성은 시간이 지나면서 서로 다른 사용 사례를 다루고 일부 문제를 수정하기 위해 발전해 왔습니다.
만약 이전에 이러한 동작에 의존했다면, 이제는 백그라운드 작업 내부에서 백그라운드 작업을 위한 리소스를 생성하고, `yield`가 있는 의존성의 리소스에 의존하지 않는 데이터만 내부적으로 사용해야합니다.
FastAPI의 여러 버전에서 무엇이 바뀌었는지 보고 싶다면, 고급 가이드의 [고급 의존성 - `yield`, `HTTPException`, `except` 및 백그라운드 작업을 사용하는 의존성](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks){.internal-link target=_blank}에서 더 자세히 읽을 수 있습니다.
## 컨텍스트 관리자 { #context-managers }
예를 들어, 동일한 데이터베이스 세션을 사용하는 대신, 백그라운드 작업 내부에서 새로운 데이터베이스 세션을 생성하고 이 새로운 세션을 사용하여 데이터베이스에서 객체를 가져와야 합니다. 그리고 데이터베이스 객체를 백그라운드 작업 함수의 매개변수로 직접 전달하는 대신, 해당 객체의 ID를 전달한 다음 백그라운드 작업 함수 내부에서 객체를 다시 가져와야 합니다
## 컨텍스트 관리자
### "컨텍스트 관리자"란?
### "컨텍스트 관리자"란 { #what-are-context-managers }
"컨텍스트 관리자"는 Python에서 `with` 문에서 사용할 수 있는 모든 객체를 의미합니다.
예를 들어, <a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank"> `with`를 사용하여 파일을 읽을 수 있습니다</a>:
예를 들어, <a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank">`with`를 사용하여 파일을 읽을 수 있습니다</a>:
```Python
with open("./somefile.txt") as f:
@@ -240,7 +254,7 @@ with open("./somefile.txt") as f:
`yield`가 있는 의존성을 생성하면 **FastAPI**는 내부적으로 이를 위한 컨텍스트 매니저를 생성하고 다른 관련 도구들과 결합합니다.
### `yield`를 사용하는 의존성에서 컨텍스트 관리자 사용하기
### `yield`를 사용하는 의존성에서 컨텍스트 관리자 사용하기 { #using-context-managers-in-dependencies-with-yield }
/// warning | 경고
@@ -255,7 +269,7 @@ Python에서는 다음을 통해 컨텍스트 관리자를 생성할 수 있습
**FastAPI**의 `yield`가 있는 의존성 내에서
`with` 또는 `async with`문을 사용하여 이들을 활용할 수 있습니다:
{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
/// tip | 팁

View File

@@ -1,15 +1,16 @@
# 전역 의존성
# 전역 의존성 { #global-dependencies }
몇몇 애플리케이션에서는 애플리케이션 전체에 의존성을 추가하고 싶을 수 있습니다.
몇몇 유형의 애플리케이션에서는 애플리케이션 전체에 의존성을 추가하고 싶을 수 있습니다.
[*경로 작동 데코레이터*에 `dependencies` 추가하기](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}와 유사한 방법으로 `FastAPI` 애플리케이션에 그것들을 추가할 수 있습니다.
[*경로 처리 데코레이터*에 `dependencies` 추가하기](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}와 유사한 방법으로 `FastAPI` 애플리케이션에 그것들을 추가할 수 있습니다.
그런 경우에, 애플리케이션의 모든 *경로 작동*에 적용될 것입니다:
그런 경우에, 애플리케이션의 모든 *경로 처리*에 적용될 것입니다:
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
그리고 [*경로 작동 데코레이터*에 `dependencies` 추가하기](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}에 대한 아이디어는 여전히 적용되지만 여기에서는 앱에 있는 모든 *경로 작동*에 적용됩니다.
## *경로 작동* 모음에 대한 의존성
그리고 [*경로 처리 데코레이터*에 `dependencies` 추가하기](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} 섹션의 모든 아이디어는 여전히 적용되지만, 이 경우에는 앱의 모든 *경로 처리*에 적용됩니다.
이후에 여러 파일들을 가지는 더 큰 애플리케이션을 구조화하는 법([더 큰 애플리케이션 - 여러 파일들](../../tutorial/bigger-applications.md){.internal-link target=_blank})을 읽을 때, *경로 작동* 모음에 대한 단일 `dependencies` 매개변수를 선언하는 법에 대해서 배우게 될 것입니다.
## *경로 처리* 그룹에 대한 의존성 { #dependencies-for-groups-of-path-operations }
나중에 여러 파일을 포함할 수도 있는 더 큰 애플리케이션을 구조화하는 법([더 큰 애플리케이션 - 여러 파일들](../../tutorial/bigger-applications.md){.internal-link target=_blank})을 읽을 때, *경로 처리* 그룹에 대한 단일 `dependencies` 매개변수를 선언하는 법을 배우게 될 것입니다.

View File

@@ -1,14 +1,14 @@
# 의존성
# 의존성 { #dependencies }
**FastAPI**는 아주 강력하지만 직관적인 **<abbr title="컴포넌트, 자원, 제공자, 서비스, 인젝터블로 알려져 있습니다">의존성 주입</abbr>** 시스템을 가지고 있습니다.
**FastAPI**는 아주 강력하지만 직관적인 **<abbr title="also known as components, resources, providers, services, injectables">의존성 주입</abbr>** 시스템을 가지고 있습니다.
이는 사용하기 아주 쉽게 설계했으며, 어느 개발자나 다른 컴포넌트와 **FastAPI**를 쉽게 통합할 수 있도록 만들었습니다.
## "의존성 주입"은 무엇입니까?
## "의존성 주입"은 무엇입니까? { #what-is-dependency-injection }
**"의존성 주입"**은 프로그래밍에서 여러분의 코드(이 경우, 경로 작동 함수)가 작동하고 사용하는 데 필요로 하는 것, 즉 "의존성"을 선언할 수 있는 방법을 의미합니다.
**"의존성 주입"**은 프로그래밍에서 여러분의 코드(이 경우, *경로 처리 함수*)가 작동하고 사용하는 데 필요로 하는 것, 즉 "의존성"을 선언할 수 있는 방법을 의미합니다.
그 후에, 시스템(이 경우 FastAPI)은 여러분의 코드가 요구하는 의존성을 제공하기 위해 필요한 모든 작업을 처리합니다.(의존성을 "주입"합니다)
그 후에, 시스템(이 경우 **FastAPI**)은 여러분의 코드가 요구하는 의존성을 제공하기 위해 필요한 모든 작업을 처리합니다.(의존성을 "주입"합니다)
이는 여러분이 다음과 같은 사항을 필요로 할 때 매우 유용합니다:
@@ -19,17 +19,17 @@
이 모든 사항을 할 때 코드 반복을 최소화합니다.
## 첫번째 단계
## 첫번째 단계 { #first-steps }
아주 간단한 예제를 봅시다. 너무 간단할 것이기에 지금 당장은 유용하지 않을 수 있습니다.
하지만 이를 통해 **의존성 주입** 시스템이 어떻게 작동하는지에 중점을 둘 것입니다.
### 의존성 혹은 "디펜더블" 만들기
### 의존성 혹은 "디펜더블" 만들기 { #create-a-dependency-or-dependable }
의존성에 집중해 봅시다.
*경로 작동 함수*가 가질 수 있는 모든 매개변수를 갖는 단순한 함수입니다:
*경로 처리 함수*가 가질 수 있는 모든 매개변수를 갖는 단순한 함수입니다:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
@@ -37,9 +37,9 @@
**단 두 줄입니다**.
그리고, 이 함수는 여러분의 모든 *경로 작동 함수*가 가지고 있는 것과 같은 형태와 구조를 가지고 있습니다.
그리고, 이 함수는 여러분의 모든 *경로 처리 함수*가 가지고 있는 것과 같은 형태와 구조를 가지고 있습니다.
여러분은 이를 "데코레이터"가 없는 (`@app.get("/some-path")`가 없는) *경로 작동 함수*라고 생각할 수 있습니다.
여러분은 이를 "데코레이터"가 없는 (`@app.get("/some-path")`가 없는) *경로 처리 함수*라고 생각할 수 있습니다.
그리고 여러분이 원하는 무엇이든 반환할 수 있습니다.
@@ -47,7 +47,7 @@
* 선택적인 쿼리 매개변수 `q`, `str`을 자료형으로 가집니다.
* 선택적인 쿼리 매개변수 `skip`, `int`를 자료형으로 가지며 기본 값은 `0`입니다.
* 선택적인 쿼리 매개변수 `limit`,`int`를 자료형으로 가지며 기본 값은 `100`입니다.
* 선택적인 쿼리 매개변수 `limit` that is an `int`, and by default is `100`.
그 후 위의 값을 포함한 `dict` 자료형으로 반환할 뿐입니다.
@@ -57,17 +57,17 @@ FastAPI는 0.95.0 버전부터 `Annotated`에 대한 지원을 (그리고 이를
옛날 버전을 가지고 있는 경우, `Annotated`를 사용하려 하면 에러를 맞이하게 될 것입니다.
`Annotated`를 사용하기 전에 최소 0.95.1로 [FastAPI 버전 업그레이드](../../deployment/versions.md#fastapi_2){.internal-link target=_blank}를 확실하게 하세요.
`Annotated`를 사용하기 전에 최소 0.95.1로 [FastAPI 버전 업그레이드](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank}를 확실하게 하세요.
///
### `Depends` 불러오기
### `Depends` 불러오기 { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### "의존자"에 의존성 명시하기
### "의존자"에 의존성 명시하기 { #declare-the-dependency-in-the-dependant }
*경로 작동 함수*의 매개변수로 `Body`, `Query` 등을 사용하는 방식과 같이 새로운 매개변수로 `Depends`를 사용합니다:
*경로 처리 함수*의 매개변수로 `Body`, `Query` 등을 사용하는 방식과 같이 새로운 매개변수로 `Depends`를 사용합니다:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
@@ -79,7 +79,7 @@ FastAPI는 0.95.0 버전부터 `Annotated`에 대한 지원을 (그리고 이를
여러분은 직접 **호출하지 않았습니다** (끝에 괄호를 치지 않았습니다), 단지 `Depends()`에 매개변수로 넘겨 줬을 뿐입니다.
그리고 그 함수는 *경로 작동 함수*가 작동하는 것과 같은 방식으로 매개변수를 받습니다.
그리고 그 함수는 *경로 처리 함수*가 작동하는 것과 같은 방식으로 매개변수를 받습니다.
/// tip | 팁
@@ -91,7 +91,7 @@ FastAPI는 0.95.0 버전부터 `Annotated`에 대한 지원을 (그리고 이를
* 올바른 매개변수를 가진 의존성("디펜더블") 함수를 호출합니다.
* 함수에서 결과를 받아옵니다.
* *경로 작동 함수*에 있는 매개변수에 그 결과를 할당합니다
* *경로 처리 함수*에 있는 매개변수에 그 결과를 할당합니다
```mermaid
graph TB
@@ -104,7 +104,7 @@ common_parameters --> read_items
common_parameters --> read_users
```
이렇게 하면 공용 코드를 한번만 적어도 되며, **FastAPI**는 *경로 작동*을 위해 이에 대한 호출을 처리합니다.
이렇게 하면 공용 코드를 한번만 적어도 되며, **FastAPI**는 *경로 처리*을 위해 이에 대한 호출을 처리합니다.
/// check | 확인
@@ -114,7 +114,7 @@ common_parameters --> read_users
///
## `Annotated`인 의존성 공유하기
## `Annotated`인 의존성 공유하기 { #share-annotated-dependencies }
위의 예제에서 몇몇 작은 **코드 중복**이 있다는 것을 보았을 겁니다.
@@ -138,25 +138,25 @@ commons: Annotated[dict, Depends(common_parameters)]
이 의존성은 계속해서 예상한대로 작동할 것이며, **제일 좋은 부분**은 **타입 정보가 보존된다는 것입니다**. 즉 여러분의 편집기가 **자동 완성**, **인라인 에러** 등을 계속해서 제공할 수 있다는 것입니다. `mypy`같은 다른 도구도 마찬가지입니다.
이는 특히 **많은 *경로 작동***에서 **같은 의존성**을 계속해서 사용하는 **거대 코드 기반**안에서 사용하면 유용할 것입니다.
이는 특히 **많은 *경로 처리***에서 **같은 의존성**을 계속해서 사용하는 **거대 코드 기반**안에서 사용하면 유용할 것입니다.
## `async`하게, 혹은 `async`하지 않게
## `async`하게, 혹은 `async`하지 않게 { #to-async-or-not-to-async }
의존성이 (*경로 작동 함수*에서 처럼 똑같이) **FastAPI**에 의해 호출될 수 있으며, 함수를 정의할 때 동일한 규칙이 적용됩니다.
의존성이 (*경로 처리 함수*에서 처럼 똑같이) **FastAPI**에 의해 호출될 수 있으며, 함수를 정의할 때 동일한 규칙이 적용됩니다.
`async def`을 사용하거나 혹은 일반적인 `def`를 사용할 수 있습니다.
그리고 일반적인 `def` *경로 작동 함수* 안에 `async def`로 의존성을 선언할 수 있으며, `async def` *경로 작동 함수* 안에 `def`로 의존성을 선언하는 등의 방법이 있습니다.
그리고 일반적인 `def` *경로 처리 함수* 안에 `async def`로 의존성을 선언할 수 있으며, `async def` *경로 처리 함수* 안에 `def`로 의존성을 선언하는 등의 방법이 있습니다.
아무 문제 없습니다. **FastAPI**는 무엇을 할지 알고 있습니다.
/// note | 참고
잘 모르시겠다면, [Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank} 문서에서 `async``await`에 대해 확인할 수 있습니다.
잘 모르시겠다면, [Async: *"In a hurry?"*](../../async.md#in-a-hurry){.internal-link target=_blank} 문서에서 `async``await`에 대해 확인할 수 있습니다.
///
## OpenAPI와 통합
## OpenAPI와 통합 { #integrated-with-openapi }
모든 요청 선언, 검증과 의존성(및 하위 의존성)에 대한 요구 사항은 동일한 OpenAPI 스키마에 통합됩니다.
@@ -164,15 +164,15 @@ commons: Annotated[dict, Depends(common_parameters)]
<img src="/img/tutorial/dependencies/image01.png">
## 간단한 사용법
## 간단한 사용법 { #simple-usage }
이를 보면, *경로 작동 함수*는 *경로*와 *작동*이 매칭되면 언제든지 사용되도록 정의되었으며, **FastAPI**는 올바른 매개변수를 가진 함수를 호출하고 해당 요청에서 데이터를 추출합니다.
이를 보면, *경로 처리 함수*는 *경로*와 *작동*이 매칭되면 언제든지 사용되도록 정의되었으며, **FastAPI**는 올바른 매개변수를 가진 함수를 호출하고 해당 요청에서 데이터를 추출합니다.
사실, 모든 (혹은 대부분의) 웹 프레임워크는 이와 같은 방식으로 작동합니다.
여러분은 이러한 함수들을 절대 직접 호출하지 않습니다. 프레임워크(이 경우 **FastAPI**)에 의해 호출됩니다.
의존성 주입 시스템과 함께라면 **FastAPI**에게 여러분의 *경로 작동 함수*가 실행되기 전에 실행되어야 하는 무언가에 여러분의 *경로 작동 함수* 또한 "의존"하고 있음을 알릴 수 있으며, **FastAPI**는 이를 실행하고 결과를 "주입"할 것입니다.
의존성 주입 시스템과 함께라면 **FastAPI**에게 여러분의 *경로 처리 함수*가 실행되기 전에 실행되어야 하는 무언가에 여러분의 *경로 처리 함수* 또한 "의존"하고 있음을 알릴 수 있으며, **FastAPI**는 이를 실행하고 결과를 "주입"할 것입니다.
"의존성 주입"이라는 동일한 아이디어에 대한 다른 일반적인 용어는 다음과 같습니다:
@@ -182,15 +182,15 @@ commons: Annotated[dict, Depends(common_parameters)]
* 인젝터블
* 컴포넌트
## **FastAPI** 플러그인
## **FastAPI** 플러그인 { #fastapi-plug-ins }
통합과 "플러그인"은 **의존성 주입** 시스템을 사용하여 구축할 수 있습니다. 하지만 실제로 **"플러그인"을 만들 필요는 없습니다**, 왜냐하면 의존성을 사용함으로써 여러분의 *경로 작동 함수*에 통합과 상호 작용을 무한대로 선언할 수 있기 때문입니다.
통합과 "플러그인"은 **의존성 주입** 시스템을 사용하여 구축할 수 있습니다. 하지만 실제로 **"플러그인"을 만들 필요는 없습니다**, 왜냐하면 의존성을 사용함으로써 여러분의 *경로 처리 함수*에 통합과 상호 작용을 무한대로 선언할 수 있기 때문입니다.
그리고 "말 그대로", 그저 필요로 하는 파이썬 패키지를 임포트하고 단 몇 줄의 코드로 여러분의 API 함수와 통합함으로써, 의존성을 아주 간단하고 직관적인 방법으로 만들 수 있습니다.
관계형 및 NoSQL 데이터베이스, 보안 등, 이에 대한 예시를 다음 장에서 볼 수 있습니다.
## **FastAPI** 호환성
## **FastAPI** 호환성 { #fastapi-compatibility }
의존성 주입 시스템의 단순함은 **FastAPI**를 다음과 같은 요소들과 호환할 수 있게 합니다:
@@ -203,7 +203,7 @@ commons: Annotated[dict, Depends(common_parameters)]
* 응답 데이터 주입 시스템
* 기타 등등.
## 간편하고 강력하다
## 간편하고 강력하다 { #simple-and-powerful }
계층적인 의존성 주입 시스템은 정의하고 사용하기 쉽지만, 여전히 매우 강력합니다.
@@ -211,7 +211,7 @@ commons: Annotated[dict, Depends(common_parameters)]
끝에는, 계층적인 나무로 된 의존성이 만들어지며, 그리고 **의존성 주입** 시스템은 (하위 의존성도 마찬가지로) 이러한 의존성들을 처리하고 각 단계마다 결과를 제공합니다(주입합니다).
예를 들면, 여러분이 4개의 API 엔드포인트(*경로 작동*)를 가지고 있다고 해봅시다:
예를 들면, 여러분이 4개의 API 엔드포인트(*경로 처리*)를 가지고 있다고 해봅시다:
* `/items/public/`
* `/items/private/`
@@ -243,8 +243,8 @@ admin_user --> activate_user
paying_user --> pro_items
```
## **OpenAPI**와의 통합
## **OpenAPI**와의 통합 { #integrated-with-openapi_1 }
이 모든 의존성은 각각의 요구사항을 선언하는 동시에, *경로 작동*에 매개변수, 검증 등을 추가합니다.
이 모든 의존성은 각각의 요구사항을 선언하는 동시에, *경로 처리*에 매개변수, 검증 등을 추가합니다.
**FastAPI**는 이 모든 것을 OpenAPI 스키마에 추가할 것이며, 이를 통해 대화형 문서 시스템에 나타날 것입니다.

View File

@@ -1,35 +1,35 @@
# JSON 호환 가능 인코더
# JSON 호환 가능 인코더 { #json-compatible-encoder }
데이터 유형(예: Pydantic 모델)을 JSON과 호환 형태로 반환해야 하는 경우가 있습니다. (예: `dict`, `list` 등)
데이터 유형(예: Pydantic 모델)을 JSON과 호환되는 형태(예: `dict`, `list` 등)로 변환해야 하는 경우가 있습니다.
예를 들면, 데이터베이스에 저장해야하는 경우입니다.
예를 들면, 데이터베이스에 저장해야 하는 경우입니다.
이를 위해, **FastAPI** 에서는 `jsonable_encoder()` 함수를 제공합니다.
이를 위해, **FastAPI**에서는 `jsonable_encoder()` 함수를 제공합니다.
## `jsonable_encoder` 사용
## `jsonable_encoder` 사용 { #using-the-jsonable-encoder }
JSON 호환 가능 데이터만 수신하는 `fake_db` 데이터베이스가 존재한다고 가정하겠습니다.
예를 들면, `datetime` 객체는 JSON과 호환되는 데이터가 아니므로 이 데이터는 받아들여지지 않습니다.
예를 들면, `datetime` 객체는 JSON과 호환되지 않으므로 이 데이터베이스는 이를 받지 않습니다.
따라서 `datetime` 객체는 <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">ISO format</a> 데이터를 포함하는 `str`로 변환되어야 합니다.
따라서 `datetime` 객체는 <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">ISO format</a> 데이터를 포함하는 `str`로 변환되어야 합니다.
같은 방식으로 이 데이터베이스는 Pydantic 모델(속성이 있는 객체)을 받지 않고, `dict` 만을 받습니다.
같은 방식으로 이 데이터베이스는 Pydantic 모델(속성이 있는 객체)을 받지 않고, `dict`만을 받습니다.
이를 위해 `jsonable_encoder` 를 사용할 수 있습니다.
이를 위해 `jsonable_encoder`를 사용할 수 있습니다.
Pydantic 모델 같은 객체를 받고 JSON 호환 가능한 버전으로 반환합니다:
Pydantic 모델 같은 객체를 받고 JSON 호환 가능한 버전 반환합니다:
{* ../../docs_src/encoder/tutorial001.py hl[5,22] *}
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
이 예시는 Pydantic 모델을 `dict`로, `datetime` 형식`str`로 변환합니다.
이 예시에서는 Pydantic 모델을 `dict`로, `datetime``str`로 변환합니다.
이렇게 호출한 결과는 파이썬 표준인 <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>로 인코딩 할 수 있습니다.
이렇게 호출한 결과는 파이썬 표준인 <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>로 인코딩할 수 있습니다.
길이가 긴 문자열 형태의 JSON 형식(문자열)의 데이터가 들어있는 상황에서는 `str` 반환하지 않습니다. JSON과 모두 호환되는 값과 하위 값이 있는 Python 표준 데이터 구조 (예: `dict`)를 반환합니다.
JSON 형식(문자열)의 데이터가 들어있는 `str` 반환하지 않습니다. JSON과 모두 호환되는 값과 하위 값이 있는 파이썬 표준 데이터 구조(예: `dict`)를 반환합니다.
/// note | 참고
실제로 `jsonable_encoder`**FastAPI** 에서 내부적으로 데이터를 변환하는 데 사용하지만, 다른 많은 곳에서도 이는 유용합니다.
`jsonable_encoder`실제로 **FastAPI**에서 내부적으로 데이터를 변환하는 데 사용하지만, 다른 많은 시나리오에서도 유용합니다.
///

View File

@@ -1,4 +1,4 @@
# 추가 데이터 자료형
# 추가 데이터 자료형 { #extra-data-types }
지금까지 일반적인 데이터 자료형을 사용했습니다. 예를 들면 다음과 같습니다:
@@ -17,7 +17,7 @@
* 데이터 검증.
* 자동 어노테이션과 문서화.
## 다른 데이터 자료형
## 다른 데이터 자료형 { #other-data-types }
아래의 추가적인 데이터 자료형을 사용할 수 있습니다:
@@ -36,7 +36,7 @@
* `datetime.timedelta`:
* 파이썬의 `datetime.timedelta`.
* 요청과 응답에서 전체 초(seconds)의 `float`로 표현됩니다.
* Pydantic은 "ISO 8601 시차 인코딩"으로 표현하는 것 또한 허용합니다. <a href="https://docs.pydantic.dev/latest/concepts/serialization/#json_encoders" class="external-link" target="_blank">더 많은 정보는 이 문서에서 확인하십시오.</a>.
* Pydantic은 "ISO 8601 time diff encoding"으로 표현하는 것 또한 허용합니다. <a href="https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers" class="external-link" target="_blank">더 많은 정보는 문서를 확인하세요</a>.
* `frozenset`:
* 요청과 응답에서 `set`와 동일하게 취급됩니다:
* 요청 시, 리스트를 읽어 중복을 제거하고 `set`로 변환합니다.
@@ -49,11 +49,11 @@
* `Decimal`:
* 표준 파이썬의 `Decimal`.
* 요청과 응답에서 `float`와 동일하게 다뤄집니다.
* 여기에서 모든 유효한 pydantic 데이터 자료형을 확인할 수 있습니다: <a href="https://docs.pydantic.dev/latest/usage/types/types/" class="external-link" target="_blank">Pydantic 데이터 자료형</a>.
* 여기에서 모든 유효한 Pydantic 데이터 자료형을 확인할 수 있습니다: <a href="https://docs.pydantic.dev/latest/usage/types/types/" class="external-link" target="_blank">Pydantic 데이터 자료형</a>.
## 예시
## 예시 { #example }
위의 몇몇 자료형을 매개변수로 사용하는 *경로 작동* 예시입니다.
위의 몇몇 자료형을 매개변수로 사용하는 *경로 처리* 예시입니다.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}

View File

@@ -1,43 +1,34 @@
# 추가 모델
# 추가 모델 { #extra-models }
지난 예제에 이어서, 연관된 모델을 여러개 갖는 것은 흔한 일입니다.
지난 예제에 이어서, 연관된 모델을 여러 개 갖는 것은 흔한 일입니다.
특히 사용자 모델의 경우에 그러한데, 왜냐하면:
* **입력 모델** 은 비밀번호를 가야 합니다.
* **출력 모델** 은 비밀번호를 가지면 안됩니다.
* **데이터베이스 모델** 은 해시처리된 비밀번호를 가질 것입니다.
* **입력 모델**은 비밀번호를 가질 수 있어야 합니다.
* **출력 모델**은 비밀번호를 가지면 안 됩니다.
* **데이터베이스 모델**은 아마도 해시 처리된 비밀번호를 가질 필요가 있을 것입니다.
/// danger | 위험
절대 사용자의 비밀번호를 평문으로 저장하지 마세요. 항상 이후에 검증 가능한 "안전한 해시(secure hash)"로 저장하세요.
만약 이게 무엇인지 모르겠다면, [security chapters](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}.에서 비밀번호 해시에 대해 배울 수 있습니다.
만약 이게 무엇인지 모르겠다면, [security chapters](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}에서 "password hash"가 무엇인지 배울 수 있습니다.
///
## 다중 모델
## 다중 모델 { #multiple-models }
아래는 비밀번호 필드와 해당 필드가 사용되는 위치를 포함하여, 각 모델들이 어떤 형태를 가질 수 있는지 전반적인 예시입니다:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### `**user_in.model_dump()` 에 대하여 { #about-user-in-model-dump }
/// info | 정보
Pydantic v1에서는 해당 메서드가 `.dict()`로 불렸으며, Pydantic v2에서는 `.model_dump()`로 이름이 변경되었습니다. `.dict()`는 여전히 지원되지만 더 이상 권장되지 않습니다.
여기에서 사용하는 예제는 Pydantic v1과의 호환성을 위해 `.dict()`를 사용하지만, Pydantic v2를 사용할 수 있다면 `.model_dump()`를 사용하는 것이 좋습니다.
///
### `**user_in.dict()` 에 대하여
#### Pydantic의 `.dict()`
#### Pydantic의 `.model_dump()` { #pydantics-model-dump }
`user_in`은 Pydantic 모델 클래스인 `UserIn`입니다.
Pydantic 모델은 모델 데이터를 포함한 `dict`를 반환하는 `.dict()` 메서드를 제공합니다.
Pydantic 모델은 모델 데이터를 포함한 `dict`를 반환하는 `.model_dump()` 메서드를 제공합니다.
따라서, 다음과 같이 Pydantic 객체 `user_in`을 생성할 수 있습니다:
@@ -48,7 +39,7 @@ user_in = UserIn(username="john", password="secret", email="john.doe@example.com
그 다음, 다음과 같이 호출합니다:
```Python
user_dict = user_in.dict()
user_dict = user_in.model_dump()
```
이제 변수 `user_dict`에 데이터가 포함된 `dict`를 가지게 됩니다(이는 Pydantic 모델 객체가 아닌 `dict`입니다).
@@ -70,7 +61,7 @@ Python의 `dict`가 다음과 같이 출력됩니다:
}
```
#### `dict` 언패킹(Unpacking)
#### `dict` 언패킹 { #unpacking-a-dict }
`user_dict`와 같은 `dict`를 함수(또는 클래스)에 `**user_dict`로 전달하면, Python은 이를 "언팩(unpack)"합니다. 이 과정에서 `user_dict`의 키와 값을 각각 키-값 인자로 직접 전달합니다.
@@ -102,31 +93,31 @@ UserInDB(
)
```
#### 다른 모델 데이터로 새 Pydantic 모델 생성
#### 다른 모델 데이터로 새 Pydantic 모델 생성 { #a-pydantic-model-from-the-contents-of-another }
위의 예제에서 `user_in.dict()`로부터 `user_dict`를 생성한 것처럼, 아래 코드는:
위의 예제에서 `user_in.model_dump()`로부터 `user_dict`를 생성한 것처럼, 아래 코드는:
```Python
user_dict = user_in.dict()
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
다음과 동일합니다:
```Python
UserInDB(**user_in.dict())
UserInDB(**user_in.model_dump())
```
...왜냐하면 `user_in.dict()``dict`이며, 이를 `**`로 Python이 "언팩(unpack)"하도록 하여 `UserInDB`에 전달하기 때문입니다.
...왜냐하면 `user_in.model_dump()``dict`이며, 이를 `**`로 Python이 "언팩(unpack)"하도록 하여 `UserInDB`에 전달하기 때문입니다.
따라서, 다른 Pydantic 모델의 데이터를 사용하여 새로운 Pydantic 모델을 생성할 수 있습니다.
#### `dict` 언패킹(Unpacking)과 추가 키워드
#### `dict` 언패킹과 추가 키워드 { #unpacking-a-dict-and-extra-keywords }
그리고 다음과 같이 추가 키워드 인자 `hashed_password=hashed_password`를 추가하면:
```Python
UserInDB(**user_in.dict(), hashed_password=hashed_password)
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
다음과 같은 결과를 생성합니다:
@@ -147,7 +138,7 @@ UserInDB(
///
## 중복 줄이기
## 중복 줄이기 { #reduce-duplication }
코드 중복을 줄이는 것은 **FastAPI**의 핵심 아이디어 중 하나입니다.
@@ -161,11 +152,11 @@ UserInDB(
모든 데이터 변환, 검증, 문서화 등은 정상적으로 작동할 것입니다.
이렇게 하면 각 모델 간의 차이점만 선언할 수 있습니다(평문 `password`가 있는 경우, `hashed_password`만 있는 경우, 혹은 비밀번호가 없는 경우):
이렇게 하면 각 모델 간의 차이점만 선언할 수 있습니다(평문 `password`, `hashed_password`, 그리고 비밀번호가 없는 경우):
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` 또는 `anyOf`
## `Union` 또는 `anyOf` { #union-or-anyof }
두 가지 이상의 타입을 포함하는 `Union`으로 응답을 선언할 수 있습니다. 이는 응답이 그 중 하나의 타입일 수 있음을 의미합니다.
@@ -175,18 +166,17 @@ OpenAPI에서는 이를 `anyOf`로 정의합니다.
/// note | 참고
<a href="https://docs.pydantic.dev/latest/concepts/types/#unions" class="external-link" target="_blank">`Union`</a>을 정의할때는 더 구체적인 타입을 먼저 포함하고, 덜 구체적인 타입을 그 뒤에 나열해야합니다. 아래 예제에서는 `Union[PlaneItem, CarItem]` 를 보면, 더 구체적인 `PlaneItem``CarItem`보다 앞에 위치합니다.
<a href="https://docs.pydantic.dev/latest/concepts/types/#unions" class="external-link" target="_blank">`Union`</a>을 정의할 때는 더 구체적인 타입을 먼저 포함하고, 덜 구체적인 타입을 그 뒤에 나열해야 합니다. 아래 예제에서는 `Union[PlaneItem, CarItem]`에서 더 구체적인 `PlaneItem``CarItem`보다 앞에 위치합니다.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### Python 3.10에서 `Union`
### Python 3.10에서 `Union` { #union-in-python-3-10 }
위의 예제에서는 `response_model` 인자 값으로 `Union[PlaneItem, CarItem]`을 전달합니다.
이 경우, 이를 **타입 어노테이션(type annotation)** 이 아닌 **인자 값(argument value)** 으로 전달하고 있기 때문에 Python 3.10에서도 `Union`을 사용해야 합니다.
이 경우, 이를 **타입 어노테이션(type annotation)**이 아닌 **인자 값(argument value)**으로 전달하고 있기 때문에 Python 3.10에서도 `Union`을 사용해야 합니다.
만약 타입 어노테이션에 사용한다면, 다음과 같이 수직 막대(|)를 사용할 수 있습니다:
@@ -194,9 +184,9 @@ OpenAPI에서는 이를 `anyOf`로 정의합니다.
some_variable: PlaneItem | CarItem
```
하지만 이를 `response_model=PlaneItem | CarItem`과 같이 할당하면 에러가 발생합니다. 이는 Python이 이를 타입 어노테이션으로 해석하지 않고, `PlaneItem``CarItem` 사이의 **잘못된 연산(invalid operation)**을 시도하기 때문입니다
하지만 이를 `response_model=PlaneItem | CarItem`과 같이 할당하면 에러가 발생합니다. 이는 Python이 이를 타입 어노테이션으로 해석하지 않고, `PlaneItem``CarItem` 사이의 **잘못된 연산(invalid operation)**을 시도하기 때문입니다.
## 모델 리스트
## 모델 리스트 { #list-of-models }
마찬가지로, 객체 리스트 형태의 응답을 선언할 수도 있습니다.
@@ -204,8 +194,7 @@ some_variable: PlaneItem | CarItem
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
## 임의의 `dict` 응답
## 임의의 `dict` 응답 { #response-with-arbitrary-dict }
Pydantic 모델을 사용하지 않고, 키와 값의 타입만 선언하여 평범한 임의의 `dict`로 응답을 선언할 수도 있습니다.
@@ -215,9 +204,8 @@ Pydantic 모델을 사용하지 않고, 키와 값의 타입만 선언하여 평
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
## 요약
## 요약 { #recap }
여러 Pydantic 모델을 사용하고, 각 경우에 맞게 자유롭게 상속하세요.
엔터티가 서로 다른 "상태"를 가져야 하는 경우, 엔터티당 단일 데이터 모델을 사용할 필요는 없습니다. 예를 들어, 사용자 "엔터티"가 `password`, `password_hash`, 또는 비밀번호가 없는 상태를 포함할 수 있는 경우처럼 말입니다.
엔터티가 서로 다른 "상태"를 가져야 하는 경우, 엔터티당 단일 데이터 모델을 사용할 필요는 없습니다. 예를 들어, 사용자 "엔터티"가 `password`, `password_hash`, 그리고 비밀번호가 없는 상태를 포함할 수 있는 경우처럼 말입니다.

View File

@@ -1,8 +1,8 @@
# 첫걸음
# 첫걸음 { #first-steps }
가장 단순한 FastAPI 파일은 다음과 같이 보일 것입니다:
{* ../../docs_src/first_steps/tutorial001.py *}
{* ../../docs_src/first_steps/tutorial001_py39.py *}
위 코드를 `main.py`에 복사합니다.
@@ -11,36 +11,51 @@
<div class="termy">
```console
$ uvicorn main:app --reload
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> 개발 서버 시작 중 🚀
<font color="#3465A4">__init__.py</font> 파일이 있는 디렉터리에서
패키지 파일 구조를 검색하는 중
<font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>에서 임포트하는 중
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> 다음 코드로 모듈에서 FastAPI 앱 오브젝트를 임포트하는 중:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> 임포트 문자열 사용: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> 서버가 <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>에서 시작됨
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> 문서는 <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>에 있음
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> 개발 모드로 실행 중이며, 프로덕션에서는 다음을 사용하세요:
<b>fastapi run</b>
로그:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> 다음 디렉터리의 변경 사항을 감시합니다:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn이 <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>에서 실행 중 <b>(</b>Press CTRL+C
to quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> WatchFiles를 사용하여 리로더 프로세스 <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b>를 시작했습니다
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> 서버 프로세스 <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>를 시작했습니다
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> 애플리케이션 시작을 기다리는 중입니다.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> 애플리케이션 시작이 완료되었습니다.
```
</div>
/// note | 참고
`uvicorn main:app` 명령은 다음을 의미합니다:
* `main`: 파일 `main.py` (파이썬 "모듈").
* `app`: `main.py` 내부의 `app = FastAPI()` 줄에서 생성한 오브젝트.
* `--reload`: 코드 변경 시 자동으로 서버 재시작. 개발 시에만 사용.
///
출력되는 줄들 중에는 아래와 같은 내용이 있습니다:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
해당 줄은 로컬에서 앱이 서비스되는 URL을 보여줍니다.
해당 줄은 로컬 머신에서 앱이 서비스되는 URL을 보여줍니다.
### 확인하기
### 확인하기 { #check-it }
브라우저로 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>를 여세요.
@@ -50,7 +65,7 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
{"message": "Hello World"}
```
### 대화형 API 문서
### 대화형 API 문서 { #interactive-api-docs }
이제 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>로 가봅니다.
@@ -58,7 +73,7 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### 대안 API 문서
### 대안 API 문서 { #alternative-api-docs }
그리고 이제, <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>로 가봅니다.
@@ -66,41 +81,41 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
### OpenAPI
### OpenAPI { #openapi }
**FastAPI**는 API를 정의하기 위한 **OpenAPI** 표준을 사용하여 여러분의 모든 API를 이용해 "스키마"를 생성합니다.
#### "스키마"
#### "스키마" { #schema }
"스키마"는 무언가의 정의 또는 설명입니다. 이를 구현하는 코드가 아니라 추상적인 설명일 뿐입니다.
#### API "스키마"
#### API "스키마" { #api-schema }
<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a>는 API의 스키마를 어떻게 정의하는지 지시하는 규격입니다.
이 스키마 정의는 API 경로, 가능한 매개변수 등을 포함합니다.
#### 데이터 "스키마"
#### 데이터 "스키마" { #data-schema }
"스키마"라는 용어는 JSON처럼 어떤 데이터의 형태를 나타낼 수도 있습니다.
이러한 경우 JSON 속성, 가지고 있는 데이터 타입 등을 뜻합니다.
#### OpenAPI와 JSON 스키마
#### OpenAPI와 JSON 스키마 { #openapi-and-json-schema }
OpenAPI는 당신의 API에 대한 API 스키마를 정의합니다. 또한 이 스키마는 JSON 데이터 스키마의 표준인 **JSON 스키마**를 사용하여 당신의 API가 보내고 받는 데이터의 정의(또는 "스키마")를 포함합니다.
#### `openapi.json` 확인
#### `openapi.json` 확인 { #check-the-openapi-json }
FastAPI는 자동으로 API의 설명과 함께 JSON (스키마)를 생성합니다.
가공되지 않은 OpenAPI 스키마가 어떻게 생겼는지 궁금하다면, FastAPI는 자동으로 여러분의 모든 API에 대한 설명과 함께 JSON (스키마)를 생성합니다.
가공되지 않은 OpenAPI 스키마가 어떻게 생겼는지 궁금하다면, 여기에서 직접 볼 수 있습니다: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
여기에서 직접 볼 수 있습니다: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
다음과 같이 시작하는 JSON을 확인할 수 있습니다:
```JSON
{
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
@@ -119,7 +134,7 @@ FastAPI는 자동으로 API의 설명과 함께 JSON (스키마)를 생성합니
...
```
#### OpenAPI의 용도
#### OpenAPI의 용도 { #what-is-openapi-for }
OpenAPI 스키마는 포함된 두 개의 대화형 문서 시스템을 제공합니다.
@@ -127,11 +142,47 @@ OpenAPI 스키마는 포함된 두 개의 대화형 문서 시스템을 제공
API와 통신하는 클라이언트(프론트엔드, 모바일, IoT 애플리케이션 등)를 위해 코드를 자동으로 생성하는 데도 사용할 수 있습니다.
## 단계별 요약
### 앱 배포하기(선택 사항) { #deploy-your-app-optional }
### 1 단계: `FastAPI` 임포트
선택적으로 FastAPI 앱을 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>에 배포할 수 있습니다. 아직 대기자 명단에 등록하지 않았다면, 등록하러 가세요. 🚀
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
이미 **FastAPI Cloud** 계정이 있다면(대기자 명단에서 초대해 드렸습니다 😉), 한 번의 명령으로 애플리케이션을 배포할 수 있습니다.
배포하기 전에, 로그인되어 있는지 확인하세요:
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
그 다음 앱을 배포합니다:
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
이게 전부입니다! 이제 해당 URL에서 앱에 접근할 수 있습니다. ✨
## 단계별 요약 { #recap-step-by-step }
### 1 단계: `FastAPI` 임포트 { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
`FastAPI`는 당신의 API를 위한 모든 기능을 제공하는 파이썬 클래스입니다.
@@ -143,45 +194,17 @@ API와 통신하는 클라이언트(프론트엔드, 모바일, IoT 애플리케
///
### 2 단계: `FastAPI` "인스턴스" 생성
### 2 단계: `FastAPI` "인스턴스" 생성 { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
여기에서 `app` 변수는 `FastAPI` 클래스의 "인스턴스"가 됩니다.
이것은 당신의 모든 API를 생성하기 위한 상호작용의 주요 지점이 될 것입니다.
이 `app`은 다음 명령에서 `uvicorn`이 참조하고 있는 것과 동일합니다:
### 3 단계: *경로 처리* 생성 { #step-3-create-a-path-operation }
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
아래처럼 앱을 만든다면:
{* ../../docs_src/first_steps/tutorial002.py hl[3] *}
이를 `main.py` 파일에 넣고, `uvicorn`을 아래처럼 호출해야 합니다:
<div class="termy">
```console
$ uvicorn main:my_awesome_api --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### 3 단계: *경로 작동* 생성
#### 경로
#### 경로 { #path }
여기서 "경로"는 첫 번째 `/`부터 시작하는 URL의 뒷부분을 의미합니다.
@@ -205,7 +228,7 @@ https://example.com/items/foo
API를 설계할 때 "경로"는 "관심사"와 "리소스"를 분리하기 위한 주요한 방법입니다.
#### 작동
#### 작동 { #operation }
"작동(Operation)"은 HTTP "메소드" 중 하나를 나타냅니다.
@@ -240,14 +263,14 @@ API를 설계할 때 일반적으로 특정 행동을 수행하기 위해 특정
우리 역시 이제부터 메소드를 "**작동**"이라고 부를 것입니다.
#### *경로 작동 데코레이터* 정의
#### *경로 처리 데코레이터* 정의 { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
`@app.get("/")`은 **FastAPI**에게 바로 아래에 있는 함수가 다음으로 이동하는 요청을 처리한다는 것을 알려줍니다.
`@app.get("/")`은 **FastAPI**에게 바로 아래에 있는 함수가 다음으로 이동하는 요청을 처리한다는 것을 알려줍니다:
* 경로 `/`
* <abbr title="HTTP GET 메소드"><code>get</code> 작동</abbr> 사용
* <abbr title="an HTTP GET method"><code>get</code> operation</abbr> 사용
/// info | `@decorator` 정보
@@ -259,7 +282,7 @@ API를 설계할 때 일반적으로 특정 행동을 수행하기 위해 특정
우리의 경우, 이 데코레이터는 **FastAPI**에게 아래 함수가 **경로** `/`의 `get` **작동**에 해당한다고 알려줍니다.
이것이 "**경로 작동 데코레이터**"입니다.
이것이 "**경로 처리 데코레이터**"입니다.
///
@@ -288,15 +311,15 @@ API를 설계할 때 일반적으로 특정 행동을 수행하기 위해 특정
///
### 4 단계: **경로 작동 함수** 정의
### 4 단계: **경로 처리 함수** 정의 { #step-4-define-the-path-operation-function }
다음은 우리의 "**경로 작동 함수**"입니다:
다음은 우리의 "**경로 처리 함수**"입니다:
* **경로**: 는 `/`입니다.
* **작동**: 은 `get`입니다.
* **함수**: 는 "데코레이터" 아래에 있는 함수입니다 (`@app.get("/")` 아래).
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
이것은 파이썬 함수입니다.
@@ -308,17 +331,17 @@ URL "`/`"에 대한 `GET` 작동을 사용하는 요청을 받을 때마다 **Fa
`async def`을 이용하는 대신 일반 함수로 정의할 수 있습니다:
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
/// note | 참고
차이점을 모르겠다면 [Async: *"바쁘신 경우"*](../async.md#_1){.internal-link target=_blank} 확인하세요.
차이점을 모르겠다면 [Async: *"바쁘신 경우"*](../async.md#in-a-hurry){.internal-link target=_blank} 확인하세요.
///
### 5 단계: 콘텐츠 반환
### 5 단계: 콘텐츠 반환 { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
`dict`, `list`, 단일값을 가진 `str`, `int` 등을 반환할 수 있습니다.
@@ -326,10 +349,31 @@ Pydantic 모델을 반환할 수도 있습니다(나중에 더 자세히 살펴
JSON으로 자동 변환되는 객체들과 모델들(ORM 등을 포함해서)이 많이 있습니다. 가장 마음에 드는 것을 사용하십시오, 이미 지원되고 있을 것입니다.
## 요약
### 6 단계: 배포하기 { #step-6-deploy-it }
한 번의 명령으로 **<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>**에 앱을 배포합니다: `fastapi deploy`. 🎉
#### FastAPI Cloud 소개 { #about-fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>**는 **FastAPI** 뒤에 있는 동일한 작성자와 팀이 만들었습니다.
최소한의 노력으로 API를 **빌드**, **배포**, **접근**하는 과정을 간소화합니다.
FastAPI로 앱을 빌드할 때의 동일한 **개발자 경험**을 클라우드에 **배포**할 때도 제공합니다. 🎉
FastAPI Cloud는 *FastAPI와 친구들* 오픈 소스 프로젝트의 주요 스폰서이자 자금 제공자입니다. ✨
#### 다른 클라우드 제공업체에 배포하기 { #deploy-to-other-cloud-providers }
FastAPI는 오픈 소스이며 표준을 기반으로 합니다. 선택한 어떤 클라우드 제공업체에도 FastAPI 앱을 배포할 수 있습니다.
클라우드 제공업체의 가이드를 따라 FastAPI 앱을 배포하세요. 🤓
## 요약 { #recap }
* `FastAPI` 임포트.
* `app` 인스턴스 생성.
* (`@app.get("/")`처럼) **경로 작동 데코레이터** 작성.
* (위에 있는 `def root(): ...`처럼) **경로 작동 함수** 작성.
* (`uvicorn main:app --reload`처럼) 개발 서버 실행.
* (`@app.get("/")`처럼) **경로 처리 데코레이터** 작성.
* (위에 있는 `def root(): ...`처럼) **경로 처리 함수** 작성.
* `fastapi dev` 명령으로 개발 서버 실행.
* 선택적으로 `fastapi deploy`로 앱 배포.

View File

@@ -1,4 +1,4 @@
# 헤더 매개변수 모델
# 헤더 매개변수 모델 { #header-parameter-models }
관련 있는 **헤더 매개변수** 그룹이 있는 경우, **Pydantic 모델**을 생성하여 선언할 수 있습니다.
@@ -10,7 +10,7 @@
///
## Pydantic 모델을 사용한 헤더 매개변수
## Pydantic 모델을 사용한 헤더 매개변수 { #header-parameters-with-a-pydantic-model }
**Pydantic 모델**에 필요한 **헤더 매개변수**를 선언한 다음, 해당 매개변수를 `Header`로 선언합니다:
@@ -18,7 +18,7 @@
**FastAPI**는 요청에서 받은 **헤더**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기
## 문서 확인하기 { #check-the-docs }
문서 UI `/docs`에서 필요한 헤더를 볼 수 있습니다:
@@ -26,7 +26,7 @@
<img src="/img/tutorial/header-param-models/image01.png">
</div>
## 추가 헤더 금지하기
## 추가 헤더 금지하기 { #forbid-extra-headers }
일부 특별한 사용 사례(흔하지는 않겠지만)에서는 수신하려는 헤더를 **제한**할 수 있습니다.
@@ -51,6 +51,22 @@ Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forb
}
```
## 요약
## 밑줄 변환 비활성화하기 { #disable-convert-underscores }
일반적인 헤더 매개변수와 마찬가지로, 매개변수 이름에 밑줄 문자가 있으면 **자동으로 하이픈으로 변환**됩니다.
예를 들어, 코드에 `save_data` 헤더 매개변수가 있으면, 기대되는 HTTP 헤더는 `save-data`이고, 문서에서도 그렇게 표시됩니다.
어떤 이유로든 이 자동 변환을 비활성화해야 한다면, 헤더 매개변수용 Pydantic 모델에서도 비활성화할 수 있습니다.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | 경고
`convert_underscores``False`로 설정하기 전에, 일부 HTTP 프록시와 서버에서는 밑줄이 포함된 헤더 사용을 허용하지 않는다는 점을 염두에 두세요.
///
## 요약 { #summary }
**Pydantic 모델**을 사용하여 **FastAPI**에서 **헤더**를 선언할 수 있습니다. 😎

View File

@@ -1,20 +1,20 @@
# 헤더 매개변수
# 헤더 매개변수 { #header-parameters }
헤더 매개변수를 `Query`, `Path` 그리고 `Cookie` 매개변수들과 같은 방식으로 정의할 수 있습니다.
## `Header` 임포트
## `Header` 임포트 { #import-header }
먼저 `Header`를 임포트합니다:
{* ../../docs_src/header_params/tutorial001.py hl[3] *}
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## `Header` 매개변수 선언
## `Header` 매개변수 선언 { #declare-header-parameters }
`Path`, `Query` 그리고 `Cookie`를 사용한 동일한 구조를 이용하여 헤더 매개변수를 선언합니다.
첫 번째 값은 기본값이며, 추가 검증이나 어노테이션 매개변수 모두 전달할 수 있습니다:
{* ../../docs_src/header_params/tutorial001.py hl[9] *}
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | 기술 세부사항
@@ -30,7 +30,7 @@
///
## 자동 변환
## 자동 변환 { #automatic-conversion }
`Header``Path`, `Query` 그리고 `Cookie`가 제공하는 것 외에 기능이 조금 더 있습니다.
@@ -46,15 +46,15 @@
만약 언더스코어를 하이픈으로 자동 변환을 비활성화해야 할 어떤 이유가 있다면, `Header``convert_underscores` 매개변수를 `False`로 설정하십시오:
{* ../../docs_src/header_params/tutorial002.py hl[10] *}
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | 경고
`convert_underscore``False`로 설정하기 전에, 어떤 HTTP 프록시들과 서버들은 언더스코어가 포함된 헤더 사용을 허락하지 않는다는 것을 명심하십시오.
`convert_underscores``False`로 설정하기 전에, 어떤 HTTP 프록시들과 서버들은 언더스코어가 포함된 헤더 사용을 허락하지 않는다는 것을 명심하십시오.
///
## 중복 헤더
## 중복 헤더 { #duplicate-headers }
중복 헤더들을 수신할 수 있습니다. 즉, 다중값을 갖는 동일한 헤더를 뜻합니다.
@@ -64,9 +64,9 @@
예를 들어, 두 번 이상 나타날 수 있는 `X-Token`헤더를 선언하려면, 다음과 같이 작성합니다:
{* ../../docs_src/header_params/tutorial003.py hl[9] *}
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
다음과 같은 두 개의 HTTP 헤더를 전송하여 해당 *경로* 와 통신할 경우:
다음과 같은 두 개의 HTTP 헤더를 전송하여 해당 *경로 처리* 와 통신할 경우:
```
X-Token: foo
@@ -84,7 +84,7 @@ X-Token: bar
}
```
## 요약
## 요약 { #recap }
`Header``Query`, `Path`, `Cookie`와 동일한 패턴을 사용하여 선언합니다.

View File

@@ -1,84 +1,95 @@
# 자습서 - 사용자 안내서
# 자습서 - 사용자 안내서 { #tutorial-user-guide }
이 자습서는 단계별로 **FastAPI**의 대부분의 기능에 대해 설명합니다.
이 자습서는 **FastAPI**의 대부분의 기능을 단계별로 사용하는 방법을 보여줍니다.
각 섹션은 이전 섹션에 기반하는 순차적인 구조성되지만, 주제로 구분되어 있기 때문에 필요에 따라 특정 섹션으로 바로 이동하여 필요한 내용을 바로 확인할 수 있습니다.
각 섹션은 이전 섹션을 바탕으로 점진적으성되지만, 주제를 분리한 구조로 되어 있어 특정 API 요구사항을 해결하기 위해 원하는 섹션으로 바로 이동할 수 있습니다.
또한 향후에도 자료로 쓰일 수 있도록 작성되었습니다.
또한 나중에 자료로도 사용할 수 있도록 만들어졌으므로, 필요할 때 다시 돌아와 정확히 필요한 내용을 확인할 수 있습니다.
그러므로 필요할 때에 다시 돌아와서 원하는 것을 정확히 찾을 수 있습니다.
## 코드 실행하기 { #run-the-code }
## 코드 실행하기
모든 코드 블록은 복사해서 바로 사용할 수 있습니다(실제로 테스트된 Python 파일입니다).
모든 코드 블록은 복사하여 바로 사용할 수 있습니다(실제로 테스트된 파이썬 파일입니다).
예제를 실행하려면 코드를 `main.py` 파일에 복사하고 다음을 사용하여 `uvicorn`을 시작합니다:
예제 중 어떤 것이든 실행하려면, 코드를 `main.py` 파일에 복사하고 다음으로 `fastapi dev`를 시작하세요:
<div class="termy">
```console
$ uvicorn main:app --reload
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
Searching for package file structure from directories
with <font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
the following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
코드를 작성하거나 복사, 편집할 때, 로컬 환경에서 실행하는 것을 **강력히 권장**합니다.
로컬 편집기에서 사용한다면, 모든 타입 검사와 자동완성 등 작성해야 하는 코드가 얼마나 적은지 보면서 FastAPI의 이점을 비로소 경험할 수 있습니다.
코드를 작성하거나 복사한 뒤 편집하고, 로컬에서 실행하는 것을 **강력히 권장**합니다.
에디터에서 사용해 보면, 작성해야 하는 코드가 얼마나 적은지, 모든 타입 검사와 자동완성 등 FastAPI의 이점을 제대로 확인할 수 있습니다.
---
## FastAPI 설치
## FastAPI 설치 { #install-fastapi }
번째 단계는 FastAPI를 설치하는 것입니다.
첫 단계는 FastAPI를 설치하는 것입니다.
자습시에는 모든 선택적인 의존성 및 기능을 함께 설치하는 것을 추천합니다:
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, **FastAPI를 설치**하세요:
<div class="termy">
```console
$ pip install "fastapi[all]"
$ pip install "fastapi[standard]"
---> 100%
```
</div>
...이는 코드를 실행하는 서버로 사용할 수 있는 `uvicorn` 또한 포함하고 있습니다.
/// note | 참고
부분적으로 설치할 수 있습니다.
`pip install "fastapi[standard]"`로 설치하면 `fastapi-cloud-cli`를 포함한 몇 가지 기본 선택적 standard 의존성이 함께 설치되며, 이를 사용해 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>에 배포할 수 있습니다.
애플리케이션을 운영 환경에 배포하려는 경우 다음과 같이 합니다:
이러한 선택적 의존성이 필요 없다면 `pip install fastapi`로 대신 설치할 수 있습니다.
```
pip install fastapi
```
추가로 서버 역할을 하는 `uvicorn`을 설치합니다:
```
pip install uvicorn
```
사용하려는 각 선택적인 의존성에 대해서도 동일합니다.
standard 의존성은 설치하되 `fastapi-cloud-cli` 없이 설치하려면 `pip install "fastapi[standard-no-fastapi-cloud-cli]"`로 설치할 수 있습니다.
///
## 고급 사용자 안내서
## 고급 사용자 안내서 { #advanced-user-guide }
**자습서 - 사용자 안내서** 다음에 읽을 수 있는 **고급 사용자 안내서**도 있습니다.
이 **자습서 - 사용자 안내서**를 읽은 뒤에 나중에 읽을 수 있는 **고급 사용자 안내서**도 있습니다.
**고급 사용자 안내서**는 현재 문서를 기반으로 하고, 동일한 개념을 사용하며, 추가적인 기능들에 대해 설명합니다.
**고급 사용자 안내서**는 문서를 바탕으로 동일한 개념을 사용하며, 몇 가지 추가 기능을 알려줍니다.
하지만 (지금 읽고 있는) **자습서 - 사용자 안내서**를 먼저 읽는 것을 권장합니다.
하지만 먼저 **자습서 - 사용자 안내서**(지금 읽고 있는 내용)를 읽어야 합니다.
**자습서 - 사용자 안내서**만으로 완전한 애플리케이션을 구축할 수 있도록 작성되었으며, 필요에 따라 **고급 사용자 안내서**의 추가적인 아이디어를 적용하여 다양한 방식으로 확장할 수 있습니다.
**자습서 - 사용자 안내서**만으로 완전한 애플리케이션을 만들 수 있도록 설계되었고, 필요에 따라 **고급 사용자 안내서**의 추가 아이디어를 활용해 다양한 방식으로 확장할 수 있습니다.

View File

@@ -1,26 +1,26 @@
# 메타데이터 및 문서화 URL
# 메타데이터 및 문서화 URL { #metadata-and-docs-urls }
**FastAPI** 응용 프로그램에서 다양한 메타데이터 구성을 사용자 맞춤 설정할 수 있습니다.
**FastAPI** 애플리케이션에서 다양한 메타데이터 구성을 사용자 맞춤 설정할 수 있습니다.
## API에 대한 메타데이터
## API에 대한 메타데이터 { #metadata-for-api }
OpenAPI 명세 및 자동화된 API 문서 UI에 사용되는 다음 필드를 설정할 수 있습니다:
| 매개변수 | 타입 | 설명 |
|----------|------|-------|
| `title` | `str` | API의 제목입니다. |
| `summary` | `str` | API에 대한 짧은 요약입니다. <small>OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능</small> |
| `summary` | `str` | API에 대한 짧은 요약입니다. <small>OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능.</small> |
| `description` | `str` | API에 대한 짧은 설명입니다. 마크다운을 사용할 수 있습니다. |
| `version` | `string` | API의 버전입니다. OpenAPI의 버전이 아닌, 여러분의 애플리케이션의 버전을 나타냅니다. 예: `2.5.0` |
| `version` | `string` | API의 버전입니다. OpenAPI의 버전이 아닌, 여러분의 애플리케이션의 버전을 나타냅니다. 예: `2.5.0`. |
| `terms_of_service` | `str` | API 이용 약관의 URL입니다. 제공하는 경우 URL 형식이어야 합니다. |
| `contact` | `dict` | 노출된 API에 대한 연락처 정보입니다. 여러 필드를 포함할 수 있습니다. <details><summary><code>contact</code> 필드</summary><table><thead><tr><th>매개변수</th><th>타입</th><th>설명</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>연락처 인물/조직의 식별명입니다.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>연락처 정보가 담긴 URL입니다. URL 형식이어야 합니다.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>연락처 인물/조직의 이메일 주소입니다. 이메일 주소 형식이어야 합니다.</td></tr></tbody></table></details> |
| `license_info` | `dict` | 노출된 API의 라이선스 정보입니다. 여러 필드를 포함할 수 있습니다. <details><summary><code>license_info</code> 필드</summary><table><thead><tr><th>매개변수</th><th>타입</th><th>설명</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>필수</strong> (<code>license_info</code>가 설정된 경우). API에 사용된 라이선스 이름입니다.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>API에 대한 <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> 라이선스 표현입니다. <code>identifier</code> 필드는 <code>url</code> 필드와 상호 배타적입니다. <small>OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>API에 사용된 라이선스의 URL입니다. URL 형식이어야 합니다.</td></tr></tbody></table></details> |
| `license_info` | `dict` | 노출된 API의 라이선스 정보입니다. 여러 필드를 포함할 수 있습니다. <details><summary><code>license_info</code> 필드</summary><table><thead><tr><th>매개변수</th><th>타입</th><th>설명</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>필수</strong> (<code>license_info</code>가 설정된 경우). API에 사용된 라이선스 이름입니다.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>API에 대한 <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> 라이선스 표현입니다. <code>identifier</code> 필드는 <code>url</code> 필드와 상호 배타적입니다. <small>OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>API에 사용된 라이선스의 URL입니다. URL 형식이어야 합니다.</td></tr></tbody></table></details> |
다음과 같이 설정할 수 있습니다:
{* ../../docs_src/metadata/tutorial001.py hl[3:16,19:32] *}
{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
/// tip
/// tip | 팁
`description` 필드에 마크다운을 사용할 수 있으며, 출력에서 렌더링됩니다.
@@ -30,79 +30,81 @@ OpenAPI 명세 및 자동화된 API 문서 UI에 사용되는 다음 필드를
<img src="/img/tutorial/metadata/image01.png">
## 라이선스 식별자
## 라이선스 식별자 { #license-identifier }
OpenAPI 3.1.0 및 FastAPI 0.99.0부터 `license_info``identifier` URL 대신 설정할 수 있습니다.
OpenAPI 3.1.0 및 FastAPI 0.99.0부터 `license_info``url` 대신 `identifier`를 설정할 수 있습니다.
예:
{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
## 태그에 대한 메타데이터
## 태그에 대한 메타데이터 { #metadata-for-tags }
`openapi_tags` 매개변수를 사용하여 경로 작동을 그룹화하는 데 사용되는 태그에 추가 메타데이터를 추가할 수 있습니다.
`openapi_tags` 매개변수를 사용하여 경로 처리을 그룹화하는 데 사용되는 여러 태그에 추가 메타데이터를 추가할 수 있습니다.
리스트는 각 태그에 대해 하나의 딕셔너리를 포함해야 합니다.
리스트는 각 태그에 대해 하나의 딕셔너리를 포함합니다.
각 딕셔너리에는 다음이 포함될 수 있습니다:
* `name` (**필수**): `tags` 매개변수에서 *경로 작동*과 `APIRouter` 사용 태그 이름과 동일한 `str`입니다.
* `description`: 태그에 대한 간단한 설명을 담은 `str`입니다. 마크다운을 사용할 수 있으며 문서 UI에 표시됩니다.
* `name` (**필수**): *경로 처리*`APIRouter``tags` 매개변수에서 사용하는 태그 이름과 동일한 `str`입니다.
* `description`: 태그에 대한 간단한 설명을 담은 `str`입니다. 마크다운을 포함할 수 있으며 문서 UI에 표시됩니다.
* `externalDocs`: 외부 문서를 설명하는 `dict`이며:
* `description`: 외부 문서에 대한 간단한 설명을 담은 `str`입니다.
* `url` (**필수**): 외부 문서의 URL을 담은 `str`입니다.
### 태그에 대한 메타데이터 생성
### 태그에 대한 메타데이터 생성 { #create-metadata-for-tags }
`users``items`에 대한 태그 예시와 함께 메타데이터를 생성하고 이를 `openapi_tags` 매개변수로 전달해 보겠습니다:
`users``items`에 대한 태그 예시로 시도해 보겠습니다.
{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
태그에 대한 메타데이터를 생성하고 이를 `openapi_tags` 매개변수로 전달하세요:
설명 안에 마크다운을 사용할 수 있습니다. 예를 들어 "login"은 굵게(**login**) 표시되고, "fancy"는 기울임꼴(_fancy_)로 표시됩니다.
{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
/// tip
설명 안에 마크다운을 사용할 수 있다는 점에 유의하세요. 예를 들어 "login"은 굵게(**login**) 표시되고, "fancy"는 기울임꼴(_fancy_)로 표시됩니다.
/// tip | 팁
사용 중인 모든 태그에 메타데이터를 추가할 필요는 없습니다.
///
### 태그 사용
### 태그 사용 { #use-your-tags }
`tags` 매개변수를 *경로 작동*`APIRouter`와 함께 사용하여 태그에 할당할 수 있습니다:
`tags` 매개변수를 *경로 처리* (`APIRouter`)와 함께 사용하여 이를 서로 다른 태그에 할당하세요:
{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
/// info
/// info | 정보
태그에 대한 자세한 내용은 [경로 작동 구성](path-operation-configuration.md#tags){.internal-link target=_blank}에서 읽어보세요.
태그에 대한 자세한 내용은 [경로 처리 구성](path-operation-configuration.md#tags){.internal-link target=_blank}에서 읽어보세요.
///
### 문서 확인
### 문서 확인 { #check-the-docs }
이제 문서를 확인하면 모든 추가 메타데이터가 표시됩니다:
<img src="/img/tutorial/metadata/image02.png">
### 태그 순서
### 태그 순서 { #order-of-tags }
각 태그 메타데이터 딕셔너리의 순서는 문서 UI에 표시되는 순서 정의합니다.
각 태그 메타데이터 딕셔너리의 순서는 문서 UI에 표시되는 순서 정의합니다.
예를 들어, 알파벳 순서상 `users``items` 뒤에 오지만, 우리는 `users` 메타데이터를 리스트의 첫 번째 딕셔너리로 추가했기 때문에 먼저 표시됩니다.
예를 들어, 알파벳 순서상 `users``items` 뒤에 오지만, 우리는 해당 메타데이터를 리스트의 첫 번째 딕셔너리로 추가했기 때문에 먼저 표시됩니다.
## OpenAPI URL
## OpenAPI URL { #openapi-url }
OpenAPI 구조는 기본적으로 `/openapi.json`에서 제공됩니다.
기본적으로 OpenAPI 스키마는 `/openapi.json`에서 제공됩니다.
`openapi_url` 매개변수를 통해 이를 설정할 수 있습니다.
하지만 `openapi_url` 매개변수 이를 설정할 수 있습니다.
예를 들어, 이를 `/api/v1/openapi.json`에 제공하도록 설정하려면:
예를 들어, 이를 `/api/v1/openapi.json` 제공하도록 설정하려면:
{* ../../docs_src/metadata/tutorial002.py hl[3] *}
{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
OpenAPI 구조를 완전히 비활성화하려면 `openapi_url=None`으로 설정할 수 있으며, 이를 사용하여 문서화 사용자 인터페이스도 비활성화됩니다.
OpenAPI 스키마를 완전히 비활성화하려면 `openapi_url=None`으로 설정할 수 있으며, 이를 사용하여 문서화 사용자 인터페이스도 비활성화됩니다.
## 문서화 URL
## 문서화 URL { #docs-urls }
포함된 두 가지 문서화 사용자 인터페이스를 설정할 수 있습니다:
@@ -115,4 +117,4 @@ OpenAPI 구조를 완전히 비활성화하려면 `openapi_url=None`으로 설
예를 들어, Swagger UI를 `/documentation`에서 제공하고 ReDoc을 비활성화하려면:
{* ../../docs_src/metadata/tutorial003.py hl[3] *}
{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}

View File

@@ -1,66 +1,95 @@
# 미들웨어
# 미들웨어 { #middleware }
미들웨어를 **FastAPI** 응용 프로그램에 추가할 수 있습니다.
"미들웨어"는 특정 *경로 작동*에 의해 처리되기 전, 모든 **요청**에 대해서 동작하는 함수입니다. 또한 모든 **응답**이 반환되기 전에도 동일하게 동작합니다.
"미들웨어"는 특정 *경로 처리*에 의해 처리되기 전, 모든 **요청**에 대해서 동작하는 함수입니다. 또한 모든 **응답**이 반환되기 전에도 동일하게 동작합니다.
* 미들웨어는 응용 프로그램으로 오는 **요청** 가져옵니다.
* **요청** 또는 다른 필요한 코드를 실행 시킬 수 있습니다.
* **요청**을 응용 프로그램의 *경로 작동*으로 전달하여 처리합니다.
* 애플리케이션 *경로 작업*에서 생성한 **응답**를 받습니다.
* **응답** 또는 다른 필요한 코드를 실행시키는 동작을 할 수 있습니다.
* **응답** 반환합니다.
* 미들웨어는 응용 프로그램으로 오는 **요청** 가져옵니다.
* 그런 다음 해당 **요청**에 대해 무언가를 하거나 필요한 코드를 실행 수 있습니다.
* 그런 다음 **요청**을 나머지 애플리케이션(어떤 *경로 처리*가)을 통해 처리되도록 전달합니다.
* 그런 다음 애플리케이션(어떤 *경로 처리*가)이 생성한 **응답**을 가져옵니다.
* 그런 다음 해당 **응답**에 대해 무언가를 하거나 필요한 코드를 실행할 수 있습니다.
* 그런 다음 **응답** 반환합니다.
/// note | 기술 세부사항
만약 `yield`를 사용 의존성을 가지고 있다면, 미들웨어가 실행되고 난 후에 exit이 실행됩니다.
`yield`를 사용하는 의존성 있다면, exit 코드는 미들웨어 *후에* 실행됩니다.
만약 (나중에 문서에서 다룰) 백그라운드 작업이 있다면, 모든 미들웨어가 실행되고 *후에* 실행됩니다.
백그라운드 작업(뒤에서 보게 될 [Background Tasks](background-tasks.md){.internal-link target=_blank} 섹션에서 다룹니다)이 있다면, 모든 미들웨어 *후에* 실행됩니다.
///
## 미들웨어 만들기
## 미들웨어 만들기 { #create-a-middleware }
미들웨어를 작성하기 위해 함수 상단에 `@app.middleware("http")` 데코레이터를 사용할 수 있습니다.
미들웨어를 만들기 위해 함수 상단에 데코레이터 `@app.middleware("http")`를 사용합니다.
미들웨어 함수는 다음 항목들을 받습니다:
미들웨어 함수는 다음을 받습니다:
* `request`.
* `request`를 매개변수로 받는 `call_next` 함수.
* 이 함수는 `request`를 해당하는 *경로 작업*으로 전달합니다.
* 그런 다음, *경로 작업*에 의해 생성 `response` 를 반환합니다.
* `response` 반환하기 전에 추가로 `response`를 수정할 수 있습니다.
* 이 함수는 `request`를 해당하는 *경로 처리*로 전달합니다.
* 그런 다음 해당 *경로 처리*가 생성 `response`를 반환합니다.
* 그런 다음 반환하기 전에 `response` 추가로 수정할 수 있습니다.
{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
/// tip | 팁
사용자 정의 헤더는 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">'X-' 접두사를 사용</a>하여 추가할 수 있습니다.
사용자 정의 독점 헤더는 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">`X-` 접두사를 사용</a>하여 추가할 수 있다는 점을 기억하세요.
그러나 만약 클라이언트의 브라우저에서 볼 수 있는 사용자 정의 헤더를 가지고 있다면, 그것들을 CORS 설정([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank})에 <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette CORS 문서</a>에 명시`expose_headers` 매개변수를 이용하여 헤더들을 추가하여야합니다.
하지만 브라우저에서 클라이언트가 볼 수 있게 하려는 사용자 정의 헤더 있다면, <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette CORS 문서</a>에 문서화`expose_headers` 매개변수를 사용해 CORS 설정([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank})에 추가해야 합니다.
///
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette.requests import request`를 사용할 수도 있습니다.
`from starlette.requests import Request`를 사용할 수도 있습니다.
**FastAPI**는 개발자에게 편의를 위해 이를 제공합니다. 그러나 Starlette에서 직접 파생되었습니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 이를 제공합니다. 하지만 이는 Starlette에서 직접 가져온 것입니다.
///
### `response`의 전과 후
### `response`의 전과 후 { #before-and-after-the-response }
*경로 작동*을 받기 전 `request`와 함께 작동할 수 있는 코드를 추가할 수 있습니다.
어떤 *경로 처리*가 받기 전에, `request`와 함께 실행될 코드를 추가할 수 있습니다.
그리고 `response` 또한 생성된 후 반환기 전에 코드를 추가 할 수 있습니다.
또한 `response` 생성된 후, 반환기 전에 코드를 추가할 수 있습니다.
예를 들어, 요청을 수행하고 응답을 생성하는데 까지 걸린 시간 값을 가지고 있는 `X-Process-Time` 같은 사용자 정의 헤더를 추가할 수 있습니다.
예를 들어, 요청을 처리하고 응답을 생성하는 데 걸린 시간을 초 단위로 담는 사용자 정의 헤더 `X-Process-Time` 추가할 수 있습니다:
{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
## 다른 미들웨어
/// tip | 팁
미들웨어에 대한 더 많은 정보는 [숙련된 사용자 안내서: 향상된 미들웨어](../advanced/middleware.md){.internal-link target=\_blank}에서 확인할 수 있습니다.
여기서는 이러한 사용 사례에서 더 정확할 수 있기 때문에 `time.time()` 대신 <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a>를 사용합니다. 🤓
다음 부분에서 미들웨어와 함께 <abbr title="교차-출처 리소스 공유">CORS</abbr>를 어떻게 다루는지에 대해 확인할 것입니다.
///
## 여러 미들웨어 실행 순서 { #multiple-middleware-execution-order }
`@app.middleware()` 데코레이터 또는 `app.add_middleware()` 메서드를 사용해 여러 미들웨어를 추가하면, 새로 추가된 각 미들웨어가 애플리케이션을 감싸 스택을 형성합니다. 마지막에 추가된 미들웨어가 *가장 바깥쪽*이고, 처음에 추가된 미들웨어가 *가장 안쪽*입니다.
요청 경로에서는 *가장 바깥쪽* 미들웨어가 먼저 실행됩니다.
응답 경로에서는 마지막에 실행됩니다.
예를 들어:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
이 경우 실행 순서는 다음과 같습니다:
* **요청**: MiddlewareB → MiddlewareA → route
* **응답**: route → MiddlewareA → MiddlewareB
이러한 스태킹 동작은 미들웨어가 예측 가능하고 제어 가능한 순서로 실행되도록 보장합니다.
## 다른 미들웨어 { #other-middlewares }
다른 미들웨어에 대한 더 많은 정보는 나중에 [숙련된 사용자 안내서: 향상된 미들웨어](../advanced/middleware.md){.internal-link target=_blank}에서 확인할 수 있습니다.
다음 섹션에서 미들웨어로 <abbr title="Cross-Origin Resource Sharing">CORS</abbr>를 처리하는 방법을 보게 될 것입니다.

View File

@@ -1,97 +1,107 @@
# 경로 작동 설정
# 경로 처리 설정 { #path-operation-configuration }
*경로 작동 데코레이터*를 설정하기 위해 전달할수 있는 몇 가지 매개변수가 있습니다.
*경로 처리 데코레이터*를 설정하기 위해 전달할 수 있는 몇 가지 매개변수가 있습니다.
/// warning | 경고
아래 매개변수들은 *경로 작동 함수*가 아닌 *경로 작동 데코레이터*에 직접 전달된다는 사실을 기억하십시오.
아래 매개변수들은 *경로 처리 함수*가 아닌 *경로 처리 데코레이터*에 직접 전달된다는 사실을 기억하세요.
///
## 응답 상태 코드
## 응답 상태 코드 { #response-status-code }
*경로 작동*의 응답에 사용될 (HTTP) `status_code`를 정의할수 있습니다.
*경로 처리*의 응답에 사용될 (HTTP) `status_code`를 정의할 수 있습니다.
`404`와 같은 `int`형 코드를 직접 전달할수 있습니다.
`404`와 같은 `int`형 코드를 직접 전달할 수 있습니다.
하지만 각 코드의 의미를 모른다면, `status`에 있는 단축 상수들을 사용할수 있습니다:
하지만 각 숫자 코드가 무엇을 의미하는지 기억하지 못한다면, `status`에 있는 단축 상수들을 사용할 수 있습니다:
{* ../../docs_src/path_operation_configuration/tutorial001.py hl[3,17] *}
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
상태 코드들은 응답에 사용되며, OpenAPI 스키마에 추가됩니다.
해당 상태 코드 응답에 사용되며, OpenAPI 스키마에 추가됩니다.
/// note | 기술 세부사항
/// note | 기술 세부사항
다음과 같이 임포트하셔도 좋습니다. `from starlette import status`.
**FastAPI**는 개발자 여러분의 편의를 위해 `starlette.status`와 동일한 `fastapi.status`를 제공합니다. 하지만 Starlette에서 직접 온 것입니다.
**FastAPI**는 개발자 여러분의 편의를 위해 `starlette.status`와 동일한 `fastapi.status`를 제공합니다. 하지만 이는 Starlette에서 직접 온 것입니다.
///
## 태그
## 태그 { #tags }
(보통 단일 `str`인) `str`로 구성된 `list`와 함께 매개변수 `tags`를 전달하여, `경로 작동`에 태그를 추가할 수 있습니다:
(보통 단일 `str`인) `str`로 구성된 `list`와 함께 매개변수 `tags`를 전달하여, *경로 처리*에 태그를 추가할 수 있습니다:
{* ../../docs_src/path_operation_configuration/tutorial002.py hl[17,22,27] *}
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
전달된 태그들은 OpenAPI의 스키마에 추가되며, 자동 문서 인터페이스에서 사용됩니다:
<img src="/img/tutorial/path-operation-configuration/image01.png">
## 요약과 기술
### Enum을 사용한 태그 { #tags-with-enums }
큰 애플리케이션이 있다면, **여러 태그**가 쌓이게 될 수 있고, 관련된 *경로 처리*에 항상 **같은 태그**를 사용하는지 확인하고 싶을 것입니다.
이런 경우에는 태그를 `Enum`에 저장하는 것이 합리적일 수 있습니다.
**FastAPI**는 일반 문자열과 동일한 방식으로 이를 지원합니다:
{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
## 요약과 설명 { #summary-and-description }
`summary``description`을 추가할 수 있습니다:
{* ../../docs_src/path_operation_configuration/tutorial003.py hl[20:21] *}
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
## 독스트링으로 만든 기술
## 독스트링으로 만든 설명 { #description-from-docstring }
설명은 보통 길어지고 여러 줄에 걸쳐있기 때문에, *경로 작동* 기술을 함수 <abbr title="함수안에 있는 첫번째 표현식으로, 문서로 사용될 여러 줄에 걸친 (변수에 할당되지 않은) 문자열"> 독스트링</abbr> 에 선언할 수 있습니다, 이를 **FastAPI**가 독스트링으로부터 읽습니다.
설명은 보통 길어지고 여러 줄에 걸쳐있기 때문에, *경로 처리* 설명을 함수 <abbr title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation 문서화에 사용되는 함수 내부 첫 표현식의 여러 줄 문자열(어떤 변수에 할당되지 않음)">docstring</abbr>에 선언할 수 있으며, **FastAPI**는 그곳에서 이를 읽습니다.
<a href="https://ko.wikipedia.org/wiki/%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4" class="external-link" target="_blank">마크다운</a> 문법으로 독스트링을 작성할 수 있습니다, 작성된 마크다운 형식의 독스트링은 (마크다운의 들여쓰기를 고려하여) 올바르게 화면에 출력됩니다.
독스트링에는 <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a>을 작성할 수 있으며, (독스트링의 들여쓰기를 고려하여) 올바르게 해석되고 표시됩니다.
{* ../../docs_src/path_operation_configuration/tutorial004.py hl[19:27] *}
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
이는 대화형 문서에서 사용됩니다:
<img src="/img/tutorial/path-operation-configuration/image02.png">
## 응답 기술
## 응답 설명 { #response-description }
`response_description` 매개변수로 응답에 관한 설명을 명시할 수 있습니다:
{* ../../docs_src/path_operation_configuration/tutorial005.py hl[21] *}
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *}
/// info | 정보
`response_description`은 구체적으로 응답을 지칭하며, `description`은 일반적인 *경로 작동*을 지칭합니다.
`response_description`은 구체적으로 응답을 지칭하며, `description`은 일반적인 *경로 처리*를 지칭합니다.
///
/// check | 확인
OpenAPI는 각 *경로 작동*이 응답에 관한 설명을 요구할 것을 명시합니다.
OpenAPI는 각 *경로 처리*가 응답에 관한 설명을 요구할 것을 명시합니다.
따라서, 응답에 관한 설명이 없을경우, **FastAPI**가 자동으로 "성공 응답" 중 하나를 생성합니다.
따라서, 응답에 관한 설명을 제공하지 않으면, **FastAPI**가 "Successful response" 중 하나를 자동으로 생성합니다.
///
<img src="/img/tutorial/path-operation-configuration/image03.png">
## 단일 *경로 작동* 지원중단
## *경로 처리* 지원중단하기 { #deprecate-a-path-operation }
단일 *경로 작동*을 없애지 않고 <abbr title="구식, 사용하지 않는것이 권장됨">지원중단</abbr>해야한다면, `deprecated` 매개변수를 전달하면 됩니다.
*경로 처리*를 제거하지 않고 <abbr title="obsolete, recommended not to use it 구식이며 사용하지 않는 것이 권장됨">deprecated</abbr>로 표시해야 한다면, `deprecated` 매개변수를 전달하면 됩니다:
{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
대화형 문서에 지원중단이라고 표시됩니다.
대화형 문서에 지원중단으로 명확하게 표시됩니다:
<img src="/img/tutorial/path-operation-configuration/image04.png">
지원중단된 경우와 지원중단 되지 않은 경우에 대한 *경로 작동*이 어떻게 보이는 지 확인하십시오.
지원중단된 *경로 처리*와 지원중단되지 않은 *경로 처리*가 어떻게 보이는지 확인해 보세요:
<img src="/img/tutorial/path-operation-configuration/image05.png">
## 정리
## 정리 { #recap }
*경로 작동 데코레이터*에 매개변수(들)를 전달함으로 *경로 작동*을 설정하고 메타데이터를 추가할수 있습니다.
*경로 처리 데코레이터*에 매개변수(들)를 전달하여 *경로 처리*를 설정하고 메타데이터를 쉽게 추가할 수 있습니다.

View File

@@ -1,116 +1,153 @@
# 경로 매개변수와 숫자 검증
# 경로 매개변수와 숫자 검증 { #path-parameters-and-numeric-validations }
`Query`를 사용하여 쿼리 매개변수에 더 많은 검증과 메타데이터를 선언하는 방법과 동일하게 `Path`를 사용하여 경로 매개변수에 검증과 메타데이터를 같은 타입으로 선언할 수 있습니다.
## 경로 임포트
## `Path` 임포트 { #import-path }
먼저 `fastapi`에서 `Path`를 임포트합니다:
먼저 `fastapi`에서 `Path`를 임포트하고, `Annotated`도 임포트합니다:
{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[3] *}
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
## 메타데이터 선언
/// info | 정보
`Query`에 동일한 매개변수를 선언할 수 있습니다.
FastAPI는 0.95.0 버전에서 `Annotated` 지원을 추가했고(그리고 이를 권장하기 시작했습니다).
예를 들어, `title` 메타데이터 값을 경로 매개변수 `item_id`에 선언하려면 다음과 같이 입력할 수 있습니다:
더 오래된 버전이 있다면 `Annotated`를 사용하려고 할 때 오류가 발생합니다.
{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[10] *}
/// note | 참고
경로 매개변수는 경로의 일부여야 하므로 언제나 필수적입니다.
즉, `...`로 선언해서 필수임을 나타내는게 좋습니다.
그럼에도 `None`으로 선언하거나 기본값을 지정할지라도 아무 영향을 끼치지 않으며 언제나 필수입니다.
`Annotated`를 사용하기 전에 최소 0.95.1까지 [FastAPI 버전 업그레이드](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank}를 꼭 하세요.
///
## 필요한 경우 매개변수 정렬하기
## 메타데이터 선언 { #declare-metadata }
`Query`에 동일한 매개변수를 선언할 수 있습니다.
예를 들어, 경로 매개변수 `item_id``title` 메타데이터 값을 선언하려면 다음과 같이 입력할 수 있습니다:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | 참고
경로 매개변수는 경로의 일부여야 하므로 언제나 필수입니다. `None`으로 선언하거나 기본값을 지정하더라도 아무 영향이 없으며, 항상 필수입니다.
///
## 필요한 대로 매개변수 정렬하기 { #order-the-parameters-as-you-need }
/// tip | 팁
`Annotated`를 사용한다면 이것은 아마 그렇게 중요하지 않거나 필요하지 않을 수 있습니다.
///
`str` 형인 쿼리 매개변수 `q`를 필수로 선언하고 싶다고 해봅시다.
해당 매개변수에 대해 아무런 선언을 할 필요가 없으므로 `Query`를 정말로 써야할 필요는 없습니다.
해당 매개변수에 대해 아무런 선언을 할 필요가 없으므로 `Query`를 정말로 써야 할 필요는 없습니다.
하지만 `item_id` 경로 매개변수는 여전히 `Path`를 사용해야 합니다.
하지만 `item_id` 경로 매개변수는 여전히 `Path`를 사용해야 합니다. 그리고 어떤 이유로 `Annotated`를 사용하고 싶지 않다고 해봅시다.
파이썬은 "기본값"이 는 값 앞에 "기본값"이 는 값을 입력하면 불평합니다.
파이썬은 "기본값"이 는 값 "기본값"이 는 값 앞에 두면 불평합니다.
그러나 매개변수들을 재정렬함으로써 기본값(쿼리 매개변수 `q`)이 없는 값을 처음 부분에 위치 할 수 있습니다.
하지만 순서를 재정렬해서 기본값이 없는 값(쿼리 매개변수 `q`)을 앞에 둘 수 있습니다.
**FastAPI**에서는 중요하지 않습니다. 이름, 타입 그리고 선언(`Query`, `Path` 등)로 매개변수를 감지하며 순서는 신경 쓰지 않습니다.
**FastAPI**에서는 중요하지 않습니다. 이름, 타입 그리고 기본값 선언(`Query`, `Path` 등)로 매개변수를 감지하며 순서는 신경 쓰지 않습니다.
따라서 함수를 다음과 같이 선언 할 수 있습니다:
따라서 함수를 다음과 같이 선언할 수 있습니다:
{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
## 필요한 경우 매개변수 정렬하기, 트릭
하지만 `Annotated`를 사용하면 이 문제가 없다는 점을 기억하세요. `Query()``Path()`에 함수 매개변수 기본값을 사용하지 않기 때문에, 순서는 중요하지 않습니다.
`Query`나 아무런 기본값으로도 `q` 경로 매개변수를 선언하고 싶지 않지만 `Path`를 사용하여 경로 매개변수를 `item_id` 다른 순서로 선언하고 싶다면, 파이썬은 이를 위한 작고 특별한 문법이 있습니다.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
`*`를 함수의 첫 번째 매개변수로 전달하세요.
## 필요한 대로 매개변수 정렬하기, 트릭 { #order-the-parameters-as-you-need-tricks }
파이썬은 `*`으로 아무런 행동도 하지 않지만, 따르는 매개변수들은 <abbr title="유래: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>로도 알려진 키워드 인자(키-값 쌍)여야 함을 인지합니다. 기본값을 가지고 있지 않더라도 그렇습니다.
/// tip | 팁
{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
`Annotated`를 사용한다면 이것은 아마 그렇게 중요하지 않거나 필요하지 않을 수 있습니다.
## 숫자 검증: 크거나 같음
///
`Query``Path`(나중에 볼 다른 것들도)를 사용하여 문자열 뿐만 아니라 숫자의 제약을 선언할 수 있습니다.
유용할 수 있는 **작은 트릭**이 하나 있지만, 자주 필요하진 않을 겁니다.
여기서 `ge=1`인 경우, `item_id``1`보다 "크거나(`g`reater) 같은(`e`qual)" 정수형 숫자여야 합니다.
만약 다음을 원한다면:
{* ../../docs_src/path_params_numeric_validations/tutorial004.py hl[8] *}
* `Query`나 어떤 기본값 없이 쿼리 매개변수 `q`를 선언하기
* `Path`를 사용해서 경로 매개변수 `item_id`를 선언하기
* 이들을 다른 순서로 두기
* `Annotated`를 사용하지 않기
## 숫자 검증: 크거나 같음 및 작거나 같음
...이를 위해 파이썬에는 작은 특별한 문법이 있습니다.
함수의 첫 번째 매개변수로 `*`를 전달하세요.
파이썬은 `*`으로 아무것도 하지 않지만, 뒤따르는 모든 매개변수는 키워드 인자(키-값 쌍)로 호출되어야 함을 알게 됩니다. 이는 <abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>로도 알려져 있습니다. 기본값이 없더라도 마찬가지입니다.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
### `Annotated`를 쓰면 더 좋습니다 { #better-with-annotated }
`Annotated`를 사용하면 함수 매개변수 기본값을 사용하지 않기 때문에 이 문제가 발생하지 않으며, 아마 `*`도 사용할 필요가 없다는 점을 기억하세요.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
## 숫자 검증: 크거나 같음 { #number-validations-greater-than-or-equal }
`Query``Path`(그리고 나중에 볼 다른 것들)를 사용하여 숫자 제약을 선언할 수 있습니다.
여기서 `ge=1`인 경우, `item_id``1`보다 "`g`reater than or `e`qual"(크거나 같은) 정수형 숫자여야 합니다.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
## 숫자 검증: 크거나 및 작거나 같음 { #number-validations-greater-than-and-less-than-or-equal }
동일하게 적용됩니다:
* `gt`: 크거나(`g`reater `t`han)
* `le`: 작거나 같은(`l`ess than or `e`qual)
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005.py hl[9] *}
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
## 숫자 검증: 부동소수, 크거나 및 작거나
## 숫자 검증: 부동소수, 크거나 및 작거나 { #number-validations-floats-greater-than-and-less-than }
숫자 검증은 `float` 값에도 동작합니다.
여기에서 <abbr title="greater than or equal"><code>ge</code></abbr>뿐만 아니라 <abbr title="greater than"><code>gt</code></abbr> 선언 할 수이 중요해집니다. 예를 들어 필요한 경우, 값이 `1`보다 작더라도 반드시 `0`보다 커야니다.
여기에서 <abbr title="greater than"><code>gt</code></abbr>를, <abbr title="greater than or equal"><code>ge</code></abbr>뿐만 아니라 선언할 수 있다이 중요해집니다. 예를 들어 값이 `1`보다 작더라도, 반드시 `0`보다 커야 한다고 요구할 수 있습니다.
즉, `0.5`는 유효한 값입니다. 그러나 `0.0` 또는 `0`은 그렇지 않습니다.
<abbr title="less than"><code>lt</code></abbr> 역시 마찬가지입니다.
{* ../../docs_src/path_params_numeric_validations/tutorial006.py hl[11] *}
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
## 요약
## 요약 { #recap }
`Query`, `Path`(아직 보지 못한 다른 것들도)를 사용하면 [쿼리 매개변수와 문자열 검증](query-params-str-validations.md){.internal-link target=_blank}에서와 마찬가지로 메타데이터와 문자열 검증을 선언할 수 있습니다.
그리고 숫자 검증 또한 선언할 수 있습니다:
* `gt`: 크거나(`g`reater `t`han)
* `ge`: 크거나 같은(`g`reater than or `e`qual)
* `lt`: 작거나(`l`ess `t`han)
* `le`: 작거나 같은(`l`ess than or `e`qual)
* `gt`: `g`reater `t`han
* `ge`: `g`reater than or `e`qual
* `lt`: `l`ess `t`han
* `le`: `l`ess than or `e`qual
/// info | 정보
`Query`, `Path`, 그리고 나중에 보게될 것들은 (여러분이 사용할 필요가 없는) 공통 `Param` 클래스의 서브 클래스입니다.
`Query`, `Path`, 그리고 나중에 보게 다른 클래스들은 공통 `Param` 클래스의 서브클래스입니다.
그리고 이들 모두는 여태까지 본 추가 검증과 메타데이터 동일한 모든 매개변수를 공유합니다.
이들 모두는 여러분이 본 추가 검증과 메타데이터에 대한 동일한 매개변수를 공유합니다.
///
/// note | 기술 세부사항
`fastapi`에서 `Query`, `Path` 등을 임포트 할 때, 이것들은 실제로 함수입니다.
`fastapi`에서 `Query`, `Path` 등을 임포트할 때, 이것들은 실제로 함수입니다.
호출되면 동일한 이름의 클래스의 인스턴스를 반환합니다.
즉, 함수인 `Query`를 임포트한 겁니다. 그리고 호출하면 `Query`라는 이름을 가진 클래스의 인스턴스를 반환합니다.
편집기에서 타입에 대한 오류를 표시하지 않도록 하기 위해 (클래스를 직접 사용하는 대신) 이러한 함수들이 있습니다.
이 함수들이 있는 이유는(클래스를 직접 사용하는 대신) 편집기에서 타입에 대한 오류를 표시하지 않도록 하기 위해서입니다.
이렇게 하면 오류를 무시하기 위한 사용자 설정을 추가하지 않고도 일반 편집기와 코딩 도구를 사용할 수 있습니다.

View File

@@ -1,8 +1,8 @@
# 경로 매개변수
# 경로 매개변수 { #path-parameters }
파이썬의 포맷 문자열 리터럴에서 사용되는 문법을 이용하여 경로 "매개변수" 또는 "변수"를 선언할 수 있습니다:
{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
경로 매개변수 `item_id`의 값은 함수의 `item_id` 인자로 전달됩니다.
@@ -12,11 +12,11 @@
{"item_id":"foo"}
```
## 타입이 있는 매개변수
## 타입이 있는 경로 매개변수 { #path-parameters-with-types }
파이썬 표준 타입 어노테이션을 사용하여 함수에 있는 경로 매개변수의 타입을 선언할 수 있습니다:
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
위의 예시에서, `item_id``int`로 선언되었습니다.
@@ -26,7 +26,7 @@
///
## 데이터 <abbr title="다음으로도 알려져 있습니다: 직렬화, 파싱, 마샬링">변환</abbr>
## 데이터 <abbr title="다음으로도 알려져 있습니다: 직렬화, 파싱, 마샬링">변환</abbr> { #data-conversion }
이 예제를 실행하고 <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>을 열면, 다음 응답을 볼 수 있습니다:
@@ -42,40 +42,41 @@
///
## 데이터 검증
## 데이터 검증 { #data-validation }
하지만 브라우저에서 <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>로 이동하면, HTTP 오류가 잘 뜨는 것을 확인할 수 있습니다:
하지만 브라우저에서 <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>로 이동하면, 다음과 같은 HTTP 오류를 볼 수 있습니다:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
경로 매개변수 `item_id` `int`가 아닌 `"foo"`기 때문입니다.
경로 매개변수 `item_id` `int`가 아닌 `"foo"`을 가졌기 때문입니다.
`int`가 아닌 `float`전달하는 경우에도 동일한 오류가 나타납니다: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
`int` 대신 `float`제공하면(예: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>) 동일한 오류가 나타납니다.
/// check | 확인
즉, 파이썬 타입 선언을 하면 **FastAPI**는 데이터 검증을 합니다.
오류에는 정확히 어느 지점에서 검증을 통과하지 못했는지 명시됩니다.
또한 오류에는 검증을 통과하지 못한 지점이 정확히 명시됩니다.
이는 API와 상호 작용하는 코드를 개발하고 디버깅하는 데 매우 유용합니다.
///
## 문서화
## 문서화 { #documentation }
그리고 브라우저에서 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>를 열면, 다음과 같이 자동 대화식 API 문서를 볼 수 있습니다:
@@ -83,23 +84,23 @@
/// check | 확인
그저 파이썬 타입 선언을 하기만 하면 **FastAPI**는 자동 대화형 API 문서(Swagger UI)를 제공합니다.
다시 한 번, 동일한 파이썬 타입 선언만으로 **FastAPI**는 자동 대화형 문서(Swagger UI 통합)를 제공합니다.
경로 매개변수가 정수형으로 명시된 것을 확인할 수 있습니다.
경로 매개변수가 정수형으로 선언된 것을 확인할 수 있습니다.
///
## 표준 기반의 이점, 대체 문서
## 표준 기반의 이점, 대체 문서 { #standards-based-benefits-alternative-documentation }
그리고 생성된 스키마는 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a> 표준에서 나온 것이기 때문에 호환되는 도구가 많이 있습니다.
그리고 생성된 스키마는 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a> 표준에서 나온 것이기 때문에 호환되는 도구가 많이 있습니다.
이 덕분에 **FastAPI**는 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>로 접속할 수 있는 (ReDoc을 사용하는) 대체 API 문서를 제공합니다:
이 덕분에 **FastAPI** 자체에서 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>로 접속할 수 있는 (ReDoc을 사용하는) 대체 API 문서를 제공합니다:
<img src="/img/tutorial/path-params/image02.png">
이와 마찬가지로 다양한 언어에 대한 코드 생성 도구를 포함하여 여러 호환되는 도구가 있습니다.
## Pydantic
## Pydantic { #pydantic }
모든 데이터 검증은 <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>에 의해 내부적으로 수행되므로 이로 인한 이점을 모두 얻을 수 있습니다. 여러분은 관리를 잘 받고 있음을 느낄 수 있습니다.
@@ -107,25 +108,31 @@
이 중 몇 가지는 자습서의 다음 장에 설명되어 있습니다.
## 순서 문제
## 순서 문제 { #order-matters }
*경로 작동*을 만들때 고정 경로를 갖고 있는 상황들을 맞닥뜨릴 수 있습니다.
*경로 처리*를 만들 때 고정 경로를 갖고 있는 상황들을 맞닥뜨릴 수 있습니다.
`/users/me`처럼, 현재 사용자의 데이터를 가져온다고 합시다.
사용자 ID를 이용해 특정 사용자의 정보를 가져오는 경로 `/users/{user_id}`도 있습니다.
*경로 작동*은 순차적으로 실행되기 때문에 `/users/{user_id}` 이전에 `/users/me`를 먼저 선언해야 합니다:
*경로 처리*는 순차적으로 평가되기 때문에 `/users/{user_id}` 이전에 `/users/me`에 대한 경로가 먼저 선언되었는지 확인해야 합니다:
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
그렇지 않으면 `/users/{user_id}` `/users/me` 요청 또한 매개변수 `user_id`의 값이 `"me"`인 것으로 "생각하게" 됩니다.
그렇지 않으면 `/users/{user_id}`에 대한 경로가 `/users/me`에도 매칭되어, 매개변수 `user_id` `"me"` 값이 들어왔다고 "생각하게" 됩니다.
## 사전정의 값
마찬가지로, 경로 처리를 재정의할 수는 없습니다:
만약 *경로 매개변수*를 받는 *경로 작동*이 있지만, *경로 매개변수*로 가능한 값들을 미리 정의하고 싶다면 파이썬 표준 <abbr title="열거형(Enumeration)">`Enum`</abbr>을 사용할 수 있습니다.
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
### `Enum` 클래스 생성
경로가 먼저 매칭되기 때문에 첫 번째 것이 항상 사용됩니다.
## 사전정의 값 { #predefined-values }
만약 *경로 매개변수*를 받는 *경로 처리*가 있지만, 가능한 유효한 *경로 매개변수* 값들을 미리 정의하고 싶다면 파이썬 표준 <abbr title="열거형(Enumeration)">`Enum`</abbr>을 사용할 수 있습니다.
### `Enum` 클래스 생성 { #create-an-enum-class }
`Enum`을 임포트하고 `str``Enum`을 상속하는 서브 클래스를 만듭니다.
@@ -133,47 +140,41 @@
가능한 값들에 해당하는 고정된 값의 클래스 어트리뷰트들을 만듭니다:
{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
/// info | 정보
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">열거형(또는 enums)</a>은 파이썬 버전 3.4 이후로 사용 가능합니다.
///
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
/// tip | 팁
혹시 궁금하다면, "AlexNet", "ResNet", 그리고 "LeNet"은 그저 기계 학습 <abbr title="기술적으로 정확히는 딥 러닝 모델 구조">모델</abbr>들의 이름입니다.
혹시 궁금하다면, "AlexNet", "ResNet", 그리고 "LeNet"은 그저 머신 러닝 <abbr title="기술적으로는 딥 러닝 모델 아키텍처">모델</abbr>들의 이름입니다.
///
### *경로 매개변수* 선언
### *경로 매개변수* 선언 { #declare-a-path-parameter }
생성한 열거형 클래스(`ModelName`)를 사용하는 타입 어노테이션으로 *경로 매개변수*를 만듭니다:
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
### 문서 확인
### 문서 확인 { #check-the-docs }
*경로 매개변수*에 사용할 수 있는 값은 미리 정의되어 있으므로 대화형 문서에서 잘 표시됩니다:
<img src="/img/tutorial/path-params/image03.png">
### 파이썬 *열거형*으로 작업하기
### 파이썬 *열거형*으로 작업하기 { #working-with-python-enumerations }
*경로 매개변수*의 값은 *열거형 멤버*가 됩니다.
#### *열거형 멤버* 비교
#### *열거형 멤버* 비교 { #compare-enumeration-members }
열거형 `ModelName`의 *열거형 멤버* 비교할 수 있습니다:
생성한 열거형 `ModelName`의 *열거형 멤버* 비교할 수 있습니다:
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
#### *열거형 값* 가져오기
#### *열거형 값* 가져오기 { #get-the-enumeration-value }
`model_name.value` 또는 일반적으로 `your_enum_member.value`를 이용하여 실제 값(위 예시의 경우 `str`)을 가져올 수 있습니다:
{* ../../docs_src/path_params/tutorial005.py hl[20] *}
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
/// tip | 팁
@@ -181,15 +182,15 @@
///
#### *열거형 멤버* 반환
#### *열거형 멤버* 반환 { #return-enumeration-members }
*경로 작동*에서 *열거형 멤버*를 반환할 수 있습니다. 이는 중첩 JSON 본문(예: `dict`)내의 값으로도 가능합니다.
*경로 처리*에서 *enum 멤버*를 반환할 수 있습니다. 이는 JSON 본문(예: `dict`) 내에 중첩된 형태로도 가능합니다.
클라이언트에 반환하기 전에 해당 값(이 경우 문자열)으로 변환됩니다:
{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
클라이언트는 아래 JSON 응답을 얻니다:
클라이언트는 아래와 같은 JSON 응답을 얻게 됩니다:
```JSON
{
@@ -198,53 +199,53 @@
}
```
## 경로를 포함하는 경로 매개변수
## 경로를 포함하는 경로 매개변수 { #path-parameters-containing-paths }
경로를 포함하는 *경로 작동* `/files/{file_path}` 있다고 해봅시다.
경로 `/files/{file_path}`를 가진 *경로 처리*가 있다고 해봅시다.
그런데 이 경우 `file_path` 자체가 `home/johndoe/myfile.txt`와 같은 경로를 포함해야 합니다.
하지만 `file_path` 자체가 `home/johndoe/myfile.txt`와 같은 *경로*를 포함해야 합니다.
이때 해당 파일의 URL은 다음처럼 됩니다: `/files/home/johndoe/myfile.txt`.
### OpenAPI 지원
### OpenAPI 지원 { #openapi-support }
테스트와 정의가 어려운 시나리오로 이어질 수 있으므로 OpenAPI는 *경로*를 포함하는 *경로 매개변수*를 내부에 선언하는 방법을 지원하지 않습니다.
그럼에도 Starlette의 내부 도구중 하나를 사용하여 **FastAPI**에서는 이가 가능합니다.
그럼에도 Starlette의 내부 도구 중 하나를 사용하여 **FastAPI**에서는 이가 가능합니다.
문서에 매개변수에 경로가 포함되어야 한다는 정보가 명시되지는 않지만 여전히 작동합니다.
또한 문서가 여전히 동작하긴 하지만, 매개변수에 경로가 포함되어야 한다는 내용을 추가로 문서화하지는 않습니다.
### 경로 변환기
### 경로 변환기 { #path-convertor }
Starlette의 옵션을 직접 이용하여 다음과 같은 URL을 사용함으로써 *path*를 포함하는 *경로 매개변수*를 선언할 수 있습니다:
Starlette의 옵션을 직접 이용하여 다음과 같은 URL을 사용함으로써 *경로*를 포함하는 *경로 매개변수*를 선언할 수 있습니다:
```
/files/{file_path:path}
```
이러한 경우 매개변수의 이름은 `file_path`이며, 마지막 부분 `:path`는 매개변수가 *경로*와 일치해야 함을 명시합니다.
이러한 경우 매개변수의 이름은 `file_path`이며, 마지막 부분 `:path`는 매개변수가 어떤 *경로*와도 매칭되어야 함을 의미합니다.
따라서 다음과 같이 사용할 수 있습니다:
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
/// tip | 팁
매개변수가 가져야 하는 값이 `/home/johndoe/myfile.txt`와 같이 슬래시로 시작(`/`)해야 할 수 있습니다.
매개변수가 선행 슬래시(`/`)가 있는 `/home/johndoe/myfile.txt`를 포함해야 할 수 있습니다.
경우 URL은: `/files//home/johndoe/myfile.txt`이며 `files` `home` 사이에 이중 슬래시(`//`)가 생깁니다.
경우 URL은: `/files//home/johndoe/myfile.txt`이며 `files` `home` 사이에 이중 슬래시(`//`)가 생깁니다.
///
## 요약
## 요약 { #recap }
**FastAPI**를 이용하면 짧고 직관적인 표준 파이썬 타입 선언을 사용하여 다음을 얻을 수 있습니다:
* 편집기 지원: 오류 검사, 자동완성 등
* 데이터 "<abbr title="HTTP 요청에서 전달되는 문자열을 파이썬 데이터로 변환">파싱</abbr>"
* 데이터 "<abbr title="HTTP 요청에서 전달되는 문자열을 파이썬 데이터로 변환">parsing</abbr>"
* 데이터 검증
* API 주석(Annotation)과 자동 문서
단 한번의 선언만으로 위 사항들을 모두 선언할 수 있습니다.
그리고 한 번만 선언하면 됩니다.
이는 대체 프레임워크와 비교했을 때 (엄청나게 빠른 성능 외에도) **FastAPI**의 주요한 장점일 것입니다.

View File

@@ -1,48 +1,48 @@
# 쿼리 매개변수 모델
# 쿼리 매개변수 모델 { #query-parameter-models }
연관된 쿼리 **매개변수** 그룹이 있다면 **Pydantic 모델** 을 사용해 선언할 수 있습니다.
연관된 **쿼리 매개변수** 그룹이 있다면 이를 선언하기 위해 **Pydantic 모델**을 생성할 수 있습니다.
이렇게 하면 **여러 곳**에서 **모델을 재사용**할 수 있을 뿐만 아니라, 매개변수에 대한 검증 및 메타데이터도 한 번에 선언할 수 있습니다. 😎
/// note | 참고
이 기능은 FastAPI 버전 `0.115.0`부터 제공됩니다. 🤓
이 기능은 FastAPI 버전 `0.115.0`부터 지원됩니다. 🤓
///
## 쿼리 매개변수와 Pydantic 모델
## Pydantic 모델과 쿼리 매개변수 { #query-parameters-with-a-pydantic-model }
필요한 **쿼리 매개변수**를 **Pydantic 모델** 안에 선언한 다음, 모델을 `Query`로 선언합니다.
필요한 **쿼리 매개변수**를 **Pydantic 모델** 안에 선언한 다음, 매개변수를 `Query`로 선언합니다:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
**FastAPI**는 요청의 **쿼리 매개변수**에서 **각 필드**의 데이터를 **추출**해 정의한 Pydantic 모델 제공합니다.
**FastAPI**는 요청의 **쿼리 매개변수**에서 **각 필드**의 데이터를 **추출**해 정의한 Pydantic 모델 제공합니다.
## 문서 확인하기
## 문서 확인하기 { #check-the-docs }
`/docs` 경로의 API 문서에서 매개변수를 확인할 수 있습니다.
`/docs`의 문서 UI에서 쿼리 매개변수를 확인할 수 있습니다:
<div class="screenshot">
<img src="/img/tutorial/query-param-models/image01.png">
</div>
## 추가 쿼리 매개변수 금지
## 추가 쿼리 매개변수 금지 { #forbid-extra-query-parameters }
몇몇의 특이한 경우에 (흔치 않지만), 허용할 쿼리 매개변수를 **제한**해야할 수 있습니다.
몇몇의 특이한 경우에 (흔치 않지만), 받으려는 쿼리 매개변수를 **제한**하고 싶을 수 있습니다.
Pydantic 모델 설정에서 `extra` 필드 `forbid` 로 설정할 수 있습니다.
Pydantic 모델 설정을 사용해 어떤 `extra` 필드 `forbid`할 수 있습니다:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
만약 클라이언트가 쿼리 매개변수로 **추가적인** 데이터를 보내려고 하면, 클라이언트는 **에러** 응답을 받게 됩니다.
클라이언트가 **쿼리 매개변수**로 **추가적인** 데이터를 보내려고 하면 **에러** 응답을 받게 됩니다.
예를 들어, 아래와 같이 만약 클라이언트가 `tool` 쿼리 매개변수에 `plumbus` 라는 값을 추가해서 보내려고 하면,
예를 들어, 아래와 같이 클라이언트가 `tool` 쿼리 매개변수에 `plumbus` 값을 보내려고 하면:
```http
https://example.com/items/?limit=10&tool=plumbus
```
클라이언트는 쿼리 매개변수 `tool` 이 허용되지 않는다는 **에러** 응답을 받게 됩니다.
쿼리 매개변수 `tool`이 허용되지 않는다는 **에러** 응답을 받게 됩니다:
```json
{
@@ -57,12 +57,12 @@ https://example.com/items/?limit=10&tool=plumbus
}
```
## 요약
## 요약 { #summary }
**FastAPI** 에서 **쿼리 매개변수** 를 선언할 때 **Pydantic 모델** 을 사용할 수 있습니다. 😎
**FastAPI**에서 **쿼리 매개변수**를 선언할 때 **Pydantic 모델**을 사용할 수 있습니다. 😎
/// tip | 팁
스포일러 경고: Pydantic 모델을 쿠키와 헤더에도 용할 수 있습니다. 이에 대해서는 이후 튜토리얼에서 다룰 예정입니다. 🤫
스포일러 경고: Pydantic 모델을 쿠키와 헤더에도 용할 수 있지만, 이에 대해서는 이후 튜토리얼에서 읽게 될 것입니다. 🤫
///

View File

@@ -1,118 +1,226 @@
# 쿼리 매개변수와 문자열 검증
# 쿼리 매개변수와 문자열 검증 { #query-parameters-and-string-validations }
**FastAPI**를 사용하면 매개변수에 대한 추가 정보 및 검증을 선언할 수 있습니다.
이 응용 프로그램을 예로 들어보겠습니다:
{* ../../docs_src/query_params_str_validations/tutorial001.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
쿼리 매개변수 `q``Optional[str]` 자료형입니다. 즉, `str` 자료형이지만 `None` 역시 될 수 있음을 뜻하고, 실제로 기본값은 `None`이기 때문에 FastAPI는 이 매개변수가 필수가 아니라는 것을 압니다.
쿼리 매개변수 `q``str | None` 자료형입니다. 즉, `str` 자료형이지만 `None` 역시 될 수 있음을 뜻하고, 실제로 기본값은 `None`이기 때문에 FastAPI는 이 매개변수가 필수가 아니라는 것을 압니다.
/// note | 참고
FastAPI는 `q`의 기본값이 `= None`이기 때문에 필수가 아님을 압니다.
`Optional[str]`에 있는 `Optional`은 FastAPI가 사용하는게 아니지만, 편집기에게 더 나은 지원과 오류 탐지를 제공하게 해줍니다.
`str | None`을 사용하면 편집기 더 나은 지원과 오류 탐지를 제공하게 해줍니다.
///
## 추가 검증
## 추가 검증 { #additional-validation }
`q`가 선택적이지만 값이 주어질 때마다 **값이 50자를 초과하지 않게** 강제하려 합니다.
`q`가 선택적이지만 값이 주어질 때마다 **길이가 50자를 초과하지 않게** 강제하려 합니다.
### `Query` 임포트
### `Query`와 `Annotated` 임포트 { #import-query-and-annotated }
이를 위해 먼저 `fastapi`에서 `Query` 임포트합니다:
이를 위해 먼저 다음을 임포트합니다:
{* ../../docs_src/query_params_str_validations/tutorial002.py hl[3] *}
* `fastapi`에서 `Query`
* `typing`에서 `Annotated`
## 기본값으로 `Query` 사용
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
이제 `Query`를 매개변수의 기본값으로 사용하여 `max_length` 매개변수를 50으로 설정합니다:
/// info | 정보
{* ../../docs_src/query_params_str_validations/tutorial002.py hl[9] *}
FastAPI는 0.95.0 버전에서 `Annotated` 지원을 추가했고(그리고 이를 권장하기 시작했습니다).
기본값 `None``Query(None)`으로 바꿔야 하므로, `Query`의 첫 번째 매개변수는 기본값을 정의하는 것과 같은 목적으로 사용됩니다.
이전 버전을 사용하면 `Annotated`를 사용하려고 할 때 오류가 발생합니다.
`Annotated`를 사용하기 전에 최소 0.95.1 버전으로 [FastAPI 버전 업그레이드](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank}를 진행하세요.
///
## `q` 매개변수의 타입에 `Annotated` 사용하기 { #use-annotated-in-the-type-for-the-q-parameter }
이전에 [Python Types Intro](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}에서 `Annotated`를 사용해 매개변수에 메타데이터를 추가할 수 있다고 말씀드린 것을 기억하시나요?
이제 FastAPI에서 사용할 차례입니다. 🚀
다음과 같은 타입 어노테이션이 있었습니다:
//// tab | Python 3.10+
```Python
q: str | None = None
```
////
//// tab | Python 3.9+
```Python
q: Union[str, None] = None
```
////
여기서 `Annotated`로 감싸서 다음과 같이 만듭니다:
//// tab | Python 3.10+
```Python
q: Annotated[str | None] = None
```
////
//// tab | Python 3.9+
```Python
q: Annotated[Union[str, None]] = None
```
////
두 버전 모두 같은 의미로, `q``str` 또는 `None`이 될 수 있는 매개변수이며 기본값은 `None`입니다.
이제 재미있는 부분으로 넘어가 봅시다. 🎉
## `q` 매개변수의 `Annotated`에 `Query` 추가하기 { #add-query-to-annotated-in-the-q-parameter }
이제 이 `Annotated`에 더 많은 정보를 넣을 수 있으므로(이 경우에는 추가 검증), `Annotated` 안에 `Query`를 추가하고 `max_length` 매개변수를 `50`으로 설정합니다:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
기본값은 여전히 `None`이므로, 매개변수는 여전히 선택적입니다.
하지만 `Annotated` 안에 `Query(max_length=50)`를 넣음으로써, 이 값에 대해 **추가 검증**을 적용하고 최대 50자까지만 허용하도록 FastAPI에 알려줍니다. 😎
/// tip | 팁
여기서는 **쿼리 매개변수**이기 때문에 `Query()`를 사용합니다. 나중에 `Path()`, `Body()`, `Header()`, `Cookie()`와 같이 `Query()`와 동일한 인자를 받는 것들도 보게 될 것입니다.
///
이제 FastAPI는 다음을 수행합니다:
* 최대 길이가 50자인지 확인하도록 데이터를 **검증**합니다
* 데이터가 유효하지 않을 때 클라이언트에게 **명확한 오류**를 보여줍니다
* OpenAPI 스키마 *경로 처리*에 매개변수를 **문서화**합니다(따라서 **자동 문서 UI**에 표시됩니다)
## 대안(이전 방식): 기본값으로 `Query` 사용 { #alternative-old-query-as-the-default-value }
이전 FastAPI 버전(<abbr title="before 2023-03">0.95.0</abbr> 이전)에서는 `Annotated`에 넣는 대신, 매개변수의 기본값으로 `Query`를 사용해야 했습니다. 주변에서 이 방식을 사용하는 코드를 볼 가능성이 높기 때문에 설명해 드리겠습니다.
/// tip | 팁
새 코드를 작성할 때와 가능할 때는 위에서 설명한 대로 `Annotated`를 사용하세요. 여러 장점이 있고(아래에서 설명합니다) 단점은 없습니다. 🍰
///
다음은 함수 매개변수의 기본값으로 `Query()`를 사용하면서 `max_length`를 50으로 설정하는 방법입니다:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
이 경우(`Annotated`를 사용하지 않는 경우) 함수에서 기본값 `None``Query()`로 바꿔야 하므로, 이제 `Query(default=None)`로 기본값을 설정해야 합니다. (최소한 FastAPI 입장에서는) 이 인자는 해당 기본값을 정의하는 것과 같은 목적을 수행합니다.
그러므로:
```Python
q: Optional[str] = Query(None)
q: str | None = Query(default=None)
```
...위 코드는 아래와 동일하게 매개변수를 선택적으로 만듭니다:
...위 코드는 기본값이 `None`인 선택적 매개변수를 만들며, 아래와 동일합니다:
```Python
q: Optional[str] = None
q: str | None = None
```
하지만 명시적으로 쿼리 매개변수를 선언합니다.
하지만 `Query` 버전은 이것이 쿼리 매개변수임을 명시적으로 선언합니다.
/// info | 정보
FastAPI는 다음 부분에 관심이 있습니다:
그 다음, `Query`로 더 많은 매개변수를 전달할 수 있습니다. 지금의 경우 문자열에 적용되는 `max_length` 매개변수입니다:
```Python
= None
q: str | None = Query(default=None, max_length=50)
```
또는:
이는 데이터를 검증할 것이고, 데이터가 유효하지 않다면 명백한 오류를 보여주며, OpenAPI 스키마 *경로 처리*에 매개변수를 문서화 합니다.
### 기본값으로 `Query` 사용 또는 `Annotated`에 넣기 { #query-as-the-default-value-or-in-annotated }
`Annotated` 안에서 `Query`를 사용할 때는 `Query``default` 매개변수를 사용할 수 없다는 점을 기억하세요.
대신 함수 매개변수의 실제 기본값을 사용하세요. 그렇지 않으면 일관성이 깨집니다.
예를 들어, 다음은 허용되지 않습니다:
```Python
= Query(None)
q: Annotated[str, Query(default="rick")] = "morty"
```
그리고 `None`을 사용하여 쿼라 매개변수가 필수적이지 않다는 것을 파악합니다.
...왜냐하면 기본값이 `"rick"`인지 `"morty"`인지 명확하지 않기 때문입니다.
`Optional` 부분은 편집기에게 더 나은 지원을 제공하기 위해서만 사용니다.
///
또한 `Query`로 더 많은 매개변수를 전달할 수 있습니다. 지금의 경우 문자열에 적용되는 `max_length` 매개변수입니다:
따라서 (가능하면) 다음과 같이 사용니다:
```Python
q: str = Query(None, max_length=50)
q: Annotated[str, Query()] = "rick"
```
이는 데이터를 검증할 것이고, 데이터가 유효하지 않다면 명백한 오류를 보여주며, OpenAPI 스키마 *경로 작동*에 매개변수를 문서화 합니다.
...또는 오래된 코드베이스에서는 다음과 같은 코드를 찾게 될 것입니다:
## 검증 추가
```Python
q: str = Query(default="rick")
```
매개변수 `min_length` 또한 추가할 수 있습니다:
### `Annotated`의 장점 { #advantages-of-annotated }
{* ../../docs_src/query_params_str_validations/tutorial003.py hl[9] *}
함수 매개변수의 기본값 방식 대신 **`Annotated`를 사용하는 것을 권장**합니다. 여러 이유로 **더 좋기** 때문입니다. 🤓
## 정규식 추가
**함수 매개변수**의 **기본값**이 **실제 기본값**이 되므로, 전반적으로 Python에 더 직관적입니다. 😌
매개변수와 일치해야 하는 <abbr title="정규표현식(regular expression), regex 또는 regexp는 문자열 조회 패턴을 정의하는 문자들의 순열입니다">정규표현식</abbr>을 정의할 수 있습니다:
FastAPI 없이도 **다른 곳에서** 같은 함수를 **호출**할 수 있고, **예상대로 동작**합니다. **필수** 매개변수(기본값이 없는 경우)가 있다면 **편집기**가 오류로 알려줄 것이고, 필수 매개변수를 전달하지 않고 실행하면 **Python**도 오류를 냅니다.
{* ../../docs_src/query_params_str_validations/tutorial004.py hl[10] *}
`Annotated`를 사용하지 않고 **(이전) 기본값 스타일**을 사용하면, FastAPI 없이 **다른 곳에서** 함수를 호출할 때도 제대로 동작하도록 함수에 인자를 전달해야 한다는 것을 **기억**해야 합니다. 그렇지 않으면 값이 기대와 다르게 됩니다(예: `str` 대신 `QueryInfo` 같은 것). 그리고 편집기도 경고하지 않고 Python도 그 함수를 실행할 때는 불평하지 않으며, 오직 내부 동작에서 오류가 발생할 때만 문제가 드러납니다.
이 특정 정규표현식은 전달 받은 매개변수 값을 검사합니다:
`Annotated`는 하나 이상의 메타데이터 어노테이션을 가질 수 있기 때문에, 이제 <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a> 같은 다른 도구에서도 같은 함수를 사용할 수 있습니다. 🚀
* `^`: 이전에 문자가 없고 뒤따르는 문자로 시작합니다.
* `fixedquery`: 정확히 `fixedquery` 값을 갖습니다.
* `$`: 여기서 끝나고 `fixedquery` 이후로 아무 문자도 갖지 않습니다.
## 검증 더 추가하기 { #add-more-validations }
**"정규표현식"** 개념에 대해 상실감을 느꼈다면 걱정하지 않아도 됩니다. 많은 사람에게 어려운 주제입니다. 아직은 정규표현식 없이도 많은 작업들을 할 수 있습니다.
`min_length` 매개변수도 추가할 수 있습니다:
하지만 언제든지 가서 배울수 있고, **FastAPI**에서 직접 사용할 수 있다는 사실을 알고 있어야 합니다.
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## 기본값
## 정규식 추가 { #add-regular-expressions }
기본값으로 사용하는 첫 번째 인자로 `None`을 전달하듯이, 다른 값을 전달할 수 있습니다.
매개변수와 일치해야 하는 <abbr title="문자열에 대한 검색 패턴을 정의하는 문자들의 순열인 정규 표현식(regular expression), regex 또는 regexp입니다.">정규 표현식</abbr> `pattern`을 정의할 수 있습니다:
`min_length``3`이고, 기본값이 `"fixedquery"`인 쿼리 매개변수 `q`를 선언해봅시다:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
{* ../../docs_src/query_params_str_validations/tutorial005.py hl[7] *}
이 특정 정규표현식 패턴은 전달 받은 매개변수 값이 다음을 만족하는지 검사합니다:
* `^`: 뒤따르는 문자로 시작하며, 앞에는 문자가 없습니다.
* `fixedquery`: 정확히 `fixedquery` 값을 가집니다.
* `$`: 여기서 끝나며, `fixedquery` 이후로 더 이상 문자가 없습니다.
**"정규 표현식"** 개념에 대해 상실감을 느꼈다면 걱정하지 않아도 됩니다. 많은 사람에게 어려운 주제입니다. 아직은 정규 표현식 없이도 많은 작업들을 할 수 있습니다.
이제 필요할 때 언제든지 **FastAPI**에서 직접 사용할 수 있다는 사실을 알게 되었습니다.
## 기본값 { #default-values }
물론 `None`이 아닌 다른 기본값을 사용할 수도 있습니다.
`q` 쿼리 매개변수에 `min_length``3`으로 설정하고, 기본값을 `"fixedquery"`로 선언하고 싶다고 해봅시다:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
/// note | 참고
기본값을 갖는 것만으로 매개변수는 선택적이 됩니다.
`None`을 포함해 어떤 타입이든 기본값을 가지면 매개변수는 선택적(필수 아님)이 됩니다.
///
## 필수로 만들기
## 필수 매개변수 { #required-parameters }
더 많은 검증이나 메타데이터를 선언할 필요가 없는 경우, 다음과 같이 기본값을 선언하지 않고 쿼리 매개변수 `q`를 필수로 만들 수 있습니다:
@@ -123,42 +231,42 @@ q: str
아래 대신:
```Python
q: Optional[str] = None
q: str | None = None
```
그러나 이제 다음과 같이 `Query`로 선언합니다:
하지만 이제는 예를 들어 다음과 같이 `Query`로 선언합니다:
```Python
q: Optional[str] = Query(None, min_length=3)
q: Annotated[str | None, Query(min_length=3)] = None
```
그래`Query`필수값으로 만들어야 할 때, 첫 번째 인자로 `...`를 사용할 수 있습니다:
따라`Query`사용하면서 값을 필수로 선언해야 할 때, 기본값을 선언하지 않으면 됩니다:
{* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *}
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
/// info | 정보
### 필수지만 `None` 가능 { #required-can-be-none }
이전에 `...`를 본적이 없다면: 특별한 단일값으로, <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">파이썬의 일부이며 "Ellipsis"라 부릅니다</a>.
매개변수가 `None`을 허용하지만 여전히 필수라고 선언할 수 있습니다. 이렇게 하면 값이 `None`이더라도 클라이언트는 값을 반드시 전송해야 합니다.
///
이를 위해 `None`이 유효한 타입이라고 선언하되, 기본값은 선언하지 않으면 됩니다:
이렇게 하면 **FastAPI**가 이 매개변수는 필수임을 알 수 있습니다.
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## 쿼리 매개변수 리스트 / 다중값
## 쿼리 매개변수 리스트 / 다중값 { #query-parameter-list-multiple-values }
쿼리 매개변수를 `Query`와 함께 명시적으로 선언할 때, 값들의 리스트나 다른 방법으로 여러 값을 받도록 선언 할 수도 있습니다.
`Query`쿼리 매개변수를 명시적으로 정의할 때 값들의 리스트를 받도록 선언할 수도 있고, 다른 말로 하면 여러 값을 받도록 선언할 수도 있습니다.
예를 들어, URL에서 여러번 나오는 `q` 쿼리 매개변수를 선언하려면 다음과 같이 작성할 수 있습니다:
예를 들어, URL에서 여러 번 나타날 수 있는 `q` 쿼리 매개변수를 선언하려면 다음과 같이 작성할 수 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial011.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
아래와 같은 URL을 사용합니다:
그 다음, 아래와 같은 URL:
```
http://localhost:8000/items/?q=foo&q=bar
```
여러 `q` *쿼리 매개변수* 값들(`foo``bar`) 파이썬 `list`*경로 작동 함수* *함수 매개변수* `q`로 전달 받습니다.
여러 `q` *쿼리 매개변수* 값들(`foo``bar`) 파이썬 `list`*경로 처리 함수*의 *함수 매개변수* `q`에서 받게 됩니다.
따라서 해당 URL에 대한 응답은 다음과 같습니다:
@@ -173,7 +281,7 @@ http://localhost:8000/items/?q=foo&q=bar
/// tip | 팁
위의 예와 같이 `list` 자료형으로 쿼리 매개변수를 선언하려면 `Query`를 명시적으로 사용해야 합니다. 그렇지 않으면 요청 본문으로 해석됩니다.
위의 예와 같이 `list` 타입으로 쿼리 매개변수를 선언하려면 `Query`를 명시적으로 사용해야 합니다. 그렇지 않으면 요청 본문으로 해석됩니다.
///
@@ -181,19 +289,19 @@ http://localhost:8000/items/?q=foo&q=bar
<img src="/img/tutorial/query-params-str-validations/image02.png">
### 쿼리 매개변수 리스트 / 기본값을 사용하는 다중값
### 쿼리 매개변수 리스트 / 기본값이 있는 다중값 { #query-parameter-list-multiple-values-with-defaults }
그리고 제공된 값이 없으면 기본 `list` 값을 정의할 수도 있습니다:
제공된 값이 없으면 기본 `list` 값을 정의할 수도 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial012.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
아래로 이동한다면:
다음으로 이동면:
```
http://localhost:8000/items/
```
`q`의 기본값은: `["foo", "bar"]`이며 응답은 다음이 됩니다:
`q`의 기본값은 `["foo", "bar"]`가 되고, 응답은 다음이 됩니다:
```JSON
{
@@ -204,21 +312,21 @@ http://localhost:8000/items/
}
```
#### `list` 사용하기
#### `list` 사용하기 { #using-just-list }
`List[str]` 대신 `list`를 직접 사용할 수도 있습니다:
`list[str]` 대신 `list`를 직접 사용할 수도 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial013.py hl[7] *}
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
/// note | 참고
이 경우 FastAPI는 리스트의 내용을 검사하지 않음을 명심하기 바랍니다.
이 경우 FastAPI는 리스트의 내용을 검사하지 않음을 명심하세요.
예를 들어, `List[int]`는 리스트 내용이 정수인지 검사(및 문서화)합니다. 하지만 `list` 단독일 경우는 아닙니다.
예를 들어, `list[int]`는 리스트 내용이 정수인지 검사(및 문서화)합니다. 하지만 `list` 단독일 경우는 아닙니다.
///
## 더 많은 메타데이터 선언
## 더 많은 메타데이터 선언 { #declare-more-metadata }
매개변수에 대한 정보를 추가할 수 있습니다.
@@ -226,7 +334,7 @@ http://localhost:8000/items/
/// note | 참고
도구에 따라 OpenAPI 지원 수준이 다를 수 있음을 명심하기 바랍니다.
도구에 따라 OpenAPI 지원 수준이 다를 수 있음을 명심하세요.
일부는 아직 선언된 추가 정보를 모두 표시하지 않을 수 있지만, 대부분의 경우 누락된 기능은 이미 개발 계획이 있습니다.
@@ -234,13 +342,13 @@ http://localhost:8000/items/
`title`을 추가할 수 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial007.py hl[10] *}
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
그리고 `description`도 추가할 수 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial008.py hl[13] *}
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## 별칭 매개변수
## 별칭 매개변수 { #alias-parameters }
매개변수가 `item-query`이길 원한다고 가정해 봅시다.
@@ -250,31 +358,99 @@ http://localhost:8000/items/
http://127.0.0.1:8000/items/?item-query=foobaritems
```
그러나 `item-query` 유효한 파이썬 변수 이름이 아닙니다.
그러나 `item-query` 유효한 파이썬 변수 이름이 아닙니다.
가장 가까운 것은 `item_query`일 겁니다.
하지만 정확히`item-query`이길 원합니다...
하지만 정확히 `item-query`이길 원합니다...
이럴 경우 `alias`를 선언할 수 있으며, 해당 별칭은 매개변수 값을 찾는 데 사용됩니다:
{* ../../docs_src/query_params_str_validations/tutorial009.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## 매개변수 사용하지 않게 하기
## 매개변수 사용 중단하기 { #deprecating-parameters }
이제는 더이상 이 매개변수를 마음에 들어하지 않는다고 가정해 봅시다.
이제는 더 이상 이 매개변수를 마음에 들어하지 않는다고 가정해 봅시다.
이 매개변수를 사용하는 클라이언트가 있기 때문에 한동안은 남겨둬야 하지만, <abbr title="구식이며, 사용하지 않는 것을 추천">사용되지 않는다(deprecated)</abbr>고 확실하게 문서에서 보여주고 싶습니다.
이 매개변수를 사용하는 클라이언트가 있기 때문에 한동안은 남겨둬야 하지만, 문서에서 <abbr title="obsolete, recommended not to use it 구식이며, 사용하지 않는 것을 추천">deprecated</abbr>로 명확하게 보여주고 싶습니다.
그렇다면 `deprecated=True` 매개변수를 `Query`로 전달합니다:
{* ../../docs_src/query_params_str_validations/tutorial010.py hl[18] *}
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
문서가 아래와 같이 보일겁니다:
<img src="/img/tutorial/query-params-str-validations/image01.png">
## 요약
## OpenAPI에서 매개변수 제외 { #exclude-parameters-from-openapi }
생성된 OpenAPI 스키마(따라서 자동 문서화 시스템)에서 쿼리 매개변수를 제외하려면 `Query``include_in_schema` 매개변수를 `False`로 설정하세요:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## 커스텀 검증 { #custom-validation }
위에 나온 매개변수들로는 할 수 없는 **커스텀 검증**이 필요한 경우가 있을 수 있습니다.
그런 경우에는 일반적인 검증(예: 값이 `str`인지 검증한 뒤) 이후에 적용되는 **커스텀 검증 함수**를 사용할 수 있습니다.
`Annotated` 안에서 <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">Pydantic의 `AfterValidator`</a>를 사용하면 이를 구현할 수 있습니다.
/// tip | 팁
Pydantic에는 <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator" class="external-link" target="_blank">`BeforeValidator`</a>와 같은 다른 것들도 있습니다. 🤓
///
예를 들어, 이 커스텀 validator는 <abbr title="ISBN means International Standard Book Number 국제 표준 도서 번호">ISBN</abbr> 도서 번호의 경우 아이템 ID가 `isbn-`으로 시작하고, <abbr title="IMDB (Internet Movie Database) is a website with information about movies 영화에 대한 정보를 제공하는 웹사이트">IMDB</abbr> 영화 URL ID의 경우 `imdb-`로 시작하는지 확인합니다:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | 정보
이는 Pydantic 2 이상 버전에서 사용할 수 있습니다. 😎
///
/// tip | 팁
데이터베이스나 다른 API 같은 **외부 구성요소**와 통신이 필요한 어떤 종류의 검증이든 해야 한다면, 대신 **FastAPI Dependencies**를 사용해야 합니다. 이에 대해서는 나중에 배우게 됩니다.
이 커스텀 validator는 요청에서 제공된 **같은 데이터만**으로 확인할 수 있는 것들을 위한 것입니다.
///
### 코드 이해하기 { #understand-that-code }
중요한 부분은 **`Annotated` 안에서 함수와 함께 `AfterValidator`를 사용한다는 것**뿐입니다. 이 부분은 건너뛰셔도 됩니다. 🤸
---
하지만 이 특정 코드 예제가 궁금하고 계속 보고 싶다면, 추가 세부사항은 다음과 같습니다.
#### `value.startswith()`를 사용한 문자열 { #string-with-value-startswith }
알고 계셨나요? `value.startswith()`를 사용하는 문자열은 튜플을 받을 수 있으며, 튜플에 있는 각 값을 확인합니다:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### 임의의 항목 { #a-random-item }
`data.items()`를 사용하면 각 딕셔너리 항목의 키와 값을 담은 튜플로 구성된 <abbr title="리스트, 세트 등처럼 for 루프로 순회할 수 있는 것">iterable object</abbr>를 얻습니다.
이 iterable object를 `list(data.items())`로 적절한 `list`로 변환합니다.
그 다음 `random.choice()`로 리스트에서 **무작위 값**을 얻어 `(id, name)` 형태의 튜플을 얻습니다. 예를 들어 `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")` 같은 값이 될 것입니다.
그 다음 이 튜플의 **두 값을** 변수 `id``name`에 **할당**합니다.
따라서 사용자가 아이템 ID를 제공하지 않더라도, 무작위 추천을 받게 됩니다.
...이 모든 것을 **단 하나의 간단한 줄**로 합니다. 🤯 Python 정말 좋지 않나요? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## 요약 { #recap }
매개변수에 검증과 메타데이터를 추가 선언할 수 있습니다.
@@ -285,12 +461,14 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
* `description`
* `deprecated`
특정 문자열 검증:
문자열에 특화된 검증:
* `min_length`
* `max_length`
* `regex`
* `pattern`
`AfterValidator`를 사용하는 커스텀 검증.
예제에서 `str` 값의 검증을 어떻게 추가하는지 살펴보았습니다.
숫자와 같은 다른 자료형에 대한 검증을 어떻게 선언하는지 확인하려면 다음 장을 확인하기 바랍니다.
숫자와 같은 다른 타입에 대한 검증을 어떻게 선언하는지 확인하려면 다음 장을 확인하기 바랍니다.

View File

@@ -1,8 +1,8 @@
# 쿼리 매개변수
# 쿼리 매개변수 { #query-parameters }
경로 매개변수의 일부가 아닌 다른 함수 매개변수를 선언하면 "쿼리" 매개변수로 자동 해석합니다.
{* ../../docs_src/query_params/tutorial001.py hl[9] *}
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
쿼리는 URL에서 `?` 후에 나오고 `&`으로 구분되는 키-값 쌍의 집합입니다.
@@ -24,11 +24,11 @@ URL의 일부이므로 "자연스럽게" 문자열입니다.
경로 매개변수에 적용된 동일한 프로세스가 쿼리 매개변수에도 적용됩니다:
* (당연히) 편집기 지원
* 데이터 <abbr title="HTTP 요청에서 전달되는 문자열을 파이썬 데이터로 변환">"파싱"</abbr>
* 데이터 <abbr title="converting the string that comes from an HTTP request into Python data">"파싱"</abbr>
* 데이터 검증
* 자동 문서화
## 기본값
## 기본값 { #defaults }
쿼리 매개변수는 경로에서 고정된 부분이 아니기 때문에 선택적일 수 있고 기본값을 가질 수 있습니다.
@@ -57,33 +57,25 @@ http://127.0.0.1:8000/items/?skip=20
* `skip=20`: URL에서 지정했기 때문입니다
* `limit=10`: 기본값이기 때문입니다
## 선택적 매개변수
## 선택적 매개변수 { #optional-parameters }
같은 방법으로 기본값을 `None`으로 설정하여 선택적 매개변수를 선언할 수 있습니다:
{* ../../docs_src/query_params/tutorial002.py hl[9] *}
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
이 경우 함수 매개변수 `q`는 선택적이며 기본값으로 `None` 값이 됩니다.
/// check | 확인
/// check
**FastAPI**는 `item_id`가 경로 매개변수이고 `q`는 경로 매개변수가 아 쿼리 매개변수라는 것을 알 정도로 충분히 똑똑합니다.
또한 **FastAPI**는 `item_id`가 경로 매개변수이고 `q`는 경로 매개변수가 아니라서 쿼리 매개변수라는 것을 알 정도로 충분히 똑똑하다는 점도 확인하세요.
///
/// note | 참고
FastAPI는 `q``= None`이므로 선택적이라는 것을 인지합니다.
`Union[str, None]`에 있는 `Union`은 FastAPI(FastAPI는 `str` 부분만 사용합니다)가 사용하는게 아니지만, `Union[str, None]`은 편집기에게 코드에서 오류를 찾아낼 수 있게 도와줍니다.
///
## 쿼리 매개변수 형변환
## 쿼리 매개변수 형변환 { #query-parameter-type-conversion }
`bool` 형으로 선언할 수도 있고, 아래처럼 변환됩니다:
{* ../../docs_src/query_params/tutorial003.py hl[9] *}
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
이 경우, 아래로 이동하면:
@@ -115,10 +107,10 @@ http://127.0.0.1:8000/items/foo?short=on
http://127.0.0.1:8000/items/foo?short=yes
```
또는 다른 어떤 변형(대문자, 첫글자만 대문자 등)이더라도 함수는 매개변수 `bool`형을 가진 `short`의 값이 `True`임을 압니다. 그렇지 않은 경우 `False`입니다.
또는 다른 어떤 변형(대문자, 첫글자만 대문자 등)이더라도 함수는 `bool` 값이 `True`인 매개변수 `short`를 보게 됩니다. 그렇지 않은 경우 `False`입니다.
## 여러 경로/쿼리 매개변수
## 여러 경로/쿼리 매개변수 { #multiple-path-and-query-parameters }
여러 경로 매개변수와 쿼리 매개변수를 동시에 선언할 수 있으며 **FastAPI**는 어느 것이 무엇인지 알고 있습니다.
@@ -126,9 +118,9 @@ http://127.0.0.1:8000/items/foo?short=yes
매개변수들은 이름으로 감지됩니다:
{* ../../docs_src/query_params/tutorial004.py hl[8,10] *}
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## 필수 쿼리 매개변수
## 필수 쿼리 매개변수 { #required-query-parameters }
경로가 아닌 매개변수에 대한 기본값을 선언할 때(지금은 쿼리 매개변수만 보았습니다), 해당 매개변수는 필수적(Required)이지 않았습니다.
@@ -136,7 +128,7 @@ http://127.0.0.1:8000/items/foo?short=yes
그러나 쿼리 매개변수를 필수로 만들려면 단순히 기본값을 선언하지 않으면 됩니다:
{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
여기 쿼리 매개변수 `needy``str`형인 필수 쿼리 매개변수입니다.
@@ -150,16 +142,17 @@ http://127.0.0.1:8000/items/foo-item
```JSON
{
"detail": [
{
"loc": [
"query",
"needy"
],
"msg": "field required",
"type": "value_error.missing"
}
]
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
@@ -180,7 +173,7 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
그리고 물론, 일부 매개변수는 필수로, 다른 일부는 기본값을, 또 다른 일부는 선택적으로 선언할 수 있습니다:
{* ../../docs_src/query_params/tutorial006.py hl[10] *}
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
위 예시에서는 3가지 쿼리 매개변수가 있습니다:
@@ -188,8 +181,8 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
* `skip`, 기본값이 `0``int`.
* `limit`, 선택적인 `int`.
/// tip | 팁
/// tip
[경로 매개변수](path-params.md#_8){.internal-link target=_blank}와 마찬가지로 `Enum`을 사용할 수 있습니다.
[경로 매개변수](path-params.md#predefined-values){.internal-link target=_blank}와 마찬가지로 `Enum`을 사용할 수 있습니다.
///

View File

@@ -1,4 +1,4 @@
# 파일 요청
# 파일 요청 { #request-files }
`File`을 사용하여 클라이언트가 업로드할 파일들을 정의할 수 있습니다.
@@ -6,23 +6,27 @@
업로드된 파일을 전달받기 위해 먼저 <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>를 설치해야합니다.
예시) `pip install python-multipart`.
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고, 활성화한 다음, 예를 들어 다음과 같이 설치하세요:
```console
$ pip install python-multipart
```
업로드된 파일들은 "폼 데이터"의 형태로 전송되기 때문에 이 작업이 필요합니다.
///
## `File` 임포트
## `File` 임포트 { #import-file }
`fastapi` 에서 `File``UploadFile` 을 임포트 합니다:
{* ../../docs_src/request_files/tutorial001.py hl[1] *}
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
## `File` 매개변수 정의
## `File` 매개변수 정의 { #define-file-parameters }
`Body``Form` 과 동일한 방식으로 파일의 매개변수를 생성합니다:
{* ../../docs_src/request_files/tutorial001.py hl[7] *}
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
/// info | 정보
@@ -40,20 +44,21 @@ File의 본문을 선언할 때, 매개변수가 쿼리 매개변수 또는 본
파일들은 "폼 데이터"의 형태로 업로드 됩니다.
*경로 작동 함수*의 매개변수를 `bytes` 로 선언하는 경우 **FastAPI**는 파일을 읽고 `bytes` 형태의 내용을 전달합니다.
*경로 처리 함수*의 매개변수를 `bytes` 로 선언하는 경우 **FastAPI**는 파일을 읽고 `bytes` 형태의 내용을 전달합니다.
이것은 전체 내용이 메모리에 저장된다는 것을 의미한다는 걸 염두하기 바랍니다. 이는 작은 크기의 파일들에 적합합니다.
어떤 경우에는 `UploadFile` 을 사용하는 것이 더 유리합니다.
## `File` 매개변수와 `UploadFile`
## `UploadFile`을 사용하는 `File` 매개변수 { #file-parameters-with-uploadfile }
`File` 매개변수를 `UploadFile` 타입으로 정의합니다:
{* ../../docs_src/request_files/tutorial001.py hl[12] *}
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
`UploadFile` 을 사용하는 것은 `bytes` 과 비교해 다음과 같은 장점이 있습니다:
* 매개변수의 기본값에서 `File()`을 사용할 필요가 없습니다.
* "스풀 파일"을 사용합니다.
* 최대 크기 제한까지만 메모리에 저장되며, 이를 초과하는 경우 디스크에 저장됩니다.
* 따라서 이미지, 동영상, 큰 이진코드와 같은 대용량 파일들을 많은 메모리를 소모하지 않고 처리하기에 적합합니다.
@@ -61,13 +66,13 @@ File의 본문을 선언할 때, 매개변수가 쿼리 매개변수 또는 본
* <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> `async` 인터페이스를 갖고 있습니다.
* file-like object를 필요로하는 다른 라이브러리에 직접적으로 전달할 수 있는 파이썬 <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> 객체를 반환합니다.
### `UploadFile`
### `UploadFile` { #uploadfile }
`UploadFile` 은 다음과 같은 어트리뷰트가 있습니다:
* `filename` : 문자열(`str`)로 된 업로드된 파일의 파일명입니다 (예: `myimage.jpg`).
* `content_type` : 문자열(`str`)로 된 파일 형식(MIME type / media type)입니다 (예: `image/jpeg`).
* `file` : <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> (<a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">파일류</a> 객체)입니다. 이것은 "파일류" 객체를 필요로하는 다른 라이브러리에 직접적으로 전달할 수 있는 실질적인 파이썬 파일입니다.
* `file` : <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> (a <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> object)입니다. 이것은 "file-like" 객체를 필요로하는 다른 함수나 라이브러리에 직접적으로 전달할 수 있는 실질적인 파이썬 파일 객체입니다.
`UploadFile` 에는 다음의 `async` 메소드들이 있습니다. 이들은 내부적인 `SpooledTemporaryFile` 을 사용하여 해당하는 파일 메소드를 호출합니다.
@@ -80,55 +85,67 @@ File의 본문을 선언할 때, 매개변수가 쿼리 매개변수 또는 본
상기 모든 메소드들이 `async` 메소드이기 때문에 “await”을 사용하여야 합니다.
예를들어, `async` *경로 작동 함수*의 내부에서 다음과 같은 방식으로 내용을 가져올 수 있습니다:
예를들어, `async` *경로 처리 함수*의 내부에서 다음과 같은 방식으로 내용을 가져올 수 있습니다:
```Python
contents = await myfile.read()
```
만약 일반적인 `def` *경로 작동 함수*의 내부라면, 다음과 같이 `UploadFile.file` 에 직접 접근할 수 있습니다:
만약 일반적인 `def` *경로 처리 함수*의 내부라면, 다음과 같이 `UploadFile.file` 에 직접 접근할 수 있습니다:
```Python
contents = myfile.file.read()
```
/// note | "`async` 기술 세부사항"
/// note | `async` 기술 세부사항
`async` 메소드들을 사용할 때 **FastAPI**는 스레드풀에서 파일 메소드들을 실행하고 그들을 기다립니다.
///
/// note | Starlette 기술 세부사항
/// note | Starlette 기술 세부사항
**FastAPI**의 `UploadFile` 은 **Starlette**의 `UploadFile` 을 직접적으로 상속받지만, **Pydantic** 및 FastAPI의 다른 부분들과의 호환성을 위해 필요한 부분들이 추가되었습니다.
///
## "폼 데이터"란
## "폼 데이터"란 { #what-is-form-data }
HTML의 폼들(`<form></form>`)이 서버에 데이터를 전송하는 방식은 대개 데이터에 JSON과는 다른 "특별한" 인코딩을 사용합니다.
**FastAPI**는 JSON 대신 올바른 위치에서 데이터를 읽을 수 있도록 합니다.
/// note | 기술 세부사항
/// note | 기술 세부사항
폼의 데이터는 파일이 포함되지 않은 경우 일반적으로 "미디어 유형" `application/x-www-form-urlencoded` 을 사용해 인코딩 됩니다.
하지만 파일이 포함된 경우, `multipart/form-data`로 인코딩됩니다. `File`을 사용하였다면, **FastAPI**는 본문의 적합한 부분에서 파일을 가져와야 한다는 것을 인지합니다.
인코딩과 폼 필드에 대해 더 알고싶다면, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><code>POST</code>에 관한<abbr title="Mozilla Developer Network">MDN</abbr>웹 문서</a> 를 참고하기 바랍니다,.
인코딩과 폼 필드에 대해 더 알고싶다면, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> web docs for <code>POST</code></a>를 참고하기 바랍니다.
///
/// warning | 경고
다수의 `File``Form` 매개변수를 한 *경로 작동*에 선언하는 것이 가능하지만, 요청의 본문이 `application/json` 가 아닌 `multipart/form-data` 로 인코딩 되기 때문에 JSON으로 받아야하는 `Body` 필드를 함께 선언할 수는 없습니다.
다수의 `File``Form` 매개변수를 한 *경로 처리*에 선언하는 것이 가능하지만, 요청의 본문이 `application/json` 가 아닌 `multipart/form-data` 로 인코딩 되기 때문에 JSON으로 받아야하는 `Body` 필드를 함께 선언할 수는 없습니다.
이는 **FastAPI**의 한계가 아니라, HTTP 프로토콜에 의한 것입니다.
///
## 다중 파일 업로드
## 선택적 파일 업로드 { #optional-file-upload }
표준 타입 애너테이션을 사용하고 기본값을 `None`으로 설정하여 파일을 선택적으로 만들 수 있습니다:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## 추가 메타데이터를 포함한 `UploadFile` { #uploadfile-with-additional-metadata }
추가 메타데이터를 설정하기 위해 예를 들어 `UploadFile`과 함께 `File()`을 사용할 수도 있습니다:
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
## 다중 파일 업로드 { #multiple-file-uploads }
여러 파일을 동시에 업로드 할 수 있습니다.
@@ -136,21 +153,11 @@ HTML의 폼들(`<form></form>`)이 서버에 데이터를 전송하는 방식은
이 기능을 사용하기 위해 , `bytes``List` 또는 `UploadFile` 를 선언하기 바랍니다:
{* ../../docs_src/request_files/tutorial002.py hl[10,15] *}
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
선언한대로, `bytes``list` 또는 `UploadFile` 들을 전송받을 것입니다.
/// note | 참고
2019년 4월 14일부터 Swagger UI가 하나의 폼 필드로 다수의 파일을 업로드하는 것을 지원하지 않습니다. 더 많은 정보를 원하면, <a href="https://github.com/swagger-api/swagger-ui/issues/4276" class="external-link" target="_blank">#4276</a>과 <a href="https://github.com/swagger-api/swagger-ui/issues/3641" class="external-link" target="_blank">#3641</a>을 참고하세요.
그럼에도, **FastAPI**는 표준 Open API를 사용해 이미 호환이 가능합니다.
따라서 Swagger UI 또는 기타 그 외의 OpenAPI를 지원하는 툴이 다중 파일 업로드를 지원하는 경우, 이들은 **FastAPI**와 호환됩니다.
///
/// note | 기술적 세부사항
/// note | 기술 세부사항
`from starlette.responses import HTMLResponse` 역시 사용할 수 있습니다.
@@ -158,6 +165,12 @@ HTML의 폼들(`<form></form>`)이 서버에 데이터를 전송하는 방식은
///
## 요약
### 추가 메타데이터를 포함한 다중 파일 업로드 { #multiple-file-uploads-with-additional-metadata }
폼 데이터로써 입력 매개변수로 업로드할 파일을 선언할 경우 `File` 을 사용하기 바랍니다.
이전과 같은 방식으로 `UploadFile`에 대해서도 `File()`을 사용해 추가 매개변수를 설정할 수 있습니다:
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
## 요약 { #recap }
`File`, `bytes`, `UploadFile`을 사용하여 폼 데이터로 전송되는 요청에서 업로드할 파일을 선언하세요.

View File

@@ -1,12 +1,12 @@
# 폼 모델
# 폼 모델 { #form-models }
FastAPI에서 **Pydantic 모델**을 이용하여 **폼 필드**를 선언할 수 있습니다.
/// info | 정보
(Form)을 사용하려면, 먼저 <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>를 설치하세요.
폼을 사용하려면, 먼저 <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>를 설치하세요.
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, 아래와 같이 설치할 수 있습니다:
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, 예를 들어 아래와 같이 설치하세요:
```console
$ pip install python-multipart
@@ -20,7 +20,7 @@ $ pip install python-multipart
///
## Pydantic 모델을 사용한 폼
## 폼을 위한 Pydantic 모델 { #pydantic-models-for-forms }
**폼 필드**로 받고 싶은 필드를 **Pydantic 모델**로 선언한 다음, 매개변수를 `Form`으로 선언하면 됩니다:
@@ -28,7 +28,7 @@ $ pip install python-multipart
**FastAPI**는 요청에서 받은 **폼 데이터**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기
## 문서 확인하기 { #check-the-docs }
문서 UI `/docs`에서 확인할 수 있습니다:
@@ -36,9 +36,9 @@ $ pip install python-multipart
<img src="/img/tutorial/request-form-models/image01.png">
</div>
## 추가 폼 필드 금지하기
## 추가 폼 필드 금지하기 { #forbid-extra-form-fields }
일부 특별한 사용 사례(흔하지는 않겠지만)에서는 Pydantic 모델에서 정의한 폼 필드 **제한**하길 원할 수도 있습니다. 그리고 **추가** 필드를 **금지**할 수도 있습니다.
일부 특별한 사용 사례(아마도 흔하지는 않겠지만)에서는 Pydantic 모델에서 선언된 폼 필드로만 **제한**하길 원할 수도 있습니다. 그리고 **추가** 필드를 **금지**할 수도 있습니다.
/// note | 참고
@@ -46,7 +46,7 @@ $ pip install python-multipart
///
Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forbid`)할 수 있습니다:
Pydantic의 모델 구성을 사용하여 `extra` 필드를 `forbid`할 수 있습니다:
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
@@ -73,6 +73,6 @@ Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forb
}
```
## 요약
## 요약 { #summary }
Pydantic 모델을 사용하여 FastAPI에서 폼 필드를 선언할 수 있습니다. 😎

View File

@@ -1,37 +1,41 @@
# 폼 및 파일 요청
# 폼 및 파일 요청 { #request-forms-and-files }
`File``Form` 을 사용하여 파일과 폼을 함께 정의할 수 있습니다.
`File``Form` 을 사용하여 파일과 폼 필드를 동시에 정의할 수 있습니다.
/// info | 정보
파일과 폼 데이터를 함께, 또는 각각 업로드하기 위해 먼저 <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>를 설치해야합니다.
업로드된 파일 및/또는 폼 데이터를 받으려면 먼저 <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>를 설치해야 합니다.
예 ) `pip install python-multipart`.
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고, 활성화한 다음 설치해야 합니다. 예:
```console
$ pip install python-multipart
```
///
## `File` 및 `Form` 업로드
## `File` 및 `Form` 임포트 { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001.py hl[1] *}
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
## `File` 및 `Form` 매개변수 정의
## `File` 및 `Form` 매개변수 정의 { #define-file-and-form-parameters }
`Body``Query`와 동일한 방식으로 파일과 폼의 매개변수를 생성합니다:
{* ../../docs_src/request_forms_and_files/tutorial001.py hl[8] *}
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
파일과 폼 필드는 폼 데이터 형식으로 업로드되 파일과 폼 필드로 전달됩니다.
파일과 폼 필드는 폼 데이터로 업로드되며, 파일과 폼 필드를 받게 됩니다.
어떤 파일`bytes`로, 또 어떤 파일`UploadFile`로 선언할 수 있습니다.
또한 일부 파일은 `bytes`로, 일부 파일은 `UploadFile`로 선언할 수 있습니다.
/// warning | 경고
다수의 `File``Form` 매개변수를 한 *경로 작동*에 선언하는 것이 가능하지만, 요청의 본문이 `application/json`가 아닌 `multipart/form-data`로 인코딩 되기 때문에 JSON으로 받아야하는 `Body` 필드를 함께 선언할 수는 없습니다.
다수의 `File``Form` 매개변수를 한 *경로 처리*에 선언하는 것이 가능하지만, 요청의 본문이 `application/json`가 아닌 `multipart/form-data`로 인코딩되기 때문에 JSON으로 받기를 기대하는 `Body` 필드를 함께 선언할 수는 없습니다.
이는 **FastAPI**의 한계가 아니라, HTTP 프로토콜에 의한 것입니다.
이는 **FastAPI**의 한계가 아니라, HTTP 프로토콜의 일부입니다.
///
## 요약
## 요약 { #recap }
하나의 요청으로 데이터와 파일들을 받아야 할 경우 `File``Form`을 함께 사용하기 바랍니다.

View File

@@ -1,4 +1,4 @@
# 폼 데이터
# 폼 데이터 { #form-data }
JSON 대신 폼 필드를 받아야 하는 경우 `Form`을 사용할 수 있습니다.
@@ -14,13 +14,13 @@ $ pip install python-multipart
///
## `Form` 임포트하기
## `Form` 임포트하기 { #import-form }
`fastapi`에서 `Form`을 임포트합니다:
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
## `Form` 매개변수 정의하기
## `Form` 매개변수 정의하기 { #define-form-parameters }
`Body` 또는 `Query`와 동일한 방식으로 폼 매개변수를 만듭니다:
@@ -28,7 +28,7 @@ $ pip install python-multipart
예를 들어, OAuth2 사양을 사용할 수 있는 방법 중 하나("패스워드 플로우"라고 함)로 `username``password`를 폼 필드로 보내야 합니다.
<abbr title="specification">사양</abbr>에서는 필드 이름이 `username``password`로 정확하게 명명되어야 하고, JSON이 아닌 폼 필드로 전송해야 합니다.
<abbr title="specification">spec</abbr>에서는 필드 이름이 `username``password`로 정확하게 명명되어야 하고, JSON이 아닌 폼 필드로 전송해야 합니다.
`Form`을 사용하면 유효성 검사, 예제, 별칭(예: `username` 대신 `user-name`) 등을 포함하여 `Body`(및 `Query`, `Path`, `Cookie`)와 동일한 구성을 선언할 수 있습니다.
@@ -44,7 +44,7 @@ $ pip install python-multipart
///
## "폼 필드"에 대해
## "폼 필드"에 대해 { #about-form-fields }
HTML 폼(`<form></form>`)이 데이터를 서버로 보내는 방식은 일반적으로 해당 데이터에 대해 "특수" 인코딩을 사용하며, 이는 JSON과 다릅니다.
@@ -56,19 +56,18 @@ HTML 폼(`<form></form>`)이 데이터를 서버로 보내는 방식은 일반
그러나 폼에 파일이 포함된 경우, `multipart/form-data`로 인코딩합니다. 다음 장에서 파일 처리에 대해 읽을 겁니다.
이러한 인코딩 및 폼 필드에 대해 더 읽고 싶다면, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><code>POST</code>에 대한 <abbr title="Mozilla Developer Network">MDN</a> 웹 문서를 참조하세요.
이러한 인코딩 및 폼 필드에 대해 더 읽고 싶다면, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><code>POST</code>에 대한 <abbr title="Mozilla Developer Network">MDN</abbr> 웹 문서를 참조하세요.
///
/// warning | 경고
*경로 작업*에서 여러 `Form` 매개변수를 선언할 수 있지만, JSON으로 수신할 것으로 예상되는 `Body` 필드와 함께 선언할 수 없습니다. 요청 본문은 `application/json` 대신에 `application/x-www-form-urlencoded`를 사용하여 인코딩되기 때문입니다.
*경로 처리*에서 여러 `Form` 매개변수를 선언할 수 있지만, JSON으로 수신할 것으로 예상되는 `Body` 필드와 함께 선언할 수 없습니다. 요청 본문은 `application/json` 대신에 `application/x-www-form-urlencoded`를 사용하여 인코딩되기 때문입니다.
이는 **FastAPI**의 제한 사항이 아니며 HTTP 프로토콜의 일부입니다.
///
## 요약
## 요약 { #recap }
폼 데이터 입력 매개변수를 선언하려면 `Form`을 사용하세요.

View File

@@ -1,81 +1,173 @@
# 응답 모델
# 응답 모델 - 반환 타입 { #response-model-return-type }
어떤 *경로 작동*이든 매개변수 `response_model`를 사용하여 응답을 위한 모델을 선언할 수 있습니다:
*경로 처리 함수*의 **반환 타입**을 어노테이션하여 응답에 사용될 타입을 선언할 수 있습니다.
함수 **매개변수**에서 입력 데이터를 위해 사용하는 것과 동일하게 **타입 어노테이션**을 사용할 수 있으며, Pydantic 모델, 리스트, 딕셔너리, 정수/불리언 같은 스칼라 값 등을 사용할 수 있습니다.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI는 이 반환 타입을 사용하여:
* 반환된 데이터를 **검증**합니다.
* 데이터가 유효하지 않다면(예: 필드가 누락된 경우), 이는 *여러분의* 앱 코드가 깨져서 의도한 값을 반환하지 못한다는 의미이며, 잘못된 데이터를 반환하는 대신 서버 오류를 반환합니다. 이렇게 하면 여러분과 클라이언트는 기대한 데이터와 데이터 형태를 받는다는 것을 확실히 할 수 있습니다.
* OpenAPI *경로 처리*의 응답에 **JSON Schema**를 추가합니다.
* 이는 **자동 문서**에서 사용됩니다.
* 또한 자동 클라이언트 코드 생성 도구에서도 사용됩니다.
하지만 가장 중요한 것은:
* 반환 타입에 정의된 내용으로 출력 데이터를 **제한하고 필터링**합니다.
* 이는 특히 **보안**에 매우 중요합니다. 아래에서 더 자세히 살펴보겠습니다.
## `response_model` 매개변수 { #response-model-parameter }
타입 선언이 말하는 것과 정확히 일치하지 않는 데이터를 반환해야 하거나 반환하고 싶은 경우가 있습니다.
예를 들어, **딕셔너리**나 데이터베이스 객체를 **반환**하고 싶지만, **Pydantic 모델로 선언**하고 싶을 수 있습니다. 이렇게 하면 Pydantic 모델이 반환한 객체(예: 딕셔너리나 데이터베이스 객체)에 대해 데이터 문서화, 검증 등 모든 작업을 수행합니다.
반환 타입 어노테이션을 추가했다면, 도구와 에디터가 함수가 선언한 타입(예: Pydantic 모델)과 다른 타입(예: dict)을 반환하고 있다는 (올바른) 오류로 불평할 것입니다.
그런 경우에는 반환 타입 대신 *경로 처리 데코레이터*의 매개변수 `response_model`을 사용할 수 있습니다.
`response_model` 매개변수는 모든 *경로 처리*에서 사용할 수 있습니다:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* 기타.
* .
{* ../../docs_src/response_model/tutorial001.py hl[17] *}
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | 참고
`response_model`은 "데코레이터" 메드(`get`, `post`, 등)의 매개변수입니다. 모든 매개변수들과 본문(body)처럼 *경로 작동 함수*가 아닙니다.
`response_model`은 "데코레이터" 메드(`get`, `post` 등)의 매개변수입니다. 모든 매개변수body처럼, *경로 처리 함수*의 매개변수가 아닙니다.
///
Pydantic 모델 어트리뷰트를 선언 것과 동일한 타입을 수신하므로 Pydantic 모델이 될 수 있지만, `List[Item]`과 같이 Pydantic 모델`list` 수도 있습니다.
`response_model`Pydantic 모델 필드에 선언하는 것과 동일한 타입을 받습니다. 따라서 Pydantic 모델이 될 수, `List[Item]`처럼 Pydantic 모델의 `list`가 될 수도 있습니다.
FastAPI는 이 `response_model` 사용하여:
FastAPI는 이 `response_model` 사용해 데이터 문서화, 검증 등을 수행하고, 또한 출력 데이터를 타입 선언에 맞게 **변환하고 필터링**합니다.
* 출력 데이터를 타입 선언으로 변환.
* 데이터 검증.
* OpenAPI *경로 작동*의 응답에 JSON 스키마 추가.
* 자동 생성 문서 시스템에 사용.
/// tip | 팁
하지만 가장 중요한 것은:
에디터, mypy 등에서 엄격한 타입 체크를 사용하고 있다면, 함수 반환 타입을 `Any`로 선언할 수 있습니다.
* 해당 모델의 출력 데이터 제한. 이것이 얼마나 중요한지 아래에서 볼 것입니다.
/// note | 기술 세부사항
응답 모델은 함수의 타입 어노테이션 대신 이 매개변수로 선언하는데, 경로 함수가 실제 응답 모델을 반환하지 않고 `dict`, 데이터베이스 객체나 기타 다른 모델을 `response_model`을 사용하여 필드 제한과 직렬화를 수행하고 반환할 수 있기 때문입니다
이렇게 하면 에디터에 의도적으로 어떤 값이든 반환한다고 알려줍니다. 하지만 FastAPI는 여전히 `response_model`을 사용하여 데이터 문서화, 검증, 필터링 등을 수행합니다.
///
## 동일한 입력 데이터 반환
### `response_model` 우선순위 { #response-model-priority }
여기서 우리는 평문 비밀번호를 포함하는 `UserIn` 모델을 선언합니다:
반환 타입과 `response_model`을 둘 다 선언하면, `response_model`이 우선순위를 가지며 FastAPI에서 사용됩니다.
{* ../../docs_src/response_model/tutorial002.py hl[9,11] *}
이렇게 하면 응답 모델과 다른 타입을 반환하는 경우에도 에디터와 mypy 같은 도구에서 사용할 올바른 타입 어노테이션을 함수에 추가할 수 있습니다. 그리고 동시에 FastAPI가 `response_model`을 사용하여 데이터 검증, 문서화 등을 수행하게 할 수도 있습니다.
또한 `response_model=None`을 사용해 해당 *경로 처리*에 대한 응답 모델 생성을 비활성화할 수도 있습니다. 이는 유효한 Pydantic 필드가 아닌 것들에 대해 타입 어노테이션을 추가하는 경우에 필요할 수 있으며, 아래 섹션 중 하나에서 예시를 볼 수 있습니다.
## 동일한 입력 데이터 반환 { #return-the-same-input-data }
여기서는 평문 비밀번호를 포함하는 `UserIn` 모델을 선언합니다:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | 정보
`EmailStr`을 사용하려면 먼저 <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>를 설치하세요.
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고, 활성화한 다음 설치해야 합니다. 예를 들어:
```console
$ pip install email-validator
```
또는 다음과 같이:
```console
$ pip install "pydantic[email]"
```
///
그리고 이 모델을 사용하여 입력을 선언하고 같은 모델로 출력을 선언합니다:
{* ../../docs_src/response_model/tutorial002.py hl[17:18] *}
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
이제 브라우저가 비밀번호로 사용자를 만들 때마다 API는 응답으로 동일한 비밀번호를 반환합니다.
이 경우, 사용자가 스스로 비밀번호를 발신했기 때문에 문제가 되지 않을 수 있습니다.
이 경우, 동일한 사용자가 비밀번호를 보내는 것이므로 문제가 되지 않을 수 있습니다.
그러나 동일한 모델을 다른 *경로 작동*에서 사용할 경우, 모든 클라이언트에게 사용자의 비밀번호를 발신할 수 있습니다.
하지만 동일한 모델을 다른 *경로 처리*에서 사용하면, 모든 클라이언트에게 사용자의 비밀번호를 보내게 될 있습니다.
/// danger | 위험
절대로 사용자의 평문 비밀번호를 저장하거나 응답으로 발신하지 마십시오.
모든 주의사항을 알고 있으며 무엇을 하는지 정확히 알고 있지 않다면, 이런 방식으로 사용자의 평문 비밀번호를 저장하거나 응답으로 보내지 마세요.
///
## 출력 모델 추가
## 출력 모델 추가 { #add-an-output-model }
대신 평문 비밀번호로 입력 모델을 만들고 해당 비밀번호 없 출력 모델을 만들 수 있습니다:
대신 평문 비밀번호를 포함하는 입력 모델과, 비밀번호 출력 모델을 만들 수 있습니다:
{* ../../docs_src/response_model/tutorial003.py hl[9,11,16] *}
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
여기서 *경로 작동 함수*가 비밀번호를 포함하는 동일한 입력 사용자를 반환할지라도:
여기서 *경로 처리 함수*가 비밀번호를 포함하는 동일한 입력 사용자를 반환하더라도:
{* ../../docs_src/response_model/tutorial003.py hl[24] *}
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...`response_model``UserOut` 모델로 선언했기 때문에 비밀번호를 포함하지 않습니다:
...비밀번호를 포함하지 않`UserOut` 모델로 `response_model`을 선언했습니다:
{* ../../docs_src/response_model/tutorial003.py hl[22] *}
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
따라서 **FastAPI**는 출력 모델에 선언지 않은 모든 데이터를 (Pydantic을 사용하여) 필터링합니다.
따라서 **FastAPI**는 출력 모델에 선언지 않은 모든 데이터를 (Pydantic을 사용하여) 필터링합니다.
## 문서에서 보기
### `response_model` 또는 반환 타입 { #response-model-or-return-type }
자동 생성 문서를 보면 입력 모델과 출력 모델이 각자의 JSON 스키마를 가지고 있음을 확인할 수 있습니다:
이 경우 두 모델이 서로 다르기 때문에, 함수 반환 타입을 `UserOut`으로 어노테이션하면 에디터와 도구는 서로 다른 클래스인데 잘못된 타입을 반환하고 있다고 불평할 것입니다.
그래서 이 예제에서는 `response_model` 매개변수로 선언해야 합니다.
...하지만 아래를 계속 읽으면 이를 극복하는 방법을 볼 수 있습니다.
## 반환 타입과 데이터 필터링 { #return-type-and-data-filtering }
이전 예제에서 계속해 봅시다. 함수에 **하나의 타입으로 어노테이션**을 하고 싶지만, 함수에서 실제로는 **더 많은 데이터**를 포함하는 것을 반환할 수 있길 원했습니다.
FastAPI가 응답 모델을 사용해 데이터를 계속 **필터링**하길 원합니다. 그래서 함수가 더 많은 데이터를 반환하더라도, 응답에는 응답 모델에 선언된 필드만 포함되게 합니다.
이전 예제에서는 클래스가 달랐기 때문에 `response_model` 매개변수를 써야 했습니다. 하지만 이는 에디터와 도구가 함수 반환 타입을 체크해 주는 지원을 받지 못한다는 뜻이기도 합니다.
하지만 대부분 이런 작업이 필요한 경우에는, 이 예제처럼 모델로 일부 데이터를 **필터링/제거**하길 원하는 경우가 많습니다.
그리고 그런 경우에는 클래스와 상속을 사용하여 함수 **타입 어노테이션**을 활용해 에디터/도구에서 더 나은 지원을 받으면서도 FastAPI의 **데이터 필터링**을 유지할 수 있습니다.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
이를 통해 이 코드는 타입 관점에서 올바르므로 에디터와 mypy 등의 도구 지원을 받을 수 있고, 동시에 FastAPI의 데이터 필터링도 받을 수 있습니다.
이게 어떻게 동작할까요? 확인해 봅시다. 🤓
### 타입 어노테이션과 도구 지원 { #type-annotations-and-tooling }
먼저 에디터, mypy 및 기타 도구가 이를 어떻게 보는지 살펴봅시다.
`BaseUser`는 기본 필드를 가집니다. 그리고 `UserIn``BaseUser`를 상속하고 `password` 필드를 추가하므로, 두 모델의 모든 필드를 포함하게 됩니다.
함수 반환 타입을 `BaseUser`로 어노테이션하지만, 실제로는 `UserIn` 인스턴스를 반환합니다.
에디터, mypy 및 기타 도구는 이에 대해 불평하지 않습니다. 타이핑 관점에서 `UserIn``BaseUser`의 서브클래스이므로, `BaseUser`인 어떤 것이 기대되는 곳에서는 *유효한* 타입이기 때문입니다.
### FastAPI 데이터 필터링 { #fastapi-data-filtering }
이제 FastAPI는 반환 타입을 보고, 여러분이 반환하는 값이 해당 타입에 선언된 필드 **만** 포함하도록 보장합니다.
FastAPI는 Pydantic을 내부적으로 여러 방식으로 사용하여, 클래스 상속의 동일한 규칙이 반환 데이터 필터링에는 적용되지 않도록 합니다. 그렇지 않으면 기대한 것보다 훨씬 더 많은 데이터를 반환하게 될 수도 있습니다.
이렇게 하면 **도구 지원**이 있는 타입 어노테이션과 **데이터 필터링**이라는 두 가지 장점을 모두 얻을 수 있습니다.
## 문서에서 보기 { #see-it-in-the-docs }
자동 생성 문서를 보면 입력 모델과 출력 모델이 각자의 JSON Schema를 가지고 있음을 확인할 수 있습니다:
<img src="/img/tutorial/response-model/image01.png">
@@ -83,29 +175,73 @@ FastAPI는 이 `response_model`를 사용하여:
<img src="/img/tutorial/response-model/image02.png">
## 응답 모델 인코딩 매개변수
## 기타 반환 타입 어노테이션 { #other-return-type-annotations }
유효한 Pydantic 필드가 아닌 것을 반환하면서도, 도구(에디터, mypy 등)가 제공하는 지원을 받기 위해 함수에 어노테이션을 달아두는 경우가 있을 수 있습니다.
### 응답을 직접 반환하기 { #return-a-response-directly }
가장 흔한 경우는 [고급 문서에서 나중에 설명하는 대로 Response를 직접 반환하는 것](../advanced/response-directly.md){.internal-link target=_blank}입니다.
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
이 간단한 경우는 반환 타입 어노테이션이 `Response` 클래스(또는 그 서브클래스)이기 때문에 FastAPI에서 자동으로 처리됩니다.
그리고 `RedirectResponse``JSONResponse`는 모두 `Response`의 서브클래스이므로, 타입 어노테이션이 올바르기 때문에 도구들도 만족합니다.
### Response 서브클래스 어노테이션 { #annotate-a-response-subclass }
타입 어노테이션에 `Response`의 서브클래스를 사용할 수도 있습니다:
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
이는 `RedirectResponse``Response`의 서브클래스이기 때문에 동작하며, FastAPI가 이 간단한 경우를 자동으로 처리합니다.
### 유효하지 않은 반환 타입 어노테이션 { #invalid-return-type-annotations }
하지만 유효한 Pydantic 타입이 아닌 다른 임의의 객체(예: 데이터베이스 객체)를 반환하고, 함수에서 그렇게 어노테이션하면, FastAPI는 그 타입 어노테이션으로부터 Pydantic 응답 모델을 만들려고 시도하다가 실패합니다.
또한, 유효한 Pydantic 타입이 아닌 타입이 하나 이상 포함된 여러 타입 간의 <abbr title='여러 타입 간 union은 "이 타입들 중 아무거나"를 의미합니다.'>union</abbr>이 있는 경우에도 동일합니다. 예를 들어, 아래는 실패합니다 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...이는 타입 어노테이션이 Pydantic 타입이 아니고, 단일 `Response` 클래스/서브클래스도 아니며, `Response``dict` 간 union(둘 중 아무거나)이기 때문에 실패합니다.
### 응답 모델 비활성화 { #disable-response-model }
위 예제에서 이어서, FastAPI가 수행하는 기본 데이터 검증, 문서화, 필터링 등을 원하지 않을 수 있습니다.
하지만 에디터나 타입 체커(예: mypy) 같은 도구 지원을 받기 위해 함수에 반환 타입 어노테이션은 유지하고 싶을 수도 있습니다.
이 경우 `response_model=None`으로 설정하여 응답 모델 생성을 비활성화할 수 있습니다:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
그러면 FastAPI는 응답 모델 생성을 건너뛰며, FastAPI 애플리케이션에 영향을 주지 않고 필요한 반환 타입 어노테이션을 어떤 것이든 사용할 수 있습니다. 🤓
## 응답 모델 인코딩 매개변수 { #response-model-encoding-parameters }
응답 모델은 아래와 같이 기본값을 가질 수 있습니다:
{* ../../docs_src/response_model/tutorial004.py hl[11,13:14] *}
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Optional[str] = None`은 기본값으로 `None`을 갖습니다.
* `description: Union[str, None] = None` (또는 Python 3.10에서 `str | None = None`)은 기본값으로 `None`을 갖습니다.
* `tax: float = 10.5`는 기본값으로 `10.5`를 갖습니다.
* `tags: List[str] = []` 빈 리스트의 기본값으로: `[]`.
* `tags: List[str] = []`는 기본값으로 빈 리스트 `[]`를 갖습니다.
그러나 실제로 저장되지 않았을 경우 결과에서 값을 생략하고 싶을 수 있습니다.
하지만 실제로 저장되지 않았을 경우 결과에서 이를 생략하고 싶을 수 있습니다.
예를 들어, NoSQL 데이터베이스에 많은 선택적 속성이 있는 모델이 있지만, 기본값으로 가득 찬 매우 긴 JSON 응답을 보내고 싶지 않습니다.
### `response_model_exclude_unset` 매개변수 사용
### `response_model_exclude_unset` 매개변수 사용 { #use-the-response-model-exclude-unset-parameter }
*경로 작동 데코레이터* 매개변수 `response_model_exclude_unset=True`로 설정 할 수 있습니다:
*경로 처리 데코레이터* 매개변수 `response_model_exclude_unset=True`로 설정할 수 있습니다:
{* ../../docs_src/response_model/tutorial004.py hl[24] *}
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
이러한 기본값은 응답에 포함되지 않고 실제로 설정된 값만 포함됩니다.
그러면 이러한 기본값은 응답에 포함되지 않고, 실제로 설정된 값만 포함됩니다.
따라서 해당 *경로 작동*에 ID가 `foo`인 항목(items)을 요청으로 보내면 (기본값을 제외한) 응답은 다음과 같습니다:
따라서 ID가 `foo`인 항목에 대해 해당 *경로 처리*로 요청 보내면, (기본값을 제외한) 응답은 다음과 같습니다:
```JSON
{
@@ -116,24 +252,18 @@ FastAPI는 이 `response_model`를 사용하여:
/// info | 정보
FastAPI는 이를 위해 Pydantic 모델의 `.dict()`의 <a href="https://docs.pydantic.dev/latest/concepts/serialization/#modeldict" class="external-link" target="_blank"> `exclude_unset` 매개변수</a>를 사용합니다.
///
/// info | 정보
아래 또한 사용할 수 있습니다:
다음도 사용할 수 있습니다:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
<a href="https://docs.pydantic.dev/latest/concepts/serialization/#modeldict" class="external-link" target="_blank">Pydantic 문서</a>에`exclude_defaults``exclude_none`에 대해 설명 대로 사용할 수 있습니다.
`exclude_defaults``exclude_none`에 대해 <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">Pydantic 문서</a>에 설명 대로 사용할 수 있습니다.
///
#### 기본값이 있는 필드를 갖는 값의 데이터
#### 기본값이 있는 필드에 값이 있는 데이터 { #data-with-values-for-fields-with-defaults }
하지만 모델의 필드가 기본값이 있어도 ID가 `bar`인 항목(items)처럼 데이터가 값을 갖는다면:
하지만 ID가 `bar`인 항목처럼, 기본값이 있는 모델의 필드에 값이 있다면:
```Python hl_lines="3 5"
{
@@ -144,12 +274,11 @@ FastAPI는 이를 위해 Pydantic 모델의 `.dict()`의 <a href="https://docs.p
}
```
응답에 해당 값들이 포함됩니다.
응답에 포함됩니다.
#### 기본값과 동일한 값을 갖는 데이터
#### 기본값과 동일한 값을 갖는 데이터 { #data-with-the-same-values-as-the-defaults }
If the data has the same values as the default ones, like the item with ID `baz`:
ID가 `baz`인 항목(items)처럼 기본값과 동일한 값을 갖는다면:
데이터가 ID `baz`인 항목처럼 기본값과 동일한 값을 갖는다면:
```Python hl_lines="3 5-6"
{
@@ -161,37 +290,37 @@ ID가 `baz`인 항목(items)처럼 기본값과 동일한 값을 갖는다면:
}
```
`description`, `tax` 그리고 `tags`가 기본값과 더라도 (기본값에서 가져오는 대신) 값들이 명시적으로 설정되었다는 것을 인지할 정도로 FastAPI는 충분히 똑똑합니다(사실, Pydantic이 충분히 똑똑합니다).
FastAPI는 충분히 똑똑해서(사실, Pydantic이 충분히 똑똑합니다) `description`, `tax`, `tags`가 기본값과 동일하더라도, 기본값에서 가져온 것이 아니라 명시적으로 설정되었다는 것을 알아냅니다.
따라서 JSON 스키마에 포함됩니다.
그래서 JSON 응답에 포함됩니다.
/// tip | 팁
`None` 뿐만 아니라 다른 어떤 것도 기본값이 될 수 있습니다.
기본값은 `None`뿐만 아니라 어떤 것이 될 수 있습니다.
리스트(`[]`), `float`인 `10.5` 등이 될 수 있습니다.
///
### `response_model_include` 및 `response_model_exclude`
### `response_model_include` 및 `response_model_exclude` { #response-model-include-and-response-model-exclude }
*경로 작동 데코레이터* 매개변수 `response_model_include` 및 `response_model_exclude`를 사용할 수 있습니다.
*경로 처리 데코레이터* 매개변수 `response_model_include` 및 `response_model_exclude`를 사용할 수 있습니다.
이들은 포함(나머지 생략)하거나 제외(나머지 포함) 할 어트리뷰트 이름 `str`의 `set`을 받습니다.
이들은 포함(나머지 생략)하거나 제외(나머지 포함)할 어트리뷰트 이름을 담은 `str`의 `set`을 받습니다.
Pydantic 모델이 하나만 있고 출력에서 일부 데이터를 제거하려는 경우 빠른 지름길로 사용할 수 있습니다.
Pydantic 모델이 하나만 있고 출력에서 일부 데이터를 제거하려는 경우, 빠른 지름길로 사용할 수 있습니다.
/// tip | 팁
하지만 이러한 매개변수 대신 여러 클래스를 사용하여 위 아이디어를 사용하는 것을 추천합니다.
하지만 이러한 매개변수 대신, 위에서 설명한 것처럼 여러 클래스를 사용하는 것을 여전히 권장합니다.
이는 일부 어트리뷰트를 생략하기 위해 `response_model_include` 또는 `response_model_exclude`를 사용하더라도 앱의 OpenAPI(및 문서) 생성 JSON 스키마가 여전히 전체 모델에 대한 스키마이기 때문입니다.
이는 일부 어트리뷰트를 생략하기 위해 `response_model_include` 또는 `response_model_exclude`를 사용하더라도, 앱의 OpenAPI(및 문서) 생성되는 JSON Schema가 여전히 전체 모델에 대한 스키마이기 때문입니다.
비슷하게 동하는 `response_model_by_alias` 역시 마찬가지로 적용됩니다.
비슷하게 동하는 `response_model_by_alias`에도 동일하게 적용됩니다.
///
{* ../../docs_src/response_model/tutorial005.py hl[31,37] *}
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | 팁
@@ -201,14 +330,14 @@ Pydantic 모델이 하나만 있고 출력에서 ​​일부 데이터를 제
///
#### `set` 대신 `list` 사용하기
#### `set` 대신 `list` 사용하기 { #using-lists-instead-of-sets }
`list` 또는 `tuple` 대신 `set`을 사용하는 법을 잊었더라도, FastAPI는 `set`으로 변환하고 정상적으로 작동합니다:
`set`을 쓰는 것을 잊고 `list`나 `tuple`을 대신 사용하더라도, FastAPI는 이를 `set`으로 변환하므로 올바르게 동작합니다:
{* ../../docs_src/response_model/tutorial006.py hl[31,37] *}
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## 요약
## 요약 { #recap }
응답 모델을 정의하고 개인정보가 필터되는 것을 보장하기 위해 *경로 작동 데코레이터*의 매개변수 `response_model`을 사용하세요.
응답 모델을 정의하고 특히 개인정보가 필터링되도록 보장하려면 *경로 처리 데코레이터*의 매개변수 `response_model`을 사용하세요.
명시적으로 설정된 값만 반환하려면 `response_model_exclude_unset`을 사용하세요.

View File

@@ -1,18 +1,18 @@
# 응답 상태 코드
# 응답 상태 코드 { #response-status-code }
응답 모델과 같은 방법으로, 어떤 *경로 작동*이`status_code` 매개변수를 사용하여 응답에 대한 HTTP 상태 코드를 선언할 수 있습니다.
응답 모델을 지정하는 것과 같은 방법으로, 어떤 *경로 처리*에서`status_code` 매개변수를 사용하여 응답에 사용할 HTTP 상태 코드를 선언할 수 있습니다:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* 기타
*
{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
/// note | 참고
`status_code` 는 "데코레이터" 메소드(`get`, `post` 등)의 매개변수입니다. 모든 매개변수들과 본문처럼 *경로 작동 함수*가 아닙니다.
`status_code` 는 "데코레이터" 메소드(`get`, `post` 등)의 매개변수입니다. 모든 매개변수들과 본문처럼 *경로 처리 함수*가 아닙니다.
///
@@ -20,75 +20,75 @@
/// info | 정보
`status_code` 는 파이썬의 `http.HTTPStatus` 와 같은 `IntEnum` 을 입력받을 수도 있습니다.
`status_code` 는 파이썬의 <a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a> 와 같은 `IntEnum` 을 입력받을 수도 있습니다.
///
`status_code` 매개변수는:
* 응답에서 해당 상태 코드를 반환합니다.
* 상태 코드를 OpenAPI 스키마( 사용자 인터페이스)에 문서화 합니다.
* 상태 코드를 OpenAPI 스키마(따라서, 사용자 인터페이스에도)에 문서화합니다:
<img src="https://fastapi.tiangolo.com/img/tutorial/response-status-code/image01.png">
<img src="/img/tutorial/response-status-code/image01.png">
/// note | 참고
어떤 응답 코드들은 해당 응답에 본문이 없다는 것을 의미하기도 합니다 (다음 항목 참고).
일부 응답 코드(다음 섹션 참고)는 응답에 본문이 없다는 것을 나타냅니다.
이에 따라 FastAPI는 응답 본문이 없음을 명시하는 OpenAPI를 생성합니다.
FastAPI는 이를 알고 있으며, 응답 본문이 없다고 명시하는 OpenAPI 문서를 생성합니다.
///
## HTTP 상태 코드에 대하여
## HTTP 상태 코드에 대하여 { #about-http-status-codes }
/// note | 참고
만약 HTTP 상태 코드에 대하여 이미 알고있다면, 다음 항목으로 넘어가십시오.
만약 HTTP 상태 코드가 무엇인지 이미 알고 있다면, 다음 섹션으로 넘어가세요.
///
HTTP는 세자리 숫자 상태 코드를 응답의 일부로 전송합니다.
HTTP에서는 응답의 일부로 3자리 숫자 상태 코드를 보냅니다.
이 상태 코드들은 각자를 식별할 수 있도록 지정된 이름이 있으나, 중요한 은 숫자 코드입니다.
이 상태 코드들은 를 식별할 수 있도록 이름이 연결되어 있지만, 중요한 부분은 숫자입니다.
요약하자면:
* `1xx` 상태 코드는 "정보"용입니다. 이들은 직접적으로는 잘 사용되지는 않습니다. 이 상태 코드를 갖는 응답은 본문을 가질 수 없습니다.
* **`2xx`** 상태 코드는 "성공적인" 응답을 위해 사용됩니다. 가장 많이 사용되는 유형입니다.
* `200`디폴트 상태 코드로, 모든 것이 "성공적임"을 의미합니다.
* 다른 예로는 `201` "생성됨"이 있습니다. 일반적으로 데이터베이스에 새로운 레코드를 생성한 후 사용합니다.
* 단, `204` "내용 없음"은 특별한 경우입니다. 이은 클라이언트에게 반환할 내용이 없는 경우 사용합니다. 따라서 응답은 본문을 가질 수 없습니다.
* **`3xx`** 상태 코드는 "리다이렉션"용입니다. 본문을 가질 수 없는 `304` "수정되지 않음"을 제외하고, 이 상태 코드를 갖는 응답에는 본문이 있을 수도, 없을 수도 있니다.
* **`4xx`** 상태 코드는 "클라이언트 오류" 응답을 위해 사용됩니다. 이것은 아마 가장 많이 사용하게 될 두번째 유형입니다.
* 일례로 `404` 는 "찾을 수 없음" 응답을 위해 사용합니다.
* 일반적인 클라이언트 오류의 경우 `400` 을 사용할 수 있습니다.
* `5xx` 상태 코드는 서버 오류에 사용됩니다. 이것들을 직접 사용할 일은 거의 없습니다. 응용 프로그램 코드나 서버의 일부에서 문제가 발생하면 자동으로 이들 상태 코드 중 하나를 반환합니다.
* `100 - 199` 는 "정보"용입니다. 직접 사용할 일은 거의 없습니다. 이 상태 코드를 갖는 응답은 본문을 가질 수 없습니다.
* **`200 - 299`** 는 "성공적인" 응답을 위한 것입니다. 가장 많이 사용하게 될 유형입니다.
* `200`기본 상태 코드로, 모든 것이 "OK"임을 의미합니다.
* 다른 예로는 `201` "생성됨"이 있습니다. 일반적으로 데이터베이스에 새 레코드를 생성한 후 사용합니다.
* 특별한 경우로 `204` "내용 없음"이 있습니다. 이 응답은 클라이언트에게 반환할 내용이 없을 때 사용되며, 따라서 응답은 본문을 가지면 안 됩니다.
* **`300 - 399`** 는 "리다이렉션"용입니다. 이 상태 코드를 갖는 응답 본문이 있을 수도 없을 수도 있으며, 본문이 없어야 하는 `304` "수정되지 않음"을 제외합니다.
* **`400 - 499`** 는 "클라이언트 오류" 응답을 위한 것입니다. 아마 두 번째로 가장 많이 사용하게 될 유형입니다.
* 예를 들어 `404` 는 "찾을 수 없음" 응답을 위해 사용합니다.
* 클라이언트의 일반적인 오류에는 `400` 그냥 사용할 수 있습니다.
* `500 - 599` 는 서버 오류에 사용됩니다. 직접 사용할 일은 거의 없습니다. 애플리케이션 코드의 일부나 서버에서 문제가 발생하면 자동으로 이들 상태 코드 중 하나를 반환합니다.
/// tip | 팁
각의 상태 코드와 이들이 의미하는 내용에 대해 더 알고싶다면 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> HTTP 상태 코드에 관한 문서</a> 를 확인하십시오.
각 상태 코드와 어떤 코드가 어떤 용도인지 더 알고 싶다면 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> HTTP 상태 코드에 관한 문서</a>를 확인하세요.
///
## 이름을 기억하는 쉬운 방법
## 이름을 기억하는 쉬운 방법 { #shortcut-to-remember-the-names }
상기 예시 참고:
이전 예시를 다시 확인해보겠습니다:
{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
`201` 은 "생성됨"를 의미하는 상태 코드입니다.
`201` 은 "생성됨"을 위한 상태 코드입니다.
하지만 모든 상태 코드들이 무엇을 의미하는지 외울 필요는 없습니다.
하지만 각각의 코드 무엇을 의미하는지 외울 필요는 없습니다.
`fastapi.status` 의 편의 변수를 사용할 수 있습니다.
{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
이것은 단순히 작업을 편리하게 하기 위한 것으로, HTTP 상태 코드와 동일한 번호를 갖고있지만, 이를 사용하면 편집기의 자동완성 기능을 사용할 수 있습니다:
이것은 단지 편의를 위한 것으로, 동일한 숫자를 갖고 있지만, 이를 통해 편집기의 자동완성 기능으로 찾을 수 있습니다:
<img src="https://fastapi.tiangolo.com/img/tutorial/response-status-code/image02.png">
<img src="/img/tutorial/response-status-code/image02.png">
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette import status` 역시 사용할 수 있습니다.
@@ -96,6 +96,6 @@ HTTP는 세자리의 숫자 상태 코드를 응답의 일부로 전송합니다
///
## 기본값 변경
## 기본값 변경 { #changing-the-default }
추후 여기서 선언하는 기본 상태 코드가 아닌 다른 상태 코드를 반환하는 방법을 [숙련된 사용자 지침서](https://fastapi.tiangolo.com/ko/advanced/response-change-status-code/){.internal-link target=_blank}에서 확인할 수 있습니다.
나중에 [고급 사용자 지침서](../advanced/response-change-status-code.md){.internal-link target=_blank}에서, 여기서 선언하는 기본값과 다른 상태 코드를 반환하는 방법을 확인할 수 있습니다.

View File

@@ -1,43 +1,21 @@
# 요청 예제 데이터 선언
# 요청 예제 데이터 선언 { #declare-request-example-data }
여러분의 앱이 받을 수 있는 데이터 예제를 선언할 수 있습니다.
여기 이를 위한 몇가지 방식이 있습니다.
## Pydantic 모델 속 추가 JSON 스키마 데이터
## Pydantic 모델 속 추가 JSON 스키마 데이터 { #extra-json-schema-data-in-pydantic-models }
생성된 JSON 스키마에 추가될 Pydantic 모델을 위한 `examples`을 선언할 수 있습니다.
//// tab | Pydantic v2
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
////
//// tab | Pydantic v1
{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *}
////
추가 정보는 있는 그대로 해당 모델의 **JSON 스키마** 결과에 추가되고, API 문서에서 사용합니다.
//// tab | Pydantic v2
Pydantic 버전 2에서 <a href="https://docs.pydantic.dev/latest/usage/model_config/" class="external-link" target="_blank">Pydantic 공식 문서: Model Config</a>에 나와 있는 것처럼 `dict`를 받는 `model_config` 어트리뷰트를 사용할 것입니다.
<a href="https://docs.pydantic.dev/latest/api/config/" class="external-link" target="_blank">Pydantic 문서: Configuration</a>에 설명된 것처럼 `dict`를 받는 `model_config` 어트리뷰트를 사용할 수 있습니다.
`"json_schema_extra"`를 생성된 JSON 스키마에서 보여주고 싶은 별도의 데이터와 `examples`를 포함하는 `dict`으로 설정할 수 있습니다.
////
//// tab | Pydantic v1
Pydantic v1에서 <a href="https://docs.pydantic.dev/1.10/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic 공식 문서: Schema customization</a>에서 설명하는 것처럼, 내부 클래스인 `Config``schema_extra`를 사용할 것입니다.
`schema_extra`를 생성된 JSON 스키마에서 보여주고 싶은 별도의 데이터와 `examples`를 포함하는 `dict`으로 설정할 수 있습니다.
////
/// tip | 팁
JSON 스키마를 확장하고 여러분의 별도의 자체 데이터를 추가하기 위해 같은 기술을 사용할 수 있습니다.
@@ -52,19 +30,19 @@ JSON 스키마를 확장하고 여러분의 별도의 자체 데이터를 추가
그 전에는, 하나의 예제만 가능한 `example` 키워드만 지원했습니다. 이는 아직 OpenAPI 3.1.0에서 지원하지만, 지원이 종료될 것이며 JSON 스키마 표준에 포함되지 않습니다. 그렇기에 `example``examples`으로 이전하는 것을 추천합니다. 🤓
문서 끝에 더 많은 읽을거리가 있습니다.
페이지 끝에 더 많은 내용을 읽을 수 있습니다.
///
## `Field` 추가 인자
## `Field` 추가 인자 { #field-additional-arguments }
Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를 선언할 수 있습니다:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## JSON Schema에서의 `examples` - OpenAPI
## JSON Schema에서의 `examples` - OpenAPI { #examples-in-json-schema-openapi }
이들 중에서 사용합니다:
다음 중 하나를 사용할 때:
* `Path()`
* `Query()`
@@ -74,45 +52,45 @@ Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를
* `Form()`
* `File()`
**OpenAPI**의 **JSON 스키마**에 추가될 부가적인 정보를 포함한 `examples` 모음을 선언할 수 있습니다.
**OpenAPI**의 **JSON 스키마**에 추가될 부가적인 정보를 포함한 `examples` 모음을 선언할 수 있습니다.
### `examples`를 포함한 `Body`
### `examples`를 포함한 `Body` { #body-with-examples }
여기, `Body()`에 예상되는 예제 데이터 하나를 포함한 `examples`를 넘겼습니다:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### 문서 UI 예시
### 문서 UI 예시 { #example-in-the-docs-ui }
위의 어느 방법과 함께라면 `/docs`에서 다음과 같이 보일 것입니다:
<img src="/img/tutorial/body-fields/image01.png">
### 다중 `examples`를 포함한 `Body`
### 다중 `examples`를 포함한 `Body` { #body-with-multiple-examples }
물론 여러 `examples`를 넘길 수 있습니다:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
이와 같이 하면 이 예제는 그 본문 데이터를 위한 내부 **JSON 스키마**의 일부가 될 것입니다.
이와 같이 하면 예제들은 그 본문 데이터 내부 **JSON 스키마**의 일부가 될 것입니다.
그럼에도 불구하고, 지금 <abbr title="2023-08-26">이 문서를 작성하는 시간</abbr>에, 문서 UI를 보여주는 역할을 맡은 Swagger UI는 **JSON 스키마** 속 데이터를 위한 여러 예제의 표현을 지원하지 않습니다. 하지만 해결 방안을 밑에서 읽어보세요.
### OpenAPI-특화 `examples`
### OpenAPI-특화 `examples` { #openapi-specific-examples }
**JSON 스키마**가 `examples`를 지원하기 전 부터, OpenAPI는 `examples`이라 불리는 다른 필드를 지원해 왔습니다.
**JSON 스키마**가 `examples`를 지원하기 전부터 OpenAPI는 `examples`이라 불리는 다른 필드를 지원해 왔습니다.
**OpenAPI-특화** `examples`는 OpenAPI 명세서의 다른 구역으로 들어갑니다. 각 JSON 스키마 내부가 아니라 **각 *경로 작동* 세부 정보**에 포함됩니다.
**OpenAPI-특화** `examples`는 OpenAPI 명세서의 다른 구역으로 들어갑니다. 각 JSON 스키마 내부가 아니라 **각 *경로 처리* 세부 정보**에 포함됩니다.
그리고 Swagger UI는 이 특정한 `examples` 필드를 한동안 지원했습니다. 그래서, 이를 다른 **문서 UI에 있는 예제**를 **표시**하기 위해 사용할 수 있습니다.
이 OpenAPI-특화 필드인 `examples`의 형태는 (`list`대신에) **다중 예제**가 포함된 `dict`이며, 각각의 별도 정보 또한 **OpenAPI**에 추가될 것입니다.
이 OpenAPI-특화 필드인 `examples`의 형태는 (`list` 대신에) **다중 예제**가 포함된 `dict`이며, 각각의 별도 정보 또한 **OpenAPI**에 추가될 것입니다.
이는 OpenAPI에 포함된 JSON 스키마 안으로 포함되지 않으며, *경로 작동*에 직접적으로 포함됩니다.
이는 OpenAPI에 포함된 JSON 스키마 안으로 포함되지 않으며, *경로 처리*에 직접적으로 포함됩니다.
### `openapi_examples` 매개변수 사용하기
### `openapi_examples` 매개변수 사용하기 { #using-the-openapi-examples-parameter }
다음 예시 속에 OpenAPI-특화 `examples` FastAPI에서 매개변수 `openapi_examples` 매개변수와 함께 선언할 수 있습니다:
다음에 대해 FastAPI에서 매개변수 `openapi_examples`로 OpenAPI-특화 `examples` 선언할 수 있습니다:
* `Path()`
* `Query()`
@@ -122,26 +100,26 @@ Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를
* `Form()`
* `File()`
`dict`의 키가 또 다른 `dict`인 각 예제와 값을 구별합니다.
`dict`의 키는 각 예제를 식별하고, 각 값은 또 다른 `dict`니다.
각각의 특정 `examples` `dict` 예제는 다음 포함 수 있습니다:
`examples` 안의 각 특정 예제 `dict`는 다음 포함 수 있습니다:
* `summary`: 예제에 대한 짧은 설명문.
* `description`: 마크다운 텍스트를 포함할 수 있는 긴 설명문.
* `value`: 실제로 보여지는 예시, 예를 들면 `dict`.
* `externalValue`: `value`의 대안이며 예제를 가키는 URL. 비록 `value`처럼 많은 도구를 지원하지 못할 수 있습니다.
* `externalValue`: `value`의 대안이며 예제를 가키는 URL. 비록 `value`처럼 많은 도구를 지원하지 못할 수 있습니다.
이를 다음과 같이 사용할 수 있습니다:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### 문서 UI에서의 OpenAPI 예시
### 문서 UI에서의 OpenAPI 예시 { #openapi-examples-in-the-docs-ui }
`Body()` 추가된 `openapi_examples`를 포함한 `/docs`는 다음과 같이 보일 것입니다:
`Body()``openapi_examples`가 추가되면 `/docs`는 다음과 같이 보일 것입니다:
<img src="/img/tutorial/body-fields/image02.png">
## 기술적 세부 사항
## 기술적 세부 사항 { #technical-details }
/// tip | 팁
@@ -167,12 +145,12 @@ JSON 스키마는 `examples`를 가지고 있지 않았고, 따라서 OpenAPI는
OpenAPI는 또한 `example``examples` 필드를 명세서의 다른 부분에 추가했습니다:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object" class="external-link" target="_blank">`(명세서에 있는) Parameter Object`</a>는 FastAPI의 다음 기능에서 쓰였습니다:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object" class="external-link" target="_blank">`Parameter Object` (명세서에 있는)</a>는 FastAPI의 다음 기능에서 쓰였습니다:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object" class="external-link" target="_blank">(명세서에 있는)`Media Type Object` `content`에 있는 `Request Body Object`</a>는 FastAPI의 다음 기능에서 쓰였습니다:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object" class="external-link" target="_blank">`Request Body Object`, `Media Type Object` (명세서에 있는)의 `content` 필드에 있는</a>는 FastAPI의 다음 기능에서 쓰였습니다:
* `Body()`
* `File()`
* `Form()`
@@ -183,15 +161,15 @@ OpenAPI는 또한 `example`과 `examples` 필드를 명세서의 다른 부분
///
### JSON 스키마의 `examples` 필드
### JSON 스키마의 `examples` 필드 { #json-schemas-examples-field }
하지만, 후에 JSON 스키마는 <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a>필드를 명세서의 새 버전에 추가했습니다.
하지만, 후에 JSON 스키마는 <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a> 필드를 명세서의 새 버전에 추가했습니다.
그리고 새로운 OpenAPI 3.1.0은 이 새로운 `examples` 필드가 포함된 최신 버전 (JSON 스키마 2020-12)을 기반으로 했습니다.
새로운 `examples` 필드는 이전의 단일 (그리고 커스텀) `example` 필드보다 우선되며, `example`은 사용하지 않는 것이 좋습니다.
그리고 이제 이 새로운 `examples` 필드는 이제 지원 중단된, 예전의 단일 (그리고 커스텀) `example` 필드보다 우선니다.
JSON 스키마의 새로운 `examples` 필드는 예제 **단순한 `list`**이며, (위에서 상술한 것처럼) OpenAPI의 다른 곳에 존재하는 dict으로 된 추가적인 메타데이터가 아닙니다.
JSON 스키마의 새로운 `examples` 필드는 예제 **단순한 `list`**일 뿐이며, (위에서 상술한 것처럼) OpenAPI의 다른 곳에 존재하는 추가 메타데이터가 있는 dict가 아닙니다.
/// info | 정보
@@ -201,24 +179,24 @@ JSON 스키마의 새로운 `examples` 필드는 예제 속 **단순한 `list`**
///
### Pydantic과 FastAPI `examples`
### Pydantic과 FastAPI `examples` { #pydantic-and-fastapi-examples }
`examples`Pydantic 모델 에 추가할 때, `schema_extra` 혹은 `Field(examples=["something"])`를 사용하면 Pydantic 모델의 **JSON 스키마**에 해당 예시가 추가됩니다.
Pydantic 모델 `examples` 추가할 때, `schema_extra` 또는 `Field(examples=["something"])`를 사용하면 그 예제는 해당 Pydantic 모델의 **JSON 스키마**에 추가됩니다.
그리고 Pydantic 모델의 **JSON 스키마**는 API의 **OpenAPI**에 포함되고, 그 후 문서 UI 속에서 사용됩니다.
FastAPI 0.99.0 이전 버전에서 (0.99.0 이상 버전은 새로운 OpenAPI 3.1.0을 사용합니다), `example` 혹은 `examples` 다른 유틸리티(`Query()`, `Body()` 등)와 함께 사용했을 때, 러한 예시는 데이터를 설명하는 JSON 스키마에 추가되지 않으며 (심지어 OpenAPI 자체 JSON 스키마에도 포함되지 않습니다), OpenAPI의 *경로 작동* 선언에 직접적으로 추가됩니다 (JSON 스키마를 사용하는 OpenAPI 부분 외에도).
FastAPI 0.99.0 이전 버전에서 (0.99.0 이상 버전은 새로운 OpenAPI 3.1.0을 사용합니다), 다른 유틸리티(`Query()`, `Body()` 등)와 함께 `example` 또는 `examples` 사용했을 때, 러한 예제는 그 데이터를 설명하는 JSON 스키마에 추가되지 않고 (OpenAPI 자체 JSON 스키마에도 포함되지 않습니다), OpenAPI의 *경로 처리* 선언에 직접적으로 추가됩니다 (JSON 스키마를 사용하는 OpenAPI 부분 밖에서).
하지만 지금은 FastAPI 0.99.0 및 이후 버전에서는 JSON 스키마 2020-12를 사용하는 OpenAPI 3.1.0과 Swagger UI 5.0.0 및 이후 버전을 사용하, 모든 것이 더 일관성을 띄고 예시는 JSON 스키마에 포함됩니다.
하지만 이제 FastAPI 0.99.0 및 이후 버전에서는 JSON 스키마 2020-12를 사용하는 OpenAPI 3.1.0과 Swagger UI 5.0.0 및 이후 버전을 사용하기 때문에, 모든 것이 더 일관성을 띄고 예제도 JSON 스키마에 포함됩니다.
### Swagger UI와 OpenAPI-특화 `examples`
### Swagger UI와 OpenAPI-특화 `examples` { #swagger-ui-and-openapi-specific-examples }
현재 (2023-08-26), Swagger UI 다중 JSON 스키마 예를 지원하지 않으며, 사용자는 다중문서에 표시하는 방법이 없었습니다.
Swagger UI 다중 JSON 스키마 예를 지원하지 않았기 때문에(2023-08-26 기준), 사용자는 문서에 여러표시할 방법이 없었습니다.
이를 해결하기 위해, FastAPI `0.103.0`은 새로운 매개변수인 `openapi_examples`를 포함하는 예전 **OpenAPI-특화** `examples` 필드를 선언하기 위한 **지원을 추가**했습니다. 🤓
이를 해결하기 위해, FastAPI `0.103.0`은 새로운 매개변수인 `openapi_examples`로 동일한 예전 **OpenAPI-특화** `examples` 필드를 선언하 **지원**을 추가했습니다. 🤓
### 요약
### 요약 { #summary }
저는 역사를 그다지 좋아하는 편이 아니라고 말하고는 했지만... "기술 역사" 강의를 가르치는 지금의 저를 보세요.
저는 역사를 그다지 좋아하는 편이 아니라고 말하고는 했지만... "기술 역사" 강의를 는 지금의 저를 보세요. 😅
요약하자면 **FastAPI 0.99.0 혹은 그 이상의 버전**으로 업그레이드하는 것은 많은 것들이 더 **고, 일관적이며 직관적이게** 되며, 여러분은 이 모든 역사적 세부 사항을 알 필요가 없습니다. 😎
요약하자면 **FastAPI 0.99.0 혹은 그 이상의 버전**으로 업그레이드하면, 많은 것들이 훨씬 더 **단순하고, 일관적이며 직관적** 되며, 여러분은 이 모든 역사적 세부 사항을 알 필요가 없습니다. 😎

View File

@@ -1,105 +1,105 @@
# 현재 사용자 가져오기
# 현재 사용자 가져오기 { #get-current-user }
이전 장에서 (의존성 주입 시스템을 기반으로 한)보안 시스템은 *경로 작동 함수*에 `str``token`을 제공했습니다:
이전 장에서 (의존성 주입 시스템을 기반으로 한) 보안 시스템은 *경로 처리 함수*에 `str``token`을 제공했습니다:
{* ../../docs_src/security/tutorial001.py hl[10] *}
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
그러나 아직도 유용하지 않습니다.
하지만 이는 여전히 그다지 유용하지 않습니다.
현재 사용자를 제공하도록 시다.
현재 사용자를 제공하도록 해봅시다.
## 유저 모델 생성하기
## 사용자 모델 생성하기 { #create-a-user-model }
먼저 Pydantic 유저 모델을 만들어 보겠습니다.
먼저 Pydantic 사용자 모델을 만들어 봅시다.
Pydantic을 사용하여 본문을 선언하는 것과 같은 방식으로 다른 곳에서 사용할 수 있습니다.
Pydantic을 사용 본문을 선언하는 것과 같은 방식으로, 다른 곳에서도 어디서든 사용할 수 있습니다:
{* ../../docs_src/security/tutorial002.py hl[5,12:16] *}
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## `get_current_user` 의존성 생성하기
## `get_current_user` 의존성 생성하기 { #create-a-get-current-user-dependency }
의존성 `get_current_user`를 만들어 봅시다.
의존성이 하위 의존성을 가질 수 있다는 것을 기억하십니까?
의존성이 하위 의존성을 가질 수 있다는 것을 기억하시나요?
`get_current_user`는 이전에 생성한 것과 동일한 `oauth2_scheme`과 종속성을 갖게 됩니다.
`get_current_user`는 이전에 생성한 것과 동일한 `oauth2_scheme`에 대한 의존성을 갖게 됩니다.
이전에 *경로 작동*에서 직접 수행했던 것과 동일하게 새 종속`get_current_user`는 하위 종속`oauth2_scheme`에서 `str``token`수신합니다.
이전에 *경로 처리*에서 직접 수행했던 것과 동일하게,의존`get_current_user`는 하위 의존`oauth2_scheme`로부터 `str``token`받게 됩니다:
{* ../../docs_src/security/tutorial002.py hl[25] *}
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## 유저 가져오기
## 사용자 가져오기 { #get-the-user }
`get_current_user`토큰을 `str`취하고 Pydantic `User` 모델을 반환하는 우리가 만든 (가짜) 유틸리티 함수를 사용합니다.
`get_current_user`우리가 만든 (가짜) 유틸리티 함수를 사용합니다. 이 함수는 `str`토큰을 받아 Pydantic `User` 모델을 반환합니다:
{* ../../docs_src/security/tutorial002.py hl[19:22,26:27] *}
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## 현재 유저 주입하기
## 현재 사용자 주입하기 { #inject-the-current-user }
이제 *경로 작동*에서 `get_current_user`동일한 `Depends`를 사용할 수 있습니다.
이제 *경로 처리*에서 `get_current_user`함께 같은 `Depends`를 사용할 수 있습니다:
{* ../../docs_src/security/tutorial002.py hl[31] *}
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Pydantic 모델인 `User``current_user`의 타입을 선언하는 것을 알아야 합니다.
`current_user`의 타입을 Pydantic 모델 `User`로 선언한다는 점에 주목하세요.
것은 모든 완료 및 타입 검사를 통해 함수 내부에서 우리를 도울 것입니다.
는 함수 내부에서 자동 완성과 타입 체크에 도움을 줍니다.
/// tip | 팁
요청 본문도 Pydantic 모델로 선언된다는 것을 기억할 것입니다.
요청 본문도 Pydantic 모델로 선언된다는 것을 기억하실지도 모릅니다.
여기서 **FastAPI**는 `Depends`를 사용하고 있기 때문에 혼동지 않습니다.
여기서 **FastAPI**는 `Depends`를 사용하고 있기 때문에 혼동지 않습니다.
///
/// check | 확인
이 의존성 시스템이 설계된 방식은 모두 `User` 모델을 반환하는 다양한 의존성(다른 "의존적인")을 가질 수 있도록 합니다.
이 의존성 시스템이 설계된 방식은 모두 `User` 모델을 반환하는 서로 다른 의존성(서로 다른 "dependables")을 가질 수 있도록 합니다.
해당 타입의 데이터를 반환할 수 있는 의존성이 하나만 있는 것으로 제한되지 않습니다.
해당 타입의 데이터를 반환할 수 있는 의존성이 하나만 있어야 하는 것으로 제한되지 않습니다.
///
## 다른 모델
## 다른 모델 { #other-models }
이제 *경로 작동 함수*에서 현재 사용자를 직접 가져올 수 있으며 `Depends`를 사용하여 **의존성 주입** 수준에서 보안 메커니즘을 처리할 수 있습니다.
이제 *경로 처리 함수*에서 현재 사용자를 직접 가져올 수 있으며, `Depends`를 사용 **의존성 주입** 수준에서 보안 메커니즘을 처리할 수 있습니다.
그리고 보안 요구 사항에 대한 모든 모델 또는 데이터 사용할 수 있습니다(이 경우 Pydantic 모델 `User`).
그리고 보안 요구 사항을 위해 어떤 모델이나 데이터 사용할 수 있습니다(이 경우 Pydantic 모델 `User`).
그러나 일부 특정 데이터 모델, 클래스 또는 타입 사용하도록 제한되지 않습니다.
하지만 특정 데이터 모델, 클래스 또는 타입 사용해야 하는 것은 아닙니다.
모델에 `id``email`이 있고 `username`길 원하십니까? 맞습니다. 이들은 동일한 도구를 사용할 수 있습니다.
모델에 `id``email`이 있고 `username`게 하고 싶으신가요? 물론입니다. 같은 도구를 사용할 수 있습니다.
`str`만 갖고 싶습니까? 아니면 그냥 `dict`를 갖고 싶습니까? 아니면 데이터베이스 클래스 모델 인스턴스를 직접 고 싶습니까? 그들은 모두 같은 방식으로 동합니다.
`str`만 갖고 싶으신가요? 아니면 `dict`만요? 또는 데이터베이스 클래스 모델 인스턴스를 직접 고 싶으신가요? 모두 같은 방식으로 동합니다.
실제로 애플리케이션에 로그인하는 사용자지만 액세스 토큰만 있는 로봇, 봇 또는 기타 시스템습니까? 다시 말하지만 모두 동일하게 동합니다.
애플리케이션에 로그인하는 사용자고, 액세스 토큰만 가진 로봇, 봇 또는 다른 시스템나요? 이것도 마찬가지로 모두 동일하게 동합니다.
애플리케이션에 필요한 모든 종류의 모델, 모든 종류의 클래스, 모든 종류의 데이터베이스 사용하십시오. **FastAPI**는 의존성 주입 시스템을 다루었습니다.
애플리케이션에 필요한 어떤 종류의 모델, 어떤 종류의 클래스, 어떤 종류의 데이터베이스 사용하세요. **FastAPI**는 의존성 주입 시스템으로 이를 지원합니다.
## 코드 사이즈
## 코드 크기 { #code-size }
이 예는 장황해 보일 수 있습니다. 동일한 파일에서 보안, 데이터 모델, 유틸리티 기능 및 *경로 작동*을 혼합하고 있음을 염두에 두십시오.
이 예는 장황해 보일 수 있습니다. 동일한 파일에서 보안, 데이터 모델, 유틸리티 함수 및 *경로 처리*를 섞어서 사용하고 있다는 점을 기억하세요.
그러나 이게 키포인트입니다.
하지만 여기 핵심이 있습니다.
보안과 종속성 주입 항목을 한 번만 작성하면 됩니다.
보안과 의존성 주입 관련 코드는 한 번만 작성니다.
그리고 원하는 만큼 복잡하게 만들 수 있습니다. 그래도 유연성과 함께 한 곳에 한 번에 작성할 수 있습니다.
그리고 원하는 만큼 복잡하게 만들 수 있습니다. 그럼에도 여전히 한 번만, 한 곳에만 작성하면 됩니다. 유연성을 모두 유지하면서요.
그러나 동일한 보안 시스템을 사용하여 수천 개의 엔드포인트(*경로 작동*)를 가질 수 있습니다.
하지만 같은 보안 시스템을 사용 수천 개의 엔드포인트(*경로 처리*)를 가질 수 있습니다.
그리고 그들 모두(또는 원하는 부)는 이러한 의존성 또는 생성한 다른 의존성을 재사용하는 이점을 얻을 수 있습니다.
그리고 그들 모두(또는 원하는 부)는 이러한 의존성 또는 여러분이 생성한 다른 의존성을 재사용하는 이점을 얻을 수 있습니다.
그리고 이 수천 개의 *경로 작동*은 모두 3줄 정도로 줄일 수 있습니다.
그리고 이 수천 개의 *경로 처리*는 3줄 정도로도 만들 수 있습니다:
{* ../../docs_src/security/tutorial002.py hl[30:32] *}
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## 요약
## 요약 { #recap }
이제 *경로 작동 함수*에서 현재 사용자를 직접 가져올 수 있습니다.
이제 *경로 처리 함수*에서 현재 사용자를 직접 가져올 수 있습니다.
우리는 이미 이들 사이에 있습니다.
우리는 이미 절반은 왔습니다.
사용자/클라이언트가 실제로 `username``password`를 보내려면 *경로 작동*을 추가하기만 하면 됩니다.
사용자/클라이언트가 실제로 `username``password`를 보내도록 하는 *경로 처리*만 추가하면 됩니다.
다음 장을 확인해 봅시다.
다음에 이어집니다.

View File

@@ -1,36 +1,36 @@
# 패스워드 해싱을 이용한 OAuth2, JWT 토큰을 사용하는 Bearer 인증
# 패스워드(해싱 포함)를 사용하는 OAuth2, JWT 토큰을 사용하는 Bearer { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
모든 보안 흐름을 구성했으므로, 이제 <abbr title="JSON Web Tokens">JWT</abbr> 토큰과 패스워드 해싱을 사용해 애플리케이션을 안전하게 만들 것입니다.
모든 보안 흐름을 구성했으므로, 이제 <abbr title="JSON Web Tokens">JWT</abbr> 토큰과 안전한 패스워드 해싱을 사용해 애플리케이션을 실제로 안전하게 만들겠습니다.
이 코드는 실제로 애플리케이션에서 패스워드싱하여 DB에 저장하는 등의 작업에 활용할 수 있습니다.
이 코드는 실제로 애플리케이션에서 사용할 수 있으며, 패스워드 해시를 데이터베이스에 저장하는 등의 작업에 활용할 수 있습니다.
이전 장에 이어서 시작해 봅시다.
이전 장에서 멈춘 지점부터 시작해 내용을 확장해 나가겠습니다.
## JWT
## JWT 알아보기 { #about-jwt }
JWT 는 "JSON Web Tokens" 의미합니다.
JWT는 "JSON Web Tokens" 의미합니다.
JSON 객체를 공백이 없는 문자열로 인코딩하는 표준이며, 다음과 같은 형태입니다:
JSON 객체를 공백이 없는 길고 밀집된 문자열로 부호화하는 표준입니다. 다음과 같은 형태입니다:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
JWT는 암호화되지 않아 누구든지 토큰에서 정보를 복원할 수 있습니다.
암호화된 것이 아니므로, 누구나 내용에서 정보를 복원할 수 있습니다.
하지만 JWT는 서명되어 있습니다. 그래서 자신이 발급한 토큰을 받았을 때, 실제로 자신이 발급한 맞는지 검증할 수 있습니다.
하지만 서명되어 있습니다. 따라서 자신이 발급한 토큰을 받았을 때, 실제로 자신이 발급한 것이 맞는지 검증할 수 있습니다.
만료 기간이 주일인 토큰을 발행했다고 가정해 봅시다. 다음 날 사용자가 토큰을 가져왔을 때, 그 사용자가 시스템에 여전히 로그인되어 있다는 것을 알 수 있습니다.
예를 들어 만료 기간이 1주일인 토큰을 생성할 수 있습니다. 그리고 사용자가 다음 날 토큰을 가지고 돌아오면, 그 사용자가 시스템에 여전히 로그인되어 있다는 것을 알 수 있습니다.
주일 뒤에는 토큰이 만료될 것이고, 사용자는 인가되지 않 새 토큰을 받기 위해 다시 로그인해야 할 것입니다. 만약 사용자(또는 제3자)가 토큰을 수정하거나 만료일을 변경하면, 서명이 일치하지 않기 때문에 알아챌 수 있을 것입니다.
1주일 뒤에는 토큰이 만료되고 사용자는 인가되지 않으므로 새 토큰을 받기 위해 다시 로그인해야 니다. 그리고 사용자(또는 제3자)가 만료 시간을 바꾸기 위해 토큰을 수정하려고 하면, 서명이 일치하지 않기 때문에 이를 알아챌 수 있니다.
만약 JWT 토큰을 다뤄보고, 작동 방식도 알아보고 싶다면 <a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a> 확인하십시오.
JWT 토큰을 직접 다뤄보고 동작 방식을 확인해보고 싶다면 <a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a> 확인하십시오.
## `PyJWT` 설치
## `PyJWT` 설치 { #install-pyjwt }
파이썬으로 JWT 토큰을 생성하고 검증하려면 `PyJWT` 를 설치해야 합니다.
Python에서 JWT 토큰을 생성하고 검증하려면 `PyJWT`를 설치해야 합니다.
[가상환경](../../virtual-environments.md){.internal-link target=_blank} 을 만들고 활성화한 다음 `pyjwt` 를 설치하십시오:
[가상환경](../../virtual-environments.md){.internal-link target=_blank}을 만들고 활성화한 다음 `pyjwt`를 설치하십시오:
<div class="termy">
@@ -42,77 +42,77 @@ $ pip install pyjwt
</div>
/// info | 참고
/// info
RSA나 ECDSA 같은 전자 서명 알고리즘을 사용하려면, `pyjwt[crypto]`라는 암호화 라이브러리 의존성을 설치해야 합니다.
RSA나 ECDSA 같은 전자 서명 알고리즘을 사용할 계획이라면, cryptography 라이브러리 의존성인 `pyjwt[crypto]` 설치해야 합니다.
자세한 내용은 <a href="https://pyjwt.readthedocs.io/en/latest/installation.html" class="external-link" target="_blank">PyJWT 설치</a> 에서 확인할 수 있습니다.
자세한 내용은 <a href="https://pyjwt.readthedocs.io/en/latest/installation.html" class="external-link" target="_blank">PyJWT Installation docs</a>에서 확인할 수 있습니다.
///
## 패스워드 해싱
## 패스워드 해싱 { #password-hashing }
"해싱(Hashing)"은 어떤 내용(여기서는 패스워드)을 해석할 수 없는 일련의 바이트 집합(단순 문자열)로 변환하는 것을 의미합니다.
"해싱(Hashing)"은 어떤 내용(여기서는 패스워드)을 알아볼 수 없는 바이트 시퀀스(그냥 문자열)로 변환하는 것을 의미합니다.
동일한 내용(똑같은 패스워드)을 해싱하면 동일한 문자열을 얻습니다.
정확히 같은 내용(정확히 같은 패스워드)을 넣으면 정확히 같은 알아볼 수 없는 문자열이 나옵니다.
하지만 그 문자열 다시 패스워드로 되돌릴 수는 없습니다.
하지만 그 알아볼 수 없는 문자열에서 다시 패스워드로 되돌릴 수는 없습니다.
### 패스워드 해싱하는 이유
### 패스워드 해싱을 사용하는 이유 { #why-use-password-hashing }
데이터베이스를 탈취당하더라도, 침입자는 사용자의 평문 패스워드 대신 해시만 얻을 수 있습니다.
데이터베이스를 탈취당하더라도, 침입자는 사용자의 평문 패스워드 대신 해시만 얻게 됩니다.
따라서 침입자는 훔친 사용자 패스워드를 다른 시스템에서 활용할 수 없습니다. (대다수 사용자가 여러 시스템에서 동일한 패스워드를 사용하기 때문에 평문 패스워드가 유출되면 위험합니다.)
따라서 침입자는 패스워드를 다른 시스템에서 사용해 보려고 시도할 수 없습니다(많은 사용자가 어디서나 같은 패스워드를 사용하므로, 이는 위험합니다).
## `passlib` 설치
## `pwdlib` 설치 { #install-pwdlib }
PassLib는 패스워드 해시를 다루 훌륭한 파이썬 패키지입니다.
pwdlib는 패스워드 해시를 다루기 위한 훌륭한 Python 패키지입니다.
많은 안전한 해 알고리즘과 도구들을 지원합니다.
많은 안전한 해 알고리즘과 이를 다루기 위한 유틸리티를 지원합니다.
추천하는 알고리즘은 "Bcrypt"입니다.
추천 알고리즘은 "Argon2"입니다.
[가상환경](../../virtual-environments.md){.internal-link target=_blank} 을 만들고 활성화한 다음 PassLib와 Bcrypt를 설치하십시오:
[가상환경](../../virtual-environments.md){.internal-link target=_blank}을 만들고 활성화한 다음 Argon2와 함께 pwdlib를 설치하십시오:
<div class="termy">
```console
$ pip install "passlib[bcrypt]"
$ pip install "pwdlib[argon2]"
---> 100%
```
</div>
/// tip | 팁
/// tip
`passlib`를 사용하여, **Django**, **Flask** 보안 플러그인이나 다른 도구로 생성한 패스워드를 읽을 수 있도록 설정할 수도 있습니다.
`pwdlib`를 사용하 **Django**, **Flask** 보안 플러그인 또는 다른 여러 도구로 생성한 패스워드를 읽을 수 있도록 설정할 수도 있습니다.
예를 들자면, FastAPI 애플리케이션과 Django 애플리케이션이 같은 데이터베이스에서 데이터를 공유할 수 있습니다. 또는 같은 데이터베이스를 사용하 Django 애플리케이션을 점진적으로 마이그레이션 할 수도 있습니다.
따라서 예를 들, FastAPI 애플리케이션과 Django 애플리케이션이 같은 데이터베이스에서 동일한 데이터를 공유할 수 있습니다. 또는 같은 데이터베이스를 사용하면서 Django 애플리케이션을 점진적으로 마이그레이션할 수도 있습니다.
그리고 사용자는 FastAPI 애플리케이션과 Django 애플리케이션에 동시에 로그인할 수 있습니다.
그리고 사용자는 Django 앱 또는 **FastAPI** 앱에서 동시에 로그인할 수 있습니다.
///
## 패스워드 해시 검증
## 패스워드 해시 검증 { #hash-and-verify-the-passwords }
필요한 도구를 `passlib`에서 임포트합니다.
`pwdlib`에서 필요한 도구를 임포트합니다.
PassLib "컨텍스트(context)"를 생성합니다. 이것은 패스워드를 해싱하고 검증하는데 사용니다.
권장 설정으로 PasswordHash 인스턴스를 생성합니다. 이 패스워드를 해싱하고 검증하는 데 사용니다.
/// tip | 팁
/// tip
PassLib 컨텍스트는 다양한 해싱 알고리즘을 사용할 수 있는 기능을 제공하며, 더 이상 사용이 권장되지 않는 오래된 해싱 알고리즘을 검증하는 기능도 포함되어 있습니다.
pwdlib는 bcrypt 해싱 알고리즘도 지원하지만 레거시 알고리즘은 포함하지 않습니다. 오래된 해시로 작업해야 한다면 passlib 라이브러리를 사용하는 것을 권장합니다.
예를 들어, 다른 시스템(Django 같은)에서 생성한 패스워드를 읽고 검증할 수 있으며, 새로운 패스워드 Bcrypt 같은 다른 알고리즘으로 해싱할 수 있습니다.
예를 들어, 다른 시스템(Django 같은)에서 생성한 패스워드를 읽고 검증하되, 새 패스워드는 Argon2나 Bcrypt 같은 다른 알고리즘으로 해싱하도록 할 수 있습니다.
그리고 동시에 그 모든 알고리즘과 호환성을 유지합니다.
그리고 동시에 그 모든 것과 호환되게 만들 수 있습니다.
///
사용자로부터 받은 패스워드를 해싱하는 유틸리티 함수를 생성합니다.
그리고 받은 패스워드가 저장된 해시와 일치하는지 검증하는 또 다른 유틸리티 함수도 생성합니다.
그리고 받은 패스워드가 저장된 해시와 일치하는지 검증하는 또 다른 유틸리티도 생성합니다.
그리고 사용자를 인증하고 반환하는 또 다른 함수도 생성합니다.
@@ -120,17 +120,17 @@ PassLib 컨텍스트는 다양한 해싱 알고리즘을 사용할 수 있는
/// note
새로운 (가짜) 데이터베이스 `fake_users_db`를 확인하면, 해시 처리된 패스워드가 어떻게 생겼는지 볼 수 있습니다: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`.
새로운 (가짜) 데이터베이스 `fake_users_db`를 확인하면, 이제 해시 처리된 패스워드가 어떻게 생겼는지 볼 수 있습니다: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## JWT 토큰 처리
## JWT 토큰 처리 { #handle-jwt-tokens }
설치된 모듈을 임포트 합니다.
설치된 모듈을 임포트합니다.
JWT 토큰 서명 사용 임의의 비밀키를 생성합니다.
JWT 토큰 서명하는 데 사용 임의의 비밀 키를 생성합니다.
안전한 임의의 비밀키를 생성하려면 다음 명령어를 사용하십시오:
안전한 임의의 비밀 키를 생성하려면 다음 명령 사용하십시오:
<div class="termy">
@@ -142,67 +142,67 @@ $ openssl rand -hex 32
</div>
그리고 생성한 비밀키를 복사해 변수 `SECRET_KEY`대입합니다. (이 예제의 변수 값을 그대로 사용하지 마십시오.)
그리고 출력 결과를 변수 `SECRET_KEY`복사합니다(예제의 값을 사용하지 마십시오).
JWT 토큰을 서명하는 데 사용될 알고리즘을 위한 변수 `ALGORITHM` 을 생성하고 `"HS256"` 으로 설정합니다.
JWT 토큰을 서명하는 데 사용될 알고리즘을 위한 변수 `ALGORITHM`을 생성하고 `"HS256"`으로 설정합니다.
토큰 만료 기간을 위한 변수를 생성합니다.
토큰 만료 위한 변수를 생성합니다.
응답을 위 토큰 엔드포인트에 사용될 Pydantic 모델을 정의합니다.
응답을 위 토큰 엔드포인트에 사용될 Pydantic 모델을 정의합니다.
새 액세스 토큰을 생성하기 위한 유틸리티 함수를 생성합니다.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
## 의존성 수정
## 의존성 업데이트 { #update-the-dependencies }
`get_current_user` 함수를 이전과 동일한 토큰을 받도록 수정하되, 이번에는 JWT 토큰을 사용하도록 합니다.
`get_current_user` 이전과 동일한 토큰을 받도록 업데이트하되, 이번에는 JWT 토큰을 사용하도록 합니다.
받은 토큰을 디코딩하 검증한 현재 사용자를 반환합니다.
받은 토큰을 디코딩하 검증한 현재 사용자를 반환합니다.
토큰이 유효하지 않다면 HTTP 오류를 반환합니다.
토큰이 유효하지 않다면 즉시 HTTP 오류를 반환합니다.
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
## `/token` 경로 작업 수정
## `/token` *경로 처리* 업데이트 { #update-the-token-path-operation }
토큰의 만료 시각을 설정하기 위해 `timedelta` 를 생성합니다.
토큰의 만료 시간으로 `timedelta`를 생성합니다.
실제 JWT 액세스 토큰을 생성하여 반환합니다.
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
### JWT "주체(subject)" `sub`에 대한 기술 세부 사항
### JWT "주체(subject)" `sub`에 대한 기술 세부사항 { #technical-details-about-the-jwt-subject-sub }
JWT 명세에 따르면 토큰의 주체를 포함하`sub`라는 키가 있습니다.
JWT 명세에 따르면 토큰의 주체를 `sub` 키가 있습니다.
사용 여부는 선택사항이지만, 사용자 식별 정보를 저장할 수 있으므로 여기서는 이를 사용합니다.
선택적으로 사용할 수 있지만, 여기에 사용자 식별 정보를 넣게 되므로 여기서는 이를 사용합니다.
JWT는 사용자를 식별하고 사용자가 API 직접 사용할 수 있도록 허용하는 것 외에도 다른 용도로 사용될 수 있습니다.
JWT는 사용자를 식별하고 사용자가 API에서 직접 작업을 수행할 수 있도록 허용하는 것 외에도 다른 용도로 사용될 수 있습니다.
예를 들어 "자동차"나 "블로그 게시물"을 식별하는 데 사용할 수 있습니다.
예를 들어 "자동차"나 "블로그 게시물"을 식별할 수 있습니다.
리고 "자동차를 운전하다"나 "블로그 게시물을 수정하다"처럼 해당 엔터티에 대한 권한을 추가할 수 있습니다.
런 다음 해당 엔터티에 대한 권한(자동차의 경우 "drive", 블로그의 경우 "edit" 등)을 추가할 수 있습니다.
후 이 JWT 토큰을 사용자(또는 봇)에게 제공하면, 그들은 계정을 따로 만들 필요 없이 API가 생성한 JWT 토큰만으로 작업(자동차 운전 또는 블로그 게시물 편집)을 수행할 수 있습니다.
리고 그 JWT 토큰을 사용자(또는 봇)에게 제공하면, 계정이 없어도 API가 생성한 JWT 토큰만으로 그 동작들(자동차 운전, 블로그 편집)을 수행할 수 있습니다.
이러한 개념을 활용하면 JWT는 훨씬 더 복잡한 시나리오에도 사용 수 있습니다.
이러한 아이디어를 활용하면 JWT는 훨씬 더 정교한 시나리오에도 사용 수 있습니다.
경우 여러 엔터티가 동일한 ID를 가질 수 있습니다. 예를 들어 foo라는 ID를 가진 사용자, 자동차, 블로그 게시물이 있을 수 있습니다.
그런 경우 여러 엔터티가 동일한 ID(예: `foo`)를 가질 수 있습니다(사용자 `foo`, 자동차 `foo`, 블로그 게시물 `foo`).
그래서 ID 충돌을 방지하기 위해, 사용자 JWT 토큰을 생성할 때 접두사로 `sub`를 추가할 수 있습니다. 예를 들어 `username:` 을 붙이는 방식입니다. 이 예제에서 `sub` `username:johndoe` 될 수 있습니다.
따라서 ID 충돌을 방지하기 위해, 사용자에 대한 JWT 토큰을 생성할 때 `sub`의 값에 접두사를 붙일 수 있습니다. 예를 들어 `username:` 같은 것입니다. 그러면 이 예제에서 `sub` `username:johndoe` 될 수 있습니다.
가장 중요한 점은 `sub` 전체 애플리케이션에서 고유한 식별자가 되어야 하 문자열이어야 한다는 입니다.
기억해야 할 중요한 점은 `sub` 전체 애플리케이션에서 고유한 식별자야 하고, 문자열이어야 한다는 입니다.
## 확인해봅시다
## 확인하기 { #check-it }
서버를 실행하고 문서로 이동하십시오: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
다음과 같은 사용자 인터페이스를 볼 수 있습니다:
다음과 같은 사용자 인터페이스가 보일 것입니다:
<img src="/img/tutorial/security/image07.png">
이전과 같은 방법으로 애플리케이션하십시오.
이전과 같은 방법으로 애플리케이션하십시오.
다음 인증 정보를 사용하십시오:
@@ -211,13 +211,13 @@ Password: `secret`
/// check
코드 어디에도 평문 패스워드 "`secret`" 이 없다는 점에 유의하십시오. 해시된 버전만 있습니다.
코드 어디에도 평문 패스워드 "`secret`"은 없고, 해시된 버전만 있다는 점에 유의하십시오.
///
<img src="/img/tutorial/security/image08.png">
`/users/me/` 를 호출하면 다음과 같은 응답을 얻을 수 있습니다:
엔드포인트 `/users/me/`를 호출하면 다음과 같은 응답을 받게 됩니다:
```JSON
{
@@ -230,44 +230,44 @@ Password: `secret`
<img src="/img/tutorial/security/image09.png">
개발자 도구를 열어보면 전송된 데이터에 토큰만 포함된 것을 확인할 수 있습니다. 패스워드는 사용자를 인증하고 액세스 토큰을 기 위한 첫 번째 요청에만 전송되며, 이후에는 전송되지 않습니다:
개발자 도구를 열어보면 전송된 데이터에 토큰만 포함되어 있고, 패스워드는 사용자를 인증하고 해당 액세스 토큰을 기 위한 첫 번째 요청에만 전송되며 이후에는 전송되지 않는 것을 확인할 수 있습니다:
<img src="/img/tutorial/security/image10.png">
/// note
`Bearer `로 시작하는 `Authorization` 헤더에 주목하십시오.
`Bearer `로 시작하는 값을 가진 `Authorization` 헤더에 주목하십시오.
///
## `scopes` 의 고급 사용법
## `scopes`의 고급 사용법 { #advanced-usage-with-scopes }
OAuth2는 "스코프(scopes)" 라는 개념을 갖고 있습니다.
OAuth2는 "scopes"라는 개념 있습니다.
이를 사용하여 JWT 토큰에 특정 권한 집합을 추가할 수 있습니다.
이를 사용 JWT 토큰에 특정 권한 집합을 추가할 수 있습니다.
이 토큰을 사용자에게 직접 제공하거나 제3자에게 제공하여, 특정 제한사항 하에있는 API와 통신하도록 할 수 있습니다.
런 다음 이 토큰을 사용자에게 직접 제공하거나 제3자에게 제공하여, 특정 제한사항 하에 API와 상호작용하도록 할 수 있습니다.
**FastAPI** 에서의 사용 방법과 통합 방식은 **심화 사용자 안내서** 에서 자세히 배울 수 있습니다.
어떻게 사용하는지, 그리고 **FastAPI**에 어떻게 통합되는지는 이후 **심화 사용자 안내서**에서 배울 수 있습니다.
## 요약
## 요약 { #recap }
지금까지 살펴본 내용을 바탕으로, OAuth2와 JWT 같은 표준을 사용하여 안전한 **FastAPI** 애플리케이션을 만들 수 있습니다.
지금까지 살펴본 내용을 바탕으로, OAuth2와 JWT 같은 표준을 사용 안전한 **FastAPI** 애플리케이션을 설정할 수 있습니다.
거의 모든 프레임워크에서 보안 처리는 상당히 복잡한 주제니다.
거의 모든 프레임워크에서 보안 처리는 꽤 빠르게 복잡한 주제가 됩니다.
이를 단순화하는 많은 패키지 데이터 모델, 데이터베이스, 사용 가능한 기능들에 대해 여러 제약이 있습니다. 그리고 지나치게 단순화하는 일부 패키지들은 심각한 보안 결함을 가질 수도 있습니다.
이를 크게 단순화하는 많은 패키지들은 데이터 모델, 데이터베이스, 사용 가능한 기능들에 대해 많은 타협을 해야 합니다. 그리고 지나치게 단순화하는 일부 패키지들은 실제로 내부에 보안 결함이 있기도 합니다.
---
**FastAPI** 는 어떤 데이터베이스, 데이터 모델, 도구도 강요하지 않습니다.
**FastAPI**는 어떤 데이터베이스, 데이터 모델, 도구타협하지 않습니다.
프로젝트에 가장 적합한 것을 선택할 수 있는 유연성을 제공합니다.
프로젝트에 가장 잘 맞는을 선택할 수 있는 모든 유연성을 제공합니다.
그리고 `passlib` `PyJWT` 처럼 잘 관리되고 널리 사용되는 패키지들을 바로 사용할 수 있습니다. **FastAPI** 는 외부 패키지 통합을 위해 복잡한 메커니즘이 필요하지 않기 때문입니다.
그리고 **FastAPI**는 외부 패키지를 통합하기 위해 복잡한 메커니즘을 요구하지 않기 때문에 `pwdlib``PyJWT` 같은 잘 관리되고 널리 사용되는 패키지들을 바로 사용할 수 있습니다.
그러나 유연성, 견고성, 보안성을 해치지 않으면서 과정을 단순화할 수 있 도구들을 제공합니다.
하지만 유연성, 견고성, 보안성을 해치지 않으면서 과정을 가능한 한 단순화할 수 있도록 도구들을 제공합니다.
그리고 OAuth2 같은 표준 프로토콜을 비교적 간단한 방으로 구현하고 사용할 수 있습니다.
또한 OAuth2 같은 안전한 표준 프로토콜을 비교적 간단한 방으로 사용하고 구현할 수 있습니다.
더 세분화된 권한 체계를 위해 OAuth2 "스코프"를 사용하는 방법은 **심화 사용자 안내서**에서 더 자세히 배울 수 있습니다. OAuth2의 스코프는 제3자 애플리케이션이 사용자를 대신해 그들의 API와 상호작용하도록 권한을 부여하기 위해, Facebook, Google, GitHub, Microsoft, X (Twitter) 등 많은 대형 인증 제공업체들이 사용하는 메커니즘입니다.
더 세분화된 권한 시스템을 위해 OAuth2 "scopes"를 사용하는 방법은, 같은 표준을 따르는 방식으로 **심화 사용자 안내서**에서 더 자세히 배울 수 있습니다. 스코프를 사용하는 OAuth2는 Facebook, Google, GitHub, Microsoft, X (Twitter) 등 많은 대형 인증 제공업체들이 제3자 애플리케이션이 사용자 대신 그들의 API와 상호작용할 수 있도록 인가하는 데 사용하는 메커니즘입니다.

View File

@@ -1,8 +1,8 @@
# 패스워드와 Bearer를 이용한 간단한 OAuth2
# 패스워드와 Bearer를 이용한 간단한 OAuth2 { #simple-oauth2-with-password-and-bearer }
이제 이전 장에서 빌드하고 누락된 부분을 추가하여 완전한 보안 흐름을 갖도록 하겠습니다.
## `username`와 `password` 얻기
## `username`와 `password` 얻기 { #get-the-username-and-password }
**FastAPI** 보안 유틸리티를 사용하여 `username``password`를 가져올 것입니다.
@@ -14,11 +14,11 @@ OAuth2는 (우리가 사용하고 있는) "패스워드 플로우"을 사용할
그리고 데이터베이스 모델은 원하는 다른 이름을 사용할 수 있습니다.
그러나 로그인 *경로 작동*의 경우 사양과 호환되도록 이러한 이름을 사용해야 합니다(예를 들어 통합 API 문서 시스템을 사용할 수 있어야 합니다).
그러나 로그인 *경로 처리*의 경우 사양과 호환되도록 이러한 이름을 사용해야 합니다(예를 들어 통합 API 문서 시스템을 사용할 수 있어야 합니다).
사양에는 또한 `username``password`가 폼 데이터로 전송되어야 한다고 명시되어 있습니다(따라서 여기에는 JSON이 없습니다).
### `scope`
### `scope` { #scope }
사양에는 클라이언트가 다른 폼 필드 "`scope`"를 보낼 수 있다고 나와 있습니다.
@@ -44,15 +44,15 @@ OAuth2의 경우 문자열일 뿐입니다.
///
## `username`과 `password`를 가져오는 코드
## `username`과 `password`를 가져오는 코드 { #code-to-get-the-username-and-password }
이제 **FastAPI**에서 제공하는 유틸리티를 사용하여 이를 처리해 보겠습니다.
### `OAuth2PasswordRequestForm`
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
먼저 `OAuth2PasswordRequestForm`을 가져와 `/token`에 대한 *경로 작동*에서 `Depends`의 의존성으로 사용합니다.
먼저 `OAuth2PasswordRequestForm`을 가져와 `/token`에 대한 *경로 처리*에서 `Depends`의 의존성으로 사용합니다.
{* ../../docs_src/security/tutorial003.py hl[4,76] *}
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm`은 다음을 사용하여 폼 본문을 선언하는 클래스 의존성입니다:
@@ -84,7 +84,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
///
### 폼 데이터 사용하기
### 폼 데이터 사용하기 { #use-the-form-data }
/// tip | 팁
@@ -100,9 +100,9 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
오류의 경우 `HTTPException` 예외를 사용합니다:
{* ../../docs_src/security/tutorial003.py hl[3,77:79] *}
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### 패스워드 확인하기
### 패스워드 확인하기 { #check-the-password }
이 시점에서 데이터베이스의 사용자 데이터 형식을 확인했지만 암호를 확인하지 않았습니다.
@@ -112,7 +112,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
두 패스워드가 일치하지 않으면 동일한 오류가 반환됩니다.
#### 패스워드 해싱
#### 패스워드 해싱 { #password-hashing }
"해싱"은 일부 콘텐츠(이 경우 패스워드)를 횡설수설하는 것처럼 보이는 일련의 바이트(문자열)로 변환하는 것을 의미합니다.
@@ -120,21 +120,15 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
그러나 횡설수설에서 암호로 다시 변환할 수는 없습니다.
##### 패스워드 해싱을 사용해야 하는 이유
##### 패스워드 해싱을 사용해야 하는 이유 { #why-use-password-hashing }
데이터베이스가 유출된 경우 해커는 사용자의 일반 텍스트 암호가 아니라 해시만 갖게 됩니다.
따라서 해커는 다른 시스템에서 동일한 암호를 사용하려고 시도할 수 없습니다(많은 사용자가 모든 곳에서 동일한 암호를 사용하므로 이는 위험할 수 있습니다).
//// tab | 파이썬 3.7 이상
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
{* ../../docs_src/security/tutorial003.py hl[80:83] *}
////
{* ../../docs_src/security/tutorial003_py310.py hl[78:81] *}
#### `**user_dict`에 대해
#### `**user_dict`에 대해 { #about-user-dict }
`UserInDB(**user_dict)`는 다음을 의미한다:
@@ -152,11 +146,11 @@ UserInDB(
/// info | 정보
`**user_dict`에 대한 자세한 설명은 [**추가 모델** 문서](../extra-models.md#about-user_indict){.internal-link target=_blank}를 다시 읽어봅시다.
`**user_dict`에 대한 자세한 설명은 [**추가 모델** 문서](../extra-models.md#about-user-in-dict){.internal-link target=_blank}를 다시 확인해보세요.
///
## 토큰 반환하기
## 토큰 반환하기 { #return-the-token }
`token` 엔드포인트의 응답은 JSON 객체여야 합니다.
@@ -174,7 +168,7 @@ UserInDB(
///
{* ../../docs_src/security/tutorial003.py hl[85] *}
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | 팁
@@ -188,19 +182,19 @@ UserInDB(
///
## 의존성 업데이트하기
## 의존성 업데이트하기 { #update-the-dependencies }
이제 의존성을 업데이트를 할 겁니다.
이 사용자가 활성화되어 있는 *경우에만* `current_user`를 가져올 겁니다.
따라서 `get_current_user`를 의존성으로 사용하는 추가 종속`get_current_active_user`를 만듭니다.
따라서 `get_current_user`를 의존성으로 사용하는 추가 의존`get_current_active_user`를 만듭니다.
이러한 의존성 모두, 사용자가 존재하지 않거나 비활성인 경우 HTTP 오류를 반환합니다.
따라서 엔드포인트에서는 사용자가 존재하고 올바르게 인증되었으며 활성 상태인 경우에만 사용자를 얻습니다:
{* ../../docs_src/security/tutorial003.py hl[58:66,69:72,90] *}
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | 정보
@@ -220,11 +214,11 @@ UserInDB(
///
## 확인하기
## 확인하기 { #see-it-in-action }
대화형 문서 열기: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
### 인증하기
### 인증하기 { #authenticate }
"Authorize" 버튼을 눌러봅시다.
@@ -240,7 +234,7 @@ UserInDB(
<img src="/img/tutorial/security/image05.png">
### 자신의 유저 데이터 가져오기
### 자신의 유저 데이터 가져오기 { #get-your-own-user-data }
이제 `/users/me` 경로에 `GET` 작업을 진행합시다.
@@ -266,7 +260,7 @@ UserInDB(
}
```
### 비활성된 유저
### 비활성된 유저 { #inactive-user }
이제 비활성된 사용자로 시도하고, 인증해봅시다:
@@ -284,7 +278,7 @@ UserInDB(
}
```
## 요약
## 요약 { #recap }
이제 API에 대한 `username``password`를 기반으로 완전한 보안 시스템을 구현할 수 있는 도구가 있습니다.

View File

@@ -1,18 +1,18 @@
# SQL (관계형) 데이터베이스
# SQL (관계형) 데이터베이스 { #sql-relational-databases }
**FastAPI**에서 SQL(관계형) 데이터베이스 사용은 필수가 아닙니다. 여러분이 원하는 **어떤 데이터베이스든** 사용할 수 있습니다.
**FastAPI**에서 SQL(관계형) 데이터베이스 사용은 필수가 아닙니다. 하지만 여러분이 원하는 **어떤 데이터베이스든** 사용할 수 있습니다.
여기서는 <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a>을 사용하는 예제를 살펴보겠습니다.
**SQLModel**은 <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a>와 Pydantic을 기반으로 구축되었습니다.SQLModel은 **SQL 데이터베이스**를 사용하는 FastAPI 애플리케이션에 완벽히 어울리도록 **FastAPI** 제작자가 설계한 도구입니다.
**SQLModel**은 <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a>와 Pydantic을 기반으로 구축되었습니다. **SQL 데이터베이스**를 사용해야 하는 FastAPI 애플리케이션에 완벽히 어울리도록 **FastAPI**와 같은 제작자가 만든 도구입니다.
/// tip | 팁
다른 SQL 또는 NoSQL 데이터베이스 라이브러리를 사용할 수도 있습니다 (일부는 <abbr title="객체 관계 매퍼(Object Relational Mapper), SQL 테이블을 나타내는 클래스를 제공하고 테이블의 행을 인스턴스로 표현하는 라이브러리를 지칭하는 용어">"ORM"</abbr>이라고도 불립니다), FastAPI는 특정 라이브러리의 사용을 강요하지 않습니다. 😎
다른 SQL 또는 NoSQL 데이터베이스 라이브러리를 사용할 수도 있습니다 (일부는 <abbr title="Object Relational Mapper: a fancy term for a library where some classes represent SQL tables and instances represent rows in those tables">"ORMs"</abbr>이라고도 불립니다), FastAPI는 특정 라이브러리의 사용을 강요하지 않습니다. 😎
///
SQLModel은 SQLAlchemy를 기반으로 하므로, SQLAlchemy에서 **지원하는 모든 데이터베이스**를 손쉽게 사용할 수 있습니다(SQLModel에서도 동일하게 지원됩니다). 예를 들면:
SQLModel은 SQLAlchemy를 기반으로 하므로, SQLAlchemy에서 **지원하는 모든 데이터베이스**를 손쉽게 사용할 수 있습니다(이것들은 SQLModel에서도 지원됩니다). 예를 들면:
* PostgreSQL
* MySQL
@@ -20,19 +20,19 @@ SQLModel은 SQLAlchemy를 기반으로 하므로, SQLAlchemy에서 **지원하
* Oracle
* Microsoft SQL Server 등.
이 예제에서는 **SQLite**를 사용합니다. SQLite는 단일 파일을 사용하고 파이썬에서 기본적으로 지원하기 때문입니다. 따라서 이 예제를 그대로 복사하여 실행할 수 있습니다.
이 예제에서는 **SQLite**를 사용합니다. SQLite는 단일 파일을 사용하고 Python에서 통합 지원하기 때문입니다. 따라서 이 예제를 그대로 복사하여 실행할 수 있습니다.
나중에 실제 프로덕션 애플리케이션에서는 **PostgreSQL**과 같은 데이터베이스 서버를 사용하는 것이 좋습니다.
나중에 프로덕션 애플리케이션에서는 **PostgreSQL**과 같은 데이터베이스 서버를 사용하는 것이 좋습니다.
/// tip | 팁
**FastAPI**와 **PostgreSQL** 포함하여 프론트엔드와 다양한 도구를 제공하는 공식 프로젝트 생성기가 있습니다: <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
프론트엔드와 더 많은 도구를 포함하여 **FastAPI**와 **PostgreSQL** 포함 공식 프로젝트 생성기가 있습니다: <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
///
이 튜토리얼은 매우 간단하고 짧습니다. 데이터베이스 기본 개념, SQL, 또는 더 복잡한 기능에 대해 배우고 싶다면, <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel 문서</a>를 참고하세요.
이 튜토리얼은 매우 간단하고 짧습니다. 데이터베이스 기본 개념, SQL, 또는 더 고급 기능에 대해 배우고 싶다면, <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel 문서</a>를 참고하세요.
## `SQLModel` 설치하기
## `SQLModel` 설치하기 { #install-sqlmodel }
먼저, [가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, `sqlmodel`을 설치하세요:
@@ -45,13 +45,13 @@ $ pip install sqlmodel
</div>
## 단일 모델로 애플리케이션 생성하기
## 단일 모델로 애플리케이션 생성하기 { #create-the-app-with-a-single-model }
우선 단일 **SQLModel** 모델을 사용하여 애플리케이션의 가장 간단한 첫 번째 버전을 생성해보겠습니다.
이후 **다중 모델**을 추가하여 보안과 유연성을 강화할 것입니다. 🤓
이후 아래에서 **여러 모델**로 보안과 유연성을 강화하며 개선하겠습니다. 🤓
### 모델 생성하기
### 모델 생성하기 { #create-models }
`SQLModel`을 가져오고 데이터베이스 모델을 생성합니다:
@@ -61,45 +61,45 @@ $ pip install sqlmodel
몇 가지 차이점이 있습니다:
* `table=True`는 SQLModel에 이 모델이 *테이블 모델*이며, 단순한 데이터 모델이 아니라 SQL 데이터베이스의 **테이블**을 나타다는 것을 알려줍니다. (다른 일반적인 Pydantic 클래스처럼) 단순한 *데이터 모델*이 아닙니다.
* `table=True`는 SQLModel에 이 모델이 *테이블 모델*이며, SQL 데이터베이스의 **테이블**을 나타내야 한다는 것을 알려줍니다. (다른 일반적인 Pydantic 클래스처럼) 단순한 *데이터 모델*이 아닙니다.
* `Field(primary_key=True)`는 SQLModel에 `id`가 SQL 데이터베이스의 **기본 키**임을 알려줍니다 (SQL 기본 키에 대한 자세한 내용은 SQLModel 문서를 참고하세요).
`int | None` 유형으로 설정하면, SQLModel은 해당 열이 SQL 데이터베이스에서 `INTEGER` 유형이며 `NULLABLE` 값이어야 한다는 것을 알 수 있습니다.
**참고:** 기본 키 필드에 `int | None`을 사용하는 이유는, Python 코드에서 *`id` 없이 객체를 생성*할 수 있게 하기 위해서입니다(`id=None`). 데이터베이스가 *저장할 때 생성해 줄 것*이라고 가정합니다. SQLModel은 데이터베이스가 `id`를 제공한다는 것을 이해하고, 데이터베이스 스키마에서 *해당 열을 null이 아닌 `INTEGER`*로 정의합니다. 자세한 내용은 <a href="https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id" class="external-link" target="_blank">기본 키에 대한 SQLModel 문서</a>를 참고하세요.
* `Field(index=True)`는 SQLModel에 해당 열에 대해 **SQL 인덱스**를 생성하도록 지시합니다. 이를 통해 데이터베이스에서 이 열로 필터링된 데이터를 읽을 때 더 빠르게 조회할 수 있습니다.
* `Field(index=True)`는 SQLModel에 해당 열에 대해 **SQL 인덱스**를 생성하도록 지시합니다. 이를 통해 데이터베이스에서 이 열로 필터링된 데이터를 읽을 때 더 빠르게 조회할 수 있습니다.
SQLModel은 `str`으로 선언된 항목이 SQL 데이터베이스에서 `TEXT` (또는 데이터베이스에 따라 `VARCHAR`) 유형의 열로 저장된다는 것을 인식합니다.
### 엔진 생성하기
### 엔진 생성하기 { #create-an-engine }
SQLModel의 `engine` (내부적으로는 SQLAlchemy `engine`)은 데이터베이스에 대한 **연결을 유지**하는 역할을 합니다.
**하나의 단일 engine 객체**를 통해 코드 전체에서 동일한 데이터베이스에 연결할 수 있습니다.
코드 전체에서 동일한 데이터베이스에 연결하기 위해 **하나의 단일 `engine` 객체**를 사용합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
`check_same_thread=False`를 사용하면 FastAPI에서 여러 스레드에서 동일한 SQLite 데이터베이스를 사용할 수 있습니다. 이는 **하나의 단일 요청**이 **여러 스레드**를 사용할 수 있기 때문에 필요합니다(예: 의존성에서 사용되는 경우).
`check_same_thread=False`를 사용하면 FastAPI에서 여러 스레드에서 동일한 SQLite 데이터베이스를 사용할 수 있습니다. 이는 **하나의 단일 요청**이 **둘 이상의 스레드**를 사용할 수 있기 때문에 필요합니다(예: 의존성에서 사용되는 경우).
걱정하지 마세요. 코드가 구조화된 방식으로 인해, 이후에 **각 요청마다 단일 SQLModel *세션*을 사용**하도록 보장할 것입니다. 실제로 것이 `check_same_thread`가 하려는 것입니다.
걱정하지 마세요. 코드가 구조화된 방식으로 인해, 이후에 **각 요청마다 단일 SQLModel *세션*을 사용**하도록 보장할 것입니다. 실제로 것이 `check_same_thread`가 하려는 것입니다.
### 테이블 생성하기
### 테이블 생성하기 { #create-the-tables }
그 다음 `SQLModel.metadata.create_all(engine)`을 사용하여 모든 *테이블 모델*의 **테이블을 생성**하는 함수를 추가합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### 세션 의존성 생성하기
### 세션 의존성 생성하기 { #create-a-session-dependency }
**`Session`**은 **메모리에 객체**를 저장하고 데이터에 필요한 모든 변경 사항을 추적한 후, **`engine`을 통해** 데이터베이스와 통신합니다.
`yield`를 사용해 FastAPI의 **의존성**을 생성하여 각 요청마다 새로운 `Session`을 제공합니다. 이는 요청당 하나의 세션만 사용되도록 보장합니다. 🤓
그런 다음 이 의존성을 사용하는 코드를 간소화하기 위해 `Annotated` 의존성 `SessionDep`을 생성합니다.
그런 다음 이 의존성을 사용하는 나머지 코드를 간소화하기 위해 `Annotated` 의존성 `SessionDep`을 생성합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### 시작 시 데이터베이스 테이블 생성하기
### 시작 시 데이터베이스 테이블 생성하기 { #create-database-tables-on-startup }
애플리케이션 시작 시 데이터베이스 테이블을 생성합니다.
@@ -115,9 +115,9 @@ SQLModel은 Alembic을 감싸는 마이그레이션 유틸리티를 제공할
///
### Hero 생성하기
### Hero 생성하기 { #create-a-hero }
각 SQLModel 모델은 Pydantic 모델이기도 하므로, Pydantic 모델을 사용할 수 있는 **타입 어노테이**에서 동일하게 사용할 수 있습니다.
각 SQLModel 모델은 Pydantic 모델이기도 하므로, Pydantic 모델을 사용할 수 있는 동일한 **타입 어노테이**에서 사용할 수 있습니다.
예를 들어, 파라미터를 `Hero` 타입으로 선언하면 **JSON 본문**에서 값을 읽어옵니다.
@@ -125,31 +125,29 @@ SQLModel은 Alembic을 감싸는 마이그레이션 유틸리티를 제공할
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
</details>
여기서는 `SessionDep` 의존성(`Session`)을 사용하여 새로운 `Hero``Session` 인스턴스에 추가하고, 데이터베이스에 변경 사항을 커밋하고, `hero` 데이터의 최신 상태를 갱신한 다음 이를 반환합니다.
여기서 `SessionDep` 의존성 (즉, `Session`)을 사용하여 새로운 `Hero``Session` 인스턴스에 추가하고, 데이터베이스에 변경 사항을 커밋하고, `hero` 데이터의 최신 상태를 갱신한 다음 이를 반환합니다.
### Heroes 조회하기
### Heroes 조회하기 { #read-heroes }
`select()`를 사용하여 데이터베이스에서 `Hero`를 **조회**할 수 있습니다. 결과에 페이지네이션을 적용하기 위해 `limit``offset`을 포함할 수 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### 단일 Hero 조회하기
### 단일 Hero 조회하기 { #read-one-hero }
단일 `Hero`를 **조회**할 수도 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Hero 삭제하기
### Hero 삭제하기 { #delete-a-hero }
`Hero`를 **삭제**하는 것도 가능합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### 애플리케이션 실행하기
### 애플리케이션 실행하기 { #run-the-app }
애플리케이션을 실행하려면 다음 명령을 사용합니다:
애플리케이션을 실행할 수 있습니다:
<div class="termy">
@@ -161,33 +159,33 @@ $ fastapi dev main.py
</div>
그런 다음 `/docs` UI로 이동하면, **FastAPI**가 해당 **model들**을 사용하여 API **문서를 생성**하는 것으르 확인할 수 있습니다. 또한 이 모델들은 데이터를 직렬화하고 검증하는 데에도 사용니다.
그런 다음 `/docs` UI로 이동하면, **FastAPI**가 **모델**을 사용 API **문서화**하고, 데이터를 **직렬화**하고 **검증**하는 데에도 사용하는 것을 확인할 수 있습니다.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image01.png">
</div>
## 여러 모델로 애플리케이션 업데이트
## 여러 모델로 애플리케이션 업데이트 { #update-the-app-with-multiple-models }
이제 애플리케이션을 약간 **리팩링**하여 **보안**과 **유연성**을 개선해 보겠습니다.
이제 애플리케이션을 약간 **리팩링**하여 **보안**과 **유연성**을 개선해 보겠습니다.
이전 애플리케이션의 UI를 보면, 지금까지는 클라이언트가 생성할 `Hero``id`직접 지정할 수 있는 것을 수 있습니다. 😱
이전 애플리케이션을 확인해 보면, 지금까지는 UI에서 클라이언트가 생성할 `Hero``id`정할 수 있게 되어 있는 것을 수 있습니다. 😱
이는 허용되어선 안 됩니다. 클라이언트가 이미 데이터베이스에 저장된 `id`를 덮어쓸 위험이 있기 때문입니다. `id` **백엔드** 또는 **데이터베이스**가 결정해야 하며, **클라이언트**가 결정해서는 안 됩니다.
렇게 해서는 안 됩니다. 클라이언트가 DB에 이미 할당되어 있는 `id`를 덮어쓸 있기 때문입니다. `id`를 결정하는 것은 **백엔드** 또는 **데이터베이스**가 해야 하며, **클라이언트**가 해서는 안 됩니다.
또한 hero `secret_name`을 생성하긴 했지만, 지금까지는 이 값을 어디에서나 반환하고 있습니다. 이는 그다지 **비밀스럽지** 않습니다... 😅
또한 hero에 대한 `secret_name`을 생성하지만, 지금까지는 이 값을 어디에서나 반환하고 있습니다. 이는 그다지 **비밀스럽지** 않습니다... 😅
이러한 문제를 해결하기 위해 몇 가지 **추가 모델**을 추가할 것입니다. 바로 여기서 SQLModel이 빛을 발하게 됩니다. ✨
이러한 문제 몇 가지 **추가 모델**을 추가해 해결하겠습니다. 바로 여기서 SQLModel이 빛을 발하게 됩니다. ✨
### 여러 모델 생성하기
### 여러 모델 생성하기 { #create-multiple-models }
**SQLModel**에서 `table=True`가 설정된 모델 클래스는 **테이블 모델**입니다.
`table=True`가 없는 모델 클래스는 **데이터 모델**, 이 실제로 몇 가지 추가 기능이 포함된 Pydantic 모델에 불과합니다. 🤓
그리고 `table=True`가 없는 모델 클래스는 **데이터 모델**인데, 이것들은 실제로는 (몇 가지 작은 추가 기능이 있는) Pydantic 모델일 뿐입니다. 🤓
SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중복 선언하지 않아도** 됩니다.
#### `HeroBase` - 기본 클래스
#### `HeroBase` - 기본 클래스 { #herobase-the-base-class }
모든 모델에서 **공유되는 필드**를 가진 `HeroBase` 모델을 시작해 봅시다:
@@ -196,14 +194,14 @@ SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - *테이블 모델*
#### `Hero` - *테이블 모델* { #hero-the-table-model }
다음으로 실제 *테이블 모델*인 `Hero`를 생성합니다. 이 모델은 다른 모델에는 항상 포함되는 건 아닌 **추가 필드**를 포함합니다:
* `id`
* `secret_name`
`Hero``HeroBase`를 상속하므로 `HeroBase`에 선언된 필드도 포함합니다. 따라서 `Hero`는 다음 **필드들도** 가지게 됩니다:
`Hero``HeroBase`를 상속하므로 `HeroBase`에 선언된 필드도 **또한** 포함합니다. 따라서 `Hero`의 모든 필드는 다음과 같습니다:
* `id`
* `name`
@@ -212,11 +210,11 @@ SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - 공개 *데이터 모델*
#### `HeroPublic` - 공개 *데이터 모델* { #heropublic-the-public-data-model }
다음으로 `HeroPublic` 모델을 생성합니다. 이 모델은 API 클라이언트에 **반환**되는 모델입니다.
`HeroPublic``HeroBase`와 동일한 필드를 가지, `secret_name`은 포함하지 않습니다.
`HeroPublic``HeroBase`와 동일한 필드를 가지므로, `secret_name`은 포함하지 않습니다.
마침내 우리의 heroes의 정체가 보호됩니다! 🥷
@@ -224,9 +222,9 @@ SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중
/// tip | 팁
반환 모델이 값이 항상 존재하고 항상 `int`(`None`이 아님)를 보장하는 것은 API 클라이언트에게 매우 유용합니다. 이를 통해 API와 통신하는 개발자가 훨씬 더 간단한 코드를 작성할 수 있습니다.
반환 모델이 값이 항상 존재하고 항상 `int`(`None`이 아님)를 보장하는 것은 API 클라이언트에게 매우 유용합니다. 이를 통해 API 클라이언트는 이런 확신을 바탕으로 훨씬 더 간단한 코드를 작성할 수 있습니다.
또한 **자동으로 생성된 클라이언트**는 더 단순한 인터페이스를 제공하므로, API와 소통하는 개발자들이 훨씬 수월하게 작업할 수 있습니다. 😎
또한 **자동으로 생성된 클라이언트**는 더 단순한 인터페이스를 제공하므로, API와 소통하는 개발자들이 API를 사용하면서 훨씬 더 좋은 경험을 할 수 있습니다. 😎
///
@@ -235,23 +233,22 @@ SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - hero 생성용 *데이터 모델*
#### `HeroCreate` - hero 생성용 *데이터 모델* { #herocreate-the-data-model-to-create-a-hero }
이제 `HeroCreate` 모델을 생성합니다. 이 모델은 클라이언트로부터 받은 데이터를 **검증**하는 역할을 합니다.
`HeroCreate``HeroBase` 동일한 필드를 가지며, 추가로 `secret_name` 포함합니다.
`HeroCreate``HeroBase` 동일한 필드를 가지며, `secret_name` 포함합니다.
클라이언트가 **새 hero 생성**할 때 `secret_name`을 보내, 이는 데이터베이스에 저장되지만, 해당 비밀 이름은 API를 통해 클라이언트에게 반환되지 않습니다.
이제 클라이언트가 **새 hero 생성**할 때 `secret_name`을 보내, 데이터베이스에 저장되지만, 비밀 이름은 API를 통해 클라이언트에게 반환되지 않습니다.
/// tip | 팁
이 방식은 **비밀번호**를 처리하는 방법과 동일합니다. 비밀번호를 받지만, 이를 API에서 반환하지는 않습니다.
비밀번호 값을 저장하기 전에 **해싱**하여 저장하고, **평문으로 저장하지 마십시오**.
또한 비밀번호 값을 저장하기 전에 **해싱**하여 저장하고, **평문으로 저장하지 마십시오**.
///
@@ -263,15 +260,15 @@ SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - hero 수정용 *데이터 모델*
#### `HeroUpdate` - hero 수정용 *데이터 모델* { #heroupdate-the-data-model-to-update-a-hero }
이전 애플리케이션에서는 **hero를 수정**할 방법이 없었지만, 이제 **다중 모델**을 통해 수정 기능을 추가할 수 있습니다. 🎉
이전 버전의 애플리케이션에서는 **hero를 수정**할 방법이 없었지만, 이제 **여러 모델**로 이를 할 수 있습니다. 🎉
`HeroUpdate` *데이터 모델*은 약간 특별한데, 새 hero 생성할 때 필요한 **모든 동일한 필드**를 가지지만, 모든 필드가 **선택적**(기본값이 있음)입니다. 이렇게 하면 hero 수정할 때 수정하려는 필드만 보낼 수 있습니다.
`HeroUpdate` *데이터 모델*은 약간 특별한데, 새 hero 생성할 때 필요한 **모든 동일한 필드**를 가지지만, 모든 필드가 **선택적**(기본값이 있음)입니다. 이렇게 하면 hero 수정할 때 수정하려는 필드만 보낼 수 있습니다.
모든 **필드가 변경되기** 때문에(타입이 `None`을 포함하고, 기본값 `None`으로 설정됨), 모든 필드를 **다시 선언**해야 합니다.
모든 **필드가 실제로 변경**되기 때문에(타입이 이제 `None`을 포함하고, 기본값도 이제 `None`됨), 우리는 필드를 **다시 선언**해야 합니다.
엄밀히 말하면 `HeroBase`를 상속할 필요는 없습니다. 모든 필드를 다시 선언하기 때문입니다. 일관성을 위해 상속을 유지하긴 했지만, 필수는 아닙니다. 이는 개인적인 취향의 문제입니다. 🤷
`HeroBase`를 상속할 필요는 없습니다. 모든 필드를 다시 선언하기 때문입니다. 일관성을 위해 상속을 유지하긴 했지만, 필수는 아닙니다. 이는 개인적인 취향의 문제입니다. 🤷
`HeroUpdate`의 필드는 다음과 같습니다:
@@ -281,61 +278,61 @@ SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### `HeroCreate`로 생성하고 `HeroPublic` 반환하기
### `HeroCreate`로 생성하고 `HeroPublic` 반환하기 { #create-with-herocreate-and-return-a-heropublic }
이제 **다중 모델**을 사용하므로 애플리케이션의 관련 부분을 업데이트할 수 있습니다.
이제 **여러 모델**을 사용하므로 애플리케이션의 관련 부분을 업데이트할 수 있습니다.
요청에서 `HeroCreate` *데이터 모델*을 받아 이를 기반으로 `Hero` *테이블 모델*을 생성합니다.
이 새 *테이블 모델* `Hero`는 클라이언트에서 보낸 필드를 가지며, 데이터베이스에서 생성된 `id`도 포함합니다.
그런 다음 함수를 통해 동일한 *테이블 모델* `Hero`를 반환합니다. 하지만 `response_model``HeroPublic` *데이터 모델*을 선언했기 때문에, **FastAPI**는 `HeroPublic`을 사용하여 데이터를 검증하고 직렬화합니다.
그런 다음 함수를 통해 동일한 *테이블 모델* `Hero` 그대로 반환합니다. 하지만 `response_model``HeroPublic` *데이터 모델*을 선언했기 때문에, **FastAPI**는 `HeroPublic`을 사용하여 데이터를 검증하고 직렬화합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | 팁
이제 **반환 타입 주석** `-> HeroPublic` 대신 `response_model=HeroPublic`을 사용합니다. 반환하는 값이 실제로 `HeroPublic`*아니기* 때문입니다.
이제 **반환 타입 어노테이션** `-> HeroPublic` 대신 `response_model=HeroPublic`을 사용합니다. 반환하는 값이 실제로 `HeroPublic`*아니기* 때문입니다.
만약 `-> HeroPublic`으로 선언했다면, 에디터와 린터에서 반환값이 `HeroPublic`이 아니라 `Hero`라고 경고했을 것입니다. 이는 적절한 경고입니다.
만약 `-> HeroPublic`으로 선언했다면, 에디터와 린터에서 `HeroPublic` 대신 `Hero`를 반환한다고 (당연히) 불평할 것입니다.
`response_model`에 선언함으로써 **FastAPI**가 이를 처리하도록 하고, 타입 어노테이션과 에디터 및 다른 도구의 도움에는 영향을 미치지 않도록 설정합니다.
`response_model`에 선언함으로써 **FastAPI**가 처리하도록 하고, 타입 어노테이션과 에디터 및 다른 도구의 도움에는 영향을 미치지 않도록 합니다.
///
### `HeroPublic`으로 Heroes 조회하기
### `HeroPublic`으로 Heroes 조회하기 { #read-heroes-with-heropublic }
이전과 동일하게 `Hero`를 **조회**할 수 있습니다. 이번에도 `response_model=list[HeroPublic]`을 사용하여 데이터가 올바르게 검증되고 직렬화되도록 보장합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### `HeroPublic`으로 단일 Hero 조회하기
### `HeroPublic`으로 단일 Hero 조회하기 { #read-one-hero-with-heropublic }
단일 hero **조회**할 수도 있습니다:
단일 hero **조회**할 수도 있습니다:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### `HeroUpdate`로 Hero 수정하기
### `HeroUpdate`로 Hero 수정하기 { #update-a-hero-with-heroupdate }
**hero를 수정**할 수도 있습니다. 이를 위해 HTTP `PATCH` 작업을 사용합니다.
코드에서는 클라이언트가 보낸 데이터를 딕셔너리 형태(`dict`)로 가져옵니다. 이는 **클라이언트가 보낸 데이터만 포함**하며, 기본값으로 들어가는 값은 제외합니다. 이를 위해 `exclude_unset=True`를 사용합니다. 이것이 주요 핵심입니다. 🪄
그리고 코드에서는 클라이언트가 보낸 모든 데이터가 담긴 `dict` 가져오는데, **클라이언트가 보낸 데이터만** 포함하고, 기본값이어서 들어가는 값은 제외합니다. 이를 위해 `exclude_unset=True`를 사용합니다. 이것이 주요 핵심입니다. 🪄
그런 다음, `hero_db.sqlmodel_update(hero_data)`를 사용하여 `hero_data`의 데이터 `hero_db` 업데이트합니다.
그런 다음, `hero_db.sqlmodel_update(hero_data)`를 사용하여 `hero_data`의 데이터 `hero_db` 업데이트합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Hero 다시 삭제하기
### Hero 다시 삭제하기 { #delete-a-hero-again }
hero **삭제**는 이전과 거의 동일합니다.
이번에는 모든 것을 리팩링하고 싶은 욕구를 만족시키지 못할 것 같습니다. 😅
이번에는 모든 것을 리팩링하고 싶은 욕구를 만족시키지 못하겠습니다. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### 애플리케이션 다시 실행하기
### 애플리케이션 다시 실행하기 { #run-the-app-again }
다음 명령을 사용해 애플리케이션을 다시 실행할 수 있습니다:
애플리케이션을 다시 실행할 수 있습니다:
<div class="termy">
@@ -347,14 +344,14 @@ $ fastapi dev main.py
</div>
`/docs` API UI로 이동하면 업데이트된 것을 확인할 수 있습니다. 클라이언트가 영웅을 생성할 때 `id`를 제공할 필요가 없게 되는 등의 변화도 보입니다.
`/docs` API UI로 이동하면 이제 업데이트되어 있고, hero를 생성할 때 클라이언트가 `id`를 보낼 것이라고 기대하지 않는 것 등을 확인할 수 있습니다.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image02.png">
</div>
## 요약
## 요약 { #recap }
<a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a>을 사용하여 SQL 데이터베이스와 상호작용하고, *데이터 모델* 및 *테이블 모델*로 코드를 간소화할 수 있습니다.
더 많은 내용을 배우고 싶다면, **SQLModel** 문서를 참고하세요. <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">SQLModel을 **FastAPI**와 함께 사용하는 것에 대한 더 긴 미니 튜토리얼</a>도 제공합니다. 🚀
더 많은 내용을 배우려면 **SQLModel** 문서를 참고하세요. **FastAPI**와 함께 SQLModel을 사용하는 더 긴 미니 <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">튜토리얼</a>도 있습니다. 🚀

View File

@@ -1,41 +1,40 @@
# 정적 파일
# 정적 파일 { #static-files }
'StaticFiles'를 사용하 디렉리에서 정적 파일을 자동으로 제공할 수 있습니다.
`StaticFiles`를 사용하 디렉리에서 정적 파일을 자동으로 제공할 수 있습니다.
## `StaticFiles` 사용
## `StaticFiles` 사용 { #use-staticfiles }
* `StaticFiles` 임포트합니다.
* 특정 경로에 `StaticFiles()` 인스턴스를 "마운트" 합니다.
* `StaticFiles` 임포트합니다.
* 특정 경로에 `StaticFiles()` 인스턴스를 "마운트"합니다.
{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette.staticfiles import StaticFiles` 를 사용할 수도 있습니다.
`from starlette.staticfiles import StaticFiles`를 사용할 수도 있습니다.
**FastAPI**는 단지 개발자인, 당신에게 편의를 제공하기 위해 `fastapi.static files` 와 동일한 `starlett.static files` 제공합니다. 하지만 사실 이것은 Starlett에서 직접 온 것입니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 `fastapi.staticfiles` `starlette.staticfiles`와 동일한 것을 제공합니다. 하지만 실제로는 Starlette에서 직접 가져온 것입니다.
///
### "마운팅" 이란
### "마운팅"이란 { #what-is-mounting }
"마운팅"은 특정 경로에 완전 "독립적인" 애플리케이션을 추가하는 것을 의미하는데, 그 후 모든 하위 경로에 대해서도 적용됩니다.
"마운팅"은 특정 경로에 완전 "독립적인" 애플리케이션을 추가하고, 그 애플리케이션이 모든 하위 경로를 처리하도록 하는 것을 의미합니다.
마운트된 응용 프로그램은 완전히 독립적이기 때문에 `APIRouter`를 사용하는 것과는 다릅니다. OpenAPI 및 응용 프로그램의 문서는 마운트된 응용 프로그램 등에서 어떤 것도 포함지 않습니다.
마운트된 애플리케이션은 완전히 독립적이므로 `APIRouter`를 사용하는 것과는 다릅니다. 메인 애플리케이션의 OpenAPI 및 문서는 마운트된 애플리케이션의 내용 등이 포함지 않습니다.
자세한 내용은 **숙련된 사용자 안내서**에서 확인할 수 있습니다.
자세한 내용은 [고급 사용자 가이드](../advanced/index.md){.internal-link target=_blank}에서 확인할 수 있습니다.
## 세부사항
## 세부사항 { #details }
첫 번째 `"/static"`은 이 "하위 응용 프로그램"이 "마운트"될 하위 경로를 가리킵니다. 따라서 `"/static"`으로 시작하는 모든 경로는 `"/static"`으로 처리니다.
첫 번째 `"/static"`은 이 "하위 애플리케이션"이 "마운트"될 하위 경로를 가리킵니다. 따라서 `"/static"`으로 시작하는 모든 경로는 이 애플리케이션이 처리니다.
`'directory="static"`은 정적 파일이 들어 있는 디렉리의 이름을 나타냅니다.
`directory="static"`은 정적 파일이 들어 있는 디렉리의 이름을 나타냅니다.
`name="static"`은 **FastAPI**에서 내부적으로 사용할 수 있는 이름을 제공합니다.
이 모든 매개변수는 "`static`"과 다를 수 있으며, 사용자 응용 프로그램의 요구 사항 및 구체적인 세부 정보에 따라 매개변수를 조정할 수 있습니다.
이 모든 매개변수는 "`static`"과 다를 수 있으며, 여러분의 애플리케이션 요구 사항 및 구체적인 세부 정보에 맞게 조정하세요.
## 추가 정보 { #more-info }
## 추가 정보
자세한 내용과 선택 사항을 보려면 <a href="https://www.starlette.dev/staticfiles/" class="external-link" target="_blank">Starlette의 정적 파일에 관한 문서</a>를 확인하십시오.
자세한 내용과 옵션은 <a href="https://www.starlette.dev/staticfiles/" class="external-link" target="_blank">Starlette의 정적 파일 문서</a>를 확인하세요.

View File

@@ -1,18 +1,18 @@
# 테스팅
# 테스팅 { #testing }
<a href="https://www.starlette.dev/testclient/" class="external-link" target="_blank">Starlette</a> 덕분에 **FastAPI** 테스트하는 일은 쉽고 즐거운 일이 되었습니다.
<a href="https://www.starlette.dev/testclient/" class="external-link" target="_blank">Starlette</a> 덕분에 **FastAPI** 애플리케이션을 테스트하는 일은 쉽고 즐거운 일이 되었습니다.
Starlette는 <a href="https://www.python-httpx.org\" class="external-link" target="_blank">HTTPX</a>를 기반으로 하며, 이는 Requests를 기반으로 설계되었기 때문에 매우 친숙하고 직관적입니다.
Starlette는 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>를 기반으로 하며, 이는 Requests를 기반으로 설계되었기 때문에 매우 친숙하고 직관적입니다.
이를 사용하면 FastAPI에서 <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a>를 직접 사용할 수 있습니다.
이를 사용하면 **FastAPI**에서 <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a>를 직접 사용할 수 있습니다.
## `TestClient` 사용하기
## `TestClient` 사용하기 { #using-testclient }
/// info | 정보
`TestClient` 사용하려면, 우선 <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a> 를 설치해야 합니다.
`TestClient` 사용하려면, 우선 <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>를 설치해야 합니다.
[virtual environment](../virtual-environments.md){.internal-link target=_blank} 를 만들고, 활성화 시킨 뒤에 설치하세요. 예시:
[virtual environment](../virtual-environments.md){.internal-link target=_blank}를 만들고, 활성화 시킨 뒤에 설치하세요. 예시:
```console
$ pip install httpx
@@ -20,52 +20,51 @@ $ pip install httpx
///
`TestClient` 를 임포트하세요.
`TestClient`를 임포트하세요.
**FastAPI** 플리케이션을 전달하여 `TestClient` 를 만드세요.
**FastAPI** 플리케이션을 전달하여 `TestClient`를 만드세요.
이름이 `test_` 로 시작하는 함수를 만드세요(`pytest` 의 표준적인 관례입니다).
이름이 `test_`로 시작하는 함수를 만드세요(`pytest`의 표준적인 관례입니다).
`httpx` 를 사용하는 것과 같은 방식으로 `TestClient` 객체를 사용하세요.
`httpx`를 사용하는 것과 같은 방식으로 `TestClient` 객체를 사용하세요.
표준적인 파이썬 문법을 이용하여 확인이 필요한 곳에 간단한 `assert` 문장을 작성하세요(역시 표준적인 `pytest` 관례입니다).
표준적인 파이썬 표현식으로 확인이 필요한 곳에 간단한 `assert` 문장을 작성하세요(역시 표준적인 `pytest` 관례입니다).
{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
/// tip | 팁
테스트를 위한 함수는 `async def` 가 아니라 `def` 로 작성됨에 주의하세요.
테스트를 위한 함수는 `async def`가 아니라 `def`로 작성됨에 주의하세요.
그리고 클라이언트에 대한 호출도 `await` 를 사용하지 않는 일반 호출입니다.
그리고 클라이언트에 대한 호출도 `await`를 사용하지 않는 일반 호출입니다.
이렇게 하여 복잡한 과정 없이 `pytest` 를 직접적으로 사용할 수 있습니다.
이렇게 하여 복잡한 과정 없이 `pytest`를 직접적으로 사용할 수 있습니다.
///
/// note | 기술 세부사항
/// note Technical Details | 기술 세부사항
`from starlette.testclient import TestClient` 역시 사용할 수 있습니다.
**FastAPI** 는 개발자의 편의를 위해 `starlette.testclient` `fastapi.testclient` 로도 제공할 뿐입니다. 이는 단지 `Starlette` 에서 직접 가져오는지의 차이일 뿐입니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.testclient``fastapi.testclient`로도 제공할 뿐입니다. 하지만 이는 Starlette에서 직접 가져니다.
///
/// tip | 팁
FastAPI 애플리케이션에 요청을 보내는 것 외에도 테스트에서 `async` 함수를 호출하고 싶다면 (예: 비동기 데이터베이스 함수), 심화 튜토리얼의 [Async Tests](../advanced/async-tests.md){.internal-link target=_blank} 를 참조하세요.
FastAPI 애플리케이션에 요청을 보내는 것 외에도 테스트에서 `async` 함수를 호출하고 싶다면 (예: 비동기 데이터베이스 함수), 심화 튜토리얼의 [Async Tests](../advanced/async-tests.md){.internal-link target=_blank}를 참조하세요.
///
## 테스트 분리하기
## 테스트 분리하기 { #separating-tests }
실제 애플리케이션에서는 테스트를 별도의 파일로 나누는 경우가 많습니다.
그리고 **FastAPI** 애플리케이션도 여러 파일이나 모듈 등으로 구성될 수 있습니다.
### **FastAPI** app 파일
### **FastAPI** app 파일 { #fastapi-app-file }
[Bigger Applications](bigger-applications.md){.internal-link target=_blank} 에 묘사된 파일 구조를 가지고 있는 것으로 가정해봅시다.
[Bigger Applications](bigger-applications.md){.internal-link target=_blank}에 묘사된 파일 구조를 가지고 있는 것으로 가정해봅시다.
```
.
@@ -76,11 +75,12 @@ FastAPI 애플리케이션에 요청을 보내는 것 외에도 테스트에서
`main.py` 파일 안에 **FastAPI** app 을 만들었습니다:
{* ../../docs_src/app_testing/main.py *}
### 테스트 파일
{* ../../docs_src/app_testing/app_a_py39/main.py *}
테스트를 위해 `test_main.py` 라는 파일을 생성할 수 있습니다. 이 파일은 동일한 Python 패키지(즉, `__init__.py` 파일이 있는 동일한 디렉터리)에 위치할 수 있습니다.
### 테스트 파일 { #testing-file }
테스트를 위해 `test_main.py`라는 파일을 생성할 수 있습니다. 이 파일은 동일한 Python 패키지(즉, `__init__.py` 파일이 있는 동일한 디렉터리)에 위치할 수 있습니다.
``` hl_lines="5"
.
@@ -90,18 +90,18 @@ FastAPI 애플리케이션에 요청을 보내는 것 외에도 테스트에서
│   └── test_main.py
```
파일들이 동일한 패키지에 위치해 있으므로, 상대 참조를 사용하여 `main` 에서 `app` 객체를 임포트 해올 수 있습니다.
파일들이 동일한 패키지에 위치해 있으므로, 상대 임포트를 사용하여 `main` 모듈(`main.py`)에서 `app` 객체를 임포트 해올 수 있습니다.
{* ../../docs_src/app_testing/test_main.py hl[3] *}
{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
...그리고 이전에 작성했던 것과 같은 테스트 코드를 작성할 수 있습니다.
## 테스트: 확장된 예시
## 테스트: 확장된 예시 { #testing-extended-example }
이제 위의 예시를 확장하고 더 많은 세부 사항을 추가하여 다양한 부분을 어떻게 테스트하는지 살펴보겠습니다.
### 확장된 FastAPI 애플리케이션 파일
### 확장된 **FastAPI** app 파일 { #extended-fastapi-app-file }
이전과 같은 파일 구조를 계속 사용해 보겠습니다.
@@ -113,100 +113,50 @@ FastAPI 애플리케이션에 요청을 보내는 것 외에도 테스트에서
│   └── test_main.py
```
이제 **FastAPI** 앱이 있는 `main.py` 파일에 몇 가지 다른 **경로 작업** 이 추가된 경우를 생각해봅시다.
이제 **FastAPI** 앱이 있는 `main.py` 파일에 몇 가지 다른 **경로 처리**가 추가된 경우를 생각해봅시다.
단일 오류를 반환할 수 있는 `GET` 작업이 있습니다.
여러 다른 오류를 반환할 수 있는 `POST` 작업이 있습니다.
두 *경로 작업* 모두 `X-Token` 헤더를 요구합니다.
두 *경로 처리* 모두 `X-Token` 헤더를 요구합니다.
//// tab | Python 3.10+
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
```Python
{!> ../../docs_src/app_testing/app_b_an_py310/main.py!}
```
### 확장된 테스트 파일 { #extended-testing-file }
////
이제는 `test_main.py`를 확장된 테스트들로 수정할 수 있습니다:
//// tab | Python 3.9+
```Python
{!> ../../docs_src/app_testing/app_b_an_py39/main.py!}
```
////
//// tab | Python 3.8+
```Python
{!> ../../docs_src/app_testing/app_b_an/main.py!}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | 팁
될 수 있으면 `Annotated` 버전 사용을 권장합나다.
///
```Python
{!> ../../docs_src/app_testing/app_b_py310/main.py!}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | 팁
될 수 있으면 `Annotated` 버전 사용을 권장합나다.
///
```Python
{!> ../../docs_src/app_testing/app_b/main.py!}
```
////
### 확장된 테스트 파일
이제는 `test_main.py` 를 확장된 테스트들로 수정할 수 있습니다:
{* ../../docs_src/app_testing/app_b/test_main.py *}
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
클라이언트가 요청에 정보를 전달해야 하는데 방법을 모르겠다면, `httpx`에서 해당 작업을 수행하는 방법을 검색(Google)하거나, `requests`에서의 방법을 검색해보세요. HTTPX는 Requests의 디자인을 기반으로 설계되었습니다.
클라이언트가 요청에 정보를 전달해야 하는데 방법을 모르겠다면, Requests의 디자인을 기반으로 설계된 HTTPX처럼 `httpx`에서 해당 작업을 수행하는 방법을 검색(Google)하거나, `requests`에서의 방법을 검색해보세요.
그 후, 테스트에서도 동일하게 적용하면 됩니다.
예시:
* *경로* 혹은 *쿼리* 매개변수를 전달하려면, URL 자체에 추가한다.
* JSON 본문을 전달하려면, 파이썬 객체 (예를들면 `dict`) 를 `json` 파라미터로 전달한다.
* JSON 대신 *폼 데이터* 를 보내야한다면, `data` 파라미터를 대신 전달한다.
* *헤더* 를 전달하려면, `headers` 파라미터에 `dict` 를 전달한다.
* *쿠키* 를 전달하려면, `cookies` 파라미터에 `dict` 를 전달한다.
* JSON 본문을 전달하려면, 파이썬 객체 (예를들면 `dict`)를 `json` 파라미터로 전달한다.
* JSON 대신 *폼 데이터*를 보내야한다면, `data` 파라미터를 대신 전달한다.
* *헤더*를 전달하려면, `headers` 파라미터에 `dict`를 전달한다.
* *쿠키*를 전달하려면, `cookies` 파라미터에 `dict`를 전달한다.
백엔드로 데이터를 어떻게 보내는지 정보를 더 얻으려면 (`httpx` 혹은 `TestClient` 를 이용해서) <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX documentation</a> 를 확인하세요.
백엔드로 데이터를 어떻게 보내는지 정보를 더 얻으려면 (`httpx` 혹은 `TestClient`를 이용해서) <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX documentation</a>를 확인하세요.
/// info | 정보
`TestClient` 는 Pydantic 모델이 아니라 JSON 으로 변환될 수 있는 데이터를 받습니다.
`TestClient`는 Pydantic 모델이 아니라 JSON으로 변환될 수 있는 데이터를 받습니다.
만약 테스트중 Pydantic 모델을 어플리케이션으로에 보내고 싶다면, [JSON 호환 가능 인코더](encoder.md){.internal-link target=_blank} 에 설명되어 있는 `jsonable_encoder` 를 사용할 수 있습니다.
만약 테스트 중 Pydantic 모델을 가지고 있고 테스트 중에 애플리케이션으로 해당 데이터를 보내고 싶다면, [JSON Compatible Encoder](encoder.md){.internal-link target=_blank}에 설명되어 있는 `jsonable_encoder`를 사용할 수 있습니다.
///
## 실행하기
## 실행하기 { #run-it }
테스트 코드를 작성하고, `pytest` 를 설치해야합니다.
그 후에는 `pytest`를 설치하기만 하면 됩니다.
[virtual environment](../virtual-environments.md){.internal-link target=_blank} 를 만들고, 활성화 시킨 뒤에 설치하세요. 예시:
[virtual environment](../virtual-environments.md){.internal-link target=_blank}를 만들고, 활성화 시킨 뒤에 설치하세요. 예시:
<div class="termy">
@@ -218,7 +168,7 @@ $ pip install pytest
</div>
`pytest` 파일과 테스트를 자동으로 감지하고 실행한 다음, 결과를 보고할 것입니다.
`pytest` 파일과 테스트를 자동으로 감지하고 실행한 다음, 결과를 보고할 것입니다.
테스트를 다음 명령어로 실행하세요.

View File

@@ -1,16 +1,16 @@
# 가상 환경
# 가상 환경 { #virtual-environments }
Python 프로젝트를 작업할 때는 **가상 환경** (또는 이와 유사한 도구)을 사용하는 것이 좋습니다. 각 프로젝트 마다 설치하는 패키지를 분리하여 관리할 수 있습니다.
Python 프로젝트를 작업할 때는 **가상 환경**(또는 이와 유사한 메커니즘)을 사용 각 프로젝트마다 설치하는 패키지를 분리하는 것이 좋습니다.
/// info | 정보
/// info
이미 가상 환경에 대해 알고 있다면, 이 섹션은 건너 뛰어도 괜찮습니다. 🤓
이미 가상 환경에 대해 알고 있고, 어떻게 생성하고 사용하는지도 알고 있다면, 이 섹션은 건너뛰어도 괜찮습니다. 🤓
///
/// tip | 팁
/// tip
**가상 환경(Virtual Environment)** **환경 변수(Environment Variable)** 와 다릅니다.
**가상 환경**은 **환경 변수**와 다릅니다.
**환경 변수**는 시스템에 존재하며, 프로그램이 사용할 수 있는 변수입니다.
@@ -18,50 +18,52 @@ Python 프로젝트를 작업할 때는 **가상 환경** (또는 이와 유사
///
/// info | 정보
/// info
이 페이지에서는 **가상 환경** 사용 방법과 작동 방식을 설명합니다.
이 페이지에서는 **가상 환경** 사용하는 방법과 작동 방식을 알려드립니다.
만약 **모든 것을 관리해주는 도구** (Python 설치까지 포함)를 사용하고 싶다면 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>를 사용해보세요.
Python 설치까지 포함해 **모든 것을 관리해주는 도구**를 도입할 준비가 되었다면 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>를 사용해 보세요.
///
## 프로젝트 생성
## 프로젝트 생성 { #create-a-project }
먼저, 프로젝트를 위한 디렉터리를 하나 생성합니다.
보통 사용자 홈 디렉터리 안에 `code`라는 디렉터리를 만들고, 그 안에 프로젝트마다 하나씩 디렉터리를 만들어 관리합니다.
제가 보통 하는 방법은 사용자 홈/유저 디렉터리 안에 `code`라는 디렉터리를 만드는 것입니다.
그리고 그 안에 프로젝트마다 디렉터리를 하나씩 만듭니다.
<div class="termy">
```console
// 홈 디렉터리로 이동
// Go to the home directory
$ cd
// 모든 코드 프로젝트를 위한 디렉터리 생성
// Create a directory for all your code projects
$ mkdir code
// code 디렉터리로 이동
// Enter into that code directory
$ cd code
// 이번 프로젝트를 위한 디렉터리 생성
// Create a directory for this project
$ mkdir awesome-project
// 해당 프로젝트 디렉터리로 이동
// Enter into that project directory
$ cd awesome-project
```
</div>
## 가상 환경 생성
## 가상 환경 생성 { #create-a-virtual-environment }
Python 프로젝트를 **처음 시작할 때**, 가상 환경을 **<abbr title="다른 방법들도 있지만, 이 간단한 가이드라인입니다">프로젝트 내부</abbr>**에 생성합니다.
Python 프로젝트를 **처음 시작할 때**, **<abbr title="다른 옵션도 있지만, 이것은 간단한 가이드라인입니다">프로젝트 내부</abbr>**에 가상 환경을 생성하세요.
/// tip | 팁
/// tip
이 작업은 **프로젝트를 처음 설정할 때 한번만** 해주면 됩니다. 이후 작업할 때 반복할 필요는 없습니다.
이 작업은 **프로젝트당 한 번만** 하면 되며, 작업할 때마다 할 필요는 없습니다.
///
//// tab | `venv`
Python 표준 라이브러리에 포함된 venv 모듈을 사용해 가상 환경을 생성할 수 있습니다.
가상 환경을 만들려면 Python에 포함된 `venv` 모듈을 사용할 수 있습니다.
<div class="termy">
@@ -71,12 +73,12 @@ $ python -m venv .venv
</div>
/// details | 명령어 상세 설명
/// details | 명령어 의미
* `python`: `python` 프로그램을 실행합니다.
* `-m`: 특정 모듈을 스크립트처럼 실행합니다. 대상 모듈을 바로 뒤에 지정합니다.
* `venv`: Python 표준 라이브러리에 포함된 `venv` 모듈을 실행합니다.
* `.venv`: 가상 환경을 `.venv` 디렉터리에 생성합니다.
* `python`: `python`이라는 프로그램을 사용합니다
* `-m`: 모듈을 스크립트로 호출합니다. 다음에 어떤 모듈인지 지정합니다
* `venv`: 보통 Python에 기본으로 설치되어 있는 `venv` 모듈을 사용합니다
* `.venv`: 새 디렉터리인 `.venv` 가상 환경을 생성합니다
///
@@ -84,7 +86,7 @@ $ python -m venv .venv
//// tab | `uv`
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>가 설치되어 있다면, uv를 통해 가상 환경을 생성할 수 있습니다.
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>가 설치되어 있다면, 이를 사용해 가상 환경을 생성할 수 있습니다.
<div class="termy">
@@ -94,31 +96,31 @@ $ uv venv
</div>
/// tip | 팁
/// tip
`uv` 기본적으로 `.venv` 디렉터리에 가상 환경을 생성합니다.
기본적으로 `uv``.venv`라는 디렉터리에 가상 환경을 생성합니다.
별도로 디렉터리 이름을 추가 인자로 넘겨 주면 경로를 지정 할 수 있습니다.
하지만 디렉터리 이름을 추가 인자로 전달해 이를 커스터마이즈할 수 있습니다.
///
////
해당 명령어는 `.venv` 디렉터리에 새로운 가상 환경을 생성합니다.
해당 명령어는 `.venv`라는 디렉터리에 새로운 가상 환경을 생성합니다.
/// details | `.venv` 또는 다른 이름
가상 환경을 다른 디렉터리에 생성할 수도 있지만, 관례적으로 `.venv` 디렉터리 이름을 사용합니다.
가상 환경을 다른 디렉터리에 생성할 수도 있지만, 관례적으로 `.venv`라는 이름을 사용합니다.
///
## 가상 환경 활성화
## 가상 환경 활성화 { #activate-the-virtual-environment }
이후 실행하는 Python 명령어와 패키지 설치가 가상 환경을 따르도록, 가상 환경을 활성화하세요.
이후 실행하는 Python 명령어와 설치하는 패키지가 새 가상 환경을 사용하도록, 가상 환경을 활성화하세요.
/// tip | 팁
/// tip
**터미널을 새로 열고** 프로젝트 작업을 시작할 때는, **항상 이 작업을** 해주세요.
프로젝트 작업을 위해 **터미널 세션**을 시작할 때마다 **매번** 이 작업을세요.
///
@@ -148,7 +150,7 @@ $ .venv\Scripts\Activate.ps1
//// tab | Windows Bash
Windows에서 Bash(예: <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>)를 사용하는 경우:
또는 Windows에서 Bash(예: <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>)를 사용하는 경우:
<div class="termy">
@@ -160,21 +162,21 @@ $ source .venv/Scripts/activate
////
/// tip | 팁
/// tip
가상 환경에 새로운 패키지를 설치할 때마다, 해당 환경을 다시 활성화하세요.
해당 환경에 **새 패키지**를 설치할 때마다, 환경을 다시 **활성화**하세요.
이렇게 하면 해당 패키지 설치 **터미널(<abbr title="command line interface">CLI</abbr>) 프로그램**을 사용할 때, 전역 설치된 다른 버전이 아니라, 가상 환경 안에 설치된 정확한 버전을 사용합니다.
이렇게 하면 해당 패키지 설치 **터미널(<abbr title="command line interface">CLI</abbr>) 프로그램**을 사용할 때, 전역으로 설치되어 있을 수도 있는(아마 필요한 버전과는 다른 버전인) 다른 프로그램이 아니라 가상 환경에 있는 것을 사용하게 됩니다.
///
## 가상 환경 활성화 여부 확인
## 가상 환경 활성화 여부 확인 { #check-the-virtual-environment-is-active }
가상 환경이 활성화되었는지 확인합니다. (이전 명령어가 제대로 작동했는지 확인합니다).
가상 환경이 활성화되어 있는지(이전 명령어가 작동했는지) 확인합니다.
/// tip | 팁
/// tip
이 단계는 **선택 사항**이지만, 모든 것이 예상대로 작동하고 있는지, 그리고 의도한 가상 환경이 활성화 되었는 지 **확인**하는 좋은 방법입니다.
이 단계는 **선택 사항**이지만, 모든 것이 예상대로 작동하고 있는지, 그리고 의도한 가상 환경을 사용하고 있는지 **확인**하는 좋은 방법입니다.
///
@@ -190,7 +192,7 @@ $ which python
</div>
`python` 위치가 프로젝트 내부(이 예시에서는 `awesome-project`)의 `.venv/bin/python` 경로로 표시된다면 성공입니다. 🎉
프로젝트 내부(이 경우 `awesome-project`)의 `.venv/bin/python`에 있는 `python` 바이너리가 표시된다면, 정상적으로 작동한 것입니다. 🎉
////
@@ -206,29 +208,29 @@ C:\Users\user\code\awesome-project\.venv\Scripts\python
</div>
`python` 위치가 프로젝트 내부(이 예시에서는 `awesome-project`)의 `.venv\bin\python` 경로로 표시된다면 성공입니다. 🎉
프로젝트 내부(이 경우 `awesome-project`)의 `.venv\Scripts\python`에 있는 `python` 바이너리가 표시된다면, 정상적으로 작동한 것입니다. 🎉
////
## pip 업그레이드
## `pip` 업그레이드 { #upgrade-pip }
/// tip | 팁
/// tip
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>를 사용한다면, `pip` 대신 `uv` 패키지를 설치하게 되므로 `pip`을 업그레이드할 필요가 없습니다. 😎
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>를 사용한다면, `pip` 대신 `uv`로 설치하게 되므로 `pip`을 업그레이드할 필요가 없습니다. 😎
///
`pip`을 사용하여 패키지를 설치하는 경우 (Python 표준 라이브러리에 포함되어 있습니다), **최신 버전으로 업그레이드**하는 것이 좋습니다.
`pip` 패키지를 설치한다면(Python에 기본으로 포함되어 있습니다) 최신 버전으로 **업그레이드**하는 것이 좋습니다.
패키지 설치 중 발생하는 다양하고 특이한 에러들은 `pip` 업그레이드로 쉽게 해결되는 경우가 많습니다.
패키지 설치 중 발생하는 다양 특이한 오류는 먼저 `pip` 업그레이드하는 것만으로 해결되는 경우가 많습니다.
/// tip | 팁
/// tip
이 작업은 보통 가상 환경을 생성한 **직후 한 번만** 하면 됩니다.
보통 이 작업은 가상 환경을 만든 직후 **한 번만** 하면 됩니다.
///
가상 환경이 활성화된 상태인지 확인한 후(앞서 설명한 명령어 사용), 아래 명령어를 실행하세요:
가상 환경이 활성화된 상태인지 확인한 다음(위의 명령어 사용) 아래를 실행하세요:
<div class="termy">
@@ -240,19 +242,39 @@ $ python -m pip install --upgrade pip
</div>
## `.gitignore` 추가하기
/// tip
**Git**을 사용하고 있다면 (사용하는 것이 좋습니다), `.gitignore` 파일을 추가해서 `.venv` 디렉터리 전체를 Git에서 제외하세요.
때로는 pip를 업그레이드하려고 할 때 **`No module named pip`** 오류가 발생할 수 있습니다.
/// tip | 팁
이 경우 아래 명령어로 pip를 설치하고 업그레이드하세요:
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>를 사용해 가상 환경을 생성했다면, 이미 이 작업이 자동으로 처리되어 있으므로 이 단계는 건너뛰어도 됩니다. 😎
<div class="termy">
```console
$ python -m ensurepip --upgrade
---> 100%
```
</div>
이 명령어는 pip가 아직 설치되어 있지 않다면 설치하며, 설치된 pip 버전이 `ensurepip`에서 제공 가능한 버전만큼 최신임을 보장합니다.
///
/// tip | 팁
## `.gitignore` 추가하기 { #add-gitignore }
이 작업도 마찬가지로, 가상 환경을 생성한 **직후 한 번만** 하면 됩니다.
**Git**을 사용하고 있다면(사용하는 것이 좋습니다), `.venv`의 모든 내용을 Git에서 제외하도록 `.gitignore` 파일을 추가하세요.
/// tip
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>로 가상 환경을 만들었다면, 이미 자동으로 처리되어 있으므로 이 단계는 건너뛰어도 됩니다. 😎
///
/// tip
가상 환경을 만든 직후 **한 번만** 하면 됩니다.
///
@@ -264,16 +286,15 @@ $ echo "*" > .venv/.gitignore
</div>
/// details | 명령어 상세 설명
/// details | 명령어 의미
* `echo "*"`: 터미널에 `*` 텍스트를 "출력"합니다 (다음 설명에서 조금 바뀝니다)
* `>`: 왼쪽 명령어의 출력 내용을 터미널에 출력하지 않고, 오른쪽에 지정된 파일로 **기록(write)** 하라는 의미입니다.
* `.gitignore`: 출력된 텍스트가 기록될 파일 이름입니다.
* `echo "*"`: 터미널에 `*` 텍스트를 "출력"합니다(다음 부분이 이를 약간 변경합니다)
* `>`: `>` 왼쪽 명령어가 터미널에 출력 내용을 터미널에 출력하지 않고, `>` 오른쪽에 있는 파일에 기록하라는 의미입니다
* `.gitignore`: 텍스트가 기록될 파일 이름입니다
그리고 Git에서 `*`는 "모든 것"을 의미합니다. 따라서 `.venv` 디렉터리 안의 모든 것을 무시하게 됩니다.
이 명령어는 다음과 같은 내용을 가진 `.gitignore` 파일을 생성합니다:
그리고 Git에서 `*`는 "모든 것"을 의미합니다. 따라서 `.venv` 디렉터리 안의 모든 것을 무시니다.
이 명령어는 다음 내용을 가진 `.gitignore` 파일을 생성합니다:
```gitignore
*
@@ -281,25 +302,25 @@ $ echo "*" > .venv/.gitignore
///
## 패키지 설치
## 패키지 설치 { #install-packages }
가상 환경을 활성화한 , 그 안에 필요한 패키지들을 설치할 수 있습니다.
환경을 활성화한 , 그 안에 패키지 설치할 수 있습니다.
/// tip | 팁
/// tip
프로젝트에 필요한 패키지를 설치하거나 업그레이드할 때는 이 작업을 **한 번** 하면 됩니다.
프로젝트에 필요한 패키지를 설치하거나 업그레이드할 때는 **한 번** 하면 됩니다.
만약 특정 패키지의 버전을 업그레이드하거나,로운 패키지를 추가할 필요가 생기면 **다시 이 작업을 반복**하면 됩니다.
버전을 업그레이드하거나 새 패키지를 추가해야 한다**다시 이 작업을** 하게 됩니다.
///
### 패키지 직접 설치
### 패키지 직접 설치 { #install-packages-directly }
급하게 작업하거나, 프로젝트에 필요한 패키지 목록을 따로 파일로 관리하고 싶지 않은 경우, 패키지를 직접 설치할 수 있습니다.
급하게 작업 중이고 프로젝트의 패키지 요구사항을 선언하는 파일을 사용하고 싶지 않다면, 패키지를 직접 설치할 수 있습니다.
/// tip | 팁
/// tip
패키지 이름과 버전 정보를 파일에 정리해두는 것(예: `requirements.txt` 또는 `pyproject.toml`)은 (매우) 좋은 생각입니다.
프로그램에 필요한 패키지와 버전을 파일(예: `requirements.txt` 또는 `pyproject.toml`)에 적어두는 것은 (매우) 좋은 생각입니다.
///
@@ -319,7 +340,7 @@ $ pip install "fastapi[standard]"
//// tab | `uv`
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>를 사용하는 경우:
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>가 있다면:
<div class="termy">
@@ -332,9 +353,9 @@ $ uv pip install "fastapi[standard]"
////
### `requirements.txt`에서 설치
### `requirements.txt`에서 설치 { #install-from-requirements-txt }
`requirements.txt` 파일이 있다면, 그 안에 명시된 패키지들을 한 번에 설치할 수 있습니다.
`requirements.txt` 있다면, 이제 이를 사용해 그 안의 패키지를 설치할 수 있습니다.
//// tab | `pip`
@@ -351,7 +372,7 @@ $ pip install -r requirements.txt
//// tab | `uv`
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>를 사용하는 경우:
<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>가 있다면:
<div class="termy">
@@ -366,7 +387,7 @@ $ uv pip install -r requirements.txt
/// details | `requirements.txt`
다음은 몇 가지 패키지를 포함한 `requirements.txt`의 예시입니다:
일부 패키지가 있는 `requirements.txt`는 다음과 같이 생겼을 수 있습니다:
```requirements.txt
fastapi[standard]==0.113.0
@@ -375,9 +396,9 @@ pydantic==2.8.0
///
## 프로그램 실행
## 프로그램 실행 { #run-your-program }
가상 환경을 활성화한 에는 프로그램을 실행할 수 있습니다. 이때 해당 가상 환경에 설치된 Python과 패키지들이 사용됩니다.
가상 환경을 활성화한 에는 프로그램을 실행할 수 있으며, 설치한 패키지가 들어있는 가상 환경 내부의 Python을 사용하게 됩니다.
<div class="termy">
@@ -389,25 +410,24 @@ Hello World
</div>
## 에디터 설정
## 에디터 설정 { #configure-your-editor }
에디터를 사용할 경우, 앞서 만든 가상 환경을 사용하도록 설정하는 것이 좋습니다. (대부분의 에디터는 자동으로 감지하기도 합니다.)
이렇게 하면 자동 완성 기능이나 코드 내 오류 표시 기능을 제대로 사용할 수 있습니다.
아마 에디터를 사용할 텐데, 자동 완성과 인라인 오류 표시를 받을 수 있도록 생성한 가상 환경을 사용하도록 설정하세요(대부분 자동 감지합니다).
:
를 들면:
* <a href="https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment" class="external-link" target="_blank">VS Code</a>
* <a href="https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html" class="external-link" target="_blank">PyCharm</a>
/// tip | 팁
/// tip
이 설정은 보통 가상 환경을 **처음 만들었을 때 한 번만** 해주면 됩니다.
보통 이 설정은 가상 환경을 만들**한 번만** 면 됩니다.
///
## 가상 환경 비활성화
## 가상 환경 비활성화 { #deactivate-the-virtual-environment }
프로젝트 작업이 끝났다면, 가상 환경을 **비활성화**할 수 있습니다.
프로젝트 작업을 마쳤다면 가상 환경을 **비활성화**할 수 있습니다.
<div class="termy">
@@ -417,54 +437,55 @@ $ deactivate
</div>
이렇게 하면 이후에 `python` 명령어를 실행했을 때, 가상 환경의 Python이나 그 안에 설치된 패키지들을 사용하지 않게 됩니다.
이렇게 하면 `python` 실행 때, 해당 가상 환경 그 안에 설치된 패키지에서 실행하려고 하지 않니다.
## 이제 작업할 준비가 되었습니다
## 작업할 준비 완료 { #ready-to-work }
이제 프로젝트 작업을 시작할 준비가 완료되었습니다.
이제 프로젝트 작업을 시작할 준비가 되었습니다.
/// tip | 팁
위 내용을 더 깊이 이해하고 싶으신가요?
/// tip
그렇다면 계속 읽어 주세요. 👇🤓
위의 내용이 무엇인지 더 이해하고 싶으신가요?
계속 읽어보세요. 👇🤓
///
## 가상 환경을 왜 사용하는가
## 가상 환경을 왜 사용하나요 { #why-virtual-environments }
FastAPI를 사용하려면 먼저 <a href="https://www.python.org/" class="external-link" target="_blank">Python</a>을 설치해야 합니다.
FastAPI로 작업하려면 <a href="https://www.python.org/" class="external-link" target="_blank">Python</a>을 설치해야 합니다.
후에는 FastAPI와 함께 사용할 **기타 패키지** **설치**해야 합니다.
다음 FastAPI와 사용하려는 다른 **패키지** **설치**해야 합니다.
패키지를 설치할 때 보통 Python에 기본 포함된 `pip` 명령어(또는 유사한 도구)를 사용합니다.
패키지를 설치할 때 보통 Python에 포함된 `pip` 명령어(또는 유사한 대안)를 사용합니다.
하지만 `pip` 직접 사용하면, 해당 패키지들은 **전역 Python 환경**(시스템 전체에 설치된 Python)에 설치됩니다.
하지만 `pip`대로 직접 사용하면, 패키지 **전역 Python 환경**(전역 Python 설치)에 설치됩니다.
### 문제점
### 문제점 { #the-problem }
그렇다면, 전역 Python 환경에 패키지를 설치하면 어떤 문제가 발생할까요?
그렇다면, 전역 Python 환경에 패키지를 설치하면 어떤 문제가 있을까요?
어느 시점이 되면, **서로 다른 패키지**에 의존하는 여러 개의 프로그램을 작성하게 될 것입니다. 그리고 이들 중 일부는 **같은 패키지의 서로 다른 버전**을 필요로 할 수 있습니다. 😱
어느 시점이 되면 **서로 다른 패키지**에 의존하는 다양한 프로그램을 작성하게 될 것입니다. 그리고 작업하는 프로젝트 중 일부는 같은 패키지의 **서로 다른 버전**에 의존할 수 있습니다. 😱
예를 들어, `마법사의 돌(philosophers-stone)` 프로젝트를 만들었다고 가정해봅시다. 이 프로그램은 `해리 포터(harry)`라는 패키지의 `v1` 버전을 **의존**합니다. 따라서 `harry`를 설치해야 합니다.
예를 들어 `philosophers-stone`이라는 프로젝트를 만들 수 있습니다. 이 프로그램은 **`harry`라는 다른 패키지의 버전 `1`**에 의존합니다. 그래서 `harry`를 설치해야 합니다.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
런데 나중에 `아즈카반의 죄수(prisoner-of-azkaban)`이라는 또 다른 프로젝트를 만들게 되었고, 이 프로젝트도 역시 `harry` 패키지를 사용합니다. 그런데 이 프로젝트는 `harry` `v3` 버전이 필요합니다.
다음, 나중에 `prisoner-of-azkaban`이라는 또 다른 프로젝트를 만들고, 이 프로젝트도 `harry`에 의존하지만, 이 프로젝트는 **`harry` 버전 `3`**이 필요합니다.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
하지만 이제 문제가 생깁니다. 로컬 가상 환경 대신에 전역 환경에 패키지를 설치하게 되면, 어떤 버전의 `harry`를 설치할지 선택해야 하기 때문입니다.
하지만 이제 문제가 생깁니다. 로컬 **가상 환경**이 아니라 전역(전역 환경)에 패키지를 설치한다면, 어떤 버전의 `harry`를 설치할지 선택해야 니다.
예를 들어, `마법사의 돌(philosophers-stone)`을 실행하고 싶다면 먼저 `harry` `v1` 버전을 다음과 같이 설치 해야 합니다:
`philosophers-stone`을 실행하고 싶다면, 먼저 `harry` 버전 `1`을 다음과 같이 설치해야 합니다:
<div class="termy">
@@ -474,7 +495,7 @@ $ pip install "harry==1"
</div>
러면 결국 전역 Python 환경에 `harry` `v1`버전이 설치된 상태가 됩니다.
리고 전역 Python 환경에 `harry` 버전 `1`이 설치된 상태가 됩니다.
```mermaid
flowchart LR
@@ -486,7 +507,7 @@ flowchart LR
end
```
하지만 이제 `아즈카반의 죄수(prisoner-of-azkaban)`을 실행하고 싶다면, `harry` `v1`버전을 제거하고 `harry` `v3`버전을 설치해야 합니다. (또는 단순히 `v3`버전을 설치하는 것만으로도 기존의 `v1`버전이 자동으로 제거됩니다.)
하지만 `prisoner-of-azkaban`을 실행하려면 `harry` 버전 `1`을 제거하고 `harry` 버전 `3`을 설치해야 합니다(또는 버전 `3`을 설치하기만 해도 버전 `1`이 자동으로 제거됩니다).
<div class="termy">
@@ -496,9 +517,9 @@ $ pip install "harry==3"
</div>
렇게 하면 이제 전역 Python 환경에 `harry` `v3`버전이 설치된 상태가 됩니다.
러면 전역 Python 환경에 `harry` 버전 `3`이 설치된 상태가 됩니다.
그리고 다시 `마법사의 돌(philosophers-stone)`을 실행하려고 하면, **작동하지** 않을 수 있습니다. 왜냐하면 이 프로그램은 `harry` `v1`버전을 필요하기 때문니다.
그리고 `philosophers-stone`을 다시 실행하려고 하면, `harry` 버전 `1` 필요하기 때문에 **작동하지 않을** 가능성이 있습니다.
```mermaid
flowchart LR
@@ -515,50 +536,49 @@ flowchart LR
end
```
/// tip | 팁
/// tip
Python 패키지들은 **새 버전**에서 **호환성 문제(breaking changes)**가 발생하지 않도록 최대한 노력하는 것이 일반적입니다. 하지만 그래도 안전하게 작업하려면, 테스트를 실행해보면서 새 버전을 의도적으로 설치하는 것이 좋습니다.
Python 패키지에서는 **새 버전**에서 **호환성을 깨뜨리는 변경(breaking changes)**을 **피하려고** 최선을 다하는 것이 매우 일반적이지만, 안전을 위해 더 최신 버전은 의도적으로 설치하고, 테스트를 실행해 모든 것이 올바르게 작동하는지 확인할 수 있을 때 설치하는 것이 좋습니다.
///
이제, 이런 일이 여러분의 **모든 프로젝트**가 사용하는 **많은 패키지**에서 동시에 발생한다고 상상해보세요. 이는 매우 관리하기 어려우며, 결국 **서로 호환되지 않는 버전**의 패키지로 프로젝트를 실행하게 될 가능성이 높고, 그로 인해 어떤 문제가 왜 발생하는지 알 수 없게 될 수 있습니다.
이제 이런 일이 여러분의 **모든 프로젝트가 의존하는** **많은** 다른 **패키지**에서도 일어난다고 상상해 보세요. 이는 관리하기가 매우 어렵습니다. 그리고 결국 일부 프로젝트는 패키지의 **호환되지 않는 버전**으로 실행하게 될 가능성이 높으며, 왜 무언가가 작동하지 않는지 알지 못하게 될 수 있습니다.
또한 사용하는 운영체제(Linux, Windows, macOS 등)에 따라 Python이 **미리 설치되어 있을 수도** 있습니다. 런 경우에는 운영체제의 동작에 필요한 특정 버전의 패키지들이 함께 설치되어 있을 수 있습니다. 이 상태에서 전역 Python 환경에 임의의 패키지를 설치하면, 운영체제에 포함된 프로그램 일부가 **깨질 위험**도 있습니다.
또한 운영체제(Linux, Windows, macOS 등)에 따라 Python이 이미 설치되어 있을 수도 있습니다. 런 경우에는 시스템에 **필요한 특정 버전**의 패키지가 일부 미리 설치되어 있을 가능성이 큽니다. 전역 Python 환경에 패키지를 설치하면, 운영체제에 포함된 프로그램 일부가 **깨질** 수 있습니다.
## 패키지들은 어디에 설치되는가
## 패키지 어디에 설치되나요 { #where-are-packages-installed }
Python을 설치하면, 컴퓨터에 여러 디렉터리와 파일들이 생성됩니다.
Python을 설치하면 컴퓨터에 몇몇 파일이 들어 있는 디렉터리가 생성됩니다.
중 일부 디렉터리는 사용자가 설치한 패키지들을 보관하는 역할을 합니다.
디렉터리 중 일부는 설치한 모든 패키지를 담는 역할을 합니다.
예를 들어, 아래 명령어를 실행하면:
다음을 실행하면:
<div class="termy">
```console
// 지금 실행하지 않아도 됩니다, 그냥 예제일 뿐이에요 🤓
// Don't run this now, it's just an example 🤓
$ pip install "fastapi[standard]"
---> 100%
```
</div>
해당 명령어는 FastAPI 코드를 포함한 압축 파일을 다운로드합니다. 이 파일은 보통 <a href="https://pypi.org/project/fastapi/" class="external-link" target="_blank">PyPI</a>에서 받아옵니다.
FastAPI 코드를 담은 압축 파일을 다운로드합니다. 보통 <a href="https://pypi.org/project/fastapi/" class="external-link" target="_blank">PyPI</a>에서 받니다.
또한 FastAPI가 의존하는 다른 패키지들도 함께 **다운로드**니다.
또한 FastAPI가 의존하는 다른 패키지들의 파일도 **다운로드**니다.
리고 그 모든 파일을 **압축 해제**한 뒤, 컴퓨터의 특정 디렉터리에 저장합니다.
다음 모든 파일을 **압축 해제**하고 컴퓨터의 디렉터리에 넣습니다.
기본적으로 파일들은 Python 설치 디렉터리, 즉 **전역 환경**에 내의 디렉터리에 저장됩니다.
기본적으로, 다운로드하고 압축 해제한 파일들은 Python 설치와 함께 제공되는 디렉터리, 즉 **전역 환경**에 저장됩니다.
## 가상 환경이란
## 가상 환경이란 무엇인가요 { #what-are-virtual-environments }
전역 환경에 모든 패키지를 설치하면서 발생하는 문제에 대한 해결책은, 작업하는 **각 프로젝트마다 가상 환경**을 사용하는 것입니다.
전역 환경에 모든 패키지를 는 문제에 대한 해결책은 작업하는 **각 프로젝트마다 가상 환경**을 사용하는 것입니다.
가상 환경은 전역 환경과 매우 유사한 하나의 **디렉터리**이며, 그 안에 해당 프로젝트를 위한 패키지들을 설치할 수 있습니다.
이렇게 하면 각 프로젝트는 자체적인 가상 환경(`.venv` 디렉터리)을 가지게 되며, 그 안에 해당 프로젝트 전용 패키지들을 보유하게 됩니다.
가상 환경은 전역 환경과 매우 유사한 하나의 **디렉터리**이며, 프로젝트의 패키지 설치할 수 있습니다.
이렇게 하면 각 프로젝트는 자체 가상 환경(`.venv` 디렉터리)과 자체 패키지를 갖게 됩니다.
```mermaid
flowchart TB
@@ -577,9 +597,9 @@ flowchart TB
stone-project ~~~ azkaban-project
```
## 가상 환경 활성화 의미
## 가상 환경 활성화한다는 것은 무엇을 의미하나요 { #what-does-activating-a-virtual-environment-mean }
가상 환경을 활성화한다는 것은, 예를 들어 다음과 같은 명령어를 실행하는 것을 의미합니다:
가상 환경을 활성화한다는 것은, 예를 들어 다음과 같은 명령어:
//// tab | Linux, macOS
@@ -607,7 +627,7 @@ $ .venv\Scripts\Activate.ps1
//// tab | Windows Bash
Windows에서 Bash(예: <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>)를 사용하는 경우:
또는 Windows에서 Bash(예: <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>)를 사용하는 경우:
<div class="termy">
@@ -619,19 +639,19 @@ $ source .venv/Scripts/activate
////
명령어는 이후에 실행될 명령어에서 사용될 [환경 변수](environment-variables.md){.internal-link target=_blank} 몇 개를 생성하거나 수정합니다.
다음 명령어들에서 사용할 수 있는 몇몇 [환경 변수](environment-variables.md){.internal-link target=_blank}를 생성하거나 수정하는 것을 의미합니다.
변수 중 하나가 바로 `PATH` 변수입니다.
변수 중 하나가 `PATH` 변수입니다.
/// tip | 팁
/// tip
`PATH` 환경 변수에 대해 더 알고 싶다면 [환경 변수 문서의 PATH 환경 변수 섹션](environment-variables.md#path-environment-variable){.internal-link target=_blank}을 참고하세요.
`PATH` 환경 변수에 대해 더 알아보려면 [환경 변수](environment-variables.md#path-environment-variable){.internal-link target=_blank} 섹션을 참고하세요.
///
가상 환경을 활성화하면, 가상 환경의 경로인 `.venv/bin` (Linux와 macOS) 또는 `.venv\Scripts`(Windows)를 `PATH` 환경 변수에 추가니다.
가상 환경을 활성화하면 가상 환경의 경로인 `.venv/bin`(Linux와 macOS) 또는 `.venv\Scripts`(Windows)를 `PATH` 환경 변수에 추가니다.
예를 들어, 가상 환경을 활성화하기 전 `PATH` 변수 다음과 같았다고 가정해봅시다:
가령 환경을 활성화하기 전에는 `PATH` 변수 다음과 같았다고 해보겠습니다:
//// tab | Linux, macOS
@@ -639,7 +659,7 @@ $ source .venv/Scripts/activate
/usr/bin:/bin:/usr/sbin:/sbin
```
시스템 다음 경로들에서 프로그램을 찾게 됩니다:
이는 시스템 다음 위치에서 프로그램을 찾는다는 뜻입니다:
* `/usr/bin`
* `/bin`
@@ -654,13 +674,13 @@ $ source .venv/Scripts/activate
C:\Windows\System32
```
시스템 다음 경로들에서 프로그램을 찾게 됩니다:
이는 시스템 다음 위치에서 프로그램을 찾는다는 뜻입니다:
* `C:\Windows\System32`
////
가상 환경을 활성화한 에는, `PATH` 변수 다음과 같은 형태가 됩니다:
가상 환경을 활성화한 에는 `PATH` 변수 다음과 같이 보일 수 있습니다:
//// tab | Linux, macOS
@@ -668,21 +688,21 @@ C:\Windows\System32
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
시스템은 가장 먼저 다음 경로에서 프로그램을 찾기 시작합니다:
이는 시스템이 이제 다음 위치에서 프로그램을 가장 먼저 찾기 시작한다는 뜻입니다:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
후에 다른 디렉터리들을 탐색합니다.
리고 나서 다른 디렉터리들을 탐색합니다.
따라서 터미널에 `python`을 입력하면, 시스템은 다음 위치에 있는 Python 프로그램을 찾게 됩니다:
따라서 터미널에 `python`을 입력하면, 시스템은 다음 위치에 Python 프로그램을 찾:
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
리고 해당 Python을 사용하게 됩니다.
을 사용하게 됩니다.
////
@@ -692,31 +712,31 @@ C:\Windows\System32
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
시스템은 가장 먼저 다음 경로에서 프로그램을 찾기 시작합니다:
이는 시스템이 이제 다음 위치에서 프로그램을 가장 먼저 찾기 시작한다는 뜻입니다:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
후에 다른 디렉터리들을 탐색합니다.
리고 나서 다른 디렉터리들을 탐색합니다.
따라서 터미널에 `python`을 입력하면, 시스템은 다음 경로에 있는 Python 프로그램을 찾게 됩니다:
따라서 터미널에 `python`을 입력하면, 시스템은 다음 위치에서 Python 프로그램을 찾:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
리고 해당 Python을 사용하게 됩니다.
을 사용하게 됩니다.
////
중요한 세부 사항 중 하나는, 가상 환경 경로가 `PATH` 변수의 가장 **앞**에 추가된다는 점입니다. 시스템은 사용 가능한 다른 Python보다 **먼저** 이 경로를 찾습니다. 그래서 터미널에서 `python`을 실행하면, 전역 환경의 Python이 아 **가상 환경에 있는** Python이 사용됩니다. (예: 전역 환경에 설치된 `python`이 있더라도 그보다 우선합니다.)
중요한 세부 사항 가상 환경 경로가 `PATH` 변수의 **앞**에 들어간다는 점입니다. 시스템은 다른 어떤 Python보다 **먼저** 이를 찾게 됩니다. 이렇게 하면 `python`을 실행할 때, 다른 어떤 `python`(예: 전역 환경의 `python`)이 아니라 **가상 환경 Python**을 사용하게 됩니다.
가상 환경을 활성화하면 이 외에도 몇 가지 다른 것들이 변경되지만, 이 그중에서도 가장 중요한 변화 중 하나입니다.
가상 환경을 활성화하면 다른 몇 가지도 변경되지만, 이것이 그중 가장 중요한 중 하나입니다.
## 가상 환경 확인하기
## 가상 환경 확인하기 { #checking-a-virtual-environment }
가상 환경이 활성화 되었는지 확인하려면, 아래 명령어를 사용할 수 있습니다:
가상 환경이 활성화되어 있는지 확인할 때는, 예를 들어 다음을 사용합니다:
//// tab | Linux, macOS, Windows Bash
@@ -746,34 +766,33 @@ C:\Users\user\code\awesome-project\.venv\Scripts\python
////
즉, 현재 사용되는 `python` 프로그램 **가상 환경 내부에 있는 것**입니다.
이는 사용 `python` 프로그램 **가상 환경 내부에 있는 것**이라는 뜻입니다.
Linux와 macOS에서는 `which`, Windows PowerShell에서는 `Get-Command` 명령어를 사용합니다.
Linux와 macOS에서는 `which`, Windows PowerShell에서는 `Get-Command`를 사용합니다.
이 명령어는 `PATH` 환경 변수에 지정된 경로들을 **순서대로 탐색**하면서 `python`이라는 이름의 프로그램을 찾습니다.
찾는 즉시, 해당 프로그램의 **경로를 출력**합니다.
이 명령어는 `PATH` 환경 변수에 있는 경로 **순서대로** 확인하면서 `python`이라는 프로그램을 찾습니다. 찾는 즉시, 그 프로그램의 **경로를 보여줍니다**.
중요한 점은 터미널에서 `python`을 실행했을 때, 실제로 실행되는 "`python`"이 어떤 것인지 정확히 알 수 있다는 입니다.
가장 중요한 부분은 `python`을 호출했을 때, 실행될 정확한 "`python`"이 무엇인지 알 수 있다는 입니다.
따라서 현재 올바른 가상 환경에 있는지 확인할 수 있습니다.
따라서 올바른 가상 환경에 있는지 확인할 수 있습니다.
/// tip | 팁
/// tip
하나의 가상 환경을 활성화한 뒤, 해당 Python을 가진 상태에서 **다른 프로젝트**로 이동하는 것은 흔히 발생합니다.
가상 환경을 하나 활성화해서 Python을 사용한 다음, **다른 프로젝트로 이동**하기 쉽습니다.
하지만 이때 이전 프로젝트의 가상 환경에 있는 **잘못된 Python 실행 파일**을 사용하게 되어 새 프로젝트가 **정상 작동하지 않을 수 있습니다.**
그리고 두 번째 프로젝트는 다른 프로젝트의 가상 환경에서 온 **잘못된 Python**을 사용하고 있기 때문에 **작동하지 않을** 수 있습니다.
그래서 현재 어떤 `python`이 사용되고 있는지 확인할 수 있는 능력은 매우 유용합니다. 🤓
어떤 `python`이 사용되고 있는지 확인할 수 있으면 유용합니다. 🤓
///
## 가상 환경을 비활성화하는 이유
## 가상 환경을 비활성화하나요 { #why-deactivate-a-virtual-environment }
예를 들어 `마법사의 돌(philosophers-stone)`이라는 프로젝트에서 작업 중이라고 해보겠습니다. 이때 해당 **가상 환경을 활성화**하고, 필요한 패키지를 설치하며 작업을 진행합니다.
예를 들어 `philosophers-stone` 프로젝트에서 작업하면서, **가상 환경을 활성화**하고, 패키지를 설치하고, 그 환경으로 작업하고 있다고 해보겠습니다.
그런데 이제 **다른 프로젝트**인 `아즈카반의 죄수(prisoner-of-azkaban)` 작업하고 싶어졌습니다.
그런데 이제 **다른 프로젝트**인 `prisoner-of-azkaban`에서 작업하고 싶습니다.
그래서 그 프로젝트 디렉터리로 이동합니다:
해당 프로젝트로 이동합니다:
<div class="termy">
@@ -783,7 +802,7 @@ $ cd ~/code/prisoner-of-azkaban
</div>
만약 `마법사의 돌(philosophers-stone)`의 가상 환경을 비활성화하지 않았다면, 터미널에서 `python`을 실행할 때 여전히 `마법사의 돌(philosophers-stone)` 가상 환경의 Python을 사용하게 됩니다.
`philosophers-stone`의 가상 환경을 비활성화하지 않면, 터미널에서 `python`을 실행할 때 `philosophers-stone`의 Python을 사용하려고 할 것입니다.
<div class="termy">
@@ -792,7 +811,7 @@ $ cd ~/code/prisoner-of-azkaban
$ python main.py
// sirius를 임포트하는 데 실패했습니다. 설치되어 있지 않아요 😱
// Error importing sirius, it's not installed 😱
Traceback (most recent call last):
File "main.py", line 1, in <module>
import sirius
@@ -800,47 +819,46 @@ Traceback (most recent call last):
</div>
하지만 `마법사의 돌(philosophers-stone)`의 가상 환경을 비활성화한 다음, `아즈카반의 죄수(prisoner-of-azkaban)` 프로젝트의 가상 환경을 활성화하면, 이제 `python` 명령어는 `아즈카반의 죄수(prisoner-of-azkaban)` 가상 환경 Python을 사용하게 됩니다.
하지만 가상 환경을 비활성화하고 `prisoner-of-askaban`에 대한 새 가상 환경을 활성화하면, `python`을 실행할 때 `prisoner-of-azkaban` 가상 환경에 있는 Python을 사용하게 됩니다.
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
// 이전 디렉터리에 있을 필요 없이, 어디서든 가상 환경을 비활성화할 수 있습니다. 다른 프로젝트 디렉터리로 이동한 후에도 괜찮아요 😎
// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎
$ deactivate
// prisoner-of-azkaban/.venv 가상 환경을 활성화합니다 🚀
// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// 이제 python을 실행하면, 이 가상 환경에 설치된 sirius 패키지를 찾게 됩니다
// Now when you run python, it will find the package sirius installed in this virtual environment
$ python main.py
못된 짓을 꾸미고 있음을 엄숙히 맹세합니다.🧙
ImportError는 이제 없습니다. 🐺
I solemnly swear 🐺
```
</div>
## 대안들
## 대안들 { #alternatives }
이 문서는 여러분이 Python 프로젝트를 시작하고, **그 내부에서** 어떻게 돌아가는지 알려주는 간단한 가이드입니다.
이 문서는 시작을 돕고, 내부에서 모든 것이 어떻게 작동하는지 알려주는 간단한 가이드입니다.
가상 환경, 패키지 의존성(Requirements), 프로젝트를 관리하는 방법에는 이 외에도 다양한 **대안**존재합니다.
가상 환경, 패키지 의존성(requirements), 프로젝트를 관리하는 방법에는 많은 **대안**이 있습니다.
만약 준비가 되었다면, **프로젝트 전체**, 패키지 의존성, 가상 환경 등을 통합적으로 **관리**할 수 있는 도구를 써보는 것도 좋습니다. 그럴 때 추천하는 도구가 바로 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>니다.
준비가 되었 **프로젝트 전체**, 패키지 의존성, 가상 환경 등을 **관리**는 도구를 사용하고 싶다면 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>를 사용해 보시길 권합니다.
`uv`는 다양한 기능을 지원합니다:
`uv`는 많은 일을 할 수 있습니다. 예를 들어:
* 다양한 버전의 **Python 설치**
* 프로젝트 **가상 환경 관리**
* **패키지 설치**
* 프로젝트의 **의존성과 버전** 관리
* 설치 패키지들과 그 버전을 **정확히 고정(lock)**해서,개발 환경과 운영 환경이 완전히 동일하게 작동할 수 있도록 보장
* 외에도 다양한 기능을 지원
* 여러 버전을 포함해 **Python 설치**
* 프로젝트 **가상 환경** 관리
* **패키지** 설치
* 프로젝트의 패키지 **의존성과 버전** 관리
* 의존성을 포함해 설치 패키지와 버전의 **정확한** 세트를 보장하여, 개발 중인 컴퓨터와 동일하게 프로덕션에서 실행할 수 있도록 합니다. 이를 **locking**이라고 합니다
* 외에도 많은 기능이 있습니다
## 결론
## 결론 { #conclusion }
여기까지 모두 읽고 이해했다면, 이제 많은 개발자들보다 가상 환경**훨씬 더 깊이 있게 이해**하게 되셨습니다. 🤓
여기까지 모두 읽고 이해했다면, 이제 많은 개발자들보다 가상 환경에 대해 **훨씬 더 많이** 알게 된 것입니다. 🤓
세부적인 내용을 알고 있으면, 언젠가 복잡해 보이는 문제를 디버깅할 때 분명히 큰 도움이 될 것입니다. 이제는 **이 모든 것들이 내부에서 어떻게 작동하는지** 알고 있기 때문입니다. 😎
이 세부 사항을 알고 있으면, 나중에 복잡해 보이는 무언가를 디버깅할 때 아마도 도움이 될 것입니다. **내부에서 어떻게 작동하는지** 알고 있기 때문입니다. 😎