[Django] python-socketio서버와 JS(Socket.io)클라이언트를 통한 채팅 서비스 구축하기(channels이용 X)
시작에 앞서 아래 두 주소는 socket.io와 python-socketio의 공식문서 사이트이다
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가지가 된다.
- 소켓서버를 연결해준다
- 채팅방에 입장한다(채팅방의 개념이 필요없다면 생략가능)
- 메시지 등을 주고 받는다 (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 |
위와같은 과정을 통해 간단하게 비동기 채팅서비스를 구현 할 수 있다.
비동기를 사용한 이유는 여러명이 함께 이용하는 단체 채팅의 경우 동기식으로 구현할 경우 채팅방에 들어와 있는사람 중 인터넷이 느린 사람이 있다면 그 다음사람들도 계속해서 메시지 전송이 지연되기 때문에 동기식으로 구현해야 한다.
# 해당 방식은 매우 기초적인 방법으로 채팅을 구현한 것임
# 해당 방식의 문제점
- DB에 저장 X => 방을 나갔다가 다시 들어올 경우 정보가 남아있지 않음
- 잠시 연결이 끊겼을때 온 채팅은 안보임
- 방 입장이 제대로 이루어지지 않을 경우 보낸 채팅이 화면에 제대로 보이지 않음
=> 위의 문제를 해결하기 위해 connect될 경우 방에 입장(join)하도록 설정