๋ชฉ์ฐจ
๐ Socket.IO ์ค์๊ฐ ํต์
Phaser Baduk Metaverse ํ๋ก์ ํธ์ Socket.IO
๋ฅผ ์ฌ์ฉํ ์ค์๊ฐ ํต์ ๊ตฌํ์ ๋ํด ์ค๋ช
ํฉ๋๋ค.
1. Socket.IO๋ ๋ฌด์์ธ๊ฐ์?
Socket.IO
๋ ์น ๋ธ๋ผ์ฐ์ ์ ์๋ฒ ๊ฐ์ ์ค์๊ฐ ์๋ฐฉํฅ ํต์ ์ ๊ฐ๋ฅํ๊ฒ ํด์ฃผ๋ JavaScript
๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค. ์ฑํ
, ๊ฒ์, ์ค์๊ฐ ์๋ฆผ ๋ฑ์ ์ฌ์ฉ๋ฉ๋๋ค.
์ฃผ์ ํน์ง:
- ์ค์๊ฐ ์๋ฐฉํฅ ํต์ .
- ์๋ ์ฌ์ฐ๊ฒฐ ๊ธฐ๋ฅ.
- ๋ค์ํ ์ ์ก ๋ฐฉ์ ์ง์ (
WebSocket
,HTTP Long Polling
๋ฑ). - ๋ฐฉ(
Room
) ๊ธฐ๋ฅ์ผ๋ก ๊ทธ๋ฃน ํต์ ๊ฐ๋ฅ. - ๋ธ๋ผ์ฐ์ ํธํ์ฑ์ด ๋ฐ์ด๋จ.
์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก:
- ์ค์๊ฐ ์ฑํ
- ๋ฉํฐํ๋ ์ด์ด ๊ฒ์
- ์ค์๊ฐ ์๋ฆผ
- ํ์ ๋๊ตฌ
- ์ค์๊ฐ ๋์๋ณด๋
2. Socket.IO vs ์ผ๋ฐ HTTP
HTTP ํต์ ์ ํ๊ณ:
- ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด์ผ๋ง ์๋ต์ ๋ฐ์ ์ ์์ต๋๋ค.
- ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ๋จผ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ ์ ์์ต๋๋ค.
- ์ค์๊ฐ ํต์ ์ด ์ด๋ ต์ต๋๋ค.
Socket.IO์ ์ฅ์ :
- ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ์ธ์ ๋ ์ง ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ ์ ์์ต๋๋ค.
- ํด๋ผ์ด์ธํธ๋ ์๋ฒ์๊ฒ ์ธ์ ๋ ์ง ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ ์ ์์ต๋๋ค.
- ์ค์๊ฐ ์๋ฐฉํฅ ํต์ ์ด ๊ฐ๋ฅํฉ๋๋ค.
3. ์๋ฒ ์ธก ๊ตฌํ
1) Socket.IO ์๋ฒ ์ค์
๋จผ์ ํ์ํ ๋ชจ๋๋ค์ ๋ถ๋ฌ์ค๊ณ ์๋ฒ๋ฅผ ์ค์ ํฉ๋๋ค:
const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const path = require('path'); const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: "*", methods: ["GET", "POST"] } });
์ค๋ช :
express
- ์น ์๋ฒ ํ๋ ์์ํฌhttp
-HTTP
์๋ฒ ์์ฑsocket.io
-Socket.IO
์๋ฒ ์์ฑcors
- ๋ค๋ฅธ ๋๋ฉ์ธ์์์ ์ ๊ทผ ํ์ฉ
2) ์ ์ ํ์ผ ์๋น ์ค์
// ์ ์ ํ์ผ ์๋น app.use(express.static(path.join(__dirname, 'public')));
์ค๋ช :
public
ํด๋์ ํ์ผ๋ค์ ์น์์ ์ ๊ทผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.HTML
,CSS
,JavaScript
ํ์ผ๋ค์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๊ณตํฉ๋๋ค.
3) ๊ฒ์ ์ํ ์ ์ฅ์ ์ค์
// ๊ฒ์ ์ํ ์ ์ฅ์ const games = new Map(); const players = new Map();
์ค๋ช :
Map
- ํค-๊ฐ ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ์๋ฃ๊ตฌ์กฐ์ ๋๋ค.games
- ํ์ฌ ์งํ ์ค์ธ ๊ฒ์๋ค์ ์ ์ฅํฉ๋๋ค.players
- ํ์ฌ ์ ์ ์ค์ธ ํ๋ ์ด์ด๋ค์ ์ ์ฅํฉ๋๋ค.
4) Socket.IO ์ฐ๊ฒฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
// Socket.IO ์ด๋ฒคํธ ์ฒ๋ฆฌ io.on('connection', (socket) => { console.log('์๋ก์ด ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ:', socket.id); // ํ๋ ์ด์ด ์ ๋ณด ์ ์ฅ players.set(socket.id, { id: socket.id, name: 'Anonymous', currentGame: null, color: null });
์ค๋ช :
io.on('connection')
- ์๋ก์ด ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐ๋ ๋ ์คํ๋ฉ๋๋ค.socket.id
- ๊ฐ ํด๋ผ์ด์ธํธ์ ๊ณ ์ ID์ ๋๋ค.players.set()
- ํ๋ ์ด์ด ์ ๋ณด๋ฅผ ์ ์ฅํฉ๋๋ค.
5) ๊ฒ์ ์ฐธ๊ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
// ๊ฒ์ ์ฐธ๊ฐ socket.on('join-game', (data) => { const { gameId, playerName } = data; const player = players.get(socket.id); if (!games.has(gameId)) { games.set(gameId, { id: gameId, players: [], gameState: null, spectators: [], createdAt: Date.now() }); } const game = games.get(gameId);
์ค๋ช :
socket.on('join-game')
- ํด๋ผ์ด์ธํธ๊ฐ ๊ฒ์ ์ฐธ๊ฐ ์์ฒญ์ ๋ณด๋ผ ๋ ์คํ๋ฉ๋๋ค.data
- ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ธ ๋ฐ์ดํฐ (gameId
,playerName
)์ ๋๋ค.- ๊ฒ์์ด ์์ผ๋ฉด ์๋ก ์์ฑํฉ๋๋ค.
- ๊ฒ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
6) ํ๋ ์ด์ด ์ถ๊ฐ ๋ฐ ๊ฒ์ ์์
// ๊ฒ์์ ํ๋ ์ด์ด ์ถ๊ฐ if (game.players.length < 2) { const color = game.players.length === 0 ? 'black' : 'white'; player.currentGame = gameId; player.color = color; player.name = playerName || `Player ${color}`; game.players.push({ id: socket.id, name: player.name, color: color }); socket.join(gameId); // ๊ฒ์ ์์ ์๋ฆผ if (game.players.length === 2) { io.to(gameId).emit('game-start', { gameId: gameId, players: game.players }); } else { socket.emit('waiting-for-player', { gameId: gameId, currentPlayers: game.players.length }); } console.log(`ํ๋ ์ด์ด ${player.name}์ด ๊ฒ์ ${gameId}์ ์ฐธ๊ฐํ์ต๋๋ค.`); }
์ค๋ช :
- ๊ฒ์์ ํ๋ ์ด์ด๊ฐ 2๋ช ๋ฏธ๋ง์ด๋ฉด ์ถ๊ฐํฉ๋๋ค.
- ์ฒซ ๋ฒ์งธ ํ๋ ์ด์ด๋ ๊ฒ์ ๋, ๋ ๋ฒ์งธ๋ ํฐ ๋์ ๋๋ค.
socket.join(gameId)
- ํน์ ๊ฒ์๋ฐฉ์ ์์ผ์ ์ถ๊ฐํฉ๋๋ค.io.to(gameId).emit()
- ํน์ ๋ฐฉ์ ๋ชจ๋ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉ์์ง๋ฅผ ์ ์กํฉ๋๋ค.socket.emit()
- ํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ๋ง ๋ฉ์์ง๋ฅผ ์ ์กํฉ๋๋ค.
7) ๊ด์ ์ ์ถ๊ฐ
} else { // ๊ด์ ์๋ก ์ถ๊ฐ game.spectators.push({ id: socket.id, name: playerName || 'Spectator' }); socket.join(gameId); socket.emit('spectator-joined', { gameId: gameId, message: '๊ด์ ์๋ก ์ฐธ๊ฐํ์ต๋๋ค.' }); console.log(`๊ด์ ์ ${playerName}์ด ๊ฒ์ ${gameId}์ ์ฐธ๊ฐํ์ต๋๋ค.`); } });
์ค๋ช :
- ๊ฒ์์ด ๊ฐ๋ ์ฐฌ ๊ฒฝ์ฐ ๊ด์ ์๋ก ์ถ๊ฐํฉ๋๋ค.
- ๊ด์ ์๋ ๊ฒ์์ ๋ณผ ์๋ง ์๊ณ ์ฐธ์ฌํ ์ ์์ต๋๋ค.
- ๊ด์ ์์๊ฒ ํน๋ณํ ๋ฉ์์ง๋ฅผ ์ ์กํฉ๋๋ค.
8) ๊ฒ์ ์ด๋ ์ฒ๋ฆฌ
// ๊ฒ์ ์ด๋ socket.on('make-move', (data) => { const { gameId, x, y, color } = data; const game = games.get(gameId); if (!game) { socket.emit('error', { message: '๊ฒ์์ ์ฐพ์ ์ ์์ต๋๋ค.' }); return; } // ํ์ฌ ํ๋ ์ด์ด์ ์ฐจ๋ก์ธ์ง ํ์ธ const currentPlayer = game.players.find(p => p.id === socket.id); if (!currentPlayer || currentPlayer.color !== color) { socket.emit('error', { message: '๋น์ ์ ์ฐจ๋ก๊ฐ ์๋๋๋ค.' }); return; } // ์ด๋์ ๋ชจ๋ ํ๋ ์ด์ด์๊ฒ ์ ์ก io.to(gameId).emit('move-made', { x: x, y: y, color: color, player: currentPlayer.name }); console.log(`${currentPlayer.name}์ด (${x}, ${y})์ ${color} ๋์ ๋์์ต๋๋ค.`); });
์ค๋ช :
socket.on('make-move')
- ํด๋ผ์ด์ธํธ๊ฐ ์ด๋ ์์ฒญ์ ๋ณด๋ผ ๋ ์คํ๋ฉ๋๋ค.- ๊ฒ์ ์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ธํฉ๋๋ค.
- ํ์ฌ ํ๋ ์ด์ด์ ์ฐจ๋ก์ธ์ง ํ์ธํฉ๋๋ค.
io.to(gameId).emit()
- ๋ชจ๋ ํ๋ ์ด์ด์๊ฒ ์ด๋ ์ ๋ณด๋ฅผ ์ ์กํฉ๋๋ค.
9) ์ฐ๊ฒฐ ํด์ ์ฒ๋ฆฌ
// ์ฐ๊ฒฐ ํด์ socket.on('disconnect', () => { console.log('ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ ํด์ :', socket.id); const player = players.get(socket.id); if (player && player.currentGame) { const game = games.get(player.currentGame); if (game) { // ํ๋ ์ด์ด๋ฅผ ๊ฒ์์์ ์ ๊ฑฐ game.players = game.players.filter(p => p.id !== socket.id); game.spectators = game.spectators.filter(s => s.id !== socket.id); // ๊ฒ์์ด ๋น์ด์์ผ๋ฉด ์ญ์ if (game.players.length === 0 && game.spectators.length === 0) { games.delete(player.currentGame); console.log(`๊ฒ์ ${player.currentGame}์ด ์ญ์ ๋์์ต๋๋ค.`); } else { // ๋ค๋ฅธ ํ๋ ์ด์ด๋ค์๊ฒ ์๋ฆผ io.to(player.currentGame).emit('player-left', { playerId: socket.id, playerName: player.name }); } } } // ํ๋ ์ด์ด ์ ๋ณด ์ญ์ players.delete(socket.id); }); });
์ค๋ช :
socket.on('disconnect')
- ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ์ด ๋์ด์ง ๋ ์คํ๋ฉ๋๋ค.- ํ๋ ์ด์ด๋ฅผ ๊ฒ์์์ ์ ๊ฑฐํฉ๋๋ค.
- ๊ฒ์์ด ๋น์ด์์ผ๋ฉด ๊ฒ์์ ์ญ์ ํฉ๋๋ค.
- ๋ค๋ฅธ ํ๋ ์ด์ด๋ค์๊ฒ ํ๋ ์ด์ด ํด์ฅ์ ์๋ฆฝ๋๋ค.
- ํ๋ ์ด์ด ์ ๋ณด๋ฅผ ์ญ์ ํฉ๋๋ค.
4. ํด๋ผ์ด์ธํธ ์ธก ๊ตฌํ
1) Socket.IO ํด๋ผ์ด์ธํธ ์ค์
HTML
ํ์ผ์์ Socket.IO
ํด๋ผ์ด์ธํธ๋ฅผ ์ค์ ํฉ๋๋ค:
<!DOCTYPE html> <html> <head> <title>๋ฐ๋ ๊ฒ์</title> <script src="/socket.io/socket.io.js"></script> </head> <body> <div id="game-container"> <h1>๋ฐ๋ ๊ฒ์</h1> <div id="game-info"></div> <div id="game-board"></div> </div> <script src="js/game.js"></script> </body> </html>
์ค๋ช :
/socket.io/socket.io.js
-Socket.IO
ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.- ์๋ฒ์์ ์๋์ผ๋ก ์ ๊ณต๋ฉ๋๋ค.
game.js
- ๊ฒ์ ๋ก์ง์ ๋ด์JavaScript
ํ์ผ์ ๋๋ค.
2) ํด๋ผ์ด์ธํธ JavaScript ์ค์
// Socket.IO ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ const socket = io(); // ์ฐ๊ฒฐ ์ํ ํ์ธ socket.on('connect', () => { console.log('์๋ฒ์ ์ฐ๊ฒฐ๋์์ต๋๋ค.'); document.getElementById('game-info').innerHTML = '์๋ฒ์ ์ฐ๊ฒฐ๋จ'; }); socket.on('disconnect', () => { console.log('์๋ฒ์์ ์ฐ๊ฒฐ์ด ๋์ด์ก์ต๋๋ค.'); document.getElementById('game-info').innerHTML = '์๋ฒ์์ ์ฐ๊ฒฐ์ด ๋์ด์ง'; });
์ค๋ช :
io()
-Socket.IO
ํด๋ผ์ด์ธํธ ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.socket.on('connect')
- ์๋ฒ์ ์ฐ๊ฒฐ๋ ๋ ์คํ๋ฉ๋๋ค.socket.on('disconnect')
- ์๋ฒ์์ ์ฐ๊ฒฐ์ด ๋์ด์ง ๋ ์คํ๋ฉ๋๋ค.
3) ๊ฒ์ ์ฐธ๊ฐ ๊ธฐ๋ฅ
// ๊ฒ์ ์ฐธ๊ฐ ํจ์ function joinGame(gameId, playerName) { socket.emit('join-game', { gameId: gameId, playerName: playerName }); } // ๊ฒ์ ์ฐธ๊ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ socket.on('waiting-for-player', (data) => { console.log('๋ค๋ฅธ ํ๋ ์ด์ด๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ค...'); document.getElementById('game-info').innerHTML = `๊ฒ์ ${data.gameId}์ ์ฐธ๊ฐํ์ต๋๋ค. ๋ค๋ฅธ ํ๋ ์ด์ด๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ค...`; }); socket.on('game-start', (data) => { console.log('๊ฒ์์ด ์์๋์์ต๋๋ค!'); document.getElementById('game-info').innerHTML = `๊ฒ์์ด ์์๋์์ต๋๋ค! ํ๋ ์ด์ด: ${data.players.map(p => p.name).join(', ')}`; // ๊ฒ์ ๋ณด๋ ์ด๊ธฐํ initializeGameBoard(); });
์ค๋ช :
socket.emit('join-game')
- ์๋ฒ์ ๊ฒ์ ์ฐธ๊ฐ๋ฅผ ์์ฒญํฉ๋๋ค.socket.on('waiting-for-player')
- ๋ค๋ฅธ ํ๋ ์ด์ด๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ํ์ ๋๋ค.socket.on('game-start')
- ๊ฒ์์ด ์์๋ ๋ ์คํ๋ฉ๋๋ค.
4) ๊ฒ์ ์ด๋ ์ฒ๋ฆฌ
// ์ด๋ ์ ์ก ํจ์ function makeMove(x, y, color) { socket.emit('make-move', { gameId: currentGameId, x: x, y: y, color: color }); } // ์ด๋ ์์ ์ฒ๋ฆฌ socket.on('move-made', (data) => { console.log(`${data.player}์ด (${data.x}, ${data.y})์ ${data.color} ๋์ ๋์์ต๋๋ค.`); // ๊ฒ์ ๋ณด๋์ ๋ ํ์ placeStone(data.x, data.y, data.color); // ํด ๋ณ๊ฒฝ updateTurn(data.color === 'black' ? 'white' : 'black'); }); // ์๋ฌ ์ฒ๋ฆฌ socket.on('error', (data) => { console.error('์๋ฌ:', data.message); alert(data.message); });
์ค๋ช :
socket.emit('make-move')
- ์๋ฒ์ ์ด๋์ ์์ฒญํฉ๋๋ค.socket.on('move-made')
- ๋ค๋ฅธ ํ๋ ์ด์ด์ ์ด๋์ ๋ฐ์ ๋ ์คํ๋ฉ๋๋ค.placeStone()
- ๊ฒ์ ๋ณด๋์ ๋์ ํ์ํ๋ ํจ์์ ๋๋ค.updateTurn()
- ํด์ ๋ณ๊ฒฝํ๋ ํจ์์ ๋๋ค.
5) ํ๋ ์ด์ด ํด์ฅ ์ฒ๋ฆฌ
// ํ๋ ์ด์ด ํด์ฅ ์ฒ๋ฆฌ socket.on('player-left', (data) => { console.log(`${data.playerName}์ด ๊ฒ์์ ๋ ๋ฌ์ต๋๋ค.`); document.getElementById('game-info').innerHTML = `${data.playerName}์ด ๊ฒ์์ ๋ ๋ฌ์ต๋๋ค.`; }); // ๊ด์ ์ ์ฐธ๊ฐ ์ฒ๋ฆฌ socket.on('spectator-joined', (data) => { console.log(data.message); document.getElementById('game-info').innerHTML = data.message; });
์ค๋ช :
socket.on('player-left')
- ๋ค๋ฅธ ํ๋ ์ด์ด๊ฐ ๊ฒ์์ ๋ ๋ ๋ ์คํ๋ฉ๋๋ค.socket.on('spectator-joined')
- ๊ด์ ์๋ก ์ฐธ๊ฐํ ๋ ์คํ๋ฉ๋๋ค.
5. ์ค์ต ์์
1) ๊ฐ๋จํ ์ฑํ ์ ํ๋ฆฌ์ผ์ด์
์๋ฒ ์ฝ๋ (chat-server.js
):
const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const app = express(); const server = http.createServer(app); const io = socketIo(server); // ์ ์ ํ์ผ ์๋น app.use(express.static('public')); // Socket.IO ์ฐ๊ฒฐ ์ฒ๋ฆฌ io.on('connection', (socket) => { console.log('์๋ก์ด ์ฌ์ฉ์ ์ฐ๊ฒฐ:', socket.id); // ์ฌ์ฉ์ ์ ์ฅ socket.on('join', (username) => { socket.username = username; io.emit('user-joined', username); console.log(`${username}์ด ์ ์ฅํ์ต๋๋ค.`); }); // ๋ฉ์์ง ์ ์ก socket.on('message', (message) => { io.emit('message', { username: socket.username, message: message, time: new Date().toLocaleTimeString() }); }); // ์ฐ๊ฒฐ ํด์ socket.on('disconnect', () => { if (socket.username) { io.emit('user-left', socket.username); console.log(`${socket.username}์ด ํด์ฅํ์ต๋๋ค.`); } }); }); const PORT = 3000; server.listen(PORT, () => { console.log(`์ฑํ ์๋ฒ๊ฐ ํฌํธ ${PORT}์์ ์คํ ์ค์ ๋๋ค.`); });
ํด๋ผ์ด์ธํธ ์ฝ๋ (public/index.html
):
<!DOCTYPE html> <html> <head> <title>์ค์๊ฐ ์ฑํ </title> <script src="/socket.io/socket.io.js"></script> <style> #chat-container { width: 400px; height: 300px; border: 1px solid #ccc; overflow-y: scroll; padding: 10px; } #message-input { width: 300px; margin-top: 10px; } </style> </head> <body> <h1>์ค์๊ฐ ์ฑํ </h1> <input type="text" id="username" placeholder="์ฌ์ฉ์๋ช " /> <button onclick="join()">์ ์ฅ</button> <br><br> <div id="chat-container"></div> <input type="text" id="message-input" placeholder="๋ฉ์์ง๋ฅผ ์ ๋ ฅํ์ธ์" /> <button onclick="sendMessage()">์ ์ก</button> <script> const socket = io(); const chatContainer = document.getElementById('chat-container'); const messageInput = document.getElementById('message-input'); function join() { const username = document.getElementById('username').value; if (username.trim()) { socket.emit('join', username); document.getElementById('username').disabled = true; } } function sendMessage() { const message = messageInput.value; if (message.trim()) { socket.emit('message', message); messageInput.value = ''; } } socket.on('user-joined', (username) => { addMessage(`์์คํ : ${username}์ด ์ ์ฅํ์ต๋๋ค.`); }); socket.on('user-left', (username) => { addMessage(`์์คํ : ${username}์ด ํด์ฅํ์ต๋๋ค.`); }); socket.on('message', (data) => { addMessage(`${data.username}: ${data.message} (${data.time})`); }); function addMessage(message) { const div = document.createElement('div'); div.textContent = message; chatContainer.appendChild(div); chatContainer.scrollTop = chatContainer.scrollHeight; } messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } }); </script> </body> </html>
2) ์คํ ๋ฐฉ๋ฒ
# ํ์ํ ํจํค์ง ์ค์น npm install express socket.io # ์๋ฒ ์คํ node chat-server.js
ํ ์คํธ ๋ฐฉ๋ฒ:
- ๋ธ๋ผ์ฐ์ ์์
http://localhost:3000
์ ์ - ์ฌ์ฉ์๋ช ์ ๋ ฅ ํ ์ ์ฅ
- ์ฌ๋ฌ ๋ธ๋ผ์ฐ์ ์ฐฝ์ ์ด์ด์ ์ค์๊ฐ ์ฑํ ํ ์คํธ
6. ๊ณ ๊ธ ๊ธฐ๋ฅ
1) ๋ฐฉ(Room) ๊ธฐ๋ฅ
// ๋ฐฉ ์์ฑ ๋ฐ ์ฐธ๊ฐ socket.on('join-room', (roomId) => { socket.join(roomId); socket.roomId = roomId; console.log(`๋ฐฉ ${roomId}์ ์ฐธ๊ฐํ์ต๋๋ค.`); }); // ๋ฐฉ์๋ง ๋ฉ์์ง ์ ์ก socket.on('room-message', (message) => { if (socket.roomId) { io.to(socket.roomId).emit('message', { username: socket.username, message: message }); } });
2) ๊ฐ์ธ ๋ฉ์์ง
// ํน์ ์ฌ์ฉ์์๊ฒ ๋ฉ์์ง ์ ์ก socket.on('private-message', (data) => { const targetSocket = io.sockets.sockets.get(data.to); if (targetSocket) { targetSocket.emit('private-message', { from: socket.username, message: data.message }); } });
3) ์ฐ๊ฒฐ ์ํ ํ์ธ
// ์ฐ๊ฒฐ ์ํ ํ์ธ setInterval(() => { if (!socket.connected) { console.log('์๋ฒ์์ ์ฐ๊ฒฐ์ด ๋์ด์ก์ต๋๋ค. ์ฌ์ฐ๊ฒฐ ์๋ ์ค...'); } }, 5000); // ์ฌ์ฐ๊ฒฐ ์๋ socket.on('reconnect', () => { console.log('์๋ฒ์ ์ฌ์ฐ๊ฒฐ๋์์ต๋๋ค.'); });
7. ์ฑ๋ฅ ์ต์ ํ
1) ์ด๋ฒคํธ ์ ํ
// ํด๋ผ์ด์ธํธ๋น ์ด๋ฒคํธ ์ ํ const rateLimit = require('socket.io-rate-limiter'); const limiter = rateLimit({ points: 10, // ์ต๋ ์ด๋ฒคํธ ์ duration: 1, // ์๊ฐ (์ด) errorMessage: '๋๋ฌด ๋ง์ ์์ฒญ์ด ๋ฐ์ํ์ต๋๋ค.' }); io.use(limiter);
2) ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
// ์ฐ๊ฒฐ ํด์ ์ ๋ฉ๋ชจ๋ฆฌ ์ ๋ฆฌ socket.on('disconnect', () => { // ๊ฒ์ ์ํ ์ ๋ฆฌ if (socket.currentGame) { cleanupGame(socket.currentGame); } // ํ๋ ์ด์ด ์ ๋ณด ์ ๋ฆฌ delete players[socket.id]; });
8. ๋ณด์ ๊ณ ๋ ค์ฌํญ
1) ์ ๋ ฅ ๊ฒ์ฆ
// ๋ฉ์์ง ๊ธธ์ด ์ ํ socket.on('message', (message) => { if (typeof message !== 'string' || message.length > 1000) { socket.emit('error', { message: '๋ฉ์์ง๊ฐ ๋๋ฌด ๊น๋๋ค.' }); return; } // XSS ๋ฐฉ์ง const sanitizedMessage = message.replace(/<script>/gi, ''); io.emit('message', { username: socket.username, message: sanitizedMessage }); });
2) ์ธ์ฆ
// ํ ํฐ ๊ธฐ๋ฐ ์ธ์ฆ socket.on('authenticate', (token) => { try { const decoded = jwt.verify(token, process.env.JWT_SECRET); socket.userId = decoded.userId; socket.emit('authenticated'); } catch (error) { socket.emit('error', { message: '์ธ์ฆ์ ์คํจํ์ต๋๋ค.' }); } });
9. ๋๋ฒ๊น ๋ฐ ๋ก๊น
1) ๋๋ฒ๊ทธ ๋ชจ๋ ํ์ฑํ
// ์๋ฒ ์ธก ๋๋ฒ๊ทธ ํ์ฑํ const io = socketIo(server, { cors: { origin: "*", methods: ["GET", "POST"] }, debug: true });
2) ๋ก๊น
// ์ฐ๊ฒฐ ๋ก๊น io.on('connection', (socket) => { console.log(`์๋ก์ด ์ฐ๊ฒฐ: ${socket.id}`); socket.on('disconnect', () => { console.log(`์ฐ๊ฒฐ ํด์ : ${socket.id}`); }); // ๋ชจ๋ ์ด๋ฒคํธ ๋ก๊น socket.onAny((eventName, ...args) => { console.log(`์ด๋ฒคํธ ${eventName}:`, args); }); });
10. ๋ค์ ๋จ๊ณ
Socket.IO
๊ธฐ๋ณธ์ ๋ฐฐ์ ๋ค๋ฉด ๋ค์์ ํ์ตํด๋ณด์ธ์:
- ์ค์๊ฐ ๊ฒ์ ๊ฐ๋ฐ - ๋ฐ๋, ์ฒด์ค, ํฑํํ ๋ฑ
- ์ค์๊ฐ ์ฑํ ์์คํ - ๊ฐ์ธ ๋ฉ์์ง, ๊ทธ๋ฃน ์ฑํ
- ์ค์๊ฐ ์๋ฆผ ์์คํ - ํธ์ ์๋ฆผ, ์ด๋ฉ์ผ ์๋ฆผ
- ์ค์๊ฐ ๋์๋ณด๋ - ๋ฐ์ดํฐ ์๊ฐํ, ๋ชจ๋ํฐ๋ง
- ํ์ ๋๊ตฌ - ์ค์๊ฐ ๋ฌธ์ ํธ์ง, ํ๋ฉด ๊ณต์
์ถ์ฒ ํ์ต ์์:
โ ์ด ํ์ด์ง๋ ์๋์ผ๋ก ์์ฑ๋์์ต๋๋ค.