๐Ÿ“Œ +) Socket.IO๋กœ ์ฑ„ํŒ…๊ธฐ๋Šฅ ๊ตฌํ˜„ ํ•ด๋ณด๊ธฐ

seul_velogยท2022๋…„ 10์›” 5์ผ
8

โœ๏ธ ํ”„๋กœ์ ํŠธ ์ฑ„ํŒ… ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•ด zoom ํด๋ก ์ฝ”๋”ฉ ์‹ค์Šต(๋ฐ”๋‹๋ผ JS, socket.io)์„ ํ•ด๋ณด๋ฉด์„œ ๊ธฐ๋กํ•œ ๋‚ด์šฉ์ด๋‹ค.(2)

WebSocket & Socket.IO

์›น ํŽ˜์ด์ง€์˜ ํ•œ๊ณ„์—์„œ ๋ฒ—์–ด๋‚˜ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ์›น ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“œ๋Š” ํ‘œ์ค€ ๊ธฐ์ˆ ์ธ WebSocket ๊ทธ๋ฆฌ๊ณ  ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์˜ ์‹ค์‹œ๊ฐ„ ์›น ๊ธฐ์ˆ ์„ ์†์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Socket.io์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž! ๐Ÿค”

  • ๊ธฐ์กด์˜ HTTP ํ”„๋กœํ† ์ฝœ์€ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด์˜ ์—ฐ๊ฒฐ์ด ์œ ์ง€๋˜์ง€ ์•Š๋Š”๋‹ค. (connectionless) ๋”ฐ๋ผ์„œ ์ƒํ˜ธ์ž‘์šฉ(์‹ค์‹œ๊ฐ„ ํ†ต์‹ )ํ•˜๋Š” ์›น ์„œ๋น„์Šค๋ฅผ ์œ„ํ•ด์„œ๋Š” ์ˆจ๊ฒจ์ง„ ํ”„๋ ˆ์ž„(Hidden Frame)์„ ์ด์šฉํ•œ ๋ฐฉ๋ฒ•์ด๋‚˜ Long Polling, Stream ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉฐ ๋ณต์žกํ•˜๊ณ  ์–ด๋ ค์šด ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค.

  • ๋ณด๋‹ค ์‰ฝ๊ฒŒ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ์›น ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋ธŒ๋ผ์šฐ์ €์™€ ์›น ์„œ๋ฒ„ ์‚ฌ์ด์— ๋”์šฑ ์ž์œ ๋กœ์šด ์–‘๋ฐฉํ–ฅ ๋ฉ”์‹œ์ง€ ์†ก์ˆ˜์‹ ์ด ํ•„์š”ํ•˜๋‹ค. ๊ทธ๋ž˜์„œ HTML5 ํ‘œ์ค€ ๊ธฐ์ˆ ๋กœ Websocket API๊ฐ€ ๋“ฑ์žฅํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.
    WebSocket์€ ์†Œ์ผ“์„ ์ด์šฉํ•˜์—ฌ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด์—์„œ ์ž์œ ๋กญ๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋ฏ€๋กœ ๊ธฐ์กด์˜ ์š”์ฒญ-์‘๋‹ต ๊ด€๊ณ„ ๋ฐฉ์‹๋ณด๋‹ค ๋” ์‰ฝ๊ฒŒ ๋ฐ์ดํ„ฐ ๊ตํ™˜์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

  • ์‹ค์‹œ๊ฐ„ ์ž‘๋™
  • ๋ธŒ๋ผ์šฐ์ €์™€ back-end๊ฐ„์˜ ์–‘๋ฐฉํ–ฅ ํ†ต์‹ 
  • ๋ฉ”์„ธ์ง€๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ event ๊ธฐ๋ฐ˜์˜ ํ†ต์‹ 

1) WebSocket

  • HTML5 ์›น ํ‘œ์ค€ ๊ธฐ์ˆ 
  • ๋งค์šฐ ๋น ๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋ฉฐ ํ†ต์‹ ํ•  ๋•Œ ์•„์ฃผ ์ ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ด์šฉํ•œ๋‹ค.
  • ์ด๋ฒคํŠธ๋ฅผ ๋‹จ์ˆœํžˆ ๋“ฃ๊ณ , ๋ณด๋‚ด๋Š” ๊ฒƒ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • HTML5 Websocket์€ ์œ ์šฉํ•œ ๊ธฐ์ˆ ์ด์ง€๋งŒ, ๋ธŒ๋ผ์šฐ์ €๋ณ„๋กœ ์ง€์›ํ•˜๋Š” ์›น์†Œ์ผ“ ๋ฒ„์ „์ด ๋‹ค๋ฅด๋ฉฐ ์˜ค๋ž˜๋œ ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฒฝ์šฐ์—” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    ๋”ฐ๋ผ์„œ JS๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์— ์ƒ๊ด€์—†์ด ์‹ค์‹œ๊ฐ„ ์›น์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” Socket.IO๋ฅผ ๋” ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

2) Socket.IO

Socket.IO๋Š” node.js ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ๊ธฐ์ˆ ๋กœ, ๊ฑฐ์˜ ๋ชจ๋“  ์›น ๋ธŒ๋ผ์šฐ์ €์™€ ๋ชจ๋ฐ”์ผ ์žฅ์น˜๋ฅผ ์ง€์›ํ•˜๋Š” ์‹ค์‹œ๊ฐ„ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ง€์› ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

  • ์—ฌ๋Ÿฌ ์„ ํƒ์ง€ ์ค‘์—์„œ websocket์„ ์ด์šฉํ•ด์„œ ์‹ค์‹œ๊ฐ„ & ์–‘๋ฐฉํ–ฅ & event๊ธฐ๋ฐ˜ ํ†ต์‹ ์„ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. (websocket์„ ์‹คํ–‰ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋‹ค.)

  • ์–ด๋–ค ๋ธŒ๋ผ์šฐ์ €๋‚˜ ํ•ธ๋“œํฐ์ด websocket์„ ์ง€์›ํ•˜์ง€ ์•Š์„ ๋•Œ(websocket์—ฐ๊ฒฐ์„ ํ•  ์ˆ˜ ์—†์„ ๊ฒฝ์šฐ) socket.IO๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•(HTTP long polling ๋“ฑ)์„ ์ด์šฉํ•ด์„œ ๊ณ„์† ์ž‘๋™์„ ํ•œ๋‹ค.
    โ†’ ์ฆ‰ socket.IO๋Š” websocket์˜ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋‹ค.

  • ์†Œ์ผ“ ์—ฐ๊ฒฐ ์‹คํŒจ ์‹œ fallback์„ ํ†ตํ•ด ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์•Œ์•„์„œ ํ•ด๋‹น ํด๋ผ์ด์–ธํŠธ์™€ ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•œ๋‹ค.
    ๋งŒ์•ฝ wifi์—ฐ๊ฒฐ์ด ์ž ์‹œ๋™์•ˆ ๋Š๊ฒจ๋„ ์žฌ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•œ๋‹ค. (webSockets์˜ ๊ฒฝ์šฐ ์ด ์žฌ์—ฐ๊ฒฐ ๊ธฐ๋Šฅ์„ ์ง์ ‘ ๋งŒ๋“ค์–ด์ค˜์•ผ๋งŒ ํ•œ๋‹ค.)

  • ๋ชจ๋“  ํ”Œ๋žซํผ, ๋ธŒ๋ผ์šฐ์ €, ๋””๋ฐ”์ด์Šค์—์„œ ์ด์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.


๐Ÿ“Œ ์–ด๋–ค ์ƒํ™ฉ์—์„œ ์–ด๋–ค๊ฑธ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?

  • ์„œ๋ฒ„์—์„œ ์—ฐ๊ฒฐ๋œ ์†Œ์ผ“(์‚ฌ์šฉ์ž)๋“ค์„ ์„ธ๋ฐ€ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด์•ผํ•˜๋Š” ์„œ๋น„์Šค์ธ ๊ฒฝ์šฐ์—๋Š” Broadcasting ๊ธฐ๋Šฅ์ด ์žˆ๋Š” socket.io๋ฅผ ์“ฐ๋Š” ๊ฒŒ ์œ ์ง€๋ณด์ˆ˜ ์ธก๋ฉด์—์„œ ์ด์ ์ด ๋งŽ๋‹ค.
  • ๊ฐ€์ƒํ™”ํ ๊ฑฐ๋ž˜์†Œ์™€ ๊ฐ™์ด ๋ฐ์ดํ„ฐ ์ „์†ก์ด ๋งŽ์€ ๊ฒฝ์šฐ์—๋Š” ๋น ๋ฅด๊ณ  ๋น„์šฉ์ด ์ ์€ ํ‘œ์ค€ WebSocket์„ ์ด์šฉํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค.





Socket IO ์„ค์น˜ํ•˜๊ธฐ

  • npm i socket.io
  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฃผ๋Š” websocket์€ socket.IO์™€ ํ˜ธํ™˜์ด ๋˜์ง€ ์•Š๋Š”๋‹ค. (socket.IO๊ฐ€ ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ฃผ๊ธฐ ๋•Œ๋ฌธ)

ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ์— socket.IO ์„ค์น˜ํ•ด์ฃผ๊ธฐ

// server.js
const io = SocketIO(httpServer);
  • sicketIO๋ฅผ ์„ค์น˜ํ•ด์ฃผ๋ฉด ํ™”๋ฉด์—์„œ io๋ผ๋Š” function์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • io๋Š” ์ž๋™์ ์œผ๋กœ back-end socket.io์™€ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ํ•จ์ˆ˜์ด๋‹ค.

// app.js
const socket = io();
  • io ํ•จ์ˆ˜๋Š” ์•Œ์•„์„œ socket.io๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์žˆ๋Š” ์„œ๋ฒ„๋ฅผ ์ฐพ๋Š”๋‹ค.

(์—ฌ๊ธฐ์„œ๋Š” WebSocket์ด ์•„๋‹ˆ๋ผ Socket์œผ๋กœ ๋ณด์ž„)

  • ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  socket์„ ์ž๋™์ ์œผ๋กœ ์ถ”์ ํ•˜๊ณ  ์žˆ๋‹ค. (์ด์ „์—๋Š” sockets.push(socket)์„ ํ–ˆ๋‹ค. โ˜บ๏ธ)
  • ์‰ฝ๊ฒŒ sockets์—์„œ socket id๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.




Socket IO ์‚ฌ์šฉํ•ด๋ณด๊ธฐ โœจย socket.emit

ROOM

  • user๊ฐ€ ์›น์‚ฌ์ดํŠธ๋กœ ๊ฐ€๋ฉด ๋ฐฉ์„ ๋งŒ๋“ค๊ฑฐ๋‚˜ ๋ฐฉ์— ์ฐธ๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” form์„ ๋ณด๊ฒŒ ๋œ๋‹ค.
  • socket IO์—๋Š” ์ด๋ฏธ room๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.

๋” ๋‚˜์€ socket IO ํ™œ์šฉ์„ฑ

1) ํŠน์ •ํ•œ event๋ฅผ emitํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.
์–ด๋–ค์ด๋ฆ„์ด๋“  ์ƒ๊ด€ X (๊ผญ message ์ด๋ฒคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ์›ํ•˜๋Š” ์ด๋ฒคํŠธ๋ฉด ๋œ๋‹ค !)

2) ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด์ „์ฒ˜๋Ÿผ string๋งŒ ์ „์†กํ•  ํ•„์š” X
โ†’ socket IO๋Š” obj๋ฅผ string์œผ๋กœ ์•Œ์•„์„œ ๋ฐ”๊ฟ”์ฃผ๊ณ , ๋‹ค์‹œ ์•Œ์•„์„œ JS object๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค.๐ŸŒŸ

// app.js
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 }); // enter_room์ด๋ผ๋Š” event๋ฅผ emit
  input.value = '';
}

form.addEventListener('submit', handleRoomSubmit);
// server.js
io.on('connection', (socket) => {
  console.log(socket);
  socket.on('enter_room', (msg) => console.log(msg)); // ์ด์ „์ฒ˜๋Ÿผ 'message' ํ•  ํ•„์š” ์—†์ด ์›ํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋„ฃ์Œ
});
// ๐Ÿ“Œ socket.on๊ณผ app.js์˜ socket.emit์˜ 'enter_room' ์ด๋ฆ„ ๋งž์ถฐ์ฃผ๊ธฐ !!
// ๐Ÿ“Œ msg๋Š” app.js์˜ ๋‘๋ฒˆ์งธ์ธ์ž๊ฐ€ ๋ณด๋‚ธ JSON ์˜ค๋ธŒ์ ํŠธ์ด๋‹ค.

3) socket.emit์˜ ์„ธ๋ฒˆ์งธ argument

// app.js
function handleRoomSubmit(e) {
  e.preventDefault();
  const input = form.querySelector('input');
  socket.emit('enter_room', { payload: input.value }, () => {
    console.log('server id done!');
  });
  input.value = '';
}

// server.js
io.on('connection', (socket) => {
  // console.log(socket);
  socket.on('enter_room', (msg, done) => {
    console.log(msg);
    setTimeout(() => {
      done();
    }, 5000);
  });
});
  • ์ฒซ ๋ฒˆ์งธ argument์—๋Š” ์ด๋ฒคํŠธ ์ด๋ฆ„
  • ๋‘ ๋ฒˆ์งธ argument์—๋Š” ๋ณด๋‚ด๊ณ ์‹ถ์€ payload (๋ฌด์ˆ˜ํžˆ ๋งŽ์ด ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.)
  • ๋งˆ์ง€๋ง‰ argument์—๋Š” ์„œ๋ฒ„์—์„œ ํ˜ธ์ถœํ•˜๋Š” function๊ฐ€ ๋“ค์–ด๊ฐ

    ๐Ÿ“Œ app.js์—์„œ ๋งˆ์ง€๋ง‰ ์ธ์ž๋กœ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๋ณด๋‚ด๋ฉด ์„œ๋ฒ„๋Š” back-end์—์„œ function์„ ํ˜ธ์ถœํ•˜์ง€๋งŒ function์€ front-end์—์„œ ์‹คํ–‰๋œ ๊ฒƒ!
    ์ˆ˜์ •) ๋๋‚  ๋•Œ ์‹คํ–‰๋˜๋Š” function์„ ๋ณด๋‚ด๊ณ  ์‹ถ์œผ๋ฉด ๋งˆ์ง€๋ง‰์ธ์ž์— ๋„ฃ์œผ๋ฉด ๋œ๋‹ค !

โ“ ๊ถ๊ธˆํ–ˆ๋˜ ๊ฒƒ
์ธ์ž์— ์–ด๋–ค๊ฑธ ๋„ฃ์–ด๋„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š”๋ฐ ์„ธ๋ฒˆ์งธ ์ธ์ž๊ฐ€ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๊ผญ ๋ฐ›๋Š”๊ฒŒ ์•„๋‹Œ๊ฑด๊ฐ€?

โ—๏ธ ๋‹ต: ๋งˆ์ง€๋ง‰ ์ธ์ž์— ํ•จ์ˆ˜๋ฅผ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค. ๐Ÿ˜€ ๊ทธ์™ธ์—๋Š” ๋ฌด์ œํ•œ ์œผ๋กœ ์ธ์ž๋ฅผ ๋„˜๊ฒจ์ฃผ๊ณ  ๋‹ค ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค.


โ“ ์–ด๋””์—์„œ ์‹คํ–‰์‹œํ‚ค๋Š” ๊ฑฐ์•ผ ๐Ÿง?

// server.js
io.on('connection', (socket) => {
  // console.log(socket);
  socket.on('enter_room', (msg, done) => {
    console.log(msg);
    setTimeout(() => {
      done('์•ˆ๋…• ํ”„๋ก ํŠธ์—”๋“œ ?'); // 1)
    }, 5000);
  });
});

// app.js
function handleRoomSubmit(e) {
  e.preventDefault();
  const input = form.querySelector('input');
  socket.emit('enter_room', { payload: input.value }, (msg) => {
    console.log('๋ฐฑ์—”๋“œ์—์„œ ๋ณด๋‚ธ ๋ฉ”์„ธ์ง€: ', msg); // 2)
  }); 
  input.value = '';
}

1) done() ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ฉด back-end์—์„œ ์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค. ๋ณด์•ˆ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (โ—๏ธ์‹ ๋ขฐํ•˜์ง€ ๋ชปํ•˜๋Š” ์ฝ”๋“œ๋ฅผ back-end์—์„œ ์‹คํ–‰์‹œํ‚ค๋ฉด ์•ˆ๋œ๋‹ค.)

2) front-end์—์„œ ์‹คํ–‰ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์ฃผ๋Š” ๊ฑฐ๋ผ๊ณ  ์ดํ•ดํ•˜๊ธฐ!
โ†’ ์ฆ‰, Front-end์—์„œ ์‹คํ–‰๋œ ์ฝ”๋“œ๋Š” back-end๊ฐ€ ์‹คํ–‰์„ ์‹œํ‚จ๊ฒƒ์ด๋‹ค. ๐Ÿค”

โœ๏ธ ํ—ท๊ฐˆ๋ฆฌ๋Š” ๋Š๋‚Œ์ด๋ผ์„œ ์ง์ ‘ ์ƒ๊ฐํ•ด๋ณด์ž๋ฉด, ํ•จ์ˆ˜ํ˜ธ์ถœ์€ ๋ฐฑ์—”๋“œ, ์‹คํ–‰์€ ํ”„๋ก ํŠธ !!!!
ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์‹คํ–‰์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๊ณ , ๋ฐฑ์—”๋“œ๊ฐ€ ๊ทธ ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ ์‹คํ–‰์ž์ฒด๋Š” ๋ฐฑ์—์„œ ๋˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ๋ฐฑ์ด ํ˜ธ์ถœํ•œ ํ”„๋ก ํŠธ๋กœ ๋„˜์–ด์™€์„œ ํ”„๋ก ํŠธ์—์„œ ์‹คํ–‰์ด ๋œ๋‹ค๊ณ  ์ดํ•ดํ–ˆ๋‹ค. ์ด๋•Œ ๋ฐฑ์—”๋“œ์—์„œ ์ธ์ž๋ฅผ ๋„ฃ์–ด์„œ ํ”„๋ก ํŠธ๋กœ ๋ณด๋‚ด๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅ!




Chat rooms ๊ด€๋ จ socket API๋“ค ์‚ดํŽด๋ณด๊ธฐโœจ

Socket.IO API


socket.join

  • socket IO ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ room์„ ์ œ๊ณตํ•œ๋‹ค.
  • ๋ฐฉ์— ์ฐธ๊ฐ€ํ•˜๊ธฐ
socket.join('') // room์ด๋ฆ„๋งŒ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.
// ๊ณต์‹๋ฌธ์„œ
io.on("connection", (socket) => {
  socket.join("room 237");
  
  console.log(socket.rooms); // Set { <socket.id>, "room 237" }

  socket.join(["room 237", "room 238"]); // ๋™์‹œ์— ์—ฌ๋Ÿฌ๋ฐฉ์— ์ฐธ๊ฐ€๋„ ๊ฐ€๋Šฅ!

  io.to("room 237").emit("a new user has joined the room"); // broadcast to everyone in the room
});

socket.leave

  • ๋ฐฉ์„ ๋‚˜๊ฐ€๊ธฐ
io.on("connection", (socket) => {
  socket.leave("room 237");

  io.to("room 237").emit(`user ${socket.id} has left the room`);
});

socket.to

  • ๋ฐฉ์ „์ฒด์— ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
io.on("connection", (socket) => {

  // to one room
// others ๋ผ๋Š”๋ฐฉ์— ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  socket.to("others").emit("an event", { some: "data" });

  // to multiple rooms
  socket.to("room1").to("room2").emit("hello");

  // or with an array
  socket.to(["room1", "room2"]).emit("hello");

  // a private message to another socket
// ๋‹ค๋ฅธ socket์˜ ID๋ฅผ ์•ˆ๋‹ค๋ฉด ํ”„๋ผ์ด๋น— ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค!
  socket.to(/* another socket id */).emit("hey");

  // WARNING: `socket.to(socket.id).emit()` will NOT work, as it will send to everyone in the room
  // named `socket.id` but the sender. Please use the classic `socket.emit()` instead.
});

socket.id & socket.rooms

// ๊ณต์‹๋ฌธ์„œ
io.on("connection", (socket) => {

  console.log(socket.rooms); // Set { <socket.id> } // 1)

  socket.join("room1");

  console.log(socket.rooms); // Set { <socket.id>, "room1" }

});
  • 1) ํ˜„์žฌ socket์ด ์žˆ๋Š” rooms๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

socket.onAny

socket.onAny((event, ...args) => {
  console.log(`got ${event}`);
});
  • ์–ด๋Š event์—์„œ๋“ ์ง€ console.log๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.
io.on('connection', (socket) => {
  socket.onAny((e) => {
    console.log(`์†Œ์ผ“์ด๋ฒคํŠธ: ${e}`);
  });
  socket.on('enter_room', (roomName, done) => {
    console.log('socket.rooms(1): ', socket.rooms); // ์†Œ์ผ“์ด ์–ด๋–ค ๋ฐฉ์— ์žˆ๋Š”์ง€ ์•Œ๊ธฐ์œ„ํ•ด ์ถœ๋ ฅ
    socket.join(roomName); // ๋ฐฉ์— ์ฐธ๊ฐ€๋ฅผ ํ•˜๊ณ 
    console.log('socket.rooms(2): ', socket.rooms); // ์†Œ์ผ“์ด ์–ด๋–ค ๋ฐฉ์— ์žˆ๋Š”์ง€ ์•Œ๊ธฐ์œ„ํ•ด ์ถœ๋ ฅ
    setTimeout(() => {
      done('์•ˆ๋…• ํ”„๋ก ํŠธ์—”๋“œ ?');
    }, 5000);
  });
});

  • ์•ž์—์ฐํžˆ๋Š” ๋ฌธ์ž๋Š” socket.id ๋ฅผ ์ฝ˜์†”๋กœ ์ถœ๋ ฅํ•˜๋ฉด ํ™•์ธ ๊ฐ€๋Šฅํ•œ, ์ฆ‰ ์†Œ์ผ“ ์•„์ด๋””์ด๋‹ค. ๐Ÿ˜€!! (์•„์ด๋””๋กœ ๊ตฌ๋ณ„๊ฐ€๋Šฅ)
    โ†’ user์˜ id๋Š” user๊ฐ€ ์žˆ๋Š” ๋ฐฉ์˜ id์™€ ๊ฐ™๋‹ค.
    โ†’ socket.IO ์—์„œ ๋ชจ๋“  socket์€ ๊ธฐ๋ณธ์ ์œผ๋กœ User์™€ Server ์‚ฌ์ด์— private room์ด ์žˆ๋‹ค!

server.sockets

  • ์„œ๋ฒ„๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , io.sockets.emit ์„ ํ•˜๋ฉด ๋ชจ๋‘์—๊ฒŒ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
io.sockets.emit("hi", "everyone");
// is equivalent to
io.of("/").emit("hi", "everyone");

server.socketsJoin(rooms)

  • ์œ ์ € ํ•œ ๋ช…์„ ๋งŒ๋“ค์–ด์„œ ๊ฐ•์ œ๋กœ socket์ด ๋ฐฉ์— ์ž…์žฅํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.
// make all Socket instances join the "room1" room
// ๋ชจ๋“  ์†Œ์ผ“์ด ์ด room์— ๊ฐ•์ œ๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ํ•จ
io.socketsJoin("room1");

// make all Socket instances in the "room1" room join the "room2" and "room3" rooms
// room1์— ์žˆ๋Š” ๋ชจ๋“  socket์„ room2์™€ room3์— ๋“ค์–ด๊ฐ€๊ฒŒ ํ•จ
io.in("room1").socketsJoin(["room2", "room3"]);

// this also works with a single socket ID
io.in(theSocketId).socketsJoin("room1");

ex.) socket์ด ์—ฐ๊ฒฐ๋˜์—ˆ์„ ๋•Œ ๋ชจ๋“  socket์ด ๊ณต์ง€๋ฐฉ์— ์ž…์žฅํ•˜๊ฒŒ ๋œ๋‹ค.

io.on('connection', (socket) => {
  io.socketsJoin('๊ณต์ง€๋ฐฉ');
...



๋ฐฉ์— ์ฐธ๊ฐ€ & ๋ฉ”์„ธ์ง€ ๋ณด๋‚ด๊ธฐ

๋ฐฉ์— ์ฐธ๊ฐ€ํ•˜๊ธฐ

// server.js
io.on('connection', (socket) => {
  socket.onAny((e) => {
    console.log(`์†Œ์ผ“์ด๋ฒคํŠธ: ${e}`);
  });
  socket.on('enter_room', (roomName, done) => {
    socket.join(roomName);
    done();
  });
});

๋ฐฉ์— ๋ˆ„๊ฐ€ ์ฐธ๊ฐ€ํ–ˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ๊ธฐ

// server.js
io.on('connection', (socket) => {
  socket.onAny((e) => {
    console.log(`์†Œ์ผ“์ด๋ฒคํŠธ: ${e}`);
  });
  socket.on('enter_room', (roomName, done) => {
    socket.join(roomName); // 1. ๋ฐฉ์— ์ฐธ๊ฐ€ํ•˜๋ฉด
    done(); // 2. ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ 
    socket.to(roomName).emit('์›ฐ์ปด'); // 4. '์›ฐ์ปด' event๋ฅผ rommName์— ์žˆ๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์—๊ฒŒ emit ํ•จ
  });
});
// ๐Ÿ“Œ ์†Œ์ผ“์ด ๋‹ค๋ฅด๋ฏ€๋กœ ์œ„ ๋…ธ๋ž‘์ƒˆ์ƒ‰ ์ฝ”๋“œ๋Š” ์‚ฌํŒŒ๋ฆฌ์—์„œ ํ•œ๋ฒˆ, ํฌ๋กฌ์—์„œ ํ•œ๋ฒˆ ๋” ์‹คํ–‰๋œ๋‹ค. ๋‘๋ฒˆ ์‹คํ–‰

// add.js
function addMessage(msg) {
  const ul = room.querySelector('ul');
  const li = document.createElement('li');
  li.innerText = msg;
  ul.appendChild(li);
}

// 3. done()์€ ํ”„๋ก ํŠธ์—”๋“œ์— ์žˆ๋Š” showRoom()์„ ์‹คํ–‰
function showRoom() {
  console.log('๋ฐฑ์—”๋“œ์—์„œ ํ˜ธ์ถœ');
  welcome.hidden = true;
  room.hidden = false;
  const h3 = room.querySelector('h3');
  h3.innerText = `Room ${roomName}`;
}

function handleRoomSubmit(e) {
  e.preventDefault();
  const input = form.querySelector('input');
  socket.emit('enter_room', input.value, showRoom);
  roomName = input.value;
  input.value = '';
}

form.addEventListener('submit', handleRoomSubmit);

socket.on('์›ฐ์ปด', () => {
  addMessage('๋ˆ„๊ตฐ๊ฐ€ ๋“ค์–ด์™”์Šต๋‹ˆ๋‹ค!');
});
  1. ์šฐ๋ฆฌ๊ฐ€ โ€˜์›ฐ์ปด' ์„ ๋ฐฉ์•ˆ์— ์žˆ๋Š” ๋ชจ๋‘์—๊ฒŒ ๋ณด๋‚ธ๋‹ค. ์šฐ๋ฆฌ๋ฅผ ์ œ์™ธํ•˜๊ณ !
    โ†’ socket.IO๋Š” ๋‚ด๊ฐ€ ๋‚˜๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ์‚ฌ๋žŒ์—๊ฒŒ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด๊ณ  ์‹ถ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ ์žˆ์œผ๋ฏ€๋กœ!
    โ†’ ๋”ฐ๋ผ์„œ ๋‚ด๋ฐฉ์—์„œ๋Š” ๋ฉ”์„ธ์ง€๋ฅผ ๋ฐ›์ง€ ์•Š์•˜์ง€๋งŒ ๋‹ค๋ฅธ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ๋ฉ”์„ธ์ง€๋ฅผ ๋ฐ›์Œ! โ–ผ



์•Œ๋ฆผ๋„์šฐ๊ธฐ

disconnecting

  • disconnect๋ž‘ ๋‹ค๋ฅด๋‹ค. disconnect๋Š” ์—ฐ๊ฒฐ์ด ์™„์ „ํžˆ ๋Š์–ด์กŒ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.
  • disconnecting์€ ๊ณ ๊ฐ์ด ์ ‘์†์„ ์ค‘๋‹จํ•  ๊ฒƒ์ด์ง€๋งŒ ์•„์ง ๋ฐฉ์„ ์™„์ „ํžˆ ๋‚˜๊ฐ€์ง€๋Š” ์•Š์€ ๊ฒƒ์ด๋‹ค.
    โ†’ ์™„์ „ํžˆ ๋Š๊ธฐ๊ธฐ ์ „์—, ์ฆ‰ ์ฐฝ์„ ๋‹ซ๊ฑฐ๋‚˜ ์ปดํ“จ๋„ˆ๊ฐ€ ๊บผ์กŒ์„ ๋•Œ ๋ฐฉ์— message๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Œ!
io.on("connection", (socket) => {
  socket.on("disconnecting", (reason) => {
    console.log(socket.rooms); // Set { ... } //<- ์—ฌ๊ธฐ์„œ Set์€ ๋ฐ˜๋ณต๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ forEach๋ฌธ ์ž‘์„ฑ ๊ฐ€๋Šฅ!
  });
});
socket.on('disconnecting', () => {
      socket.rooms.forEach(room => socket.to(room).emit('๋ฐ”์ด'))
    });
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐ์ด ๋Š์–ด์ง€๊ธฐ ์ „์— ๋งˆ์ง€๋ง‰ โ€˜๋ฐ”์ดโ€™ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

๋– ๋‚ฌ์„๋•Œ ๋ฉ”์„ธ์ง€ ๋ณด๋‚ด๊ธฐ

io.on('connection', (socket) => {
  socket.onAny((e) => {
    console.log(`์†Œ์ผ“์ด๋ฒคํŠธ: ${e}`);
  });
  socket.on('enter_room', (roomName, done) => {
    socket.join(roomName); 
    done(); 
    socket.to(roomName).emit('์›ฐ์ปด'); 
    socket.on('disconnecting', () => {
      socket.rooms.forEach((room) => socket.to(room).emit('๋ฐ”์ด'));
    });
  });
});

socket.on('๋ฐ”์ด', () => {
  addMessage('๋ˆ„๊ตฐ๊ฐ€ ๋– ๋‚ฌ์Šต๋‹ˆ๋‹ค ใ… ใ… ...');
});

๋ฉ”์„ธ์ง€ ๋ณด๋‚ด๊ธฐ

// server.js
io.on('connection', (socket) => {
  socket.onAny((e) => {
    console.log(`์†Œ์ผ“์ด๋ฒคํŠธ: ${e}`);
  });
  socket.on('enter_room', (roomName, done) => {
    socket.join(roomName); // 1. ๋ฐฉ์— ์ฐธ๊ฐ€ํ•˜๋ฉด
    done(); // 2. ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ 
    socket.to(roomName).emit('์›ฐ์ปด'); // 4. '์›ฐ์ปด' event๋ฅผ rommName์— ์žˆ๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์—๊ฒŒ emit ํ•จ
    socket.on('disconnecting', () => {
      socket.rooms.forEach((room) => socket.to(room).emit('๋ฐ”์ด'));
    });
    socket.on('new_message', (msg, room, done) => {
      socket.to(room).emit('new_message', msg); // new_message ์ด๋ฆ„์ด ๊ฐ™์•„๋„ ์ƒ๊ด€ ์—†๋‹ค. / ์ด ์ž‘์—…์ด ๋๋‚œ ๋’ค ์•„๋ž˜ done ํ•จ์ˆ˜ ํ˜ธ์ถœ
      done();
    });
  });
});


// app.js
function addMessage(msg) {
  const ul = room.querySelector('ul');
  const li = document.createElement('li');
  li.innerText = msg;
  ul.appendChild(li);
}

	...

// socket.on('new_message', (msg)=>{addMessage(msg)})์™€ ๊ฐ™์Œ
socket.on('new_message', addMessage);



๋‹‰๋„ค์ž„ ์ง€์ •ํ•˜๊ธฐ

๋ฉ”์„ธ์ง€ ์•ž์— ๋‹‰๋„ค์ž„ ์ถ”๊ฐ€ํ•˜๊ธฐ

// ํ•จ์ˆ˜ ๋กœ์ง ๋ณ€๊ฒฝ
// app.js
function handleNicknameSubmit(e) {
  e.preventDefault();
  const input = room.querySelector('#name input');
  socket.emit('nickname', input.value);
}

...
  const msgForm = room.querySelector('#msg');
  const nameForm = room.querySelector('#name');
  msgForm.addEventListener('submit', handleMessageSubmit);
  nameForm.addEventListener('submit', handleNicknameSubmit);
...

// server.js
socket.on('nickname', (nickname) => (socket['nickname'] = nickname));
io.on('connection', (socket) => {
  socket['nickname'] = '์ต๋ช…';
  socket.onAny((e) => {
    console.log(`์†Œ์ผ“์ด๋ฒคํŠธ: ${e}`);
  });
  socket.on('enter_room', (roomName, done) => {
    socket.join(roomName); 
    done(); 
    socket.to(roomName).emit('์›ฐ์ปด', socket.nickname); 
  });
  socket.on('disconnecting', () => {
    socket.rooms.forEach((room) =>
      socket.to(room).emit('๋ฐ”์ด', socket.nickname)
    );
  });
  socket.on('new_message', (msg, room, done) => {
    socket.to(room).emit('new_message', `${socket.nickname}: ${msg}`);
    done();
  });
  socket.on('nickname', (nickname) => (socket['nickname'] = nickname));
  
});



Adapter

  • Adapter๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•˜๋Š” ์ผ์€ ๋‹ค๋ฅธ ์„œ๋ฒ„๋“ค ์‚ฌ์ด์— ์‹ค์‹œ๊ฐ„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋™๊ธฐํ™” ํ•˜๋Š” ๊ฒƒ!๐Ÿ˜€

  • ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ์—์„œ Adapter๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์žˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ์ €์žฅํ•˜๊ณ  ์žˆ์ง€ ์•Š์Œ. โ†’ ์šฐ๋ฆฌ๊ฐ€ ์„œ๋ฒ„๋ฅผ ์ข…๋ฃŒํ•˜๊ณ  ๋‹ค์‹œ ์‹œ์ž‘ํ•  ๋•Œ ๋ชจ๋“  room๊ณผ message์™€ socket์€ ์—†์–ด์ง„๋‹ค.
    โ†’ โ—๏ธ ์šฐ๋ฆฌ๋Š” ๋ฐฑ์—”๋“œ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์‹ถ์Œ !!

  • ๋˜ํ•œ ์•ฑ ์•ˆ์— ๋งŽ์€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์žˆ์„ ๋•Œ, ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•ด์„œ connection์„ ์—ด์–ด๋‘ฌ์•ผํ•œ๋‹ค. (์‚ฌํŒŒ๋ฆฌ์™€ ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์šฐ๋ฆฌ ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ–ˆ๋“ฏ)
    โ†’ ๊ทธ๋Ÿฌํ•œ ์—ฐ๊ฒฐ์€ ์–ด๋”˜๊ฐ€์— ์žˆ์–ด์•ผ ํ•˜๊ณ , ์ฆ‰ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ์— ์žˆ์–ด์•ผ ํ•œ๋‹ค.
    โ†’ ์ฆ‰ ์„œ๋ฒ„๋Š” ์ด connection์„ ์˜คํ”ˆ๋œ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.

  • ๋งŽ์€ ๋ธŒ๋ผ์šฐ์ €๋“ค์€ ํ•˜๋‚˜์˜ ์„œ๋ฒ„์— connection๋“ค์„ ์—ด ๊ฒƒ โ†’ ์„œ๋ฒ„์— ๋งŽ์€ connection์ด ๋“ค์–ด์˜จ๋‹ค.
    ์„œ๋ฒ„๋Š” ๊ทธ ๋งŽ์€ connection๋“ค์„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•  ๊ฒƒ์ด๋ฉฐ ์„œ๋ฒ„ 2 ํ˜น์€ 3๊ฐœ๋ฅผ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค. ์ด๊ฒฝ์šฐ ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋™์ผํ•œ ์„œ๋ฒ„์— ์—ฐ๊ฒฐ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ ์ˆ˜ ์žˆ๋‹ค.
    โ†’ ์ด๋•Œ Adapter ๋ฅผ ์‚ฌ์šฉ ํ•ด์„œ ์„œ๋ฒ„ A์— ์žˆ๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„ B์— ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

  • Adapter๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ํ†ตํ•˜๋Š” ์ฐฝ๋ฌธ!

  • Adapter๋Š” ๋ˆ„๊ฐ€ ์—ฐ๊ฒฐ๋˜์—ˆ๋Š”์ง€, ํ˜„์žฌ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— room์ด ์–ผ๋งˆ๋‚˜ ์žˆ๋Š”์ง€ ์•Œ๋ ค์ค€๋‹ค.



Map(get,set)์„ ํ†ตํ•ด Private rooms์™€ Public rooms ์•Œ๊ธฐ

Private rooms์™€ Public rooms

  • ๋‘ ๊ฐœ์˜ ํ”„๋ผ์ด๋น— ๋ฃธ & ํ•œ ๊ฐœ์˜ ํผ๋ธ”๋ฆญ ๋ฃธ โ†’ โ€˜123โ€™ ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Rooms ํ™•์ธํ•˜๊ธฐ & Map์— ์žˆ๋Š” ๋ชจ๋“  roomsํ™•์ธ, room ID๋„ ํ™•์ธ


๐Ÿ“Œย ๋‚ด๊ฐ€ ํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ : ๋ชจ๋“  sids์— ๋Œ€ํ•œ map์„ ๋งŒ๋“ค๊ธฐ

const sids = new Map()
undefined

sids.set('nCc3Xbqinz2m9Pe_AAAN', true)
Map(1) {'nCc3Xbqinz2m9Pe_AAAN' => true}

sids.set('KMA__nvZDIDHjaLWAAAP', true)
Map(2) {'nCc3Xbqinz2m9Pe_AAAN' => true, 'KMA__nvZDIDHjaLWAAAP' => true}

sids
Map(2) {'nCc3Xbqinz2m9Pe_AAAN' => true, 'KMA__nvZDIDHjaLWAAAP' => true}

const rooms = new Map()
undefined

rooms.set('KMA__nvZDIDHjaLWAAAP', true)
Map(1) {'KMA__nvZDIDHjaLWAAAP' => true}

rooms.set('nCc3Xbqinz2m9Pe_AAAN', true)
Map(2) {'KMA__nvZDIDHjaLWAAAP' => true, 'nCc3Xbqinz2m9Pe_AAAN' => true}

rooms.set('seul',true)
Map(3) {'KMA__nvZDIDHjaLWAAAP' => true, 'nCc3Xbqinz2m9Pe_AAAN' => true, 'seul' => true}
  • ์šฐ๋ฆฌ์—๊ฒ sids๊ฐ€ ์žˆ๋‹ค โ†’ ์šฐ๋ฆฌ ๋ฐฑ์—”๋“œ์— ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  sockets๋“ค์˜ map
  • ๋ชจ๋“  socket์€ private room ์ด ์žˆ๋‹ค๋Š” ๊ฑธ ๊ธฐ์–ตํ•˜๊ธฐ!! ๐Ÿง
    โ†’ id๊ฐ€ ๋ฐฉ์ œ๋ชฉ์ธ ๊ฒฝ์šฐ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๊ฐ€ private message๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  • get๊ณผ key๋ฅผ ์ด์šฉํ•ด์„œ key๊ฐ€ socket ID ์ธ์ง€ ์•„๋‹ˆ๋ฉด ๋ฐฉ์ œ๋ชฉ์ธ์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
rooms.forEach((_,key)=> {
    if(sids.get(key) !== undefined){ // ํ”„๋ผ์ด๋น— ๋ฃธ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.
        console.log(key)
    }
})
// KMA__nvZDIDHjaLWAAAP
// VM10984:3 nCc3Xbqinz2m9Pe_AAAN


rooms.forEach((_,key)=> {
    if(sids.get(key) === undefined){ // ํผ๋ธ”๋ฆญ ๋ฃธ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.
        console.log(key)
    }
})
// seul

Map ์ ์šฉํ•ด๋ณด๊ธฐ

// io.sockets.adapter๋กœ ๋ถ€ํ„ฐ sids์™€ rooms๋ฅผ ๊ฐ€์ ธ์™€์„œ ์œ„ ์˜ˆ์‹œ์™€ ๊ฐ™์€ ์ž‘์—…
function publicRooms() {
  const {
    sockets: {
      adapter: { sids, rooms },
    },
  } = io;
  // const sids = io.sockets.adapter.sids; // 
  // const rooms = io.sockets.adapter.rooms; // ์ด ์ฝ”๋“œ ๋‘์ค„์€ ์œ„์ฒ˜๋Ÿผ ์ •์˜ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค

  const publicRooms = [];
  rooms.forEach((_, key) => {
    if (sids.fet(key) === undefined) {
      publicRooms.push(key);
    }
  });
  return publicRooms;
}



๋ฉ”์„ธ์ง€๋ฅผ ๋ชจ๋‘์—๊ฒŒ ๋ณด๋‚ด๊ธฐ

server.sockets.emit

  • ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  socket์—๊ฒŒ message๋ฅผ ๋ณด๋‚ด์ค€๋‹ค.
// server.js
function publicRooms() {
  const {
    sockets: {
      adapter: { sids, rooms }, // sids๋ž‘ rooms๋ฅผ io๋กœ๋ถ€ํ„ฐ ๊ตฌ์กฐ๋ถ„ํ•ด ํ•ด์˜ค๋Š” ์ฝ”๋“œ
    },
  } = io;

  const publicRooms = [];
  rooms.forEach((_, key) => {
    if (sids.get(key) === undefined) {
      publicRooms.push(key);
    }
  });
  return publicRooms;
}

socket.on('enter_room', (roomName, done) => {
    socket.join(roomName);
    done();
    socket.to(roomName).emit('์›ฐ์ปด', socket.nickname); // 1)
    io.sockets.emit('room_change', publicRooms()); // 2)
  });


// app.js
// socket.on('room_change', (msg) => console.log(msg));์™€ ๊ฐ™์Œ
socket.on('room_change', console.log);
  • 1) ํ•˜๋‚˜์˜ ์†Œ์ผ“์—๋งŒ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด์ค€๋‹ค.
  • 2) ๋ชจ๋“  ์†Œ์ผ“์— ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด์ค€๋‹ค.
socket.on('disconnect', () => {
    io.sockets.emit('room_change', publicRooms());
  });



์—ด๋ ค์žˆ๋Š” rooms ๋ณด๊ธฐ

// server.js
socket.on('enter_room', (roomName, done) => {
    socket.join(roomName);
    done();
    socket.to(roomName).emit('์›ฐ์ปด', socket.nickname);
    io.sockets.emit('room_change', publicRooms());
  });
socket.on('disconnect', () => {
    io.sockets.emit('room_change', publicRooms());
  });


// app.js
socket.on('room_change', (rooms) => {
	roomList.innerHTML = ''; // 2)
  const roomList = welcome.querySelector('ul');
  if (rooms.length === 0) { // 1)
    roomList.innerHTML = '';
    return;
  }
  rooms.forEach((room) => {
    const li = document.createElement('li');
    li.innerText = room;
    roomList.append(li);
  });
});
  • 1) [] ์ผ๋•Œ๋Š” ์—…๋ฐ์ดํŠธ๋ฅผ ์•ˆํ•ด์ฃผ๋ฏ€๋กœ(?) ์‚ฌ์šฉ์ž๊ฐ€ ๋‚˜๊ฐ€๋”๋ผ๋„ ๋ฐฉ ์ด๋ฆ„์ด ์กด์žฌํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐฐ์—ด์˜ ๊ธธ์ด์— ๋”ฐ๋ผ ๋ณด์—ฌ์งˆ ๋ฌธ๊ตฌ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค ๋‚˜๊ฐ€์„œ ๋ฐฐ์—ด์ด ๊ธธ์ด๊ฐ€ 0์ผ๊ฒฝ์šฐ โ€˜โ€™ ๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.

  • 2) โ—๏ธํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด์ฃผ์ง€ ์•Š์•˜๋”๋‹ˆ ๋ฐฉ์ด๋ฆ„์ด ์ค‘๋ณต์œผ๋กœ ์ƒ์„ฑ ๋˜์—ˆ๋‹ค. (์„ธ ๊ฐœ ๋ธŒ๋ผ์šฐ์ € ์ด์ƒ์ผ๋•Œ ํ™•์ธ ๊ฐ€๋Šฅ)



๋ฐฉ ์•ˆ์— ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค ์„ธ๊ธฐ / Set

Set

  • Set์€ ์œ ๋‹ˆํฌํ•˜๋‹ค (์ค‘๋ณต๋˜์ง€ ์•Š์Œ)
  • ์‚ฌ์ด์ฆˆ๋„ ์•Œ ์ˆ˜ ์žˆ์Œ / ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•  ์ˆ˜๋„ ์žˆ์Œ
const food = new Set(['a','b','b'])

console.log(food) // Set(2) {'a', 'b'}
console.log(food.size) // 2
  • ์ฆ‰, ํ•œ ๋ฐฉ์— ๋ช‡๋ช…์ด ์žˆ๋Š”์ง€ ์•Œ๊ธฐ ์œ„ํ•ด์„œ๋Š” rooms ์—์„œ ์ฐพ๊ณ ์žˆ๋Š” ๋ฐฉ์˜ key ๋ฅผ ์–ป๊ณ  set.size ํ•ด์ฃผ๋ฉด ๋ ๋“ฏํ•˜๋‹ค. ๐Ÿ˜€

rooms ์‚ฌ์ด์ฆˆ ์–ป๊ธฐ

function countRoom(roomName) {
  io.sockets.adapter.rooms.get(roomName)?.size
}

socket.on('enter_room', (roomName, done) => {
    socket.join(roomName);
    done();
    socket.to(roomName).emit('์›ฐ์ปด', socket.nickname, countRoom(roomName));
    io.sockets.emit('room_change', publicRooms());
  });

  socket.on('disconnecting', () => { // 1)
    socket.rooms.forEach((room) => // 2)
      socket.to(room).emit('๋ฐ”์ด', socket.nickname, countRoom(roomName)-1) // 3)
    );
  });
  • roomName์„ ์–ป์„ ์ˆ˜๋„ ์žˆ๊ณ  ์–ป์ง€ ์•Š์„ ๋•Œ๋„ ์žˆ์œผ๋ฏ€๋กœ ? ๋ฅผ ๋ถ™์—ฌ์ค€๋‹ค.
  • io.socketsadapter.rooms.get(roomName) ์ด๊ฒŒ Set์ž„์„ ์•Œ๊ณ ์žˆ์œผ๋ฏ€๋กœ ๋ฐ”๋กœ .size ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

1) ๋ฐฉ์„ ์™„์ „ํžˆ ๋– ๋‚˜์ง€ ์•Š์€ ์ƒํƒœ์ด๋‹ˆ ์•„์ง room์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
2) ํ•˜์ง€๋งŒ ์•„์ง ๋ฐฉ์„ ๋– ๋‚˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์šฐ๋ฆฌ๋„ ํฌํ•จ๋˜์–ด์„œ ๊ณ„์‚ฐ๋œ๋‹ค.
3) ๋”ฐ๋ผ์„œ -1์„ ํ•œ๋‹ค.

โ†’ ์ฆ‰ โ€˜์›ฐ์ปด' ๊ณผ โ€˜๋ฐ”์ด' ๋ฅผ ํ•  ๋•Œ newCount๋ฅผ ๋ฐ›๋Š”๋‹ค๋Š” ์˜๋ฏธ! โ†’ app.js (ํ”„๋ก ํŠธ์ชฝ)์—์„œ๋„ ํ•ด์ฃผ์ž.

socket.on('์›ฐ์ปด', (user, newCount) => {
  const h3 = room.querySelector('h3');
  h3.innerText = `Room ${roomName} (${newCount})`;
  addMessage(`${user}๋‹˜์ด ๋“ค์–ด์™”์Šต๋‹ˆ๋‹ค!`);
});

socket.on('๋ฐ”์ด', (left, newCount) => {
  const h3 = room.querySelector('h3');
  h3.innerText = `Room ${roomName} (${newCount})`;
  addMessage(`${left}๋‹˜์ด ๋– ๋‚ฌ์Šต๋‹ˆ๋‹ค ใ… ใ… ...`);
});




React + Socket.IO

โœ๏ธ ๋ฆฌ์•กํŠธ์™€ Socket.IO๋ฅผ ํ™œ์šฉํ•ด์„œ ์ฑ„ํŒ… ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

๐Ÿ“Œ ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์•ˆ๋œจ๋Š” ์ด์Šˆ ํ•ด๊ฒฐํ•˜๊ธฐ
โ“ ์™œ ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ๋œจ์ง€ ์•Š์„๊นŒ? ๐Ÿค”

  • server์—์„œ addUser์—๊ฒŒ ์ •๋ณด๋ฅผ ๋„˜๊ฒจ์ค„๋•Œ..
    const { user } = addUser({ id: socket.id }, name, room);
    โ†’ ์ˆ˜์ •ํ›„
    const { user } = addUser({ id: socket.id, name, room });
    ์ด๋ ‡๊ฒŒ ๋„˜๊ฒจ์คฌ์–ด์•ผ ํ–ˆ๋‹ค..!

โœ๏ธ ์—๋Ÿฌ ์—ญ์ถ”์ !!
1) ํด๋ผ์ด์–ธํŠธ Chat.js ์—์„œ socket.emit โ€˜์ž…์žฅโ€™ ๋ฐ์ดํ„ฐ ๋„˜์–ด๊ฐ€๋Š” ๊ฑฐ ํ™•์ธ! โ†’ ์ฝ˜์†” ๋ฌธ์ œ์—†์Œ
2) ์„œ๋ฒ„ index.js ์—์„œ socket.on '์ž…์žฅ' ์—์„œ ๋ฐ์ดํ„ฐ ๋ฐ›์•„์˜ค๋Š” ๊ฑฐ ํ™•์ธ! โ†’ ์ฝ˜์†” ๋ฌธ์ œ์—†์Œ
3) addUser์— ๊ฐ’์„ ์ž˜ ๋„˜๊ธฐ๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ!! โ†’ addUser์—์„œ ์ฝ˜์†” ํ™•์ธํ–ˆ๋”๋‹ˆ name๊ณผ room์ด undefined๊ฐ€ ์ž˜ ์ฐํž˜!!
โ†’ 2) ์—์„œ ์ž˜ ๋ฐ›์•„์™€์ง€๋Š”๋ฐ ์™œ addUser์—์„œ ์•ˆ๋˜๋Š”์ง€ ๊ณ ๋ฏผํ•˜๋ฉฐ ํ•ด๊ฒฐ!!!! ๐Ÿ˜€โœจ




Admin Panel

  • @socket.io/admin-ui

ํ”„๋กœ์ ํŠธ ์ฑ„ํŒ… ๊ธฐ๋Šฅ ๊ตฌํ˜„์— ์•ž์„œ Websocket๊ณผ Socket.IO ๊ฐœ๋… ์•Œ๊ณ  ์ตํžˆ๊ธฐ. ์‹ค์Šต ํ”„๋กœ์ ํŠธ !
1. JavaScript + Websocket ๐Ÿ‘‰ WebSocket ์œผ๋กœ ์ฑ„ํŒ…๊ธฐ๋Šฅ ๊ตฌํ˜„ ํ•ด๋ณด๊ธฐ
2. JavaScript + Socket.IO ๐Ÿ‘‰ Socket.IO๋กœ ์ฑ„ํŒ…๊ธฐ๋Šฅ ๊ตฌํ˜„ ํ•ด๋ณด๊ธฐ
3. React + Socket.IO ๐Ÿ‘‰ Socket.IO๋กœ ์ฑ„ํŒ…๊ธฐ๋Šฅ ๊ตฌํ˜„ ํ•ด๋ณด๊ธฐ
โœจ with-dog ํ”„๋กœ์ ํŠธ ์ฑ„ํŒ… ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๐Ÿ‘‰ ์ฑ„ํŒ…๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ


reference) websocket, socket.io

์ฐธ๊ณ 1 ์ฐธ๊ณ 2 ์ฐธ๊ณ 3

profile
๊ธฐ์–ต๋ณด๋‹จ ๊ธฐ๋ก์„ โœจ

0๊ฐœ์˜ ๋Œ“๊ธ€