줌 클론코딩 – 노마드 코더 Nomad Coders
WebSockets, SocketIO, WebRTC
nomadcoders.co
노마드코더의 줌 클론코딩 강의를 보고 정리한 내용입니다.

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 |