-
Django Channels 사용법 - 공식 문서개발일지 2022. 6. 21. 23:06
Django Channels 사용법 - 공식문서 내용정리
필요한 패키지
django, channels, channels-redis, Docker OR Elastic Cache
1. Django
- Django Cannels 이기 때문에 당연히 기본 베이스인 Django가 필요
- 최신 버전이면 상관없다고 들었지만 공식문서상 Django 3.0보다 작은 경우 해당 버전에 대한 문서를 참조!
2. Channels.
- Channels 를 사용하기 위한 패키지
3. channels-redis
- 소켓을 열고 닫을 때 캐시 데이터를 사용하는데 이때 접근할 수있도록 해주는 라이브러리(?)
- 메세지를 주고 받을 때 필요하다고 Error가 떴음 ( 없어도 Django 실행은 됨)
4. Docker OR Elastic Cache
- Docker 인 경우 공식문서에서 사용하고 자기 컴퓨터에서 진행할 때 사용함
- 일반 DB 가 아니라 메모리 캐시(?) 인 Redis를 사용
- 다른 컴퓨터를 사용할 경우 외부 컴퓨터로 URL 접속은 되지만 Docker 와 연결이되질 않음
- 그래서 아에 배포를 해서 테스트함 배포시 AWS의 Ealstic Cache를 사용함
- Docker도 될 것같은데 시도는 안해 봄
이 4가지는 pip 를 이용해서 설치
기존 Django와 비교하여 수정 및 만들어야 할 곳
1. asgi.py
import os from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter from django.core.asgi import get_asgi_application import chat.routing os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<setting.py 있는 이름>.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), # Just HTTP for now. (We can add other protocols later.) "websocket": AuthMiddlewareStack( URLRouter( <앱>.routing.websocket_urlpatterns ) ), })
ASGI가 자세히는 아직도 이해가 안되지만 미들웨어이고 해당 Application으로 실행을 했을 때 웹소켓용 URL를 받기 위한 세팅이라고 이해했다.
2. setting.py
# django-chaneels 를 위한 설정 ASGI_APPLICATION = "Setting.py 있는 폴더.asgi.application" # Channels Layer CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [('Redis 호스트', 6379)], }, }, }
hosts에는 Docker의 경우 Local에서 작업할 땐 127.0.0.1 을 적어줬고
Elastic Cache를 사용헀을 땐 기본 엔드포인트를 적어주었다. - 뒤에 포트 번호 떼는거 잊지 않기!!
여기까지가 Django 에게 Django-channels를 쓰기위한 세팅값을 주는 설정이였다.
3. urls.py , view.py
해당 파일들은 Django 기본 세팅이니 넘어가고 여기선 해당 View에서 렌더링해주는 HTML의 JavaScript 가 중요함
<script> {#const roomName = JSON.parse(document.getElementById('room-name').textContent);#} # 웹소켓을 연결할 URL 주소를 보내기 const chatSocket = new WebSocket( 'ws://' + window.location.host + '/ws/chat/' + {{ room_name }} + '/' ); # 웹소켓에 메세지가 도착헀을 때 동작 chatSocket.onmessage = function(e) { const data = JSON.parse(e.data); document.querySelector('#chat-log').value += (data.message + '\n'); }; # 웹소켓이 닫혔을 때 chatSocket.onclose = function(e) { console.error('Chat socket closed unexpectedly'); }; document.querySelector('#chat-message-input').focus(); document.querySelector('#chat-message-input').onkeyup = function(e) { if (e.keyCode === 13) { // enter, return document.querySelector('#chat-message-submit').click(); } }; # 웹소켓으로 메세지 보낼 때 document.querySelector('#chat-message-submit').onclick = function(e) { const messageInputDom = document.querySelector('#chat-message-input'); const message = messageInputDom.value; chatSocket.send( message ); messageInputDom.value = ''; }; </script>
4. 앱/routing.py
# chat/routing.py from django.urls import re_path from . import consumers from . import custom_consumers websocket_urlpatterns = [ re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()), ]
연결을 위한 웹소켓 URL를 생성, 이후 만들 Consumer.py와 연결하면된다.
routing 과 consumers 는 장고의 urls, views 관계와 비슷하다.
Django urls - routing.py
Django views - consumers.py5. 앱/comsumers.py
- 공식문서 참조
# chat/consumers.py import json from asgiref.sync import async_to_sync from channels.generic.websocket import WebsocketConsumer class ChatConsumer(WebsocketConsumer): # 연결됬을 때 실행되는 함수 def connect(self): # self.scope['url_route'] = /ws/localhost:8000/ws/chat/1 self.room_name = self.scope['url_route']['kwargs']['room_name'] # 임의로 그룹명을 만든 과정 -> 수정가능 self.room_group_name = 'chat_%s' % self.room_name # Join room group -> group 연결된 소켓을 같은 그룹명에 연결 # -> 같은 그룹명이면 같은 메세지 받음 async_to_sync(self.channel_layer.group_add)( self.room_group_name, self.channel_name ) self.accept() # 연결 해제 될때 실행되는 함수 def disconnect(self, close_code): # Leave room group async_to_sync(self.channel_layer.group_discard)( self.room_group_name, self.channel_name ) # 메세지를 보낼 때 실행 def receive(self, text_data): # Send message to room group async_to_sync(self.channel_layer.group_send)( self.room_group_name, { 'type': 'chat_message', 'message': text_data } ) # 메세지를 받을 때 실행 def chat_message(self, event): # 이벤트가 발생할 때 실행되므로 내부의 message를 꺼내야함 # Send message to WebSocket self.send(text_data=json.dumps({ 'message': event['message'] }))
여기까지가 실제 접속 및 통신을 할 수 있는 세팅이다.
이제 실제로 캐시 데이터를 사용하기 위해
docker
$ docker run -p 6379:6379 -d redis:5
AWS
Elastic Cache 에서 Redis 생성
참고로 보안그룹이나 포트설정 다해줘야하며 로컬과 연결했을때 동작을 안했음 ( 몇번 해보고 안되서 EC2와 연결 )
해당 세팅들이 끝났다면 실시간 채팅을 확인해 보면 된다!!
내가 생각 하는 동작 과정은
1. 브라우저에서 urls.py에서 작성한 url로 이동
2. 해당 url에 따른 view 파일 실행
3. 렌더링 된 HTML 파일 아래 <scriopt> 에서 websocket 연결
4. websocket 용 URL로 연결 시도
5. routing.py 의 URL에 있다면 해당 URL과 연결된 consumers.py 실행
6. 첫 연결이면 connect 함수 실행
7. Django 와 연결된 Channels에 그룹명을 만들어 해당 웹소켓을 넣음
8. 같은 그룹명에 속한 웹소켓은 서로 연결되며 연결된 상태를 유지
9. 연결 성공!
10. 메세지를 보내거나 받을 때 웹소켓을 열고 닫으며 공통 메모리 캐시를 읽고 쓰면서 전달 (동시에 일어나는 것 같음 )
11. 해당 URL - Consumer에 받기, 보내기 함수 실행
12. JavaScript로 화면에 표시!
정확하진 않지만 대략 이렇게 동작하는 것같다. 앞으로 더 많은 테스트와 커스텀을 진행해봐야 알 것 같다.
단점은 연결이 된 이전 내용은 다 날라간다는 것
연결이 된 후부터 내용을 받을 수 있다.
지금까지의 내용은 동기적 방법인 것같고 공식문서 마지막에 비동기 처리를 안내해줌!
자세한 내용은 참고!
https://channels.readthedocs.io/en/stable/index.html
Django Channels — Channels 3.0.4 documentation
Channels is a project that takes Django and extends its abilities beyond HTTP - to handle WebSockets, chat protocols, IoT protocols, and more. It’s built on a Python specification called ASGI. Channels builds upon the native ASGI support available in Dja
channels.readthedocs.io
커스텀 성공한 부분
- 그룹명을 똑같이 만들어서 URL이 달라도 같은 메세지를 주고 받을 수 있음
- URL 수정가능
- Consumer.py 에서 간단한 로직 가능 - DB 저장을 할 예정
'개발일지' 카테고리의 다른 글
DRF - VIEW 종류 (0) 2022.06.20 DRF - Serializer custom create (0) 2022.06.19 Django - OneToOneField, ForeignKey, ManyToManyField (0) 2022.06.16