Django Channels를 이용하여 처음으로 간단한 웹 프로젝트를 구성하였고, 이 과정에서 겪었던 이슈들을 기록합니다.
시작전에
배포하기 전 Channel layer로 InMemoryChannelLayer를 사용하고 있었고, 이를 RedisChannelLayer로 변경하면서 생긴 이슈를 먼저 설명하고 넘어가겠습니다.
Channel layer로 Redis 이용하기
기존에 추가적인 DB 이용 없이 간단하게 프로젝트를 구성하기 위해서 InMemoryChannelLayer를 사용하였다. Heroku에서 간단하게 add-ons을 이용하여 redis를 추가해줄 수 있었기에, 권장하지 않는 InMemoryChannelLayer를 버리고 RedisChannelLayer로 넘어가기로 결심했다.
# settings.py
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer',
}
}
기존의 해당 부분을 아래와 같이 간단히 바꿔주는 것으로 해결이 가능하다. 보통은.
#settings.py
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [os.environ.get('REDIS_URL', 'redis://localhost:6379')],
},
}
}
channel-layer를 변경하고 channel_group에 2개 이상의 클라이언트를 연결하면, 특정 함수를 실행 시키는 과정에서 에러(ValueError: No handler for message type 'new_type')가 발생했다. 에러를 발생시키는 아래와 유사했다.
# consumer.py
"""
event = {
'type': 'common_send',
# others
}
"""
async def common_send(self, event):
# event 변경
event['type'] = 'new_type'
await self.send_json(event)
여기서의 문제점은 parameter로 넘겨받은 event를 복제하거나 하지 않고 그대로 변경한 점이 문제가 되었다. group_send($GROUP, $EVENT)를 활용하여 consumer들에게 event를 전달하면, 같은 group에 포함된 각각의 consumer들은 event['type']을 확인하여 적절한 함수를 실행 시킨다.
InMemoryChannelLayer 같은 경우에는 각각 복사한 event를 넘겨 주어서 문제 없이 동작 된 것으로 추측된다. 하지만 RedisChannelLayer에서는 같은 event 객체를 통해 함수를 실행시켰기 때문에, 처음 event를 마주한 consumer는 올바르게 동작 했지만, 2번째 이상 부터는 type에 들어있는 'new_type'에 해당하는 함수를 찾을 수 없었기 때문에 문제가 발생했다.
event를 복제해서 이용하는 것으로 위의 문제를 해결할 수 있었다.
Heroku Django Tutorial 따라하기
기존에 Heroku에 Django를 배포하기 위해서는 몇 가지 설정들이 필요했지만, 글 작성기준으로 Configuring Django Apps for Heroku를 통해 정말 간단하게 해결 할 수 있었다.
pip install django-heroku
pip install gunicorn
pip install psycopg2 # for postgresql
# Procfile
web: gunicorn myproject.wsgi
# settings.py
import django_heroku
# Activate Django-Heroku.
django_heroku.settings(locals())
이렇게 파일을 작성하고 heroku app에 add-on으로 postgresql을 하나 붙여주면 끝이다. settings.py에서 추가적인 DB 설정이 필요하지 않고, local에서 사용하는 DB 설정을 그대로 남겨놔도 문제 없이 알아서 잘 배포된다.
ASGI
ASGI, or the Asynchronous Server Gateway Interface
위의 과정까지 따라하고 실행하면 WSGI를 이용해서 Django 프로젝트를 실행시킨다. Channels는 WSGI를 통해서는 실행시킬 수 없다. 위의 상태로 배포하면, 웹소켓에 연결하는 과정에서 404 에러를 마주하게 될 것이다. 그럼 ASGI를 이용하여 배포하는 방법을 알아보자.
# myproject/routing.py
ASGI_APPLICATION = "myproject.routing.application"
...
# myproject/asgi.py
import os
import django
from channels.routing import get_default_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
django.setup()
application = get_default_application()
pip install daphne
# Procfile
daphne mafia.asgi:application --port $PORT --bind 0.0.0.0 -v2
위와 같이 파일들을 변경해주고, Heroku에 배포하면 드디어 정상 작동을 하는 모습을 확인 할 수 있을 것이다.
이와 같이 설정하면, 웹소켓과 관련되지 않은 부분도 모두 ASGI를 이용하게 된다. 이는 좋지 않은 방식인데 이와 관련해서는 추가적으로 찾아보면 해결 방법을 찾을 수 있을 것이다. 이 글에서는 다루지 않을 것이다.
References
https://channels.readthedocs.io/en/latest/deploying.html
https://devcenter.heroku.com/articles/django-app-configuration
https://channels.readthedocs.io/en/latest/asgi.html
http://asgi.readthedocs.io/