알고보니? 지난주를 마감하는 기록을 쓰지 않았기에 늦었지만 지금 써보려고 한다. 정리할 것은 내가 이해한 django websocket의 흐름이다. 난 라우팅 설정을 asgi.py에 직접 썼다. 프로토콜 타입 라우터를 통해 어떤 유형의 프로토콜을 진행할지 결정할 수 있다.
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": TokenAuthMiddleware(
URLRouter([
path('ws/alert/', commu.NotificationConsumer.as_asgi()),
]),
),
})
http 연결의 경우 get_asgi_application을...(기존 앱들이다) 웹소켓의 경우 미들웨어를 거친 아래의 라우팅을 사용하기로 말이다. JWT를 사용하는 프로젝트였기에 사용자 지정 미들웨어를 생성해서 사용하였다. 이 미들웨어 tokenAuthMiddleware를 거친 Consumer Class들을 url과 매핑하여 연결한다. 이제 프로토콜을 ws: 형태로, 해당하는 url에 들어가면 해당 consumer에서 정의된대로 연결된다
from channels.generic.websocket import AsyncWebsocketConsumer
import json
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.user = self.scope["user"]
self.group_name = f"user_{self.user.id}"
await self.channel_layer.group_add(self.group_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.group_name, self.channel_name)
async def receive(self, text_data):
pass
async def notify(self, event):
await self.send(text_data=json.dumps(event))
먼저 connect를 통해 연결될 때의 동작이 진행된다. TokenAuthMiddleware에서 전달받은 스코프의 키 "user"로 부터 user 데이터를 건네받는다. group_name은 user_id로 부터 생성되어 각 유저마다 개별적인 채널 레이어 그룹을 부여받는다. channel_layer.group_add에서 group_name과 channel_name을 통해 개별 채널을 할당받았는데, channel_name은 클래스명으로 부터 자연스럽게 생성되는 것으로 나는 채널레이어가 전체 웹소켓 연결이라면 채널은 하나의 아파트(class)를, 그룹은 동 하나(특정 유저층)라고 이해했다.
이렇게 할당을 받아 accept되면 웹소켓 연결이 된 것이다. 내 예제에서 클라이언트와 연결이 해제되면 disconnect, 클라이언트가 서버로 메세지를 보낸다면 receive 메서드가(알림인데 유저가 서버로 알림을 보낼리가 없으니 여기선 비어있다), 마지막으로 서버에서 보낸 알람을 받기 위해 내가 생성한 notify 메서드가 비동기적으로 실행된다.
@receiver(post_save, sender=Comment)
def send_notification(sender, instance, created, **kwargs):
if created:
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
f"user_{instance.post.user.id}",
{
"type": "notify", # Calls NotificationConsumer.notify
"event": "New Comment",
"nickname": instance.user.nickname,
"comment": instance.content,
"post": instance.post.title,
},
)
메세지를 보내는 역할은 시그널에서 정의했는데, Comment가 save() 될때마다 실행된다. 채널 레이어의 약속된 그룹명을 박아넣고 아래의 형태의 객체를 보내는 것이다. 서버에서 각 클라이언트로 메세지를 보낼 때에는 notify 처럼 event를 포함한 메서드를 생성하여 처리하자.
'일지' 카테고리의 다른 글
2023.06.15 (0) | 2023.06.16 |
---|---|
2023.06.14 (0) | 2023.06.15 |
2023.06.13 (1) | 2023.06.13 |
2023.06.12 (0) | 2023.06.13 |
2023.06.09 (0) | 2023.06.09 |