inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

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

채팅방 참여자 목록 노출

안녕하세요 선생님,

91

sunnnwo

작성한 질문수 23

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도 맞춰줘 봤지만, 잘 해결이 되질 않습니다. 어떤식으로 이 에러를 처리해야할까요. 오늘도 좋은 하루 되시길 바랍니다.

감사합니다.

python django django-channels

답변 1

1

이진석

안녕하세요.

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

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

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

0

sunnnwo

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

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

1

이진석

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

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

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

1

sunnnwo

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

0

sunnnwo

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

유저목록 확인 문제 질문드립니다.

0

164

2

하나의 채팅방만 만들어보려고 하는데 잘 안되고 있습니다.

0

103

1

도커와 연동 관련 질문드립니다.

0

205

3

채팅방 참여자 목록 - 채팅방 입장/퇴장 실시간 이벤트 처리

0

165

2

안녕하세요, onopen() 문제로 질문드립니다.

0

137

2

Consumer Instances 관련 질문 있습니다.

0

72

2

안녕하세요, 요청은 채널스에서 먼저 받고, http 요청은 장고를 통해서 처리한다고 하셨는데요.

0

79

2

기능 구현 질문 드립니다.

0

115

1

git에 있는 코드를 다운 받아 실행 해봤는데 에러가 났습니다.

0

153

2

ValueError: No route found for path 'ws/liveblog/'.

0

134

2

지정 경로에 템플릿 파일 만드는 단축키가 뭔가요?

0

121

2

채팅 내역을 영구적으로 저장하고 싶습니다.

0

89

1

질문이 있습니다.

0

164

1

구독 채팅 구현

0

212

1

헷갈려서 질문드립니다.

0

355

2

@login_required 장식자를 적용한후에는 로그인을 성공하면 채팅방으로 어떻게 이동을 하는 건가요?

1

276

1

docker run -d --restart always --name redis7 --publish 6379:6379 redis:7

0

261

1

websocket 자바스크립트 클라이언트 구현?

0

361

1

메세지 리액션 : 좋아요. 질문 드립니다.

0

359

1

{유저명}님이 메세지 입력 중입니다. 메세지 질문드립니다.

0

610

1

채팅 로비에서 유저수 노출을 위하여

0

326

1

채팅방에서 마지막 유저가 나가면 채팅방 자동 삭제 질문드립니다.

0

432

1

동기방식의 consumer 클래스와 비동기방식의 consumer클래스의 차이가 뭔지 궁금합니다.

0

425

1

라이브러리 인식

0

416

2