์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[Node.js] 12์žฅ ์›น ์†Œ์ผ“์œผ๋กœ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „์†กํ•˜๊ธฐ

22-23/22-23 Node.js

by dev otcroz 2022. 12. 22. 10:00

๋ณธ๋ฌธ

728x90

๐Ÿ” ์›น ์†Œ์ผ“

์‹ค์‹œ๊ฐ„ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ์ „์†ก์„ ์œ„ํ•œ ๊ธฐ์ˆ 

  • WS ํ”„๋กœํ† ์ฝœ ์‚ฌ์šฉ
  • ๋…ธ๋“œ์—์„œ๋Š” WS๋‚˜  Socket.IO ๊ฐ™์€ ํŒจํ‚ค์ง€๋ฅผ ํ†ตํ•ด ์›น ์†Œ์ผ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ์ฒ˜์Œ ์›น ์†Œ์ผ“ ์—ฐ๊ฒฐ์ด ์ด๋ฃจ์–ด์ง€๊ณ  ๋‚˜๋ฉด ๊ณ„์† ์—ฐ๊ฒฐ๋œ ์ƒํƒœ๋กœ ์žˆ์œผ๋ฏ€๋กœ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ๋Š”์ง€ ๋”ฐ๋กœ ์š”์ฒญ ๋ณด๋‚ผ ํ•„์š” ์—†์Œ
  • HTTP ํ”„๋กœํ† ์ฝœ๊ณผ ํฌํŠธ ๊ณต์œ  ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ๋‹ค๋ฅธ ํฌํŠธ์— ์—ฐ๊ฒฐํ•  ํ•„์š” ์—†์Œ

 

โœ… ํด๋ง(polling)

์›น ์†Œ์ผ“ ์ด์ „์—๋Š” HTTP ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „์†ก ๊ตฌํ˜„ํ–ˆ๊ณ , ๋Œ€ํ‘œ์ ์ธ ๋ฐฉ๋ฒ•์ด ํด๋ง์ด๋‹ค.

HTTP๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ ํ–ฅํ•˜๋Š” ๋‹จ๋ฐฉํ–ฅ ํ†ต์‹ ์ด๋ฏ€๋กœ ์ฃผ๊ธฐ์ ์œผ๋กœ ์„œ๋ฒ„์— ์ƒˆ๋กœ์šด ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ๋Š”์ง€ ์š”์ฒญ ๋ณด๋‚ธ ํ›„, ์žˆ๋‹ค๋ฉด ๊ฐ€์ ธ์˜ค๋Š” ๋‹จ์ˆœ ๋ฌด์‹ํ•œ ๋ฐฉ๋ฒ•.

 

โœ… ์„œ๋ฒ„์„ผํŠธ ์ด๋ฒคํŠธ(Server Sent Events, SSE)

EventSource ๊ฐ์ฒด ์‚ฌ์šฉ

์ฒ˜์Œ์— ํ•œ ๋ฒˆ๋งŒ ์—ฐ๊ฒฐํ•˜๋ฉด ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์— ์ง€์†์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ธ๋‹ค. ์›น ์†Œ์ผ“๊ณผ์˜ ๋‹ค๋ฅธ ์ ์€ ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์—†๋‹ค.

 

๐Ÿ“Œ Socket.IO

์›น ์†Œ์ผ“์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

 

๐Ÿ“Œ ์›น ์†Œ์ผ“ ์ƒํƒœ

  • CONNECTING: ์—ฐ๊ฒฐ ์ค‘
  • OPEN: ์—ด๋ฆผ, ์—ด๋ฆผ์ผ ๋•Œ๋งŒ ์—๋Ÿฌ ์—†์ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  • CLOSING: ๋‹ซ๋Š” ์ค‘
  • CLOSED: ๋‹ซํž˜

 

๐Ÿ“Œ ์›น ์†Œ์ผ“ ๋กœ์ง ์ฝ”๋“œ (ws ํŒจํ‚ค์ง€)

//socket.js
const WebSocket=require('ws');

module.exports = (server) => {
    const wss = new WebSocket.Server({server}); //์ต์Šคํ”„๋ ˆ์Šค ์„œ๋ฒ„๋ฅผ ์›น ์†Œ์ผ“ ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐ

	//์›น์†Œ์ผ“ ์„œ๋ฒ„์— ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋ถ™์ด๊ธฐ
    wss.on('connection', (ws, req) => { //์›น์†Œ์ผ“ ์—ฐ๊ฒฐ ์‹œ
    
    	//ํด๋ผ์ด์–ธํŠธ ip ์•Œ์•„๋‚ด๊ธฐ
        const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
        
        console.log('์ƒˆ๋กœ์šด ํด๋ผ์ด์–ธํŠธ ์ ‘์†', ip);
        ws.on('message', (message) => { //ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹  ์‹œ
            console.log(message);
        });
        ws.on('error', (error) => { //์—๋Ÿฌ ์‹œ
            console.error(error);
        });
        ws.on('close', () => { //์—ฐ๊ฒฐ ์ข…๋ฃŒ ์‹œ
            console.log('ํด๋ผ์ด์–ธํŠธ ์ ‘์† ํ•ด์ œ', ip);
            clearInterval(ws.interval);
        });

        ws.interval = setInterval(() => { //3์ดˆ๋งˆ๋‹ค ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
            if(ws.readyState === ws.OPEN){
                ws.send('์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค.');
            }
        }, 3000);
    });
};

ws ํŒจํ‚ค์ง€๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ์›น ์†Œ์ผ“์„ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•  ๋•Œ ์ข‹๋‹ค.

ws ๋ชจ๋“ˆ ๋ถˆ๋Ÿฌ์˜จ ํ›„ ์ต์Šคํ”„๋ ˆ์Šค ์„œ๋ฒ„๋ฅผ ์›น ์†Œ์ผ“๊ณผ ์—ฐ๊ฒฐํ–ˆ๋‹ค. ์—ฐ๊ฒฐ ํ›„์—๋Š” ์›น ์†Œ์ผ“ ์„œ๋ฒ„(wss)์— ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋ถ™์ธ๋‹ค. ์›น ์†Œ์ผ“์€ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์œผ๋ฏ€๋กœ ํ•ญ์ƒ ๋Œ€๊ธฐํ•ด์•ผ ํ•œ๋‹ค.

 

 

๐Ÿ” Socket.IO

๊ตฌํ˜„ํ•˜๋ ค๋Š” ์„œ๋น„์Šค๊ฐ€ ์ข€ ๋” ๋ณต์žกํ•ด์ง„๋‹ค๋ฉด Socket.IO๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํŽธํ•˜๋‹ค.

//socket.js
const SocketIO = require('socket.io');

module.exports = (server) => {
    const io = SocketIO(server, { path: '/socket.io' });

    io.on('connection', (socket) => { // ์›น์†Œ์ผ“ ์—ฐ๊ฒฐ ์‹œ
        const req = socket.request;
        const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
        console.log('์ƒˆ๋กœ์šด ํด๋ผ์ด์–ธํŠธ ์ ‘์†!', ip, socket.id, req.ip);
        socket.on('disconnect', () => { // ์—ฐ๊ฒฐ ์ข…๋ฃŒ ์‹œ
            console.log('ํด๋ผ์ด์–ธํŠธ ์ ‘์† ํ•ด์ œ', ip, socket.id);
            clearInterval(socket.interval);
        });
        socket.on('error', (error) => { // ์—๋Ÿฌ ์‹œ
            console.error(error);
        });
        socket.on('reply', (data) => { // ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฉ”์‹œ์ง€
            console.log(data);
        });
        socket.interval = setInterval(() => { // 3์ดˆ๋งˆ๋‹ค ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
            socket.emit('news', 'Hello Socket.IO');
        }, 3000);
    });
};
  • socket.io ํŒจํ‚ค์ง€๋ฅผ ๋ถˆ๋Ÿฌ์™€ ์ต์Šคํ”„๋ ˆ์Šค ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐ
  • ์—ฐ๊ฒฐ ํ›„์—๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋ถ™์ธ๋‹ค. connection ์ด๋ฒคํŠธ๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ ‘์†ํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๊ณ , ์ฝœ๋ฐฑ์œผ๋กœ ์†Œ์ผ“ ๊ฐ์ฒด(socket)๋ฅผ ์ œ๊ณต
  • io์™€ socket ๊ฐ์ฒด๊ฐ€ Socket.IO์˜ ํ•ต์‹ฌ์ด๋‹ค.

socket ๊ฐ์ฒด์— ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ

  • disconnect: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฐ๊ฒฐ์„ ๋Š์—ˆ์„ ๋•Œ ๋ฐœ์ƒ
  • error: ํ†ต์‹  ๊ณผ์ •์—์„œ ์—๋Ÿฌ๊ฐ€ ๋‚˜์™”์„ ๋•Œ ๋ฐœ์ƒ
  • reply: ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋งŒ๋“  ์ด๋ฒคํŠธ, ํด๋ผ์ด์–ธํŠธ์—์„œ reply๋ผ๋Š” ์ด๋ฒคํŠธ๋ช…์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ๋•Œ ์„œ๋ฒ„์—์„œ ๋ฐ›๋Š” ๋ถ€๋ถ„

 

ํด๋ผ์ด์–ธํŠธ ๋ถ€๋ถ„

<!-- veiws/index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>GIF ์ฑ„ํŒ…๋ฐฉ</title>
</head>
<body>
<div>F12๋ฅผ ๋ˆŒ๋Ÿฌ console ํƒญ๊ณผ network ํƒญ์„ ํ™•์ธํ•˜์„ธ์š”.</div>
<script src="/socket.io/socket.io.js"></script>
<script>
    const socket = io.connect('http://localhost:8005', { 
        path: '/socket.io',
        transports: ['websocket'],
    });
    socket.on('news', function (data) {
        console.log(data);
        socket.emit('reply', 'Hello Node.JS');
    });
</script>
</body>
</html>
  • /socket.io/socket.io.js๋Š” Socket.IO์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ์ œ๊ณตํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ์ด๋ฉฐ, ์ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์™€ ์œ ์‚ฌํ•œ API๋กœ ์›น ์†Œ์ผ“ ํ†ต์‹  ๊ฐ€๋Šฅ
  • ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” io ๊ฐ์ฒด์— ์„œ๋ฒ„ ์ฃผ์†Œ๋ฅผ ์ ์–ด ์—ฐ๊ฒฐ
  • ws ํ”„๋กœํ† ์ฝœ์ด ์•„๋‹ˆ๋ผ http ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉ

 

๐Ÿ” ๋ฏธ๋“ค์›จ์–ด์™€ ์†Œ์ผ“ ์—ฐ๊ฒฐ

Socket.IO๋„ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ express-session์„ ๊ณต์œ ํ•˜๋ฉด ๋œ๋‹ค.

 

๐Ÿ“Œ socket.js.์˜ axios ์š”์ฒญ

socket.js์—์„œ axios ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๋Š” ์š”์ฒญ์ž๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ์ง€ ์•Š๋‹ค. ๋ธŒ๋ผ์šฐ์ €์—์„œ axios ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๋Š” ์ž๋™์œผ๋กœ ์ฟ ํ‚ค๋ฅผ ๊ฐ™์ด ๋„ฃ์–ด์„œ ๋ณด๋‚ด์ง€๋งŒ, ์„œ๋ฒ„์—์„œ axios ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๋Š” ์ฟ ํ‚ค๊ฐ€ ๊ฐ™์ด ๋ณด๋‚ด์ง€์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ express-session์ด ์š”์ฒญ์ž๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ํŒ๋‹จํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ์š”์ฒญ ํ—ค๋”์— ์„ธ์…˜ ์ฟ ํ‚ค๋ฅผ ์ง์ ‘ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค.

io ๊ฐ์ฒด์— cookie-parser๋ฅผ ์—ฐ๊ฒฐํ•œ ํ›„ axios ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ connet.sid ์ฟ ํ‚ค๋ฅผ ์ง์ ‘ ์„ค์ •ํ•œ๋‹ค.

 

 

๐Ÿ” ๊ธฐํƒ€ Socket.IO API

  • ํŠน์ •์ธ์—๊ฒŒ ๋ฉ”์„ธ์ง€ ๋ณด๋‚ด๋Š” API, ํŠน์ • ๋ฐฉ์— ๋ณด๋‚ด๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ, ๋ฐฉ ์•„์ด๋”” ๋Œ€์‹  ํŠน์ •์ธ์˜ ์†Œ์ผ“ ์•„์ด๋””๋ฅผ ๋„ฃ๋Š”๋‹ค๋Š” ์ ์—์„œ ๋‹ค๋ฅด๋‹ค.
socket. to (์†Œ์ผ“ ์•„์ด๋””).emit(์ด๋ฒคํŠธ, ๋ฐ์ดํ„ฐ);
  • ๋‚˜๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋‘์—๊ฒŒ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ
socket.broadcast.emit(์ด๋ฒคํŠธ, ๋ฐ์ดํ„ฐ);
socket.broadcast. to (๋ฐฉ์•„์ด๋””).emit(์ด๋ฒคํŠธ, ๋ฐ์ดํ„ฐ);

 

 

๐Ÿ” ํ•ต์‹ฌ ์ •๋ฆฌ

  • ์›น ์†Œ์ผ“๊ณผ HTTP๋Š” ๊ฐ™์€ ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋”ฐ๋กœ ํฌํŠธ๋ฅผ ์„ค์ •ํ•  ํ•„์š” ์—†๋‹ค.
  • ์›น ์†Œ์ผ“์€ ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์ด๋ฏ€๋กœ ์„œ๋ฒ„๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ”„๋ก ํŠธ์—”๋“œ ์ชฝ ์Šคํฌ๋ฆฝํŠธ๋„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • Socket.IO๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์›น ์†Œ์ผ“์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ๊นŒ์ง€ ์‹ค์‹œ๊ฐ„ ํ†ต์‹ ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Socket.IO ๋„ค์ž„์ŠคํŽ˜์ด์Šค์™€ ๋ฐฉ ๊ตฌ๋ถ„์„ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ํ•„์š”ํ•œ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  • app.set('io', io)๋กœ ์†Œ์ผ“ ๊ฐ์ฒด๋ฅผ ์ต์Šคํ”„๋ ˆ์Šค์™€ ์—ฐ๊ฒฐํ•˜๊ณ , req.app.get('io')๋กœ ๋ผ์šฐํ„ฐ์—์„œ ์†Œ์ผ“ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹
  • ์†Œ์ผ“ ํ†ต์‹ ๊ณผ ํ•จ๊ป˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐ์ž‘์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ์†Œ์ผ“๋งŒ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ๋ณด๋‹ค๋Š” HTTP ๋ผ์šฐํ„ฐ๋ฅผ ๊ฑฐ์น˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

Node.js

Editor : seol

728x90

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ