# 동시성과 async / await { #concurrency-and-async-await }

*경로 처리 함수*에서의 `async def` 문법에 대한 세부사항과 비동기 코드, 동시성 및 병렬성에 대한 배경

## 바쁘신가요? { #in-a-hurry }

<abbr title="too long; didn't read - 너무 길어서 읽지 않음"><strong>TL;DR:</strong></abbr>

다음과 같이 `await`를 사용해 호출하라고 안내하는 제3자 라이브러리를 사용하는 경우:

```Python
results = await some_library()
```

다음처럼 *경로 처리 함수*를 `async def`를 사용해 선언하십시오:

```Python hl_lines="2"
@app.get('/')
async def read_results():
    results = await some_library()
    return results
```

/// note | 참고

`async def`로 생성된 함수 내부에서만 `await`를 사용할 수 있습니다.

///

---

데이터베이스, API, 파일시스템 등과 의사소통하는 제3자 라이브러리를 사용하고, 그것이 `await` 사용을 지원하지 않는 경우(현재 대부분의 데이터베이스 라이브러리가 그러합니다), *경로 처리 함수*를 일반적인 `def`를 사용해 선언하십시오:

```Python hl_lines="2"
@app.get('/')
def results():
    results = some_library()
    return results
```

---

만약 여러분의 애플리케이션이 (어째서인지) 다른 어떤 것과도 통신하고 그 응답을 기다릴 필요가 없다면, 내부에서 `await`를 사용할 필요가 없더라도 `async def`를 사용하세요.

---

잘 모르겠다면, 일반적인 `def`를 사용하세요.

---

**참고**: *경로 처리 함수*에서 필요한 만큼 `def`와 `async def`를 혼용할 수 있으며, 각각에 대해 가장 알맞은 옵션을 선택해 정의하면 됩니다. FastAPI가 올바르게 처리합니다.

어쨌든 위의 어떤 경우에서도 FastAPI는 여전히 비동기적으로 동작하며 매우 빠릅니다.

하지만 위의 단계를 따르면, 몇 가지 성능 최적화를 할 수 있습니다.

## 기술적 세부사항 { #technical-details }

최신 파이썬 버전은 **“코루틴”**이라고 하는 것을 사용하는 **“비동기 코드”**를 **`async` 및 `await`** 문법과 함께 지원합니다.

아래 섹션들에서 해당 문장을 부분별로 살펴보겠습니다:

* **비동기 코드**
* **`async`와 `await`**
* **코루틴**

## 비동기 코드 { #asynchronous-code }

비동기 코드는 언어 💬 가 코드의 어느 한 부분에서 컴퓨터/프로그램 🤖 에게, 어느 시점에는 어딘가에서 *다른 무언가*가 끝날 때까지 기다려야 한다고 말할 수 있는 방법이 있다는 의미입니다. 그 *다른 무언가*를 "slow-file" 📝 이라고 해보겠습니다.

따라서 그 시간 동안 컴퓨터는 "slow-file" 📝 이 끝나는 동안 다른 작업을 하러 갈 수 있습니다.

그 다음 컴퓨터/프로그램 🤖 은 다시 기다리는 중이기 때문에 기회가 있을 때마다 돌아오거나, 혹은 그 시점에 해야 할 작업을 모두 끝낼 때마다 돌아옵니다. 그리고 기다리던 작업 중 이미 끝난 것이 있는지 확인하면서, 해야 했던 작업을 수행합니다.

다음으로, 완료된 첫 번째 작업(우리의 "slow-file" 📝 이라고 해보겠습니다)을 가져와서, 그에 대해 해야 했던 작업을 계속합니다.

이 "다른 무언가를 기다리는 것"은 일반적으로 프로세서와 RAM 메모리 속도에 비해 상대적으로 "느린" <abbr title="Input and Output - 입력/출력">I/O</abbr> 작업을 의미합니다. 예를 들어 다음을 기다리는 것입니다:

* 네트워크를 통해 클라이언트가 데이터를 보내는 것
* 네트워크를 통해 클라이언트가 여러분의 프로그램이 보낸 데이터를 받는 것
* 시스템이 디스크의 파일 내용을 읽어서 프로그램에 전달하는 것
* 프로그램이 시스템에 전달한 내용을 디스크에 쓰는 것
* 원격 API 작업
* 데이터베이스 작업이 완료되는 것
* 데이터베이스 쿼리가 결과를 반환하는 것
* 기타 등등

실행 시간의 대부분이 <abbr title="Input and Output - 입력/출력">I/O</abbr> 작업을 기다리는 데 소비되기 때문에, 이를 "I/O bound" 작업이라고 부릅니다.

이것은 컴퓨터/프로그램이 느린 작업과 "동기화"되어, 아무것도 하지 않은 채 그 작업이 끝나는 정확한 시점만 기다렸다가 결과를 가져와 일을 계속할 필요가 없기 때문에 "비동기"라고 불립니다.

대신 "비동기" 시스템에서는, 작업이 끝나면 컴퓨터/프로그램이 하러 갔던 일을 마칠 때까지 잠시(몇 마이크로초) 줄에서 기다렸다가, 다시 돌아와 결과를 받아 이를 사용해 작업을 계속할 수 있습니다.

"동기"(“비동기”의 반대)는 보통 "순차"라는 용어로도 불리는데, 컴퓨터/프로그램이 다른 작업으로 전환하기 전에 모든 단계를 순서대로 따르기 때문이며, 그 단계들에 기다림이 포함되어 있더라도 마찬가지입니다.

### 동시성과 햄버거 { #concurrency-and-burgers }

위에서 설명한 **비동기** 코드에 대한 개념은 때때로 **"동시성"**이라고도 불립니다. 이는 **"병렬성"**과는 다릅니다.

**동시성**과 **병렬성**은 모두 "대략 같은 시간에 일어나는 서로 다른 일들"과 관련이 있습니다.

하지만 *동시성*과 *병렬성*의 세부적인 개념에는 꽤 차이가 있습니다.

차이를 보기 위해, 다음의 햄버거 이야기를 상상해보세요:

### 동시 햄버거 { #concurrent-burgers }

여러분은 짝사랑 상대와 패스트푸드를 먹으러 갔고, 점원이 여러분 앞 사람들의 주문을 받는 동안 줄을 서서 기다립니다. 😍

<img src="/img/async/concurrent-burgers/concurrent-burgers-01.png" class="illustration">

이제 여러분 차례가 되어, 여러분과 짝사랑 상대를 위해 매우 고급스러운 햄버거 2개를 주문합니다. 🍔🍔

<img src="/img/async/concurrent-burgers/concurrent-burgers-02.png" class="illustration">

점원은 주방의 요리사에게 무언가를 말해, (지금은 앞선 손님들의 주문을 준비하고 있더라도) 여러분의 햄버거를 준비해야 한다는 것을 알게 합니다.

<img src="/img/async/concurrent-burgers/concurrent-burgers-03.png" class="illustration">

여러분이 돈을 냅니다. 💸

점원은 여러분 차례 번호를 줍니다.

<img src="/img/async/concurrent-burgers/concurrent-burgers-04.png" class="illustration">

기다리는 동안, 여러분은 짝사랑 상대와 함께 자리를 고르고 앉아 오랫동안 대화를 나눕니다(여러분의 햄버거는 매우 고급스럽기 때문에 준비하는 데 시간이 좀 걸립니다).

짝사랑 상대와 테이블에 앉아 햄버거를 기다리는 동안, 그 사람이 얼마나 멋지고 귀엽고 똑똑한지 감탄하며 시간을 보낼 수 있습니다 ✨😍✨.

<img src="/img/async/concurrent-burgers/concurrent-burgers-05.png" class="illustration">

기다리며 대화하는 동안, 때때로 여러분은 카운터에 표시되는 번호를 확인해 여러분 차례인지 봅니다.

그러다 어느 순간 마침내 여러분 차례가 됩니다. 여러분은 카운터에 가서 햄버거를 받고, 테이블로 돌아옵니다.

<img src="/img/async/concurrent-burgers/concurrent-burgers-06.png" class="illustration">

여러분과 짝사랑 상대는 햄버거를 먹으며 좋은 시간을 보냅니다. ✨

<img src="/img/async/concurrent-burgers/concurrent-burgers-07.png" class="illustration">

/// info | 정보

아름다운 일러스트: <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">Ketrina Thompson</a>. 🎨

///

---

이 이야기에서 여러분이 컴퓨터/프로그램 🤖 이라고 상상해보세요.

줄을 서 있는 동안, 여러분은 그냥 쉬고 😴, 차례를 기다리며, 그다지 "생산적인" 일을 하지 않습니다. 하지만 점원은 주문만 받지(음식을 준비하진 않기) 때문에 줄이 빠르게 줄어들어 괜찮습니다.

그 다음 여러분 차례가 되면, 여러분은 실제로 "생산적인" 일을 합니다. 메뉴를 처리하고, 무엇을 먹을지 결정하고, 짝사랑 상대의 선택을 확인하고, 결제하고, 올바른 현금이나 카드를 냈는지 확인하고, 정확히 청구되었는지 확인하고, 주문에 올바른 항목들이 들어갔는지 확인하는 등등을 합니다.

하지만 그 다음에는, 아직 햄버거를 받지 못했더라도, 햄버거가 준비될 때까지 기다려야 🕙 하므로 점원과의 작업은 "일시정지" ⏸ 상태입니다.

하지만 번호를 들고 카운터에서 벗어나 테이블에 앉으면, 여러분은 짝사랑 상대에게 관심을 전환 🔀 하고, 그에 대한 "작업" ⏯ 🤓 을 할 수 있습니다. 그러면 여러분은 다시 짝사랑 상대에게 작업을 거는 매우 "생산적인" 일을 하게 됩니다 😍.

그 다음 점원 💁 이 카운터 화면에 여러분 번호를 띄워 "햄버거를 만들었어요"라고 말하지만, 표시된 번호가 여러분 차례로 바뀌었다고 해서 즉시 미친 듯이 뛰어가지는 않습니다. 여러분은 여러분 번호를 갖고 있고, 다른 사람들은 그들의 번호를 갖고 있으니, 아무도 여러분 햄버거를 훔쳐갈 수 없다는 것을 알기 때문입니다.

그래서 여러분은 짝사랑 상대가 이야기를 끝낼 때까지 기다린 다음(현재 작업 ⏯ / 처리 중인 작업 🤓 을 끝내고), 부드럽게 미소 지으며 햄버거를 가지러 가겠다고 말합니다 ⏸.

그 다음 여러분은 카운터로 가서 🔀, 이제 끝난 초기 작업 ⏯ 으로 돌아와 햄버거를 받고, 감사 인사를 하고, 테이블로 가져옵니다. 이로써 카운터와 상호작용하는 그 단계/작업이 끝납니다 ⏹. 그리고 이는 새로운 작업인 "햄버거 먹기" 🔀 ⏯ 를 만들지만, 이전 작업인 "햄버거 받기"는 끝났습니다 ⏹.

### 병렬 햄버거 { #parallel-burgers }

이제 이것이 "동시 햄버거"가 아니라 "병렬 햄버거"라고 상상해봅시다.

여러분은 짝사랑 상대와 함께 병렬 패스트푸드를 먹으러 갑니다.

여러분은 여러 명(예: 8명)의 점원이 동시에 요리사이기도 하여 여러분 앞 사람들의 주문을 받는 동안 줄을 서 있습니다.

여러분 앞의 모든 사람들은, 8명의 점원 각각이 다음 주문을 받기 전에 바로 햄버거를 준비하러 가기 때문에, 카운터를 떠나지 않고 햄버거가 준비될 때까지 기다립니다.

<img src="/img/async/parallel-burgers/parallel-burgers-01.png" class="illustration">

마침내 여러분 차례가 되어, 여러분과 짝사랑 상대를 위해 매우 고급스러운 햄버거 2개를 주문합니다.

여러분이 돈을 냅니다 💸.

<img src="/img/async/parallel-burgers/parallel-burgers-02.png" class="illustration">

점원은 주방으로 갑니다.

여러분은 번호표가 없으므로, 다른 사람이 여러분보다 먼저 햄버거를 가져가지 못하도록 카운터 앞에 서서 기다립니다 🕙.

<img src="/img/async/parallel-burgers/parallel-burgers-03.png" class="illustration">

여러분과 짝사랑 상대는 햄버거가 나오면 다른 사람이 끼어들어 가져가지 못하게 하느라 바쁘기 때문에, 짝사랑 상대에게 집중할 수 없습니다. 😞

이것은 "동기" 작업이며, 여러분은 점원/요리사 👨‍🍳 와 "동기화"되어 있습니다. 점원/요리사 👨‍🍳 가 햄버거를 완성해 여러분에게 주는 정확한 순간에 그 자리에 있어야 하므로, 여러분은 기다려야 🕙 하고, 그렇지 않으면 다른 사람이 가져갈 수도 있습니다.

<img src="/img/async/parallel-burgers/parallel-burgers-04.png" class="illustration">

그러다 점원/요리사 👨‍🍳 가 카운터 앞에서 오랫동안 기다린 🕙 끝에 마침내 햄버거를 가지고 돌아옵니다.

<img src="/img/async/parallel-burgers/parallel-burgers-05.png" class="illustration">

여러분은 햄버거를 받아 짝사랑 상대와 테이블로 갑니다.

그냥 먹고, 끝입니다. ⏹

<img src="/img/async/parallel-burgers/parallel-burgers-06.png" class="illustration">

대부분의 시간을 카운터 앞에서 기다리는 데 🕙 썼기 때문에, 대화하거나 작업을 걸 시간은 많지 않았습니다. 😞

/// info | 정보

아름다운 일러스트: <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">Ketrina Thompson</a>. 🎨

///

---

이 병렬 햄버거 시나리오에서, 여러분은 두 개의 프로세서(여러분과 짝사랑 상대)를 가진 컴퓨터/프로그램 🤖 이며, 둘 다 기다리고 🕙 오랫동안 "카운터에서 기다리기" 🕙 에 주의를 ⏯ 기울입니다.

패스트푸드점에는 8개의 프로세서(점원/요리사)가 있습니다. 동시 햄버거 가게는 2개(점원 1명, 요리사 1명)만 있었을 것입니다.

하지만 여전히 최종 경험은 그다지 좋지 않습니다. 😞

---

이것이 햄버거의 병렬 버전에 해당하는 이야기입니다. 🍔

좀 더 "현실적인" 예시로, 은행을 상상해보세요.

최근까지 대부분의 은행에는 여러 은행원 👨‍💼👨‍💼👨‍💼👨‍💼 과 긴 줄 🕙🕙🕙🕙🕙🕙🕙🕙 이 있었습니다.

모든 은행원이 한 고객씩 순서대로 모든 일을 처리합니다 👨‍💼⏯.

그리고 여러분은 오랫동안 줄에서 기다려야 🕙 하며, 그렇지 않으면 차례를 잃습니다.

아마 은행 🏦 업무를 보러 갈 때 짝사랑 상대 😍 를 데려가고 싶지는 않을 것입니다.

### 햄버거 예시의 결론 { #burger-conclusion }

"짝사랑 상대와의 패스트푸드점 햄버거" 시나리오에서는 기다림 🕙 이 많기 때문에, 동시 시스템 ⏸🔀⏯ 을 사용하는 것이 훨씬 더 합리적입니다.

대부분의 웹 애플리케이션이 그렇습니다.

매우 많은 사용자들이 있고, 서버는 그들의 좋지 않은 연결을 통해 요청이 전송되기를 기다립니다 🕙.

그리고 응답이 돌아오기를 다시 기다립니다 🕙.

이 "기다림" 🕙 은 마이크로초 단위로 측정되지만, 모두 합치면 결국 꽤 많은 대기 시간이 됩니다.

그래서 웹 API에는 비동기 ⏸🔀⏯ 코드를 사용하는 것이 매우 합리적입니다.

이러한 종류의 비동기성은 NodeJS가 인기 있는 이유(비록 NodeJS가 병렬은 아니지만)이자, 프로그래밍 언어로서 Go의 강점입니다.

그리고 이것이 **FastAPI**로 얻는 것과 같은 수준의 성능입니다.

또한 병렬성과 비동기성을 동시에 사용할 수 있으므로, 대부분의 테스트된 NodeJS 프레임워크보다 더 높은 성능을 얻고, C에 더 가까운 컴파일 언어인 Go와 동등한 성능을 얻을 수 있습니다 <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(모두 Starlette 덕분입니다)</a>.

### 동시성이 병렬성보다 더 나은가요? { #is-concurrency-better-than-parallelism }

아니요! 그게 이 이야기의 교훈은 아닙니다.

동시성은 병렬성과 다릅니다. 그리고 많은 기다림이 포함되는 **특정한** 시나리오에서는 더 낫습니다. 그 때문에 웹 애플리케이션 개발에서는 일반적으로 병렬성보다 훨씬 더 낫습니다. 하지만 모든 것에 해당하진 않습니다.

그래서 균형을 맞추기 위해, 다음의 짧은 이야기를 상상해보세요:

> 여러분은 크고 더러운 집을 청소해야 합니다.

*네, 이게 전부입니다*.

---

어디에도 기다림 🕙 은 없고, 집의 여러 장소에서 해야 할 일이 많을 뿐입니다.

햄버거 예시처럼 거실부터, 그 다음은 부엌처럼 순서를 정할 수도 있지만, 어떤 것도 기다리지 🕙 않고 계속 청소만 하기 때문에, 순서는 아무런 영향을 주지 않습니다.

순서가 있든 없든(동시성) 끝내는 데 걸리는 시간은 같고, 같은 양의 일을 하게 됩니다.

하지만 이 경우, 전(前) 점원/요리사이자 현(現) 청소부가 된 8명을 데려올 수 있고, 각자(그리고 여러분)가 집의 구역을 하나씩 맡아 청소한다면, 추가 도움과 함께 모든 일을 **병렬**로 수행하여 훨씬 더 빨리 끝낼 수 있습니다.

이 시나리오에서 (여러분을 포함한) 각 청소부는 프로세서가 되어, 맡은 일을 수행합니다.

그리고 실행 시간의 대부분이 기다림이 아니라 실제 작업에 쓰이고, 컴퓨터에서 작업은 <abbr title="Central Processing Unit - 중앙 처리 장치">CPU</abbr>가 수행하므로, 이런 문제를 "CPU bound"라고 부릅니다.

---

CPU bound 작업의 흔한 예시는 복잡한 수학 처리가 필요한 것들입니다.

예를 들어:

* **오디오** 또는 **이미지** 처리
* **컴퓨터 비전**: 이미지는 수백만 개의 픽셀로 구성되며, 각 픽셀은 3개의 값/색을 갖습니다. 보통 그 픽셀들에 대해 동시에 무언가를 계산해야 합니다.
* **머신러닝**: 보통 많은 "matrix"와 "vector" 곱셈이 필요합니다. 숫자가 있는 거대한 스프레드시트를 생각하고, 그 모든 수를 동시에 곱한다고 생각해보세요.
* **딥러닝**: 머신러닝의 하위 분야이므로 동일하게 적용됩니다. 다만 곱해야 할 숫자가 있는 스프레드시트가 하나가 아니라, 아주 큰 집합이며, 많은 경우 그 모델을 만들고/또는 사용하기 위해 특별한 프로세서를 사용합니다.

### 동시성 + 병렬성: 웹 + 머신러닝 { #concurrency-parallelism-web-machine-learning }

**FastAPI**를 사용하면 웹 개발에서 매우 흔한 동시성의 이점을( NodeJS의 주요 매력과 같은) 얻을 수 있습니다.

또한 머신러닝 시스템처럼 **CPU bound** 워크로드에 대해 병렬성과 멀티프로세싱(여러 프로세스를 병렬로 실행)을 활용할 수도 있습니다.

이것은 파이썬이 **데이터 사이언스**, 머신러닝, 특히 딥러닝의 주요 언어라는 단순한 사실과 더해져, FastAPI를 데이터 사이언스/머신러닝 웹 API 및 애플리케이션(그 외에도 많은 것들)에 매우 잘 맞는 선택으로 만들어 줍니다.

프로덕션에서 이 병렬성을 어떻게 달성하는지 보려면 [배포](deployment/index.md){.internal-link target=_blank} 섹션을 참고하세요.

## `async`와 `await` { #async-and-await }

최신 파이썬 버전에는 비동기 코드를 정의하는 매우 직관적인 방법이 있습니다. 이 방법은 이를 평범한 "순차" 코드처럼 보이게 하고, 적절한 순간에 여러분을 위해 "기다림"을 수행합니다.

결과를 주기 전에 기다림이 필요한 작업이 있고, 이러한 새로운 파이썬 기능을 지원한다면, 다음과 같이 작성할 수 있습니다:

```Python
burgers = await get_burgers(2)
```

여기서 핵심은 `await`입니다. 이는 파이썬에게 `get_burgers(2)`가 그 일을 끝낼 때까지 🕙 기다리도록 ⏸ 말하고, 그 결과를 `burgers`에 저장하기 전에 완료되기를 기다리라고 합니다. 이를 통해 파이썬은 그동안(예: 다른 요청을 받는 것처럼) 다른 일을 하러 갈 수 있다는 것 🔀 ⏯ 을 알게 됩니다.

`await`가 동작하려면, 이 비동기성을 지원하는 함수 내부에 있어야 합니다. 그러려면 `async def`로 선언하기만 하면 됩니다:

```Python hl_lines="1"
async def get_burgers(number: int):
    # 햄버거를 만들기 위한 비동기 처리를 수행
    return burgers
```

...`def` 대신:

```Python hl_lines="2"
# 비동기가 아닙니다
def get_sequential_burgers(number: int):
    # 햄버거를 만들기 위한 순차 처리를 수행
    return burgers
```

`async def`를 사용하면, 파이썬은 그 함수 내부에서 `await` 표현식에 주의해야 하며, 그 함수의 실행을 "일시정지" ⏸ 하고 다시 돌아오기 전에 다른 일을 하러 갈 수 있다는 것 🔀 을 알게 됩니다.

`async def` 함수를 호출하고자 할 때는, 그 함수를 "await" 해야 합니다. 따라서 아래는 동작하지 않습니다:

```Python
# 동작하지 않습니다. get_burgers는 async def로 정의되었습니다
burgers = get_burgers(2)
```

---

따라서, `await`로 호출할 수 있다고 말하는 라이브러리를 사용한다면, 다음과 같이 그것을 사용하는 *경로 처리 함수*를 `async def`로 만들어야 합니다:

```Python hl_lines="2-3"
@app.get('/burgers')
async def read_burgers():
    burgers = await get_burgers(2)
    return burgers
```

### 더 세부적인 기술적 사항 { #more-technical-details }

`await`는 `async def`로 정의된 함수 내부에서만 사용할 수 있다는 것을 눈치채셨을 것입니다.

하지만 동시에, `async def`로 정의된 함수는 "await" 되어야 합니다. 따라서 `async def`를 가진 함수는 `async def`로 정의된 함수 내부에서만 호출될 수 있습니다.

그렇다면, 닭이 먼저냐 달걀이 먼저냐처럼, 첫 번째 `async` 함수는 어떻게 호출할 수 있을까요?

**FastAPI**로 작업한다면 걱정할 필요가 없습니다. 그 "첫" 함수는 여러분의 *경로 처리 함수*가 될 것이고, FastAPI는 올바르게 처리하는 방법을 알고 있기 때문입니다.

하지만 FastAPI 없이 `async` / `await`를 사용하고 싶다면, 그것도 가능합니다.

### 여러분만의 async 코드 작성하기 { #write-your-own-async-code }

Starlette(그리고 **FastAPI**)는 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>를 기반으로 하고 있으며, 파이썬 표준 라이브러리 <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a>와 <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a> 모두와 호환됩니다.

특히, 코드에서 더 고급 패턴이 필요한 고급 동시성 사용 사례에서는 직접 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>를 사용할 수 있습니다.

그리고 FastAPI를 사용하지 않더라도, 높은 호환성을 확보하고 그 이점(예: *structured concurrency*)을 얻기 위해 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>로 여러분만의 async 애플리케이션을 작성할 수도 있습니다.

저는 AnyIO 위에 얇은 레이어로 또 다른 라이브러리를 만들었는데, 타입 어노테이션을 조금 개선하고 더 나은 **자동완성**, **인라인 오류** 등을 얻기 위한 것입니다. 또한 **이해**하고 **여러분만의 async 코드**를 작성하도록 돕는 친절한 소개와 튜토리얼도 제공합니다: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. 특히 **async 코드와 일반**(blocking/동기) 코드를 **결합**해야 한다면 아주 유용합니다.

### 비동기 코드의 다른 형태 { #other-forms-of-asynchronous-code }

`async`와 `await`를 사용하는 이 스타일은 언어에서 비교적 최근에 추가되었습니다.

하지만 비동기 코드를 다루는 일을 훨씬 더 쉽게 만들어 줍니다.

거의 동일한 문법이 최근 브라우저와 NodeJS의 최신 JavaScript에도 포함되었습니다.

하지만 그 이전에는 비동기 코드를 처리하는 것이 훨씬 더 복잡하고 어려웠습니다.

이전 버전의 파이썬에서는 스레드 또는 <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a>를 사용할 수 있었을 것입니다. 하지만 코드를 이해하고, 디버깅하고, 이에 대해 생각하는 것이 훨씬 더 복잡합니다.

이전 버전의 NodeJS/브라우저 JavaScript에서는 "callback"을 사용했을 것입니다. 이는 "callback hell"로 이어집니다.

## 코루틴 { #coroutines }

**코루틴**은 `async def` 함수가 반환하는 것에 대한 매우 고급스러운 용어일 뿐입니다. 파이썬은 그것이 함수와 비슷한 무언가로서 시작할 수 있고, 어느 시점에 끝나지만, 내부에 `await`가 있을 때마다 내부적으로도 일시정지 ⏸ 될 수 있다는 것을 알고 있습니다.

하지만 `async` 및 `await`와 함께 비동기 코드를 사용하는 이 모든 기능은 종종 "코루틴"을 사용한다고 요약됩니다. 이는 Go의 주요 핵심 기능인 "Goroutines"에 비견됩니다.

## 결론 { #conclusion }

위의 같은 문장을 다시 봅시다:

> 최신 파이썬 버전은 **“코루틴”**이라고 하는 것을 사용하는 **“비동기 코드”**를 **`async` 및 `await`** 문법과 함께 지원합니다.

이제 더 이해가 될 것입니다. ✨

이 모든 것이 FastAPI(Starlette을 통해)를 구동하고, 인상적인 성능을 내게 하는 원동력입니다.

## 매우 세부적인 기술적 사항 { #very-technical-details }

/// warning | 경고

이 부분은 아마 건너뛰어도 됩니다.

이것들은 **FastAPI**가 내부적으로 어떻게 동작하는지에 대한 매우 세부적인 기술사항입니다.

(코루틴, 스레드, 블로킹 등) 같은 기술 지식이 꽤 있고 FastAPI가 `async def`와 일반 `def`를 어떻게 처리하는지 궁금하다면, 계속 읽어보세요.

///

### 경로 처리 함수 { #path-operation-functions }

*경로 처리 함수*를 `async def` 대신 일반적인 `def`로 선언하면, (서버를 블로킹할 수 있으므로 직접 호출하는 대신) 외부 스레드풀에서 실행되고 그 결과를 await 합니다.

위에서 설명한 방식으로 동작하지 않는 다른 async 프레임워크를 사용해본 적이 있고, 아주 작은 성능 향상(약 100 나노초)을 위해 계산만 하는 사소한 *경로 처리 함수*를 일반 `def`로 정의하곤 했다면, **FastAPI**에서는 그 효과가 정반대가 될 수 있다는 점에 유의하세요. 이런 경우에는 *경로 처리 함수*에서 블로킹 <abbr title="Input/Output - 입력/출력: 디스크 읽기 또는 쓰기, 네트워크 통신.">I/O</abbr> 를 수행하는 코드를 사용하지 않는 한 `async def`를 사용하는 편이 더 낫습니다.

그럼에도 두 경우 모두, **FastAPI**는 이전에 사용하던 프레임워크보다 [여전히 더 빠를](index.md#performance){.internal-link target=_blank} 가능성이 높습니다(또는 최소한 비슷합니다).

### 의존성 { #dependencies }

[의존성](tutorial/dependencies/index.md){.internal-link target=_blank}에도 동일하게 적용됩니다. 의존성이 `async def` 대신 표준 `def` 함수라면, 외부 스레드풀에서 실행됩니다.

### 하위 의존성 { #sub-dependencies }

서로를 필요로 하는 여러 의존성과 [하위 의존성](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank}을 함수 정의의 매개변수로 가질 수 있으며, 그중 일부는 `async def`로, 다른 일부는 일반 `def`로 생성되었을 수 있습니다. 그래도 정상 동작하며, 일반 `def`로 생성된 것들은 "await"되는 대신 (스레드풀에서) 외부 스레드에서 호출됩니다.

### 다른 유틸리티 함수 { #other-utility-functions }

직접 호출하는 다른 모든 유틸리티 함수는 일반 `def`나 `async def`로 생성될 수 있으며, FastAPI는 호출 방식에 영향을 주지 않습니다.

이는 FastAPI가 여러분을 위해 호출하는 함수(즉, *경로 처리 함수*와 의존성)와 대비됩니다.

유틸리티 함수가 `def`로 만든 일반 함수라면, 스레드풀이 아니라 직접(코드에 작성한 대로) 호출됩니다. 그리고 `async def`로 생성된 함수라면, 코드에서 호출할 때 그 함수를 `await` 해야 합니다.

---

다시 말하지만, 이것들은 아마도 이를 찾고 있었던 경우에 유용한 매우 세부적인 기술사항입니다.

그렇지 않다면, 위 섹션의 가이드라인이면 충분합니다: <a href="#in-a-hurry">바쁘신가요?</a>.
