비동기 View (async def)를 활용한 I/O 바운드 작업 성능 향상

Django 3.1부터 지원되는 비동기 View를 활용하여 I/O 바운드 작업의 성능을 획기적으로 개선하는 방법

비동기가 필요한 이유

  • I/O 바운드 작업: 외부 API 호출, 데이터베이스 쿼리, 파일 읽기/쓰기 등 입출력 시스템의 응답 속도에 의해 제한되는 작업

  • 동기 방식의 문제: I/O 작업 완료까지 서버 자원이 대기하여 전체 처리량 저하

  • 비동기 방식의 장점: I/O 대기 시간 동안 다른 요청을 처리하여 제한된 자원으로 더 많은 동시 요청 처리 가능

async def View 기본 사용법

# 동기 View - 순차적 처리 (약 2초 소요)
class SyncApiView(APIView):
    def get(self, request, *args, **kwargs):
        weather_data = requests.get("https://api.weather.com/seoul").json()
        restaurant_data = requests.get("https://api.restaurants.com/gangnam").json()
        return Response({"weather": weather_data, "restaurants": restaurant_data})

# 비동기 View - 동시 처리 (약 1초 소요)
class AsyncApiView(APIView):
    async def get(self, request, *args, **kwargs):
        async with aiohttp.ClientSession() as session:
            tasks = [
                fetch_url(session, "https://api.weather.com/seoul"),
                fetch_url(session, "https://api.restaurants.com/gangnam")
            ]
            results = await asyncio.gather(*tasks)
        return Response({"weather": results[0], "restaurants": results[1]})

Django ORM과 비동기

  • Django 4.1부터 ORM의 대부분 작업이 비동기 지원

  • 기존 메서드명 앞에 a가 붙은 비동기 메서드 사용: aget, acreate, aupdate, adelete

  • 비동기를 지원하는 DB 드라이버 필요: PostgreSQL용 psycopg3, MySQL용 aiomysql

# 동기 ORM
user = User.objects.get(pk=1)

# 비동기 ORM
user = await User.objects.aget(pk=1)

주요 주의사항

"Async All the Way" 원칙

  • 비동기 함수 안에서 동기 I/O 함수를 직접 호출하면 이벤트 루프 차단

  • 모든 I/O 관련 라이브러리를 비동기 지원 버전으로 교체 필요

sync_to_async 어댑터

from asgiref.sync import sync_to_async

async_send_sms = sync_to_async(send_legacy_sms, thread_sensitive=True)
result = await async_send_sms("010-1234-5678", "Hello")
  • 레거시 동기 코드를 어쩔 수 없이 사용해야 할 때만 최후의 수단으로 활용

  • 스레드 오버헤드 발생으로 성능에 민감한 코드에서는 남용 금지

async def 사용 기준

  • 사용 권장: 여러 I/O 작업을 동시 실행 가능하거나, 단일 I/O 작업이 매우 느린 경우 (500ms 이상)

  • 사용 불필요: CPU 바운드 작업이 주를 이루는 경우

배포 환경 고려사항

  • ASGI 서버 필요: Uvicorn 사용 (WSGI 서버로는 동작 불가)

  • 커넥션 풀링: 더 많은 동시 요청 처리로 인한 DB 커넥션 부족 방지를 위해 PgBouncer 등 활용

  • 실무 배포: Gunicorn + Uvicorn Worker 조합 사용

gunicorn myproject.asgi:application -k uvicorn.workers.UvicornWorker -w 4

Last updated