안녕하세요.
이번 글에서는 FastAPI로 코드 작성 시
sync/async 설정과 관련하여 유의해야할 사항을 정리해보겠습니다.
결론부터 말씀드리면
"비동기 함수들로 올바르게 구성된 엔드포인트 함수는 async def로 작성하는 것이 좋지만,
동기 함수로 실행해야 하는 경우라면 단순 def로 작성해야 한다." 입니다.
예시 코드
다음과 같은 엔드포인트를 구성하는 함수가 있다고 가정하겠습니다.
# 올바른 선언 예시: async def 내에 비동기 함수
@app.get('/wait')
async def example():
await asyncio.sleep(5) # 비동기 함수
return 0
이 경우는 비동기 함수로 구성된 올바른 async def 엔드포인트 함수 예시로
5초 대기 후 응답을 반환하는 로직이 실행될 것이며,
wait를 동시에 여러번 호출해도 각 호출은 5초 이후 응답이 정상적으로 반환됩니다.
그러나, 다음과 같이 동기 함수가 async def 내에 선언되었다면 이야기는 달라집니다.
# 잘못된 선언 예시: async def 내에 동기 함수
@app.get('/wait')
async def example():
time.sleep(5) # 동기 함수
return 0
이렇게 선언한 경우, 동시에 엔드포인트를 여러번 호출하게 된다면
각 호출은 앞의 호출의 응답이 올때까지 기다려야하며,
2번째 호출은 10초, 3번째 호출은 15초, ... 이런 식으로 큰 delay가 발생하게 됩니다.
동기 함수가 어쩔 수 없이 포함되어야 하는 로직이라면
다음과 같이 def 내에 선언해야 병목 현상을 막을 수 있습니다.
# 올바른 선언 예시: def 내에 동기 함수
@app.get('/wait')
def example():
time.sleep(5) # 동기 함수
return 0
이 경우도 첫 경우와 마찬가지로 같은 엔드포인트를 여러번 호출해도
5초 이후에는 응답이 잘 도착하는 것을 확인할 수 있습니다.
현상 원인 분석
이러한 현상이 나타나는 원인을 분석하면 다음과 같습니다.
async def 엔드포인트 함수: main thread 내에서 실행
def 엔드포인트 함수: thread pool에서 별도의 thread 할당
즉, async def 엔드포인트 내에 시간이 오래 걸리는 동기 함수가 포함되어 있다면
메인 쓰레드 내에서 병목이 발생하여 위와 같은 delay 현상이 발생 가능하고
이는 성능 저하로 이어질 수 있습니다.
다만, def 엔드포인트의 경우에는 쓰레드 풀에서 별도의 쓰레드를 할당받아
각 호출은 독립적인 작업을 수행하므로, 이러한 병목에서 자유롭습니다.
이러한 현상을 FastAPI 공식 문서에서는 다음과 같이 비유하고 있습니다.
async def 엔드포인트 함수: 주문 카운터는 1곳이나 주문 후 번호표를 받고 와서 기다릴 수 있음
def 엔드포인트 함수: 주문을 여러 카운터에서 받으나 주문 후 그 카운터에서 계속 기다려야 함
해당 내용을 상세히 설명한 FastAPI 공식 문서 링크는 다음과 같으니
더 궁금하신분들은 확인해보세요.
Concurrency and async / await - FastAPI
FastAPI framework, high performance, easy to learn, fast to code, ready for production
fastapi.tiangolo.com
이 글이 FastAPI 서버 구현 과정에서 도움이 되시기를 기원하겠습니다.
잘 봐주셔서 감사드립니다.
'Python > Backend' 카테고리의 다른 글
FastAPI에서 Form 데이터에 대한 무결성 검증 방법 (0) | 2025.03.07 |
---|---|
FastAPI에서 docs 및 redoc에 상세 response model 부여 방법 - 오류 명세 작성 예시 (2) | 2025.01.20 |
FastAPI 서버를 HTTPS로 실행하는 방법 (2) | 2024.12.01 |