๋ชฉ์ฐจ
๐ 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' } });
๋ฐฉ ์ ๊ทผ ์ ์ด:
- io.on('connection', (socket) =
{ 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(); }; }, []);
๐ ๊ด๋ จ ๋ฌธ์
- Socket.IO - ๋ฐ๋ ๋ฉํ๋ฒ์ค ์ค์๊ฐ ํต์ ํ๋กํ ์ฝ
- Node.js - ๋ฐ๋ ๋ฉํ๋ฒ์ค JavaScript ๋ฐํ์
- Express.js - ๋ฐ๋ ๋ฉํ๋ฒ์ค ์น ํ๋ ์์ํฌ