====== ๐ 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'' ํด๋ผ์ด์ธํธ๋ฅผ ์ค์ ํฉ๋๋ค:
๋ฐ๋ ๊ฒ์
**์ค๋ช
:**
* ''/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''):**
์ค์๊ฐ ์ฑํ
์ค์๊ฐ ์ฑํ
==== 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(/