노마드코더의 줌 클론코딩 강의를 보고 정리한 내용입니다.
SocketIO
실시간, 양방향, event 기반의 통신을 가능하게 하는 프레임워크
socketIO는 websocket을 이용한다. 만약 websocket을 지원하지 않으면 다른 것(http long-polling)을 사용한다.
서버 코드
import http from "http";
import { Server } from "socket.io";
import express from "express";
const app = express();
app.set("view engine", "pug");
app.set("views", __dirname + "/views");
app.use("/public", express.static(__dirname + "/public"));
app.get("/", (req, res) => res.render("home"));
app.get("/*", (req, res) => res.redirect("/"));
const httpServer = http.createServer(app); //http 서버
const wsServer = new Server(httpServer);
wsServer.on("connection", (socket) => {
console.log(socket);
});
const handleListen = () => console.log("Listening ong http://localhost:3000");
httpServer.listen(3000, handleListen);
socket.io를 설치하고, socket.io를 사용해서 서버를 만든다.
그리고 연결이 되면, socket을 콘솔에 출력한다.
클라이언트 코드
const socket = io();
저 한 줄 코드로 socket.io를 사용하는 서버와 연결해준다.
view.pug
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 Noom
link(rel="stylesheet", href="https://unpkg.com/mvp.css")
body
header
h1 Noom
main
script(src="/socket.io/socket.io.js")
script(src="/public/js/app.js")
script를 사용해서 socket.io를 사용할 수 있도록 import 해준다.
그리고 아까했던 것처럼 브라우저에서 서버에 메시지를 보내보자
서버측
wsServer.on("connection", (socket) => {
socket.on("enter_room", (msg, done) => {
console.log(msg); //전달받은 메시지 출력
setTimeout(() => {
done(); //frontend에 있는 함수를 호출
}, 5000);
});
});
클라이언트 측
const socket = io();
const welcome = document.getElementById("welcome");
const form = welcome.querySelector("form");
function handleRoomSubmit(e) {
e.preventDefault();
const input = form.querySelector("input");
socket.emit("enter_room", { payload: input.value }, () => {
console.log("server is done!");
});
input.value = "";
}
form.addEventListener("submit", handleRoomSubmit);
클라이언트에서 emit 함수를 사용해서 이벤트를 보낼 수 있다. 첫번째 인자는 사용자가 정의할 수 있고, 원하는 이벤트 명을 작성하면 된다. 대신에 서버측에서 사용하는 이벤트 이름과 동일하게 작성해줘야한다.
두번째 인자로는 데이터를 보내고, 세번째 인자에는 콜백 함수를 넣을 수 있다.
서버측을 보면 "enter_room" 이벤트가 발생했을 때 on 함수를 사용해서 요청을 받을 수 있다.
두번째 인자에서 데이터와 콜백 함수를 받고, 서버측에서 콜백 함수를 실행할 수 있다. 즉, 프론트에 있는 함수를 서버에서 실행시킬 수 있다.
즉, 실행 버튼은 서버가 누르는 거고, 실행되는 함수는 프론트 측에 있다고 생각하면 된다. 중요한건 함수는 백엔드가 아닌 프론트에서 실행된다. 또한 콜백 함수에 인자도 넣어서 사용할 수 있다.
websocket을 사용했을 때는 클라이언트측에서 서버측에 text만 보낼 수 있었는데, socketio를 사용하면 string, 객체, 숫자 등 다양한 타입의 데이터를 보낼 수 있고, 또 여러개의 인자들을 전송할 수 있다.
또한, socketio는 연결이 종료되면 자동으로 재연결을 시도한다.
만약 서버가 여러개 있는 경우
A서버를 사용하는 클라이언트가 B서버를 사용하는 클라이언트에게 메시지를 전송하고 싶다면 adapter를 사용한다.
adpater는 메모리상에 존재하는데 콘솔로 출력해보면 다음과 같이 나온다.
<ref *2> Adapter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
nsp: <ref *1> Namespace {
_events: [Object: null prototype] { connection: [Function (anonymous)] },
_eventsCount: 1,
_maxListeners: undefined,
sockets: Map(1) { 'epWA4ejirQqvlOAZAAAB' => [Socket] },
_fns: [],
_ids: 0,
server: Server {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
_nsps: [Map],
parentNsps: Map(0) {},
_path: '/socket.io',
clientPathRegex: /^\/socket\.io\/socket\.io(\.msgpack|\.esm)?(\.min)?\.js(\.map)?(?:\?|$)/,
_connectTimeout: 45000,
_serveClient: true,
_parser: [Object],
encoder: Encoder {},
_adapter: [class Adapter extends EventEmitter],
sockets: [Circular *1],
opts: {},
eio: [Server],
httpServer: [Server],
engine: [Server],
[Symbol(kCapture)]: false
},
name: '/',
adapter: [Circular *2],
[Symbol(kCapture)]: false
},
rooms: Map(1) {
'epWA4ejirQqvlOAZAAAB' => Set(1) { 'epWA4ejirQqvlOAZAAAB' }
},
sids: Map(1) {
'epWA4ejirQqvlOAZAAAB' => Set(1) { 'epWA4ejirQqvlOAZAAAB' }
},
encoder: Encoder {},
[Symbol(kCapture)]: false
}
1. rooms
어플리케이션에 있는 모든 room을 볼 수 있다.
2. socketID
=> roomID와 socketID가 동일하다면 그 room은 Private용 room이고, 그 외에는 Public용 room이라고 할 수 있다.
모든 socket은 private room을 가지고 있다. 즉, rooms목록에 socketId와 동일한 목록을 제외하고 남은 것이 public room이라고 할 수 있다.
//publicRoom을 반환하는 함수
function publicRooms() {
const {
sockets: {
adapter: { sids, rooms },
},
} = wsServer;
const publicRooms = [];
rooms.forEach((_, key) => {
if (sids.get(key) === undefined) {
publicRooms.push(key);
}
});
return publicRooms;
}
채팅방에 사용자 수 가져오기
function countRoom(roomName) {
return wsServer.sockets.adapter.rooms.get(roomName)?.size;
}
###### 중략 ######
socket.on("enter_room", (roomName, done) => {
socket.join(roomName);
done();
//하나의 socket에만 메시지 보내기
socket.to(roomName).emit("welcome", socket.nickname, countRoom(roomName));
//모든 socket에 메시지 보내기
wsServer.sockets.emit("room_change", publicRooms());
});
//방에서 나가기 바로 직전에 실행된다. 아직 방에서 나가지는 않은 상태
socket.on("disconnecting", () => {
socket.rooms.forEach((room) =>
socket.to(room).emit("bye", socket.nickname, countRoom(room) - 1); //나가는 중이기 때문에 본인을 제외하도록 -1 해주기
);
});
서버에서 사용자 수를 가져오는 함수를 선언해준다. = countRoom
그리고 방에 들어왔을 때, 방에서 나갈때 방에 있는 사용자 수를 같이 브라우저에 보내준다.
socket.on("welcome", (user, newCount) => {
const h3 = room.querySelector("h3");
h3.innerText = `Room ${roomName} (${newCount})`;
addMessage(`${user} arrived!`);
});
socket.on("bye", (left, newCount) => {
const h3 = room.querySelector("h3");
h3.innerText = `Room ${roomName} (${newCount})`;
addMessage(`${left} left ㅠㅠ`);
});
브라우저에서는 들어올 때와, 나갈 때 방 이름 옆에 사용자의 수를 변경해준다.
'개발 공부 > Web' 카테고리의 다른 글
[Web] WebRTC (0) | 2022.07.29 |
---|---|
[Web] WebSocket 정리 (0) | 2022.07.26 |
라디오 버튼을 이미지로 구현하기 [이전 블로그 게시글] (0) | 2022.02.16 |
[정리] 웹 폰트란? [이전 블로그 게시글] (0) | 2022.02.16 |