Language/Python

[python] django channels - web socket 연동 (2)

도토리즈 2023. 5. 30. 21:02

 

- chat/templates/chat/room.html 생성

- room.html 

<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Chat Room</title>
</head>
<body>
    <textarea id="chat-log" cols="100" rows="20"></textarea><br>
    <input id="chat-message-input" type="text" size="100"><br>
    <input id="chat-message-submit" type="button" value="Send">
    {{ room_name|json_script:"room-name" }}
    <script>
        const roomName = JSON.parse(document.getElementById('room-name').textContent);

        const chatSocket = new WebSocket(
            'ws://'
            + window.location.host
            + '/ws/chat/'
            + roomName
            + '/'
        );

        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(JSON.stringify({
                'message': message
            }));
            messageInputDom.value = '';
        };
    </script>
</body>
</html>

 

- chat/views.py에 아래 코드 추가

def room(request, room_name):
    return render(request, "chat/room.html", {"room_name": room_name})

 

- chat/urls.py에 아래 코드 추가

# chat/urls.py
from django.urls import path
from . import views
urlpatterns = [
    #...
    path("<str:room_name>/", views.room, name="room"),
]

- 채팅 방 생성

 

but 메시지 입력하면 오류 뜸

콘솔 보면 이런 오류가 떠있음 consumer를 만들어야함

 

- chat/consumer.py 생성

- consumer.py에 아래 코드 복붙

# chat/consumers.py
import json

from channels.generic.websocket import WebsocketConsumer


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        pass

    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]

        self.send(text_data=json.dumps({"message": message}))

 

- chat/routing.py 생성

- routing.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()),
]

 

- config/asgi.py 수정

import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
# Initialize Django ASGI application early to ensure the AppRegistry
# is populated before importing code that may import ORM models.
django_asgi_app = get_asgi_application()

import chat.routing

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(chat.routing.websocket_urlpatterns))
        ),
    }
)

 - migrate 후 다시 실행

$ python manage.py migrate
$ python manage.py runserver

- 127.0.0.1:8000/chat/lobby/ 접속

이번에 hello라고 입력하면 콘솔 오류도 안뜨고 잘 실행

 

- 채널 계층 활성화

docker run -p 6379:6379 -d redis:5

Redis를 백업 저장소로 사용하는 채널 계층을 사용

 

- channels_redis 설치

$ pip install channels_redis

- config/settings.py CHANNEL_LAYERS 추가

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}

 

- chat/consumer.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}))

- 두 개의 세션으로 127.0.0.1:8000/chat/lobby/  접속 시 서로 메시지 공유됨

 

 

 

비동기식으로 재 작성하는 코드는 튜토리얼 3참고 ! 

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

 

https://www.youtube.com/watch?v=cw8-KFVXpTE 

그리고 이분 유튜브도 설명 잘 나와있음 ! 참고하시면 좋을듯 !