Long Polling으로 구현한 채팅

지난번에 Polling과 Long Polling을 구현한 간단한 샘플을 만들어 보았습니다.

그런데 Long Polling의 샘플이 좀 아쉬운거 같았습니다. 그때는 서버가 5초 뒤 응답을 해주는 형태로 구현을 했는데, Long Polling 보다는
단순히 서버에서 요청을 처리하는 시간이 오래걸리는 것과 비슷한 형태인거 같아서 Long Polling을 이용한 채팅을 새로 만들었습니다.

샘플 코드가 동작하는것을 영상으로도 찍어두었습니다.

Server

서버는 두개의 API를 가지고 있습니다.

  • GET : /chat/:id - 서버에서 응답을 받기위한 Long Polling용 API입니다. (id는 클라를 식별하기 위해 받는걸로 했습니다.)
  • POST : /chat - 클라에서 받은 요청을 다시 다른 클라이언트로 보내기 위해 사용하는 API 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let resList = [];
// 클라이언트에서 최초에 Long Polling 연결을 위한 API 입니다.
app.get("/chat/:id", function (req, res) {
res.name = req.params.id; // id는 로깅을 위한 클라이언트 식별용으로 받았습니다.
resList.push(res); // resList에 응답 객체를 추가만 해줍니다.
console.log("polling-"+req.params.id);
});

// 클라이언트에서 채팅을 입력했을때 호출되는 API 입니다.
app.post("/chat", function (req, res) {
let body = req.body;

for (let user of resList) {
user.send(body); // resList에 있는 응답 객체에게 응답을 보냅니다.
console.log(body.type + " (To: "+body.name +"/From: "+user.name+")");
}
resList = []; // resList를 비워줍니다.
res.send();
});

polling 요청과, 요청된 메시지가 어느곳에서 와서 어느곳으로 가는지 로그를 남깁니다.

Client

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
$(function () {
longPolling(); // Long Polling을 시작합니다.
setTimeout(() => join(), 1000); // longPolling() 이후 바로 join()을 호출했더니 상황에 따라 join이 먼저 처리되는 경우도 있어 1초의 시간을 두고 호출되게 처리했습니다.
});

// 이름을 입력받아 식별자로 사용합니다.
let name = prompt("input your name");
$("h3").append(name);

let exit = false;
let longPolling = function () {
$.ajax({
url: "/chat/"+name,
method: "GET",
timeout: 10000, // timeout은 10초로 설정하고, timeout 일때는 다시 longPolling()을 호출합니다.
beforeSend: function (xhr, opts) {
if (exit) { // 종료 flag가 true일때 더이상 서버로 요청을 하지 않습니다.
xhr.abort();
}
},
success: function (data) {
printChatting(data);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
if (XMLHttpRequest.readyState == 0 && textStatus !== "timeout") { // 서버와 연결이 안되었으나 timeout이 아닌경우만 종료처리합니다.
exit = true;
}
},
complete: longPolling // ajax처리가 종료되면 다시 longPolling()을 호출합니다. (ajax lifecycle 참고)
});
console.log("polling");
};

// 처음 채팅방 입장시 메시지를 남깁니다.
let join = function () {
let data = {
name: name,
type: "welcomeMsg"
};

$.ajax({
url: "/chat",
method: "POST",
dataType: "json",
data: data
});
console.log("join");
};

// 서버에서 받은 응답값을 이용해 채팅창에 출력하는 함수입니다.
let printChatting = function (data) {
if (data.type === "welcomeMsg") {
$(".chat_log ul").append("<p style='color: blue;'>" + (data.name === name ? "방에 입장했습니다." : data.name + "님이 방에 입장했습니다.") + "</p>");
} else if (data.type === "chattingMsg") {
$(".chat_log ul").append("<p>" + (data.name === name ? "나" : data.name) + " : " + data.message + "</p>");
}
};

// 채팅 입력후 전송버튼을 눌렀을때 API 호출을 합니다.
$("#btn_send").on("click", function () {
let data = {
name: name,
message: $("#chat_input").val(),
type: "chattingMsg"
};

$.ajax({
url: "/chat",
method: "POST",
dataType: "json",
data: data,
complete: function () {
$("#chat_input").val("");
}
});
});

처음 LongPolling을 요청후, welcomeMsg를 날립니다.
그 이후부터 채팅이 없어도 지속적으로 API를 호출하고, 10초의 타임아웃 이후에도 계속 API를 호출합니다.
가장 마지막으로 호출된 API는 서버의 응답이 있을때까지 대기중 으로 처리됩니다

정리

간단하게 Long Polling을 이용한 채팅을 구현해 보았는데, 많은 인원이 참여하는 채팅이나, 여러개의 방이 존재하는 채팅방을 Long Polling을 이용해서 구현하기에는 조금 어렵지 않을까 라는 생각이 들었습니다. 페이스북에서 구현한 채팅은 iframe 형태로 구현이 되었다고 하는데 그 방법은 어떻게 처리가 되는지도 한번 알아보면 좋을거 같습니다.

위에서 사용된 샘플 코드는 여기에 있습니다.

공유하기