Study/django

[udemy] Django channels - create your own web chat application - 강의 후기

bluebamus 2025. 3. 27. 18:35

강좌 정보 : https://www.udemy.com/course/django-channels-create-your-own-web-chat-application/?couponCode=ST22MT240325G1

저장소 : https://github.com/m7md-almalki/Django-channels-course

학습 코드 저장소 : https://github.com/bluebamus/master-django-channels

 

1. 채널 구조

   - 채널의 routing은 url과 같고 consumers는 views와 같다.

   - WS(websockert)은 http로 접근하는게 아닌, "ws://127.0.0.1:8000/websocket/" 형식으로 접근한다. 

   - consumer는 동기식과 비동기식이 있다. 학습에서는 WebsocketConsumer, 동기식 웹소켓을 사용한다.

 

2. WebsocketConsumer 주요 함수 정리

   2.1. connect(self)

      2.1.1. WebSocket 연결 요청을 처리하는 함수

         - 클라이언트가 WebSocket을 열면 자동으로 호출됨
         - 기본적으로 .accept()를 호출해야 정상적으로 연결됨

 

      2.1.2. 사용 예시

def connect(self):
    self.accept()  # 클라이언트의 WebSocket 연결을 승인
    print(f"WebSocket 연결됨: {self.channel_name}")

 

   2.2. disconnect(self, close_code)

      2.2.1. WebSocket 연결이 끊어질 때 실행되는 함수

         - 사용자가 페이지를 닫거나 인터넷이 끊길 때 호출됨
         - close_code는 연결이 종료된 이유를 나타냄

 

      2.2.2. 사용 예시

def disconnect(self, close_code):
    print(f"WebSocket 연결 해제: {self.channel_name}, 코드: {close_code}")

 

   2.3. receive(self, text_data)

      2.3.1. 클라이언트로부터 메시지를 받을 때 실행되는 함수

         - 클라이언트가 WebSocket을 통해 JSON 또는 일반 텍스트 데이터를 보내면 호출됨
         - 데이터는 text_data로 전달됨
         - json.loads(text_data)를 사용하여 JSON 데이터를 파싱할 수 있음

 

      2.3.2. 사용 예시

import json

def receive(self, text_data):
    data = json.loads(text_data)
    print(f"메시지 수신: {data}")

 

   2.4. send(self, text_data)

      2.4.1. 클라이언트에게 메시지를 보낼 때 사용

         - JSON 데이터를 보낼 경우 json.dumps(data)를 사용해야 함

 

      2.4.2. 사용 예시

import json

def send_message(self, message):
    data = {"type": "chat_message", "message": message}
    self.send(json.dumps(data))  # JSON 형식으로 메시지 전송

 

   2.5. channel_layer.send(channel_name, message)

      2.5.1. 특정 채널로 메시지를 보낼 때 사용

         - 다른 Consumer에게 메시지를 보내는 용도로 사용됨
         - channel_name은 메시지를 받을 Consumer의 이름
         - message는 JSON 형식의 데이터

 

      2.5.2. 사용 예시 (다른 사용자에게 메시지 보내기)

from asgiref.sync import async_to_sync

def receive(self, text_data):
    data = json.loads(text_data)
    
    # 상대방의 채널 찾기
    other_user = User.objects.get(id=data["to_user_id"])
    user_channel = models.UserChannel.objects.get(user=other_user)

    # 메시지 전송
    async_to_sync(self.channel_layer.send)(
        user_channel.channel_name,
        {
            "type": "chat_message",
            "message": data["message"]
        }
    )

 

   2.6. channel_layer.group_add(group_name, channel_name)

      2.6.1. 특정 그룹에 WebSocket을 추가

         - 여러 WebSocket을 그룹화할 때 사용


      2.6.2. 사용 예시 (채팅방 그룹에 추가)

from asgiref.sync import async_to_sync

def connect(self):
    self.accept()
    group_name = "chat_room_1"

    async_to_sync(self.channel_layer.group_add)(
        group_name,
        self.channel_name
    )

 

   2.7. channel_layer.group_discard(group_name, channel_name)

      2.7.1. 그룹에서 WebSocket을 제거

         - 연결이 끊어질 때 그룹에서 제거해야 함

 

      2.7.2. 사용 예시 (채팅방 그룹에서 제거)

def disconnect(self, close_code):
    group_name = "chat_room_1"

    async_to_sync(self.channel_layer.group_discard)(
        group_name,
        self.channel_name
    )

 

   2.8. channel_layer.group_send(group_name, message)

      2.8.1. 그룹에 속한 모든 WebSocket에 메시지 전송

         - 같은 채팅방에 있는 모든 사용자에게 메시지를 보낼 때 사용

 

      2.8.2. 사용 예시 (채팅방의 모든 사용자에게 메시지 보내기)

def receive(self, text_data):
    data = json.loads(text_data)
    group_name = "chat_room_1"

    async_to_sync(self.channel_layer.group_send)(
        group_name,
        {
            "type": "chat_message",
            "message": data["message"]
        }
    )

 

   2.9. 사용자 정의 핸들러 (chat_message(self, event))

      2.9.1. group_send 또는 channel_layer.send를 통해 호출되는 함수

         - event["type"]에 정의된 핸들러 이름과 일치해야 실행됨

 

      2.9.2. 사용 예시 (그룹 메시지 처리) python 복사 편집

def chat_message(self, event):
    message = event["message"]
    self.send(text_data=json.dumps({"message": message}))

 

   2.10. 정리

함수명 설명
connect(self) WebSocket 연결 요청을 처리
disconnect(self, close_code) WebSocket 연결 해제 시 실행
receive(self, text_data) 클라이언트로부터 메시지 수신
send(self, text_data) 클라이언트에게 메시지 전송
channel_layer.send(channel_name, message) 특정 WebSocket에 메시지 전송
channel_layer.group_add(group_name, channel_name) 그룹에 WebSocket 추가
channel_layer.group_discard(group_name, channel_name) 그룹에서 WebSocket 제거
channel_layer.group_send(group_name, message) 그룹의 모든 WebSocket에 메시지 전송
chat_message(self, event) group_send에서 실행되는 핸들러

 

3. 데이터 형식

   - 다양항 방식을 사용할 수 있다.

data = {
    "type": "recevier_function",
    "type_of_data": "new_message",
    "data": text_data.get("message"),
}
print("user_channel_name.channel_name :",user_channel_name.channel_name)
async_to_sync(self.channel_layer.send)(
    user_channel_name.channel_name, data
)
async_to_sync(self.channel_layer.send)(
    user_channel.channel_name,
    {
        "type": "chat_message",
        "message": "the_messages_have_been_seen_from_the_other"
    }
)

 

   - channel_name 뒤에 오는 파라미터의 내용은 정해진 형식이 없다. 하지만 type는 하나의 규칙이 있다.

   - type의 이름이 클래스 내 함수 명일 경우, send를 하기 전 해당 함수를 먼저 호출한다

   - type와 매칭되는 함수 이름이 없는 경우, client에게 전달한다.

 

   3.1. 상세 동작 설명

      3.1.1. type이 함수명과 동일한 경우

         - Channels는 type의 값에 해당하는 메서드를 자동으로 호출합니다.
         - 예를 들어 type이 "chat_message"일 때 chat_message라는 함수가 존재하면, 그 함수가 자동으로 실행됩니다.

 

      3.1.2. type에 해당하는 함수가 없는 경우

         -  만약 type에 해당하는 함수가 Consumer 클래스 안에 정의되지 않은 경우, 메시지는 클라이언트로 직접 전송됩니다.
         - 메시지 처리 로직이 없기 때문에 메시지가 그대로 클라이언트로 전송되며, 클라이언트는 메시지를 받아 처리할 수 있습니다.

 

4. 강의 중 문제

   - 윈도우에서 channels의 동작이 제대로 수행되지 않았다. 사용자의 세션이 제대로 이루어지지 않아 user의 접근시 정보가 공란으로 나왔다.

      - 리눅스에서 작업 하는 것으로 해결함

 

   - 테스트 중 연결이 계속 끊기는 현상이 발견 되었다.

      - 일정 시간 후 발생되는 것으로 보아 세션의 타임아웃으로 보였다.

      - 설정 파일의 파라미터를 변경하건, ping을 이용한 더미 데이터를 보내는 것으로 유지할 수 있다.

 

5. 고도화 

   - 강의는 1:1 학습만 제공한다. layer를 이용하여 그룹을 만들 수 있는 것으로 보아 그룹에 추가 및 삭제를 하고 그룹 전송을 할 수 있는것으로 해결할 수 있을것 같다.

   - redis가 아닌, 인메모리를 사용했다. redis를 사용하면 많은 문제를 해결할 수 있을 것이다.

   - 예외처리가 하나도 되지 않았지만, 대략적인 발생 위치들은 예상되기에 문제되지 않았다.

 

6. 강의 후기

   - 1:1 채팅을 위한 최소한, 필수적인 내용들을 쉽게 설명했다.

   - 영어를 천천히 구사하며 발음도 상당히 깔끔했다.

   - 메시지의 저장, 채널의 저장을 위한 DB table을 만들고, 이를 이용하여 이전 데이터를 가져오고 읽은 메시지를 읽음 처리하는 기능까지 구현한다.

   - 큰 줄기의 필요한 기능은 구현해보기 때문에, 생각을 조금만 한다면 추가로 고도화 할 내용들을 금방 찾고 이해할 수 있다.

   - 초급 혹은 나처럼 예전 강의로 리마인드 하기에 좋은 강의인것 같다.