인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

sunnnwo님의 프로필 이미지

작성한 질문수

파이썬/장고로 웹채팅 서비스 만들기 (Feat. Channels) - 기본편

채팅방 참여자 목록 노출

안녕하세요 선생님,

작성

·

43

0

#consumers.py

from asgiref.sync import async_to_sync
from channels.generic.websocket import JsonWebsocketConsumer

from chat.models import Room


# 모든 유저가 고정된 채널 레이어 그룹을 가질것.
class ChatConsumer(JsonWebsocketConsumer):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        SQUARE_GROUP_NAME = "square"
        self.group_name = [SQUARE_GROUP_NAME]
        self.room = None

    def connect(self):
        user = self.scope['user']

        if not user.is_authenticated:
            self.close()
        else:
            room_name = self.scope['url_route']['kwargs']['room_pk']

            try:
                self.room = Room.objects.get(pk=room_name)
            except Room.DoesNotExist: #지정 룸 pk에 룸 인스턴스가 없을 경우 웹소켓 연결요청 수락.
                pass
            else:
                self.group_name = self.SQUARE_GROUP_NAME

                is_new_join = self.room.user_join(self.channel_name, user)
                if is_new_join:
                    async_to_sync(self.channel_layer.group_send)(
                        self.group_name,
                        {
                            "type": "chat.user.join",
                            "username": user.username,
                        }
                    )
                async_to_sync(self.channel_layer.group_add)(
                    self.group_name,
                    self.channel_name
                )
                self.accept()
                
    def disconnect(self, code):
        if self.group_name:
            async_to_sync(self.channel_layer.group_discard)(
                self.group_name,
                self.channel_name
            )
        user = self.scope['user']

        if self.room is not None:
            is_last_leave = self.room.user_leave(self.channel_name, user)
            if is_last_leave:
                async_to_sync(self.channel_layer.group_send)(
                    self.group_name,
                    {
                        "type": "chat.user.leave",
                        "username": user.username,
                    }
                )

    def chat_user_join(self, message_dict):
        self.send_json({
            "type": "chat.user.join",
            "username": message_dict["username"],
        })

    def chat_user_leave(self, message_dict):
        self.send_json({
            "type": "chat.user.leave",
            "username": message_dict["username"],
        })

    def chat_message(self, message_dict):
        self.send_json({
            "type": "chat.message",
            "message": message_dict["message"],
            "sender": message_dict["sender"],
        })



    def receive_json(self, content, **kwargs):
        user = self.scope["user"]
        _type = content["type"]

        if _type == "chat.message":
            message = content["message"]
            sender = user.username

            async_to_sync(self.channel_layer.group_send)(
                self.SQUARE_GROUP_NAME,
                {
                   "type": "chat.message",
                    "message": message,
                    "sender": sender,
                }
            )
        else:
            print(f"Invalid message type : ${_type}")

    room_name = self.scope['url_route']['kwargs']['room_pk']
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 'room_pk'

이런 에러가 나서, urls.py, views.py, index.html도 맞춰줘 봤지만, 잘 해결이 되질 않습니다. 어떤식으로 이 에러를 처리해야할까요. 오늘도 좋은 하루 되시길 바랍니다.

감사합니다.

답변 1

1

이진석님의 프로필 이미지
이진석
지식공유자

안녕하세요.

self.scope["url_route"]["kwargs"] 사전은 websocket_urlpatterns 에 명시한 path에 정의한 값들로 구성됩니다. 장고 View 함수에서 인자로 전달받는 값과 같습니다.

그러니 path("ws/chat/<int:room_pk>/", ...) 처럼 room_pk 항목이 있으셔야 합니다.

살펴보시고 댓글 남겨주세요. :-)

sunnnwo님의 프로필 이미지
sunnnwo
질문자

지금 채팅방 입장 퇴장 알림 부분을 하고 있었습니다.

이 부분을 하기 위해서 모델과 연동하여 하는데, 제 생각에는 여태까지 채팅 Url을 Room 모델과 관계없이 그냥 열어서 쓰다가 발생한거같습니다. 채팅방 만들기 기능을 안넣고, 그냥 광장채팅방만 만들어서 쓰려고 하거든요. 이런 경우는 room 모델에 그냥 광장채팅방 이름을 만들어서 넣어주면 되는건가요?

이진석님의 프로필 이미지
이진석
지식공유자

Room 은 채팅방 접속 여부를 확인할 목적일 뿐이구요.

단일 채팅방만 운영하실 경우, 그룹명이 정해져있는 것이니까 굳이 따로 광장채팅방 room을 생성하실 필요가 없을 듯 하구요.

Consumer에서 속할 그룹명이 정해져있는 상황이니, 앞선 liveblog 예시처럼 Consumer 클래스의 groups 리스트 속성으로 groups = ["square"] 로 쓰시면, consumer에서 알아서 지정 그룹에 추가하고 제거까지 해줍니다.

sunnnwo님의 프로필 이미지
sunnnwo
질문자

감사합니다 선생님, 한번 맞춰서 해보겠습니다.

sunnnwo님의 프로필 이미지
sunnnwo
질문자

선생님 이부분은 어느 강의를 들어보면 될까요?

sunnnwo님의 프로필 이미지

작성한 질문수

질문하기