mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-23 22:29:34 -05:00
🌐 Update translations for ko (update-outdated)
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
# 소개
|
||||
# 소개 { #about }
|
||||
|
||||
FastAPI에 대한 디자인, 영감 등에 대해 🤓
|
||||
FastAPI, 그 디자인, 영감 등에 대해 🤓
|
||||
|
||||
@@ -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 문서)에 포함되지 않습니다.
|
||||
|
||||
|
||||
@@ -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를 전달한 다음 백그라운드 태스크 함수 내부에서 객체를 다시 가져오면 됩니다.
|
||||
|
||||
@@ -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")` 콜백에서 인스턴스화할 수 있습니다.
|
||||
|
||||
///
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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 이벤트(startup 및 shutdown)는 메인 애플리케이션에 대해서만 실행되며, [서브 애플리케이션 - Mounts](sub-applications.md){.internal-link target=_blank}에는 실행되지 않음을 유의하세요.
|
||||
|
||||
@@ -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** 개발에 기부하고 있습니다. 🎉 😄
|
||||
|
||||
@@ -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` 파라미터를 선언하고 그 안에서 상태 코드를 설정할 수 있습니다. 단, 마지막으로 설정된 상태 코드가 우선 적용된다는 점을 유의하세요.
|
||||
|
||||
@@ -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>에서 확인할 수 있습니다.
|
||||
|
||||
@@ -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`를 사용하는/선언하는 방법을 확인할 수 있습니다.
|
||||
|
||||
@@ -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` 매개변수를 사용하세요.
|
||||
|
||||
@@ -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} 섹션에서 더 알아볼 수 있습니다.
|
||||
|
||||
@@ -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>를 확인하세요.
|
||||
|
||||
@@ -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 = {}
|
||||
```
|
||||
|
||||
|
||||
@@ -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] *}
|
||||
|
||||
@@ -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> 문서를 확인하세요.
|
||||
|
||||
///
|
||||
|
||||
@@ -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에서 직접 가져온 것입니다.
|
||||
|
||||
///
|
||||
|
||||
@@ -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>.
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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 등 데이터 검증, 직렬화 및 문서화를 제공하는 웹 애플리케이션 프레임워크(또는 도구 집합)와 비교하세요. 통합된 자동 데이터 검증, 직렬화 및 문서화를 제공하는 프레임워크입니다.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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> 제공)를 볼 수 있습니다:
|
||||
|
||||

|
||||
|
||||
## 대안 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> 제공)를 볼 수 있습니다:
|
||||
|
||||

|
||||
|
||||
## 단일 파일 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 캐시**를 신경 쓰면 **빌드 시간을 최소화**해 생산성을 최대화할 수 있습니다(그리고 지루함도 피할 수 있습니다). 😎
|
||||
|
||||
@@ -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** 애플리케이션을 배포할 때 아마 염두에 두어야 할 몇 가지 주요 개념을 보여드리겠습니다(대부분은 다른 유형의 웹 애플리케이션에도 적용됩니다).
|
||||
|
||||
다음 섹션에서 염두에 둘 더 많은 세부사항과 이를 위한 몇 가지 기술을 볼 수 있습니다. ✨
|
||||
|
||||
@@ -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**에 대해 알아보세요. 해당 도구들이 다른 **배포 개념들**도 간단히 해결하는 방법이 있다는 것을 확인할 수 있습니다. ✨
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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>에서 이에 대해 자세히 알아볼 수 있습니다.
|
||||
|
||||
많은 경우에서, 환경 변수가 어떻게 유용하고 적용 가능한지 바로 명확하게 알 수는 없습니다. 하지만 개발할 때 다양한 시나리오에서 계속 나타나므로 이에 대해 아는 것이 좋습니다.
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
@@ -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를 수동으로 작성하세요.
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
### 대안 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.
|
||||
|
||||

|
||||
|
||||
## 예제 심화
|
||||
## 예제 업그레이드 { #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 문서는 새 본문을 포함해 자동으로 업데이트됩니다:
|
||||
|
||||

|
||||
|
||||
* "Try it out" 버튼을 클릭하면, 매개변수를 채울 수 있게 해주고 직접 API와 상호작용 할 수 있습니다:
|
||||
* "Try it out" 버튼을 클릭하면, 매개변수를 채우고 API와 직접 상호작용할 수 있습니다:
|
||||
|
||||

|
||||
|
||||
* 그러고 나서 "Execute" 버튼을 누르면, 사용자 인터페이스는 API와 통신하고 매개변수를 전송하며 그 결과를 가져와서 화면에 표시합니다:
|
||||
* 그런 다음 "Execute" 버튼을 클릭하면, 사용자 인터페이스가 API와 통신하고 매개변수를 전송한 뒤 결과를 받아 화면에 표시합니다:
|
||||
|
||||

|
||||
|
||||
### 대안 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):
|
||||
|
||||

|
||||
|
||||
### 요약
|
||||
### 요약 { #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
|
||||
|
||||

|
||||
|
||||
더 많은 기능을 포함한 보다 완전한 예제의 경우, <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 라이센스 조약에 따라 라이센스가 부여됩니다.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 배우기
|
||||
# 배우기 { #learn }
|
||||
|
||||
여기 **FastAPI**를 배우기 위한 입문 자료와 자습서가 있습니다.
|
||||
여기 **FastAPI**를 배우기 위한 입문 섹션과 자습서가 있습니다.
|
||||
|
||||
여러분은 FastAPI를 배우기 위해 **책**, **강의**, **공식 자료** 그리고 추천받은 방법을 고려할 수 있습니다. 😎
|
||||
여러분은 이것을 FastAPI를 배우기 위한 **책**, **강의**, **공식**이자 권장되는 방법으로 생각할 수 있습니다. 😎
|
||||
|
||||
@@ -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 (지속적인 배포).
|
||||
|
||||
@@ -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>가 있습니다.
|
||||
|
||||
///
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# 리소스
|
||||
# 리소스 { #resources }
|
||||
|
||||
추가 리소스, 외부 링크, 기사 등. ✈️
|
||||
추가 리소스, 외부 링크 등. ✈️
|
||||
|
||||
@@ -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`를 임포트해 사용하여 백그라운드 작업을 추가합니다.
|
||||
|
||||
@@ -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` 를 사용할 수 있습니다.
|
||||
|
||||
|
||||
@@ -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**에 본문을 키 안에 삽입하도록 지시할 수 있습니다.
|
||||
|
||||
@@ -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 모델이 제공하는 최대 유연성을 확보하면서 코드를 간단하고 짧고 우아하게 유지할 수 있습니다.
|
||||
|
||||
물론 아래의 이점도 있습니다:
|
||||
하지만 아래의 모든 이점도 있습니다:
|
||||
|
||||
* 편집기 지원 (자동완성이 어디서나!)
|
||||
* 데이터 변환 (일명 파싱/직렬화)
|
||||
* 편집기 지원(어디서나 자동완성!)
|
||||
* 데이터 변환(일명 파싱/직렬화)
|
||||
* 데이터 검증
|
||||
* 스키마 문서화
|
||||
* 자동 문서
|
||||
|
||||
@@ -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} 문서를 확인하세요.
|
||||
|
||||
@@ -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>를 선언할 수 있습니다. 😎
|
||||
|
||||
@@ -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`를 사용해 쿠키를 선언합니다.
|
||||
|
||||
@@ -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에서 직접 제공됩니다.
|
||||
|
||||
///
|
||||
|
||||
@@ -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`).
|
||||
|
||||
@@ -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**는 코드 반복을 최소화하도록 도와주는 것을 중요하게 생각하기 때문입니다.
|
||||
|
||||
///
|
||||
|
||||
@@ -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` 애플리케이션 전체에 의존성을 추가하는 법을 볼 것입니다.
|
||||
|
||||
@@ -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 | 팁
|
||||
|
||||
|
||||
@@ -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` 매개변수를 선언하는 법을 배우게 될 것입니다.
|
||||
|
||||
@@ -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 스키마에 추가할 것이며, 이를 통해 대화형 문서 시스템에 나타날 것입니다.
|
||||
|
||||
@@ -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**에서 내부적으로 데이터를 변환하는 데 사용하지만, 다른 많은 시나리오에서도 유용합니다.
|
||||
|
||||
///
|
||||
|
||||
@@ -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] *}
|
||||
|
||||
|
||||
@@ -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`, 그리고 비밀번호가 없는 상태를 포함할 수 있는 경우처럼 말입니다.
|
||||
|
||||
@@ -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">'/home/user/code/awesomeapp'</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)
|
||||
|
||||

|
||||
|
||||
### 대안 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)
|
||||
|
||||

|
||||
|
||||
### 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`로 앱 배포.
|
||||
|
||||
@@ -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**에서 **헤더**를 선언할 수 있습니다. 😎
|
||||
|
||||
@@ -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`와 동일한 패턴을 사용하여 선언합니다.
|
||||
|
||||
|
||||
@@ -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">'/home/user/code/awesomeapp'</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 }
|
||||
|
||||
이 **자습서 - 사용자 안내서** 다음에 읽을 수 있는 **고급 사용자 안내서**도 있습니다.
|
||||
이 **자습서 - 사용자 안내서**를 읽은 뒤에 나중에 읽을 수 있는 **고급 사용자 안내서**도 있습니다.
|
||||
|
||||
**고급 사용자 안내서**는 현재 문서를 기반으로 하고, 동일한 개념을 사용하며, 추가적인 기능들에 대해 설명합니다.
|
||||
**고급 사용자 안내서**는 이 문서를 바탕으로 동일한 개념을 사용하며, 몇 가지 추가 기능을 알려줍니다.
|
||||
|
||||
하지만 (지금 읽고 있는) **자습서 - 사용자 안내서**를 먼저 읽는 것을 권장합니다.
|
||||
하지만 먼저 **자습서 - 사용자 안내서**(지금 읽고 있는 내용)를 읽어야 합니다.
|
||||
|
||||
**자습서 - 사용자 안내서**만으로도 완전한 애플리케이션을 구축할 수 있도록 작성되었으며, 필요에 따라 **고급 사용자 안내서**의 추가적인 아이디어를 적용하여 다양한 방식으로 확장할 수 있습니다.
|
||||
**자습서 - 사용자 안내서**만으로 완전한 애플리케이션을 만들 수 있도록 설계되었고, 필요에 따라 **고급 사용자 안내서**의 추가 아이디어를 활용해 다양한 방식으로 확장할 수 있습니다.
|
||||
|
||||
@@ -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] *}
|
||||
|
||||
@@ -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>를 처리하는 방법을 보게 될 것입니다.
|
||||
|
||||
@@ -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 }
|
||||
|
||||
*경로 작동 데코레이터*에 매개변수(들)를 전달함으로 *경로 작동*을 설정하고 메타데이터를 추가할수 있습니다.
|
||||
*경로 처리 데코레이터*에 매개변수(들)를 전달하여 *경로 처리*를 설정하고 메타데이터를 쉽게 추가할 수 있습니다.
|
||||
|
||||
@@ -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`라는 이름을 가진 클래스의 인스턴스를 반환합니다.
|
||||
|
||||
편집기에서 타입에 대한 오류를 표시하지 않도록 하기 위해 (클래스를 직접 사용하는 대신) 이러한 함수들이 있습니다.
|
||||
이 함수들이 있는 이유는(클래스를 직접 사용하는 대신) 편집기에서 타입에 대한 오류를 표시하지 않도록 하기 위해서입니다.
|
||||
|
||||
이렇게 하면 오류를 무시하기 위한 사용자 설정을 추가하지 않고도 일반 편집기와 코딩 도구를 사용할 수 있습니다.
|
||||
|
||||
|
||||
@@ -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**의 주요한 장점일 것입니다.
|
||||
|
||||
@@ -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 모델을 쿠키와 헤더에도 사용할 수 있지만, 이에 대해서는 이후 튜토리얼에서 읽게 될 것입니다. 🤫
|
||||
|
||||
///
|
||||
|
||||
@@ -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` 값의 검증을 어떻게 추가하는지 살펴보았습니다.
|
||||
|
||||
숫자와 같은 다른 자료형에 대한 검증을 어떻게 선언하는지 확인하려면 다음 장을 확인하기 바랍니다.
|
||||
숫자와 같은 다른 타입에 대한 검증을 어떻게 선언하는지 확인하려면 다음 장을 확인하기 바랍니다.
|
||||
|
||||
@@ -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`을 사용할 수 있습니다.
|
||||
|
||||
///
|
||||
|
||||
@@ -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`을 사용하여 폼 데이터로 전송되는 요청에서 업로드할 파일을 선언하세요.
|
||||
|
||||
@@ -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에서 폼 필드를 선언할 수 있습니다. 😎
|
||||
|
||||
@@ -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`을 함께 사용하기 바랍니다.
|
||||
|
||||
@@ -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`을 사용하세요.
|
||||
|
||||
@@ -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`을 사용하세요.
|
||||
|
||||
@@ -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}에서, 여기서 선언하는 기본값과 다른 상태 코드를 반환하는 방법을 확인할 수 있습니다.
|
||||
|
||||
@@ -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 혹은 그 이상의 버전**으로 업그레이드하면, 많은 것들이 훨씬 더 **단순하고, 일관적이며 직관적**이 되며, 여러분은 이 모든 역사적 세부 사항을 알 필요가 없습니다. 😎
|
||||
|
||||
@@ -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`를 보내도록 하는 *경로 처리*만 추가하면 됩니다.
|
||||
|
||||
다음 장을 확인해 봅시다.
|
||||
다음에 이어집니다.
|
||||
|
||||
@@ -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와 상호작용할 수 있도록 인가하는 데 사용하는 메커니즘입니다.
|
||||
|
||||
@@ -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`를 기반으로 완전한 보안 시스템을 구현할 수 있는 도구가 있습니다.
|
||||
|
||||
|
||||
@@ -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>도 있습니다. 🚀
|
||||
|
||||
@@ -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>를 확인하세요.
|
||||
|
||||
@@ -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`는 파일과 테스트를 자동으로 감지하고 실행한 다음, 결과를 보고할 것입니다.
|
||||
|
||||
테스트를 다음 명령어로 실행하세요.
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
여기까지 모두 읽고 이해했다면, 이제 많은 개발자들보다 가상 환경을 **훨씬 더 깊이 있게 이해**하게 되셨습니다. 🤓
|
||||
여기까지 모두 읽고 이해했다면, 이제 많은 개발자들보다 가상 환경에 대해 **훨씬 더 많이** 알게 된 것입니다. 🤓
|
||||
|
||||
이런 세부적인 내용을 알고 있으면, 언젠가 복잡해 보이는 문제를 디버깅할 때 분명히 큰 도움이 될 것입니다. 이제는 **이 모든 것들이 내부에서 어떻게 작동하는지** 알고 있기 때문입니다. 😎
|
||||
이 세부 사항을 알고 있으면, 나중에 복잡해 보이는 무언가를 디버깅할 때 아마도 도움이 될 것입니다. **내부에서 어떻게 작동하는지** 알고 있기 때문입니다. 😎
|
||||
|
||||
Reference in New Issue
Block a user