TIL. 최종프로젝트(20) 웹소켓 상세처리

2023년 06월 28일 by barryjung

    TIL. 최종프로젝트(20) 웹소켓 상세처리 목차

[오늘 한일]

  • 웹소켓 consumer 리팩토링
  • 알림, 퀴즈 처리기능 연결

 

[오늘 배운점]

장고 channels

<웹소켓 상세처리>

어제 밤에는 웹소켓 consumer파일을 리팩토링을 했다.

상시연결과 알림기능에 대한 골자가 잡혔기때문에 그것대로 전체 웹소켓 처리를

합칠 부분은 합치고 뺄부분은 빼가면서 핵심적인 부분만 남도록 코드를 수정했다.

 

결과적으로 리팩토링한 consumer파일을 기반으로 배틀 퀴즈처리 부분의 작성도

수월하게 이뤄질수 있었다.

 

웹소켓을 상세히 처리하는 방법에 대해 정리해보겠다.

어제 적었던 기본구조에서 단계별로 원하는 동작이 추가 된다.

그래서, 원하는 모습의 웹소켓 통신을 만들수 있다.

 

우리는 하나의 웹소켓이 상시 유지되면서

기본적으론 알림수신, 배틀페이지에서는 배틀 채널로 변경되도록 하기로 했다.

그래서 connect때 처리되던 채널 가입을

receive로 옮겼으며 connect시에는 알림 수신 채널을 연결하게 했다.

 

1. channel_layer의 group_add 

channel_layer 모듈은 특정 채널에 가입시키고 채널에 메세지를 전송하는 역할을 한다.

채널명보다는 그룹명을 사용하게된다.

self.channel_name
group

채널은 레디스를 통해 생성되는 한 연결단위를 말한다.

서버와 각 클라이언트 관계 각각을 지칭한다.

그룹은 이를 묶는 개념이다.

 

self.channel_name은 connect에 내포되어 있어 정의되고 처리되는 channel명으로,

어제 말한것처럼 라우팅 경로를 참조한 특정값이 된다.

 

그룹은 channel을 그룹에 가입시키는 동작시 참조되며, 이름은 바꾸어줘도 된다.

    async def connect(self):
        await self.accept()
        self.room_group_name = "user_%s" % self.scope["user"].id
        await self.channel_layer.group_add(self.room_group_name, self.channel_name)

예컨대 위처럼 처리해줄수 있다. 실제 프로젝트에 작성한 내용이다.

channel_layer에 group_add 메소드는 첫번째 인자에 그룹명을, 두번째 인자에 채널명을 입력받는다.

그럼 해당 채널을 해당 그룹에 더해주게 된다.

 

※ 각 개인에게 웹소켓 메세지를 줘야 할 경우에도, (우리의 경우에는 알림수신)

그 개인의 channel명 대신, 개인에 해당할 group명을 부여하고 group을 타겟으로 send하는게 적절하다.

https://stackoverflow.com/questions/59789894/django-channels-setting-custom-channel-name 참고한 사이트.

 

요청자의 channel명이야 어떻게 알아낼 방법이 있지만,

타겟 유저의 channel명을 특정하기는 매우 어렵다.

(실제 프린트 해보면 웹소켓 연결마다 channel명이 바뀌며,

이 값은 유저정보로는 특정하기 어렵다)

 

 

2. channel_layer의 group_send

그냥 send는 연결된 유저에게 메세지를 전송하는 것과는 다르게

        message = {
            "type": "send_message",
            "method": "notification",
            "message": notifications,
        }
        await self.channel_layer.group_send(self.room_group_name, message)

channel_layer의 group_send는 그룹내 모든 채널에게 메세지를 전송한다.

그래서 채팅방 기능에 적합하다.

(위에 말한대로 특정 타겟유저에게 전송할때도 사용했다)

 

group_send메소드는 특이한 규칙이있다.

바로 message json값의 작성법인데, 여기에는 type이 포함된다.

그리고 group_send메소드는 type의 이름을 가진 self.메소드를 찾아 연결해주는 시도를 한다.

즉, 해당 이름의 메소드가 있어야한다.

 

통상적으로 type에는 websocket이나, websocket.send 같은걸 쓴다고 한다.

    async def send_message(self, event):
        await self.send(text_data=json.dumps(event))

위와 같은 식으로 메소드가 존재해야 하며 여기서 send처리에 대해 정의를 해야한다.

그럼 해당 send의 형태로 그룹의 각 채널에 전송하게 된다.

 

여기에서 event를 가공해주는 것도 가능하다.

event는 위의 message의 json을 그대로 담고 있는데,

거기에서 message부분만 잘라내는 처리가 여기서 이뤄지기도 한다.

 

 

3. self.send

위에서 말한대로 send는 지금 통신중인(요청한) 유저에게 메세지를 보내는 것이다.

인자로 메세지만 남으면된다.

 

 

4. DB처리

웹소켓 통신중 DB 내용이 생성되거나 제공되거나 처리 될수 있다.

DB처리 문을 별도 함수로 작성해서 필요한 위치별로 넣어준다.

 

 

5. receive 분기

웹소켓은 연결되고 해제되고, 그리고 메세지를 수신한다.

수신시 동작을 담당하는게 receive다.

수신시 처리해줄 내용이 다양하고 여러 분기로 나눠져야 했다.

receive를 하위 여러개 메소드로 분기하여 기능이 연결되도록, 딕셔너리를 이용해 분기를 만들었다.