====== ๐ Socket.IO ======
Socket.IO๋ ์ค์๊ฐ ์๋ฐฉํฅ ํต์ ์ ์ํ JavaScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ์น ์ ํ๋ฆฌ์ผ์ด์
์์ ์ค์๊ฐ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๊ฒ ํด์ค๋๋ค.
===== ๐ ์ ์ =====
**Socket.IO**๋ ์น์์ผ์ ๊ธฐ๋ฐ์ผ๋ก ํ ์ค์๊ฐ ์๋ฐฉํฅ ํต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค. ์๋ ์ฌ์ฐ๊ฒฐ, ์ด๋ฒคํธ ๊ธฐ๋ฐ ํต์ , ํฌ๋ก์ค ๋ธ๋ผ์ฐ์ ํธํ์ฑ ๋ฑ์ ์ ๊ณตํ์ฌ ์ค์๊ฐ ์ ํ๋ฆฌ์ผ์ด์
๊ฐ๋ฐ์ ์ฝ๊ฒ ๋ง๋ค์ด์ค๋๋ค.
===== ๐ฏ ์ฃผ์ ํน์ง =====
**1. ์ค์๊ฐ ์๋ฐฉํฅ ํต์ **
- ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ ์ค์๊ฐ ๋ฐ์ดํฐ ๊ตํ
- ์ด๋ฒคํธ ๊ธฐ๋ฐ ๋ฉ์์ง ์ ์ก
- ์๋ ์ฌ์ฐ๊ฒฐ ๊ธฐ๋ฅ
**2. ํฌ๋ก์ค ๋ธ๋ผ์ฐ์ ํธํ์ฑ**
- ๋ชจ๋ ์ฃผ์ ๋ธ๋ผ์ฐ์ ์ง์
- ํด๋ฐฑ ๋ฉ์ปค๋์ฆ์ผ๋ก HTTP ํด๋ง ์ง์
- ์๋ ์ ์ก ๋ฐฉ์ ์ ํ
**3. ๋ฐฉ(Room) ์์คํ
**
- ํน์ ๊ทธ๋ฃน์๋ง ๋ฉ์์ง ์ ์ก
- ๋ค์์คํ์ด์ค ์ง์
- ์ฌ์ฉ์๋ณ ๊ฐ์ธ ๋ฉ์์ง
===== ๐๏ธ ์๋ฒ ์ธก ๊ตฌํ =====
**๊ธฐ๋ณธ ์๋ฒ ์ค์ :**
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, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
// ์ฐ๊ฒฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
io.on('connection', (socket) => {
console.log('์๋ก์ด ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ:', socket.id);
// ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ฉ์์ง ์์
socket.on('chat-message', (data) => {
console.log('๋ฐ์ ๋ฉ์์ง:', data);
// ๋ชจ๋ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉ์์ง ์ ์ก
io.emit('chat-message', {
user: data.user,
message: data.message,
timestamp: new Date()
});
});
// ์ฐ๊ฒฐ ํด์ ์ฒ๋ฆฌ
socket.on('disconnect', () => {
console.log('ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ ํด์ :', socket.id);
});
});
server.listen(3000, () => {
console.log('Socket.IO ์๋ฒ๊ฐ ํฌํธ 3000์์ ์คํ ์ค์
๋๋ค.');
});
**๋ฐฉ(Room) ๊ด๋ฆฌ:**
io.on('connection', (socket) => {
// ๋ฐฉ ์ฐธ๊ฐ
socket.on('join-room', (roomId) => {
socket.join(roomId);
console.log(`์ฌ์ฉ์๊ฐ ๋ฐฉ ${roomId}์ ์ฐธ๊ฐํ์ต๋๋ค.`);
// ๋ฐฉ์ ๋ค๋ฅธ ์ฌ์ฉ์๋ค์๊ฒ ์๋ฆผ
socket.to(roomId).emit('user-joined', {
userId: socket.id,
message: '์๋ก์ด ์ฌ์ฉ์๊ฐ ์ฐธ๊ฐํ์ต๋๋ค.'
});
});
// ๋ฐฉ์์ ๋ฉ์์ง ์ ์ก
socket.on('room-message', (data) => {
io.to(data.roomId).emit('message', {
user: data.user,
message: data.message,
timestamp: new Date()
});
});
// ๋ฐฉ ๋๊ฐ๊ธฐ
socket.on('leave-room', (roomId) => {
socket.leave(roomId);
socket.to(roomId).emit('user-left', {
userId: socket.id,
message: '์ฌ์ฉ์๊ฐ ๋ฐฉ์ ๋๊ฐ์ต๋๋ค.'
});
});
});
===== ๐ฎ ํด๋ผ์ด์ธํธ ์ธก ๊ตฌํ =====
**ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ:**
import io from 'socket.io-client';
// ์๋ฒ์ ์ฐ๊ฒฐ
const socket = io('http://localhost:3000');
// ์ฐ๊ฒฐ ์ด๋ฒคํธ
socket.on('connect', () => {
console.log('์๋ฒ์ ์ฐ๊ฒฐ๋์์ต๋๋ค.');
console.log('์์ผ ID:', socket.id);
});
socket.on('disconnect', () => {
console.log('์๋ฒ์์ ์ฐ๊ฒฐ์ด ๋์ด์ก์ต๋๋ค.');
});
// ๋ฉ์์ง ์์
socket.on('chat-message', (data) => {
console.log('์ ๋ฉ์์ง:', data);
displayMessage(data);
});
// ๋ฉ์์ง ์ ์ก
function sendMessage(message) {
socket.emit('chat-message', {
user: '์ฌ์ฉ์๋ช
',
message: message
});
}
**๋ฐฉ ๊ด๋ฆฌ (ํด๋ผ์ด์ธํธ):**
// ๋ฐฉ ์ฐธ๊ฐ
function joinRoom(roomId) {
socket.emit('join-room', roomId);
}
// ๋ฐฉ์์ ๋ฉ์์ง ์ ์ก
function sendRoomMessage(roomId, message) {
socket.emit('room-message', {
roomId: roomId,
user: '์ฌ์ฉ์๋ช
',
message: message
});
}
// ๋ฐฉ ์ด๋ฒคํธ ์์
socket.on('user-joined', (data) => {
console.log('์ ์ฌ์ฉ์ ์ฐธ๊ฐ:', data);
});
socket.on('user-left', (data) => {
console.log('์ฌ์ฉ์ ํด์ฅ:', data);
});
socket.on('message', (data) => {
displayMessage(data);
});
===== ๐ฏ ์ด๋ฒคํธ ์์คํ
=====
**๊ธฐ๋ณธ ์ด๋ฒคํธ:**
// ์๋ฒ ์ธก
io.on('connection', (socket) => {
// ์ปค์คํ
์ด๋ฒคํธ
socket.on('user-login', (userData) => {
console.log('์ฌ์ฉ์ ๋ก๊ทธ์ธ:', userData);
socket.broadcast.emit('user-online', userData);
});
socket.on('typing', (data) => {
socket.broadcast.emit('user-typing', data);
});
socket.on('stop-typing', (data) => {
socket.broadcast.emit('user-stop-typing', data);
});
});
// ํด๋ผ์ด์ธํธ ์ธก
socket.on('user-online', (userData) => {
console.log('์๋ก์ด ์ฌ์ฉ์ ์จ๋ผ์ธ:', userData);
});
socket.on('user-typing', (data) => {
showTypingIndicator(data.user);
});
socket.on('user-stop-typing', (data) => {
hideTypingIndicator(data.user);
});
===== ๐ ์ธ์ฆ ๋ฐ ๋ณด์ =====
**๋ฏธ๋ค์จ์ด๋ฅผ ํตํ ์ธ์ฆ:**
// ์๋ฒ ์ธก
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (token) {
// ํ ํฐ ๊ฒ์ฆ
verifyToken(token)
.then(user => {
socket.user = user;
next();
})
.catch(err => {
next(new Error('์ธ์ฆ ์คํจ'));
});
} else {
next(new Error('ํ ํฐ์ด ํ์ํฉ๋๋ค'));
}
});
// ํด๋ผ์ด์ธํธ ์ธก
const socket = io('http://localhost:3000', {
auth: {
token: 'your-jwt-token'
}
});
**๋ฐฉ ์ ๊ทผ ์ ์ด:**
{
socket.on('join-private-room', (roomId) => {
// ์ฌ์ฉ์ ๊ถํ ํ์ธ
if (hasRoomAccess(socket.user.id, roomId)) {
socket.join(roomId);
socket.emit('room-joined', { roomId });
} else {
socket.emit('access-denied', {
message: '์ด ๋ฐฉ์ ์ ๊ทผํ ๊ถํ์ด ์์ต๋๋ค.'
});
}
});
});
===== ๐ ์ค์๊ฐ ํต๊ณ =====
**์ฐ๊ฒฐ ํต๊ณ ์์ง:**
// ์๋ฒ ์ธก
let connectionStats = {
totalConnections: 0,
activeConnections: 0,
peakConnections: 0
};
io.on('connection', (socket) => {
connectionStats.totalConnections++;
connectionStats.activeConnections++;
if (connectionStats.activeConnections > connectionStats.peakConnections) {
connectionStats.peakConnections = connectionStats.activeConnections;
}
socket.on('disconnect', () => {
connectionStats.activeConnections--;
});
});
// ํต๊ณ API ์๋ํฌ์ธํธ
app.get('/socket-stats', (req, res) => {
res.json(connectionStats);
});
**์ค์๊ฐ ๋ชจ๋ํฐ๋ง:**
// ํด๋ผ์ด์ธํธ ์ธก
socket.on('connect', () => {
updateConnectionStatus('์ฐ๊ฒฐ๋จ');
});
socket.on('disconnect', () => {
updateConnectionStatus('์ฐ๊ฒฐ ๋๊น');
});
socket.on('reconnect', (attemptNumber) => {
console.log(`์ฌ์ฐ๊ฒฐ ์๋ ${attemptNumber}๋ฒ์งธ`);
});
socket.on('reconnect_error', (error) => {
console.log('์ฌ์ฐ๊ฒฐ ์ค๋ฅ:', error);
});
===== ๐ฎ ๊ฒ์ ์ ํ๋ฆฌ์ผ์ด์
=====
**์ค์๊ฐ ๊ฒ์ ๊ตฌํ:**
// ์๋ฒ ์ธก
io.on('connection', (socket) => {
socket.on('join-game', (gameId) => {
socket.join(gameId);
// ๊ฒ์ ์ํ ์ ์ก
const gameState = getGameState(gameId);
socket.emit('game-state', gameState);
});
socket.on('game-move', (data) => {
const { gameId, move } = data;
// ๊ฒ์ ๋ก์ง ์ฒ๋ฆฌ
const result = processGameMove(gameId, move, socket.id);
if (result.valid) {
// ๋ชจ๋ ํ๋ ์ด์ด์๊ฒ ์
๋ฐ์ดํธ ์ ์ก
io.to(gameId).emit('game-update', {
move: move,
gameState: result.gameState
});
} else {
socket.emit('invalid-move', { reason: result.reason });
}
});
});
// ํด๋ผ์ด์ธํธ ์ธก
socket.on('game-state', (gameState) => {
updateGameDisplay(gameState);
});
socket.on('game-update', (data) => {
updateGameDisplay(data.gameState);
animateMove(data.move);
});
function makeMove(move) {
socket.emit('game-move', {
gameId: currentGameId,
move: move
});
}
===== ๐ ๏ธ ๋ฌธ์ ํด๊ฒฐ =====
**์ฐ๊ฒฐ ๋ฌธ์ :**
// ์ฌ์ฐ๊ฒฐ ์ค์
const socket = io('http://localhost:3000', {
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
timeout: 20000
});
// ์ฐ๊ฒฐ ์ํ ๋ชจ๋ํฐ๋ง
socket.on('connect_error', (error) => {
console.log('์ฐ๊ฒฐ ์ค๋ฅ:', error);
});
socket.on('reconnect_failed', () => {
console.log('์ฌ์ฐ๊ฒฐ ์คํจ');
});
**๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง:**
// ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ ๋ฆฌ
function cleanup() {
socket.off('chat-message');
socket.off('user-joined');
socket.disconnect();
}
// ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ ์ ์ ๋ฆฌ
useEffect(() => {
return () => {
cleanup();
};
}, []);
===== ๐ ๊ด๋ จ ๋ฌธ์ =====
* [[wiki:it:dream_of_enc:metaverse:socketio|Socket.IO]] - ๋ฐ๋ ๋ฉํ๋ฒ์ค ์ค์๊ฐ ํต์ ํ๋กํ ์ฝ
* [[wiki:it:dream_of_enc:metaverse:nodejs|Node.js]] - ๋ฐ๋ ๋ฉํ๋ฒ์ค JavaScript ๋ฐํ์
* [[wiki:it:dream_of_enc:metaverse:express|Express.js]] - ๋ฐ๋ ๋ฉํ๋ฒ์ค ์น ํ๋ ์์ํฌ