Server, Deploy๐ŸŒ/Server๐Ÿ›œ

Socket.IO์— ๋Œ€ํ•ด์„œ

JanuDev 2025. 10. 31. 17:09

๊ณต์‹ ์›น์‚ฌ์ดํŠธ : https://socket.io/

 

Socket.IO๋Š” ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ(๋ธŒ๋ผ์šฐ์ €)๊ฐ€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํ†ต์‹  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. 

๋ณดํ†ต Http์š”์ฒญ์€ ํ•œ๋ฒˆ ๋ณด๋‚ด๋ฉด ๋์ด์ง€๋งŒ, socket.io๋Š” ์—ฐ๊ฒฐ์„ ๊ณ„์† ์œ ์ง€ํ•ด์„œ ์„œ๋ฒ„๊ฐ€ ๋จผ์ € ๋ง์„ ๊ฑธ ์ˆ˜๋„ ์žˆ๋‹ค. ์ด ์—ฐ๊ฒฐ์„ ์†Œ์ผ“(Socket)์ด๋ผ ๋ถ€๋ฅด๊ณ , ์ด ์†Œ์ผ“์ด ์—ฐ๊ฒฐ๋˜๊ณ  ๋Š๊ธฐ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์„ ๋•Œ ๋งˆ๋‹ค ์ด๋ฒคํŠธ(event)๊ฐ€ ๋ฐœ์ƒ๋œ๋‹ค.

 

Http๋Š” ์งˆ๋ฌธ-๋‹ต๋ณ€์˜ ๊ตฌ์กฐ,
Socket์€ ๊ณ„์† ๋Œ€ํ™”ํ•˜๋Š” **์ฑ„ํŒ…๋ฐฉ**์˜ ๊ตฌ์กฐ

 

1. Socket.IO๋ž€

์›น ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๊ฐ„์— ์‹ค์‹œ๊ฐ„(Real-time), ์–‘๋ฐฉํ–ฅ(Bidirectional), ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜(Event-based) ํ†ต์‹ ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

์›น์—์„œ ์ฑ„ํŒ…, ์•Œ๋ฆผ, ์‹ค์‹œ๊ฐ„ ํ˜‘์—… ๋ฌธ์„œ ๋“ฑ ๋ฐ์ดํ„ฐ๊ฐ€ ๋Š์ž„์—†์ด ์ฃผ๊ณ ๋ฐ›์•„์ ธ์•ผ ํ•˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ๋„๊ตฌ ์ค‘ ํ•˜๋‚˜์ด๋‹ค.

 

2. Socket.IO์˜ ํŠน์ง•

socket.io๋Š” ์›น์†Œ์ผ“(WebSocket)์ด๋ผ๋Š” ํ†ต์‹  ํ”„๋กœํ† ์ฝœ์„ ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก '์‚ฌ์šฉํ•˜๊ธฐ ํŽธํ•œ ์ธํ„ฐํŽ˜์ด์Šค'์™€ '์—ฐ๊ฒฐ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ด๋Š” ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ๋“ค'์„ ์ถ”๊ฐ€ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ํ•œ๋‹ค.

  • ์‹ค์‹œ๊ฐ„ ์–‘๋ฐฉํ–ฅ ํ†ต์‹  : ํด๋ผ์ด์–ธํŠธ(๋ธŒ๋ผ์šฐ์ €) - ์„œ๋ฒ„๊ฐ€ ์–ธ์ œ๋“ ์ง€ ์„œ๋กœ์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  • ์ž๋™ ์žฌ์—ฐ๊ฒฐ ๋ฐ ํด๋ฐฑ(Fallback) : ํ†ต์‹ ์ด ๋Š์–ด์ ธ๋„ ์ž๋™์œผ๋กœ ์žฌ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•˜๋ฉฐ, ์‚ฌ์šฉํ•˜๋Š” ๋ธŒ๋ผ์šฐ์ €๋‚˜ ๋„คํŠธ์›Œํฌ ํ™˜๊ฒฝ์ด ์›น์†Œ์ผ“์„ ์ง€์›ํ•˜์ง€ ์•Š์„ ๋•Œ ์ž๋™์œผ๋กœ ๋‹ค๋ฅธ ํ†ต์‹  ๊ธฐ์ˆ ๋กœ ์ „ํ™˜ํ•œ๋‹ค(Fallback). ์ด ๋•์— ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋“  ์‹ค์‹œ๊ฐ„ ํ†ต์‹ ์„ ๋ณด์žฅํ•œ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์€ ์—ฌ๋Ÿฌ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•œ๋‹ค.
    • HTTP long-polling
    • WebSocket
    • WebTransport
  • ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ : ๋ฐ์ดํ„ฐ๋Š” ํŠน์ • ์ด๋ฒคํŠธ ์ด๋ฆ„๊ณผ ํ•จ๊ป˜ ์ฃผ๊ณ ๋ฐ›์•„์ง€๋Š”๋ฐ, ์˜ˆ๋ฅผ ๋“ค์–ด socket.emit('chat message', '์•ˆ๋…•')์ฒ˜๋Ÿผ ์ด๋ฒคํŠธ๋ฅผ ์ „์†กํ•˜๊ณ , socket.on('chat message', ()=>{})๋กœ ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•œ๋‹ค.

3. Socket.IO์˜ ๊ตฌ์„ฑ ์š”์†Œ

Socket.IO๋Š” ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„, ๋‘ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.

1๏ธโƒฃ ํด๋ผ์ด์–ธํŠธ(Client-side)

์ฃผ๋กœ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ JavaScript์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ socket.io-client๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๊ณ , ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๊ฑฐ๋‚˜ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›๋Š”๋‹ค.

 

Socket.IO - client์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

import { io } from 'socket.io-client'

// 1. ์„œ๋ฒ„ ์—ฐ๊ฒฐ(์ฃผ์†Œ๋Š” ์„œ๋ฒ„์˜ socket.io ์ฃผ์†Œ)
const socket = io('https://example.com', {
  path: '/gateway', // ์„œ๋ฒ„ ์†Œ์ผ“ ๊ฒฝ๋กœ
  auth: { userId: 'user01' }, // ์„œ๋ฒ„๋กœ ๋ณด๋‚ผ ์ธ์ฆ ์ •๋ณด
  transports: ['websocket'], // WebSocket๋งŒ ์‚ฌ์šฉ
})

// 2. ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐ๋˜์—ˆ์„ ๋•Œ ์‹คํ–‰
socket.on('connect', () => {
  console.log('โœ… ์—ฐ๊ฒฐ ์„ฑ๊ณต:', socket.id)
})

// 3. ์„œ๋ฒ„๊ฐ€ ๋ณด๋‚ธ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์„ ๋•Œ(๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ–ˆ์„ ๋•Œ) ์‹คํ–‰
socket.on('message', (msg) => {
  console.log('๐Ÿ“ฉ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ˆ˜์‹ :', msg)
})

// 4. ์—ฐ๊ฒฐ์ด ๋Š์–ด์กŒ์„ ๋•Œ ์‹คํ–‰
socket.on('disconnect', () => {
  console.log('๐Ÿ”Œ ์—ฐ๊ฒฐ ๋Š๊น€')
})

// 5. ์—ฐ๊ฒฐ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์‹คํ–‰
socket.on('connect_error', (err) => {
  console.log('โŒ ์—ฐ๊ฒฐ ์˜ค๋ฅ˜:', err.message)
})

// 6. ๋ฉ”์‹œ์ง€ ์ „์†ก
socket.emit('message', { text: '์•ˆ๋…• ์„œ๋ฒ„!' })
  • io()๋กœ ์—ฐ๊ฒฐ
  • on('eventName', callback)์œผ๋กœ ์ด๋ฒคํŠธ ๋“ฃ๊ณ 
  • emit('eventName', date)๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ธ๋‹ค. 

2๏ธโƒฃ ์„œ๋ฒ„(Server-side)

์ฃผ๋กœ Node.js ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ ์„œ๋ฒ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ socket.io๋ฅผ ์„ค์น˜ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค.

ํด๋ผ์ด์–ธํŠธ์˜ ์—ฐ๊ฒฐ์„ ๊ด€๋ฆฌํ•˜๊ณ , ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉฐ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ broadcast(๋™์‹œ์— ์—ฌ๋Ÿฌ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „์†ก(ํ•˜๊ฑฐ๋‚˜ ํŠน์ • ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ๋งŒ ๋ณด๋‚ธ๋‹ค.

 

Socket.IO - server์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

import { Server } from 'socket.io'

// 1. ์„œ๋ฒ„ ์ƒ์„ฑ(ํฌํŠธ 3000์—์„œ WebSocket ์„œ๋ฒ„ ์‹œ์ž‘)
const io = new Server(3000, {
  path: '/gateway', // ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฐ๊ฒฐํ•  ๊ฒฝ๋กœ
})

// 2. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฐ๊ฒฐ๋˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ
io.on('connection', (socket) => {
  console.log(`โœ… ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ๋จ: ${socket.id}`)
  
  // 2 - 1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ 
  socket.on('message', (msg) => {
    console.log('๐Ÿ“ฉ ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ :', msg)
  })
  
  // 2 - 2. ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋‹ค์‹œ ๋ฉ”์‹œ์ง€ ์ „์†ก
  socket.emit('message', `์„œ๋ฒ„๊ฐ€ ์‘๋‹ตํ•จ : ${msg.text}`)
  
  
  // 2 - 3. ์—ฐ๊ฒฐ ๋Š๊น€ ์ฒ˜๋ฆฌ
  socket.on('disconnect', () => {
    console.log(`๐Ÿ”Œ ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์ข…๋ฃŒ: ${socket.id}`)
  })
  
  // 2 - 4. ์—ฐ๊ฒฐ ์ค‘ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
  socket.on('error', (err) => {
    console.log('โŒ ์†Œ์ผ“ ์˜ค๋ฅ˜:', err)
  })
})
  • new Server()๋กœ WebSocket ์„œ๋ฒ„๋ฅผ ์—ด๊ณ  ํด๋ผ์ด์–ธํŠธ๋ฅผ ๊ธฐ๋‹ค๋ฆฐ๋‹ค
  • io.on()์œผ๋กœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฐ๊ฒฐ๋˜๋ฉด ์ž๋™์œผ๋กœ ํ˜ธ์ถœํ•œ๋‹ค
  • socket.on()์œผ๋กœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ ๋‹ค์–‘ํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค. socket๊ฐ์ฒด๋Š” ์—ฐ๊ฒฐ๋œ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋Œ€ํ‘œํ•จ
  • socket.emit()์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•œ๋‹ค. 

4. ์ „์ฒด ํ๋ฆ„

  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ io(url)๋กœ ์—ฐ๊ฒฐ ์š”์ฒญ
  2. ์„œ๋ฒ„ io.on('connection')์—์„œ ์—ฐ๊ฒฐ ์ˆ˜๋ฝ - socket.id ๋ถ€์—ฌ
  3. ํด๋ผ์ด์–ธํŠธ → ์„œ๋ฒ„: emit('message', data)
  4. ์„œ๋ฒ„ →  ํด๋ผ์ด์–ธํŠธ : socket.emit('message', data) ๋˜๋Š” broadcast.emit
  5. ์—ฐ๊ฒฐ ๋Š๊น€ → disconnect ์ด๋ฒคํŠธ ๋ฐœ์ƒ
  6. ์˜ค๋ฅ˜ ๋ฐœ์ƒ → error ์ด๋ฒคํŠธ ๋ฐœ์ƒ

[์ฐธ๊ณ  ์ž๋ฃŒ]

https://socket.io/

 

Socket.IO

Reliable Rest assured! In case the WebSocket connection is not possible, it will fall back to HTTP long-polling. And if the connection is lost, the client will automatically try to reconnect.

socket.io

https://share.google/1ZPNeuBDlSpa3Z24c

 

Socket.IO์˜ ์ •์ฒด: Socket.IO์™€ ์›น์†Œ์ผ“์€ ๋™์ผํ•œ ๊ฐœ๋…์ผ๊นŒ?

Socket.io ์ •์ฒด ํ•œ ์ค„๋กœ ์ •๋ฆฌ

velog.io