
WebSocket이란?
WebSocket은 웹 페이지와 웹 서버간의 지속적이고 양방향 통신을 가능하게 하는 프로토콜을 말하며 일반적인 HTTP와 달리 WebSocket은 연결이 한번 수립되면 지속적으로 유지됩니다. 이를 통해 서버나 클라이언트가 실시간으로 데이터를 주고 받을 수 있게 됩니다. 실시간으로 데이터를 주고 받기 때문에 채팅방, 가상화폐 거래소 같은 곳에 많이 쓰입니다.
- 실시간 웹 통신 : WebSocket은 웹 브라우저와 서버 간의 실시간 양방향 통신을 가능하게 하여 웹 브라우저 상에서 사용자의 동작에 따라 실시간으로 반응하거나 서버의 상태를 즉시 반영할 수 있습니다.
- 성능 향상 : 일반적인 HTTP 요청과 응답은 각 요청마다 header 정보가 포함되어 전송되는 반면 WebSocket 연결은 초기 handshake 이후에는 추가적인 header 정보 없이 데이터만 전송되므로 네트워크 오버헤드가 줄어듭니다.
- 지속적인 연결 : WebSocket 연결은 한번 수립되면 지속적으로 유지되므로 지속적인 통신이 필요한 애플리케이션에 효율적입니다.
- 서버 자원의 효율성 : WebSocket은 지속적인 연결을 통해 서버 자원의 효율적인 사용이 가능하게 합니다. 일반적인 HTTP 통신에서는 주기적인 폴링이 필요하지만 WebSocket을 사용하면 이러한 폴링의 필요성이 줄어들어 서버의 부담을 덜어줍니다.
- 프로토콜 호환성 : WebSocket은 초기 연결을 수렵하기 위해 HTTP/HTTPS 프로토콜을 사용하므로 기존 웹 인프라와 호환성이 좋습니다.
- 방화벽 및 프록시 호환성 : WebSocket은 기본적으로 80 및 443 포트를 사용하여 통신하기 때문에 대부분의 방화벽이나 프록시를 통과하는데 문제가 없습니다.
socket.io
socket.io는 WebSocket을 포함한 여러 통신 기술을 추상화하여 실시간 웹 애플리케이션을 쉽게 구축 할 수 있도록 도와주는 JavaScript 라이브러리로 WebSocket을 지원하지 않는 브라우저나 환경에서도 작동하도록 폴백 메커니즘을 제공합니다.
WebSocket을 활용한 채팅방 구현

app.js
// 채팅방 만들기
// 유저간의 귓속말
const express = require("express");
const path = require("path");
const socketIo = require("socket.io");
const app = express();
app.set("views", path.join(__dirname, "page"));
app.set("view engine", "ejs");
const server = app.listen(8080, () => {
console.log("Server On");
});
app.get("/", (req, res) => {
res.render("main");
});
let userId = [];
let userList = [];
let userTab1 = [];
let userTab2 = [];
const io = socketIo(server);
// 유저들의 소켓이 생성되면
io.sockets.on("connection", (socket) => {
console.log("user connect");
// 유저 접속시 배열에 추가
// 유저의 socket.id를 배열에 저장
// user가 방에 들어오면
socket.on("joinRoom", (room, name) => {
socket.join(room);
// 같은 방에 있는 유저에게 이벤트 푸쉬
// 누가 방에 들어왔는지 알려줌
if (room == "room1") {
userTab1.push({ name, id: socket.id });
io.to(room).emit("joinRoom1", room, name, userTab1);
}
if (room == "room2") {
userTab2.push({ name, id: socket.id });
io.to(room).emit("joinRoom2", room, name, userTab2);
}
});
// 유저가 방을 떠날 때
socket.on("leaveRoom", (room, name) => {
// 유저가 채팅방을 떠날때 이벤트 푸쉬
socket.leave(room);
if (room == "room1") {
userTab1 = userTab1.filter((value) => value.name != name);
// 누가 나갔는지 같은 방에 있는 유저에게 푸쉬
io.to(room).emit("leaveRoom", room, name, userTab1);
}
if (room == "room2") {
userTab2 = userTab2.filter((value) => value.name != name);
io.to(room).emit("leaveRoom", room, name, userTab2);
}
});
// 같은 방에 있는 유저끼리 채팅보이기
socket.on("chat", (room, name, msg) => {
io.to(room).emit("chat", name, msg);
});
socket.on("chat2", (id, name, msg) => {
io.to(id).emit("chat2", name, " 귓속말 " + msg);
});
socket.on("whisper", (room, username) => {
if (room == "room1") {
const whisperId = userTab1.filter((value) => value.name == username);
io.sockets.emit("whisper", whisperId, username);
}
if (room == "room2") {
const whisperId = userTab2.filter((value) => value.name == username);
io.sockets.emit("whisper", whisperId, username);
}
});
// 유저의 접속이 끊어지면
socket.on("disconnect", () => {
console.log("user logout");
// 유저 배열에서 삭제
// filter로 해당 대상의 이름을 찾아 제거
// 해당 userId가 아니면 배열에 다시 저장
// userList = userList.filter((value) => value != socket.id);
userTab1 = userTab1.filter((value) => value.id != socket.id);
userTab2 = userTab2.filter((value) => value.id != socket.id);
userId = userId.filter((value) => value != socket.id);
});
});
page/main.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="/socket.io/socket.io.js"></script>
<style>
body {
position: relative;
height: 100vh;
}
.content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 500px;
height: 500px;
border: 1px solid;
position: relative;
box-sizing: border-box;
}
#send {
position: fixed;
bottom: 0;
width: 100%;
border: 1px solid;
box-sizing: border-box;
display: flex;
left: 0;
}
#send #msg {
border: 0;
box-sizing: border-box;
padding: 10px;
width: 90%;
}
#send #sendBtn {
background-color: antiquewhite;
border: none;
box-sizing: border-box;
padding: 10px;
width: 10%;
}
#messages {
margin: 0;
padding: 0;
}
#messages li {
list-style: none;
}
#login {
width: 300px;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.join_text {
background-color: white;
}
.leave_text {
background-color: gray;
border: 1px solid gray;
}
#main {
display: none;
}
.userlist {
width: 100px;
height: 500px;
border: 1px solid;
position: absolute;
top: 0;
right: -100px;
box-sizing: border-box;
}
#userlisttab {
list-style: none;
}
</style>
</head>
<body>
<div class="content">
<div>chat</div>
<div id="login">
<p>login</p>
<input type="text" id="username" />
<button id="loginBtn">connect</button>
</div>
<div id="main">
<select name="" id="rooms">
<option value="room1">room 1</option>
<option value="room2">room 2</option>
</select>
<ul id="messages">
<div id="send">
<input type="text" id="msg" />
<button id="sendBtn">send</button>
</div>
<div id="send2">
<input type="text" id="msg2" />
<button id="sendBtn2">whispering</button>
</div>
</ul>
</div>
<div class="userlist">
<ul id="userlisttab"></ul>
</div>
</div>
</body>
<script>
window.onload = () => {
// 로그인 버튼을 누르면
loginBtn.onclick = () => {
login.style.display = "none";
main.style.display = "block";
const name = username.value;
// 선택한 select의 value 값을 가져옴
let room = rooms.options[rooms.selectedIndex].value;
// socket에 연결 시도
const socket = io.connect();
socket.emit("joinRoom", room, name);
// select 태그의 선택이 바뀌었을때
rooms.onchange = function (e) {
let el = e.target;
// 다른 채팅방을 선택하여 유저가 떠남
socket.emit("leaveRoom", room, name);
room = rooms.options[el.selectedIndex].value;
// 기존에 있던 방을 떠난 후 새로운 방에 조인
socket.emit("joinRoom", room, name);
};
socket.on("joinRoom1", (room, name, userTab1) => {
let chatLi = document.createElement("li");
let chatText = `${name} 님이 ${room} 에 입장하셧습니다.`;
chatLi.append(chatText);
messages.appendChild(chatLi);
userlisttab.innerHTML = "";
userTab1.forEach((el) => {
let chatLi2 = document.createElement("li");
chatLi2.classList.add("userArr");
chatLi2.append(el.name);
userlisttab.append(chatLi2);
});
});
socket.on("joinRoom2", (room, name, userTab2) => {
let chatLi = document.createElement("li");
let chatText = `${name} 님이 ${room} 에 입장하셧습니다.`;
chatLi.append(chatText);
messages.appendChild(chatLi);
userlisttab.innerHTML = "";
userTab2.forEach((el) => {
let chatLi2 = document.createElement("li");
chatLi2.append(el.name);
userlisttab.append(chatLi2);
});
});
// 채팅방을 떠나면
socket.on("leaveRoom", (room, name, userTab) => {
let chatLi = document.createElement("li");
let chatText = `${name} 님이 ${room} 에 나갔습니다.`;
chatLi.append(chatText);
messages.append(chatLi);
userlisttab.innerHTML = "";
userTab.forEach((el) => {
let chatLi2 = document.createElement("li");
chatLi2.append(el.name);
userlisttab.append(chatLi2);
});
});
// 같은 채팅방에 있는 사람만 채팅이 보임
socket.on("chat", (name, msg) => {
let chatLi = document.createElement("li");
let chatText = `${name} : ${msg}`;
chatLi.append(chatText);
messages.append(chatLi);
});
socket.on("chat2", (name, msg) => {
let chatLi = document.createElement("li");
let chatText = `${name} : ${msg}`;
chatLi.append(chatText);
messages.append(chatLi);
});
sendBtn.onclick = () => {
socket.emit("chat", room, name, msg.value);
msg.values = "";
};
sendBtn2.onclick = () => {
socket.emit("whisper", room, msg2.value);
socket.on("whisper", ([whisperId], name) => {
const { id } = whisperId;
socket.emit("chat2", id, name, msg.value);
});
msg.values = "";
};
userlisttab.onclick = function (e) {
const username = e.target.innerHTML;
msg2.value = username;
};
};
};
</script>
</html>

- 3명의 사용자가 채팅방에 입장하였고 각각 채팅을 치고 있는 상황
- test1의 유저가 test3에게 귓속말을 하였고 test2는 귓속말에 대한 내용이 보이지 않음
- test2 유저 혼자 room2 방으로 이동하였고 우측 사용자 탭에서 빠진걸 볼 수 있음
- room2에 입장한 유저가 채팅을 입력하여도 room1에 있는 유저에게 보이지 않음
728x90
'Nodejs' 카테고리의 다른 글
| [NodeJs] Cors와 Axios를 활용한 간단한 게시판 만들기 (0) | 2023.09.06 |
|---|---|
| [NodeJs] WebSocket을 활용한 좌석예매 하기 (0) | 2023.09.06 |
| [NodeJs] Sequelize 알아보기 (0) | 2023.08.30 |
| [NodeJs] Express-session 알아보기 (0) | 2023.08.30 |
| [NodeJs] JWT 알아보기 (0) | 2023.08.29 |