채팅(python-socketio)

[Django] python-socketio서버와 JS(Socket.io)클라이언트를 통한 채팅 서비스 구축하기(channels이용 X)

coding232624 2024. 2. 23. 15:44

시작에 앞서 아래 두 주소는 socket.io와 python-socketio의 공식문서 사이트이다

https://socket.io/

 

Socket.IO

Reliable Rest assured! In case the WebSocket connection is not possible, it will fall back to HTTP long-polling. And if the connection is lost, the client will automatically try to reconnect.

socket.io

https://python-socketio.readthedocs.io/en/stable/

 

python-socketio — python-socketio documentation

 

python-socketio.readthedocs.io

 

Django에서 채팅 서비스를 배포하려 하는데 Channels가 아닌 조금 더 확장성이 넓은 socket.io를 통해 구현해보고 싶은 마음에 python-socketio를 통해 소켓서버를 구축하고 js를 통해 클라이언트를 구축하기로 결정했다.

 

이 과정에서 가장 힘들었던 점은 레퍼런스가 별로 없다는 점이었다. 이 과정에서 js로 클라이언트를 구현하고 python-socketio를 통해 소켓서버를 구현해 서로를 연결해 주는 과정에서 많은 시행착오가 있었다...

그 끝에 알게 된 내용을 정리해 볼까 한다.

 

우선 js에서 클라이언트를 구성해 줄 경우에는 크게 나눌경우 아래와 같이 3가지가 된다.

  1. 소켓서버를 연결해준다
  2. 채팅방에 입장한다(채팅방의 개념이 필요없다면 생략가능)
  3. 메시지 등을 주고 받는다 (js에서 html태그를 추가하는 과정과 유사)

코드의 경우 아래와 같이 구성하였습니다.(주석포함)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- 메시지 내용을 입력받기 -->
    <input id="input" /><button onclick="clk()">Send</button>
    <!-- 메시지들을 기록할 공간 -->
    <ul id="messages"></ul>


    <!-- Socket.io 라이브러리 불러오기 -->
    <script
      src="https://cdn.socket.io/4.0.1/socket.io.min.js"
      crossorigin="anonymous"
    ></script>
    <!-- socket 클라이언트 구현 -->
    <script>
      // socket 서버 연결
      let socket = io.connect(
        location.protocol + "//" + document.domain + ":" + location.port
      );
 
      // 채팅방에 입장하기 // 채팅방의 개념이 없이 모두가 한곳에서 채팅을 할 경우 생략
      socket.emit("join", room_name);
 
      // "Send" 버튼을 누를 경우 실행되는 함수로 전달받은 메시지 내용을 socket서버로 전송
      function clk() {
        //input에 입력된 내용을 받아와 text에 저장
        let text = document.getElementById("input").value;
        //입력받은 내용을 socket서버에 전달
        socket.emit("message", text);
        //입력창 비우기
        document.getElementById("input").value = "";
      }
 
      // socket서버에서 넘겨준 정보를 바탕으로 각 클라이언트에 메시지 내용을 보여주기
      socket.on("message"function (data) {
        //li태그 추가
        let li = document.createElement("li");
        //li채그에 socket에서 전달 받은 메시지 내용을 추가하기
        li.appendChild(document.createTextNode(data));
        //전달받은 메시지를 최종적으로 html의 messages를 id로 갖는 태그에 추가
        document.getElementById("messages").appendChild(li);
      });
    </script>
  </body>
</html>
 
cs

 

 

그 후 python-socketio를 통해 소켓서버를 구성은 아래와 같이 할 수 있다.

(Django 프레임워크를 이용해 비동기로 구현하였음)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import os
import socketio
from django.core.asgi import get_asgi_application
 
# 기본 세팅 설정
os.environ.setdefault('DJANGO_SETTINGS_MODULE''프로젝트 이름.settings')
 
# 비동기 모드로 설정
sio = socketio.AsyncServer(async_mode='asgi')
application = get_asgi_application()
asgi_app = get_asgi_application()
 
#이벤트를 처리하겠다는 표시
@sio.event
# 소켓서버 연결하기
async def connect(sid, environ):
    print('connect', sid)
 
#이벤트를 처리하겠다는 표시
@sio.event
# 채팅방에 입장하는 함수
async def join(sid, room_name):
    #방에 입장시킴
    await sio.enter_room(sid, room_name)
 
#이벤트를 처리하겠다는 표시
@sio.event
#채팅방이 존재하는 메시지 보내는 함수
async def message(sid, data,room_name):
    #해당 채팅방에 들어와있는 사람에게만 data를 보내줌
    await sio.emit('message', data, room=room_name)
 
#이벤트를 처리하겠다는 표시
@sio.event
#채팅방 X, 모두에게 메시지를 보내는 함수
async def message(sid, data):
    # socket서버에 연결된 모두에게 data를 전달
    await sio.emit('message', data)
 
#이벤트를 처리하겠다는 표시
@sio.event
# socket연결을 종료
def disconnect(sid):
    print('disconnect', sid)
 
application = socketio.ASGIApp(sio, asgi_app)
cs

 

위와같은 과정을 통해 간단하게 비동기 채팅서비스를 구현 할 수 있다.

비동기를 사용한 이유는 여러명이 함께 이용하는 단체 채팅의 경우 동기식으로 구현할 경우 채팅방에 들어와 있는사람 중 인터넷이 느린 사람이 있다면 그 다음사람들도 계속해서 메시지 전송이 지연되기 때문에 동기식으로 구현해야 한다.

 

# 해당 방식은 매우 기초적인 방법으로 채팅을 구현한 것임

# 해당 방식의 문제점

  1. DB에 저장 X => 방을 나갔다가 다시 들어올 경우 정보가 남아있지 않음
  2. 잠시 연결이 끊겼을때 온 채팅은 안보임
  3. 방 입장이 제대로 이루어지지 않을 경우 보낸 채팅이 화면에 제대로 보이지 않음

=> 위의 문제를 해결하기 위해 connect될 경우 방에 입장(join)하도록 설정