์‚ฌ์šฉ์ž ๋„๊ตฌ

์‚ฌ์ดํŠธ ๋„๊ตฌ


wiki:glossary:server_operations:socketio

๐Ÿ”Œ 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 - ๋ฐ”๋‘‘ ๋ฉ”ํƒ€๋ฒ„์Šค ์›น ํ”„๋ ˆ์ž„์›Œํฌ
wiki/glossary/server_operations/socketio.txt ยท ๋งˆ์ง€๋ง‰์œผ๋กœ ์ˆ˜์ •๋จ: ์ €์ž syjang0803

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki