[FastAPI 공식문서] FastAPI - (3) 동시성과 async / await

이영락·2024년 10월 9일

개발자 기본기

목록 보기
37/53

🏖️ 동시성과 async / await

1. 경로 작동 함수에서의 async def 문법과 비동기 코드

비동기 코드는 컴퓨터가 다른 작업을 기다리는 동안, 그 시간에 다른 작업을 수행하는 방식으로 동작합니다. 비동기 함수 내부에서 await를 사용하여 해당 작업이 완료될 때까지 비동기적으로 대기할 수 있습니다.

  • 예: 데이터를 처리하기 위한 느린 파일 작업이 있을 때, 해당 파일이 처리되는 동안 다른 작업을 병행할 수 있습니다.

2. 요약

다음과 같은 코드에서 await를 사용하는 경우:

results = await some_library()

async def를 사용하여 경로 작동 함수를 선언하세요:

@app.get('/')
async def read_results():
    results = await some_library()
    return results
  • Tip: async def로 선언된 함수 내부에서만 await를 사용할 수 있습니다.

만약, 제3의 라이브러리가 await를 지원하지 않는다면, def를 사용하여 일반적인 함수로 선언해야 합니다:

@app.get('/')
def results():
    results = some_library()
    return results

FastAPI는 일반적인 defasync def를 혼용할 수 있고, 시스템 성능을 최적화하기 위해 올바르게 처리됩니다.

3. 비동기 코드의 이해

3.1 비동기 코드란?

비동기 코드는 언어가 다른 작업의 완료를 기다리며 실행을 일시정지하는 방식으로 작동합니다. 이는 느린 작업이 완료될 때까지 기다리며, 다른 작업을 처리할 수 있는 기회를 제공합니다.

  • I/O 작업 대기: 파일을 읽거나 네트워크 통신을 기다리는 시간이 비교적 느리기 때문에, 이 동안 다른 작업을 할 수 있습니다.
  • 네트워크 통신: 클라이언트로부터 데이터를 받거나, 서버에서 데이터를 전송하는 경우도 비동기적으로 처리될 수 있습니다.

3.2 비동기 코드의 동작 방식

비동기 시스템은 작업이 완료될 때까지 기다릴 필요 없이, 다른 작업을 처리할 수 있습니다. 컴퓨터는 그동안 다른 요청을 처리하거나, 자원을 활용해 작업을 처리하고, 작업이 끝나면 그 결과를 받아 다시 돌아옵니다.

4. 동시성 & 병렬성의 차이

4.1 동시성과 병렬성

동시성병렬성은 둘 다 동시에 여러 작업을 처리하는 방식과 관련이 있습니다. 그러나 두 개념은 본질적으로 다릅니다:

  • 동시성: 여러 작업을 번갈아가며 하나의 프로세서가 처리하는 방식. 예를 들어, 파일을 다운로드하면서 동시에 다른 파일을 읽을 수 있습니다.
  • 병렬성: 여러 프로세서가 동시에 각기 다른 작업을 처리하는 방식. 여러 프로세서가 각각 다른 작업을 동시에 실행합니다.

4.2 버거 예시로 이해하는 동시성

<동시 버거>

당신은 짝사랑 상대😍와 패스트푸드🍔를 먹으러 갔습니다. 당신은 점원💁이 당신 앞에 있는 사람들의 주문을 받을 동안 줄을 서서 기다리고 있습니다.

이제 당신의 순서가 되어서, 당신은 당신과 짝사랑 상대😍를 위한 두 개의 고급스러운 버거🍔를 주문합니다.

당신이 돈을 냅니다 💸.

점원 💁 은 주방 👨‍🍳 에 요리를 하라고 전달하고, 따라서 그들은 당신의 버거 🍔 를 준비해야한다는 사실을 알게됩니다(그들이 지금은 당신 앞 고객들의 주문을 준비하고 있을지라도 말입니다).

점원 💁 은 당신의 순서가 적힌 번호표를 줍니다.

기다리는 동안, 당신은 짝사랑 상대 😍 와 함께 테이블을 고르고, 자리에 앉아 오랫동안 (당신이 주문한 버거는 꽤나 고급스럽기 때문에 준비하는데 시간이 조금 걸립니다 ✨🍔✨) 대화를 나눕니다.

짝사랑 상대 😍 와 테이블에 앉아서 버거 🍔 를 기다리는 동안, 그 사람 😍 이 얼마나 멋지고, 사랑스럽고, 똑똑한지 감탄하며 시간을 보냅니다 ✨😍✨.

짝사랑 상대 😍 와 기다리면서 얘기하는 동안, 때때로, 당신은 당신의 차례가 되었는지 보기 위해 카운터의 번호를 확인합니다.

그러다 어느 순간, 당신의 차례가 됩니다. 카운터에 가서, 버거 🍔 를 받고, 테이블로 다시 돌아옵니다.

당신과 짝사랑 상대 😍 는 버거 🍔 를 먹으며 좋은 시간을 보냅니다 ✨.
당신이 이 이야기에서 컴퓨터 / 프로그램 🤖 이라고 상상해보십시오.

줄을 서서 기다리는 동안, 당신은 아무것도 하지 않고 😴 당신의 차례를 기다리며, 어떠한 "생산적인" 일도 하지 않습니다. 하지만 점원 💁 이 (음식을 준비하지는 않고) 주문을 받기만 하기 때문에 줄이 빨리 줄어들어서 괜찮습니다.

그다음, 당신이 차례가 오면, 당신은 실제로 "생산적인" 일 🤓 을 합니다. 당신은 메뉴를 보고, 무엇을 먹을지 결정하고, 짝사랑 상대 😍 의 선택을 묻고, 돈을 내고 💸 , 맞는 카드를 냈는지 확인하고, 비용이 제대로 지불되었는지 확인하고, 주문이 제대로 들어갔는지 확인을 하는 작업 등등을 수행합니다.

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

하지만 번호표를 받고 카운터에서 나와 테이블에 앉으면, 당신은 짝사랑 상대 😍 와 그 "작업" ⏯ 🤓 에 번갈아가며 🔀 집중합니다. 그러면 당신은 다시 짝사랑 상대 😍 에게 작업을 거는 매우 "생산적인" 일 🤓 을 합니다.

점원 💁 이 카운터 화면에 당신의 번호를 표시함으로써 "버거 🍔 가 준비되었습니다"라고 해도, 당신은 즉시 뛰쳐나가지는 않을 것입니다. 당신은 당신의 번호를 갖고있고, 다른 사람들은 그들의 번호를 갖고있기 때문에, 아무도 당신의 버거 🍔 를 훔쳐가지 않는다는 사실을 알기 때문입니다.

그래서 당신은 짝사랑 상대 😍 가 이야기를 끝낼 때까지 기다린 후 (현재 작업 완료 ⏯ / 진행 중인 작업 처리 🤓 ), 정중하게 미소짓고 버거를 가지러 가겠다고 말합니다 ⏸.

그다음 당신은 카운터에 가서 🔀 , 초기 작업을 이제 완료하고 ⏯ , 버거 🍔 를 받고, 감사하다고 말하고 테이블로 가져옵니다. 이로써 카운터와의 상호작용 단계 / 작업이 종료됩니다 ⏹.

이전 작업인 "버거 받기"가 종료되면 ⏹ "버거 먹기"라는 새로운 작업이 생성됩니다 🔀 ⏯.

동시성 버거 가게 시나리오:

  • 줄을 서서 주문을 하고, 버거가 준비될 동안 번호표를 받은 후 다른 일을 할 수 있습니다.
  • 이처럼 비동기 시스템에서는 작업이 완료될 때까지 기다리지 않고, 대기 시간에 다른 작업을 처리할 수 있습니다.
<병렬 버거¶>
이제 "동시 버거"가 아닌 "병렬 버거"를 상상해보십시오.

당신은 짝사랑 상대 😍 와 함께 병렬 패스트푸드 🍔 를 먹으러 갔습니다.

당신은 여러명(8명이라고 가정합니다)의 점원이 당신 앞 사람들의 주문을 받으며 동시에 요리 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳 도 하는 동안 줄을 서서 기다립니다.

당신 앞 모든 사람들이 버거가 준비될 때까지 카운터에서 떠나지 않고 기다립니다 🕙 . 왜냐하면 8명의 직원들이 다음 주문을 받기 전에 버거를 준비하러 가기 때문입니다.

마침내 당신의 차례가 왔고, 당신은 당신과 짝사랑 상대 😍 를 위한 두 개의 고급스러운 버거 🍔 를 주문합니다.

당신이 비용을 지불합니다 💸 .

점원이 주방에 갑니다 👨‍🍳 .

당신은 번호표가 없기 때문에 누구도 당신의 버거 🍔 를 대신 가져갈 수 없도록 카운터에 서서 기다립니다 🕙 .

당신과 짝사랑 상대 😍 는 다른 사람이 새치기해서 버거를 가져가지 못하게 하느라 바쁘기 때문에 🕙 , 짝사랑 상대에게 주의를 기울일 수 없습니다 😞 .

이것은 "동기" 작업이고, 당신은 점원/요리사 👨‍🍳 와 "동기화" 되었습니다. 당신은 기다리고 🕙 , 점원/요리사 👨‍🍳 가 버거 🍔 준비를 완료한 후 당신에게 주거나, 누군가가 그것을 가져가는 그 순간에 그 곳에 있어야합니다.

카운터 앞에서 오랫동안 기다린 후에 🕙 , 점원/요리사 👨‍🍳 가 당신의 버거 🍔 를 가지고 돌아옵니다.

당신은 버거를 받고 짝사랑 상대와 함께 테이블로 돌아옵니다.

단지 먹기만 하다가, 다 먹었습니다 🍔 ⏹.

카운터 앞에서 기다리면서 🕙 너무 많은 시간을 허비했기 때문에 대화를 하거나 작업을 걸 시간이 거의 없었습니다 😞 .

병렬 버거 가게 시나리오:

  • 여러 명의 점원이 동시에 버거를 만들지만, 기다리는 동안 손님은 버거가 나올 때까지 다른 일을 하지 못하고 기다려야 합니다.
  • 이는 동기적인 방식으로, 작업이 순차적으로 처리됩니다.

5. 동시성 + 병렬성: 웹 + 머신러닝¶

FastAPI를 사용하면 웹 개발에서는 매우 흔한 동시성의 이점을 얻을 수 있다.

머신러닝 시스템과 같이 CPU에 묶인 작업을 위해 병렬성과 멀티프로세싱(다수의 프로세스를 병렬적으로 동작시키는 것)을 이용하는 것도 가능합니다.

파이썬이 데이터 사이언스, 머신러닝과 특히 딥러닝에 의 주된 언어라는 간단한 사실에 더해서, 이것은 FastAPI를 데이터 사이언스 / 머신러닝 웹 API와 응용프로그램에 (다른 것들보다) 좋은 선택지가 되게 한다.

6. async와 await의 개념

최신 파이썬async def를 사용하여 비동기 작업을 처리할 수 있습니다. async def 함수 내부에서는 await 키워드를 사용해 특정 작업이 완료될 때까지 대기할 수 있습니다.

  • 예시:
async def get_burgers(number: int):
    return await some_async_function(number)

위 예시에서 await 키워드는 some_async_function() 작업이 완료될 때까지 기다리라고 명령하는 것입니다.

6.1 async 함수의 작동 방식

async def로 선언된 함수는 비동기적으로 처리되며, 함수 내에서 await 키워드를 사용해 대기 작업을 처리합니다.

  • 예시:
@app.get('/burgers')
async def read_burgers():
    burgers = await get_burgers(2)
    return burgers

여기서 FastAPIasync def를 이용해 대기 작업을 최적화하여 처리할 수 있으며, 이를 통해 웹 애플리케이션에서 높은 성능을 제공합니다.

7. 코루틴

코루틴은 async def 함수가 반환하는 것을 칭하는 매우 고급스러운 용어일 뿐이다.
파이썬은 그것이 시작되고 어느 시점에서 완료되지만 내부에 await가 있을 때마다 내부적으로 일시정지⏸될 수도 있는 함수와 유사한 것이라는 사실을 알고있다.

그러나 asyncawait와 함께 비동기 코드를 사용하는 이 모든 기능들은 "코루틴"으로 간단히 요약됩니다. 이것은 Go의 주된 핵심 기능인 "고루틴"에 견줄 수 있다.

8. FastAPI와의 활용

FastAPI는 비동기 웹 애플리케이션 개발에서 동시성과 병렬성을 모두 지원합니다. FastAPI를 사용하면 비동기 시스템의 이점을 활용해 매우 빠른 응답 속도를 얻을 수 있습니다.

또한, 병렬성과 비동기성을 동시에 사용할 수 있기 때문에 CPU에 묶인 작업(예: 이미지 처리, 머신러닝 모델 실행 등)도 효과적으로 처리할 수 있습니다.


profile
AI Engineer / 의료인공지능

0개의 댓글