Published on

[Django] 장고의 비동기 뷰와 ORM

Authors
  • avatar
    Name
    Almer Minified
    Twitter

[Django] 비동기 뷰와 ORM까지 알아보기

비동기 작업이란?

비동기는 I/O 처리해야하는 네트워크 요청이나 데이터베이스 쿼리에서 대기 시간을 줄이고 서버의 부하를 줄이는 데에 편리하다. 동기로 하면 동시 작업이 안되는데 비동기로 하면 그게 가능하다.

기본적인 async 사용법

장고에서 비동기 뷰는 async def를 사용한다. 잘 생각해보면 Python의 비동기 함수 즉 async function을 의미한다. await 키워드를 사용하여 비동기적으로 실행될 수 있는 다른 함수나 메소드를 불러올 수 있게 된다. await나 promise나 비슷하다 javascript라면 promise, flutter의 Dart라면 future다.

from django.http import JsonResponse
from django.core.cache import cache

async def my_asyncview(request):
data = await cache.get('my_key')
return JsonResponse({'data': data})

위에서처럼 작업하면 캐시가 가져온 후에나 JsonResponse가 반환되게 된다. 캐시는 워낙 반응이 빠르니 좀 와닿는 예제로 보면

from django.http import JsonResponse
import httpx

async def sample_view(request):
async with httpx.AsyncClient() as client:
response = await client.get('https://yourwebsite.com')
data = response.json()
return JsonResponse(data)

이것인데 웹에 요청을 보내고 응답이 와야만 리턴을 하게된다. 컴퓨터에 여러개의 작업을 해야 한다면 저 요청을 보낸 후 응답을 기다리는동안 딴 걸 하면 되는데 만약 저기를 비동기처리해주지 않으면 저 요청에 응답이 올때가지 한세월 기다리고 있게 된다.

쿼리셋에서 비동기 처리하기

데이터가 쌓이게 되면 쿼리셋도 은근 시간이 걸린다.

from myapp.models import MyModel

async def get_slowed_data(request):
  data = await SlowModel.objects.async_get(id=1)
  return JsonResponse({'data': data.name})

이런 코드가 있어서 만약 비동기로 async_get을 쓰지 않고 get을 쓴다면 저 응답이 오기 전까진 아무것도 못한다. 그러나 async_get을 쓰면 제대로 저 응답을 받은다음에 리턴해줄 수 있게 된다. 이런 점에서 장점이 드러난다. 혹시나 따로 gunicorn이나 uwsgi같은걸 쓰지 않는다면 효율적인 측면에서 이 방법을 쓰는 것이 적절하다.

ASGI 설정

비동기로 장고를 작업하려면 ASGI를 작업해야한다. 요즘 장고에는 그냥 내재되어 있다. 예전엔 따로 설치해줘야 했지만 요즘은 깔기만 해도 설치되어있더라. 여기에다가 비동기 서버를 연결만 해주면 된다. 그 서버의 역할을 할 수 잇는것은 Uvicorn이나 Daphne이다. 거의 사용법은 같은데 pip로 설치한 후에 asgi.py파일을 설정해주면 된다.

uvicorn asyncproject.asgi:application

이렇게 실행해주면된다.

백그라운드 작업을 위한 Celery사용법

Celery는 분산 작업을 위한 큐 시스템이다. 장고에서 비동기로 실행되어야 하는 작업을 처리해준다. Celery를 사용하면 데이터베이스 마이그레이션, 이메일 전송, 크론 잡 같은걸 비동기적으로 쓸 수 있다.

pip install celery

이렇게 하면 설치된다. 그 후엔 브로커라는 걸 설정해야하는데 브로커는 Redis를 의미한다. 즉 캐시서버이다. 캐시서버를 만약 Redis로 했다면

from celery import Celery

app = Celery('yourapp', broker='redis://localhost:6379/0')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

이렇게 설치하면 되는데 autodiscover_tasks를 해줘야 이게 제대로 인식한다. 그 다음엔 shared_task를 정의해줘야하는데 파일명을 tasks.py로 장고 프로젝트 내에 생성한다. settings.py랑 같은 위치에 설치하는게 편리할 수 있다.

from celery import shared_task

@shared_task
def add(x, y):
  return x + y

이렇게 하는 것이다. 그럼 이제

celery -A myproject worker -l info
``` 이 명령어를 통해
셀러리를 실행하면 잘 연동될 것이다. 저렇게 정의한 shared_task는 장고 앱
내에서 캐시 서버를 통해 관리되는데

from myapp.tasks import add add.delay(4, 4)

이런식으로 장고 내 어디서나 관리되지만 같은 캐시서버를 쓰는 장고 내 어떤
함수던 같은 결과를 공유하게 된다. 이걸로 레이스컨디션을 막는데 도움을 줄 수도 있다.