[Node.js] 12์ฅ ์น ์์ผ์ผ๋ก ์ค์๊ฐ ๋ฐ์ดํฐ ์ ์กํ๊ธฐ
๐ ์น ์์ผ
์ค์๊ฐ ์๋ฐฉํฅ ๋ฐ์ดํฐ ์ ์ก์ ์ํ ๊ธฐ์
- 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