Django Channel로 WebSocket 구현

Posted on 2021-08-30 by GKSRUDTN99
Django Rest Framework DRF Django Channel WebSocket

1. 설치 및 SetUp

  1. Django 프로젝트 생성
  2. pip로 channelschannels_redis를 설치한다
  3. Django 프로젝트에서 chat이라는 이름의 App을 생성한다.
  4. INSTALLED_APPSchannelschat을 추가한다.
  5. myapp/asgi.py파일에 다음과 같이 작성한다.
# mysite/asgi.py
import os

import django
from channels.http import AsgiHandler
from channels.routing import ProtocolTypeRouter

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
django.setup()

application = ProtocolTypeRouter({
  "http": AsgiHandler(),
  # Just HTTP for now. (We can add other protocols later.)
})
  1. settings.pyASGI_APPLICATION = 'myapp.asgi.application'을 추가한다.
  2. redis를 설치한다.
brew install redis
brew services start redis

2. consumer 생성하기

  • consumer는 WebSocket을 사용하는 객체로, WebSocket의 connect, disconnet와 기타 action들을 정의한다.
  1. chat/consumers.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.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # Join room 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
        )

    # Receive message from WebSocket
    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Send message to room group
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    # Receive message from room group
    def chat_message(self, event):
        message = event['message']

        # Send message to WebSocket
        self.send(text_data=json.dumps({
            'message': message
        }))
  • channel layer를 통해 consumer들의 그룹을 생성할 수 있다.
  • group_send를 통해 data와 type을 보내면, type을 보고 Group에 속한 모든 consumer들은 type의 이릉믈 가진 함수를 실행한다.
  1. chat/routings.py 파일을 생성하고, 다음 내용을 작성한다.
# chat/routing.py
from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
  • roomname을 parameter로 받는 path를 정의했다.
  • roomname은 ChatConsumer에서 접근이 가능해, group의 이름을 정하는데 쓰인다.
  1. myapp/asgi.py 파일을 열고, 다음 내용을 수정한다.
# mysite/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", "mysite.settings")

application = ProtocolTypeRouter({
  "http": get_asgi_application(),
  "websocket": AuthMiddlewareStack(
        URLRouter(
            chat.routing.websocket_urlpatterns
        )
    ),
})
  • http://로 시작하는 요청은 원래 존재하던 django project로 넘기고, ws://로 시작하는 websocket 요청은 chat 폴더 내 routing 파일로 넘긴다는 의미이다.
  1. Channel_layers 활성화를 위해 settings.py 파일 수정하기
  2. settings.py 파일에 다음 내용을 추가한다.
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

3. 프론트에서 웹소켓 생성하기

  1. 웹소켓 생성
const chatSocket = new WebSocket(
            'ws://'
            + window.location.host
            + '/ws/chat/'
            + roomName
            + '/'
        );
  1. 웹소켓에 메시지가 들어왔을 때, 콜백 함수 정의
chatSocket.onmessage = function(e) {
            const data = JSON.parse(e.data);
            document.querySelector('#chat-log').value += (data.message + '\n');
        };

redis를 도커로 설치하기

docker-compose를 통해 redis를 설치한다면, docker-compose 파일에 다음과 같이 작성하여 redis container를 실행한다.

redis:
    image: redis:alpine
    command: redis-server --port 6379
    ports:
      - 6379:6379

이와 같은 방식으로 실행되는 redis container의 host는 localhost(127.0.0.1)이 아니므로,
channel_layers config 설정에서 '127.0.0.1' 대신 'redis'를 사용해야한다.

공식문서

https://channels.readthedocs.io/en/stable/tutorial/part_2.html