[Daily morning study] WebSocket vs Server-Sent Events
#daily morning study
WebSocket๊ณผ SSE, ์ธ์ ๋ฌด์์ ์ธ๊น
์ค์๊ฐ ํต์ ์ด ํ์ํ ์ํฉ์ด๋ฉด WebSocket ์๋๋ฉด SSE(Server-Sent Events) ์ค ํ๋๋ฅผ ๊ณ ๋ฅด๊ฒ ๋๋ค. ๋ ๋ค HTTP ๊ธฐ๋ฐ์ด์ง๋ง ๋ฐฉํฅ์ฑ๊ณผ ๊ตฌ์กฐ๊ฐ ๋ค๋ฅด๋ค.
WebSocket
WebSocket์ ์๋ฐฉํฅ(full-duplex) ํต์ ํ๋กํ ์ฝ์ด๋ค. ํด๋ผ์ด์ธํธ๊ฐ HTTP ์
๊ทธ๋ ์ด๋ ์์ฒญ์ ๋ณด๋ด๋ฉด, ์๋ฒ๊ฐ 101 Switching Protocols๋ก ์๋ตํ๋ฉด์ TCP ์ฐ๊ฒฐ์ด WebSocket ์ฐ๊ฒฐ๋ก ์ ํ๋๋ค. ์ฐ๊ฒฐ์ด ์๋ฆฝ๋ ์ดํ์๋ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๋ชจ๋ ์ธ์ ๋ ๋ฉ์์ง๋ฅผ ๋ณด๋ผ ์ ์๋ค.
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
โ HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
์ด ํธ๋์ ฐ์ดํฌ๊ฐ ๋๋๋ฉด HTTP ์ฐ๊ฒฐ์ด ์ข ๋ฃ๋๊ณ , ๋์ผํ TCP ์์ผ ์์์ WebSocket ํ๋ ์์ ์ฃผ๊ณ ๋ฐ๋๋ค.
Node.js ์๋ฒ ์์ (ws ๋ผ์ด๋ธ๋ฌ๋ฆฌ)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
console.log('๋ฐ์ ๋ฉ์์ง:', message.toString());
ws.send('์์ฝ: ' + message.toString());
});
ws.on('close', () => {
console.log('ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ ์ข
๋ฃ');
});
});
ํด๋ผ์ด์ธํธ ์์
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => {
ws.send('์๋
ํ์ธ์');
};
ws.onmessage = (event) => {
console.log('์๋ฒ๋ก๋ถํฐ:', event.data);
};
ws.onclose = () => {
console.log('์ฐ๊ฒฐ ๋๊น โ ์ฌ์ฐ๊ฒฐ ๋ก์ง ํ์');
};
WebSocket์ ์๋ ์ฌ์ฐ๊ฒฐ์ ์ ๊ณตํ์ง ์๋๋ค. ์ฐ๊ฒฐ์ด ๋๊ธฐ๋ฉด ์ง์ ์ฌ์ฐ๊ฒฐ ๋ก์ง์ ๊ตฌํํด์ผ ํ๋ค.
Server-Sent Events (SSE)
SSE๋ ์๋ฒ โ ํด๋ผ์ด์ธํธ ๋จ๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ์ด๋ค. ๋ณ๋ ํ๋กํ ์ฝ ์
๊ทธ๋ ์ด๋ ์์ด Content-Type: text/event-stream์ ์ฌ์ฉํ๋ ์ผ๋ฐ HTTP ๋กฑ-๋ฆฌ๋น ์ฐ๊ฒฐ์ด๋ค. ํด๋ผ์ด์ธํธ๋ EventSource API๋ก ๊ตฌ๋
ํ๊ณ , ๋ธ๋ผ์ฐ์ ๊ฐ ์๋์ผ๋ก ์ฌ์ฐ๊ฒฐ์ ์ฒ๋ฆฌํ๋ค.
Express ์๋ฒ ์์
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// ์ด๋ฒคํธ ID๋ฅผ ๋ณด๋ด๋ฉด ์ฌ์ฐ๊ฒฐ ์ ํด๋ผ์ด์ธํธ๊ฐ Last-Event-ID ํค๋๋ก ๋ณด๋ด์ค
let id = 0;
const timer = setInterval(() => {
res.write(`id: ${id++}\n`);
res.write(`event: update\n`);
res.write(`data: ${JSON.stringify({ time: Date.now() })}\n\n`);
}, 1000);
req.on('close', () => {
clearInterval(timer);
});
});
SSE ์ด๋ฒคํธ ํ์์ \n\n์ผ๋ก ๊ฐ ์ด๋ฒคํธ๋ฅผ ๊ตฌ๋ถํ๋ค. ํ๋๋ data:, event:, id:, retry: ๋ค ๊ฐ์ง๋ค.
ํด๋ผ์ด์ธํธ ์์
const eventSource = new EventSource('/events');
eventSource.addEventListener('update', (event) => {
const data = JSON.parse(event.data);
console.log('์๊ฐ:', data.time);
});
eventSource.onerror = () => {
// ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ ์ฌ์ฐ๊ฒฐ์ ์๋ํจ
console.log('์ฐ๊ฒฐ ์ค๋ฅ โ ์๋ ์ฌ์๋ ์ค');
};
// ๋ ์ด์ ํ์ ์์ผ๋ฉด ๋ช
์์ ์ผ๋ก ๋ซ๊ธฐ
// eventSource.close();
ํต์ฌ ๋น๊ต
| ํญ๋ชฉ | WebSocket | SSE |
|---|---|---|
| ํต์ ๋ฐฉํฅ | ์๋ฐฉํฅ | ์๋ฒ โ ํด๋ผ์ด์ธํธ ๋จ๋ฐฉํฅ |
| ํ๋กํ ์ฝ | WS / WSS | HTTP / HTTPS |
| ๋ฐ์ดํฐ ํ์ | ํ ์คํธ + ๋ฐ์ด๋๋ฆฌ | ํ ์คํธ๋ง (UTF-8) |
| ์๋ ์ฌ์ฐ๊ฒฐ | ์์ (์ง์ ๊ตฌํ) | ์์ (๋ธ๋ผ์ฐ์ ๋ด์ฅ) |
| ๋ฐฉํ๋ฒฝ ํต๊ณผ | ํ๋ก์ ์ค์ ๋ฐ๋ผ ๋ค๋ฆ | ํ์ค HTTP๋ผ ๋์ฒด๋ก ๋ฌด๋ |
| ๋ธ๋ผ์ฐ์ ์ง์ | ๋งค์ฐ ๋์ | ๋์ (IE ์ ์ธ) |
| HTTP/2 ๋ฉํฐํ๋ ์ฑ | ๋ถ๊ฐ | ๊ฐ๋ฅ |
| ๋์ ์ฐ๊ฒฐ ์ ํ | ์์ | HTTP/1.1์์ ๋๋ฉ์ธ๋น 6๊ฐ |
HTTP/2๋ฅผ ์ฐ๋ฉด SSE์ ๋์ ์ฐ๊ฒฐ 6๊ฐ ์ ํ์ด ์ฌ๋ผ์ง๋ค. ํ๋์ TCP ์ฐ๊ฒฐ์์ ์ฌ๋ฌ ์คํธ๋ฆผ์ ๋ฉํฐํ๋ ์ฑํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ํ ํ์ฅ ์ ๊ณ ๋ ค์ฌํญ
WebSocket
WebSocket ์ฐ๊ฒฐ์ ํน์ ์๋ฒ ์ธ์คํด์ค์ ๋งบ์ด์ง๋ค. ์ฌ๋ฌ ์๋ฒ๋ก ์ค์ผ์ผ์์ํ ๋ ๊ฐ์ ํด๋ผ์ด์ธํธ๊ฐ ๋งค๋ฒ ๋์ผํ ์๋ฒ๋ก ๋ผ์ฐํ ๋๋๋ก ์คํฐํค ์ธ์ ์ ์ค์ ํ๊ฑฐ๋, Redis Pub/Sub ๊ฐ์ ๊ณต์ ๋ฉ์์ง ๋ธ๋ก์ปค๋ฅผ ๋์ด ์๋ฒ ๊ฐ ๋ฉ์์ง๋ฅผ ๋๊ธฐํํด์ผ ํ๋ค.
ํด๋ผ์ด์ธํธ A โโโ ์๋ฒ 1 โโโ Redis Pub/Sub โโโ ์๋ฒ 2 โโโ ํด๋ผ์ด์ธํธ B
SSE
SSE๋ ๋กฑ-๋ฆฌ๋น HTTP ์ฐ๊ฒฐ์ด๋ผ ๋์ผํ ๋ฌธ์ ๊ฐ ์๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก Redis๋ ๋ฉ์์ง ํ๋ฅผ ํตํด ์ด๋ฒคํธ๋ฅผ ๋ธ๋ก๋์บ์คํธํ๋ ๊ตฌ์กฐ๊ฐ ์ผ๋ฐ์ ์ด๋ค.
์ด๋ค ๊ฑธ ์ ํํ ๊น
WebSocket์ด ์ ํฉํ ๊ฒฝ์ฐ
- ์ฑํ , ํ์ ํธ์ง๊ธฐ์ฒ๋ผ ํด๋ผ์ด์ธํธ๋ ์๋ฒ๋ก ๋ฐ์ดํฐ๋ฅผ ์์ฃผ ๋ณด๋ด์ผ ํ ๋
- ์จ๋ผ์ธ ๊ฒ์, ์ฃผ์ ๊ฑฐ๋ ํ๋ซํผ์ฒ๋ผ ๋งค์ฐ ๋ฎ์ ๋ ์ดํด์๊ฐ ํ์ํ ๋
- ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ(์ด๋ฏธ์ง, ํ์ผ ์กฐ๊ฐ)๋ฅผ ์ค์๊ฐ์ผ๋ก ์ ์กํด์ผ ํ ๋
SSE๊ฐ ์ ํฉํ ๊ฒฝ์ฐ
- ๋ด์ค ํผ๋, ์๋ฆผ, ๋ผ์ด๋ธ ์ค์ฝ์ด์ฒ๋ผ ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก๋ง ๋ฐ์ดํฐ๊ฐ ํ๋ฅผ ๋
- ๊ตฌํ์ ์ต๋ํ ๋จ์ํ๊ฒ ์ ์งํ๊ณ ์ถ์ ๋ (์ถ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ํ์ค HTTP๋ก ๋์)
- ์ฌ์ฐ๊ฒฐ ์ฒ๋ฆฌ๋ฅผ ๋ธ๋ผ์ฐ์ ์๊ฒ ๋งก๊ธฐ๊ณ ์ถ์ ๋
- ๋ก๊ทธ ์คํธ๋ฆฌ๋ฐ, ๋น๋ ์งํ ์ํ ๋ฑ ๋จ์ ํ ์คํธ ์คํธ๋ฆฌ๋ฐ์ ์ ํฉ
WebSocket์ ์๋ฐฉํฅ์ด ํ์ํ ๋ ์ฐ๊ณ , ์๋ฒ ํธ์๋ง ํ์ํ๋ค๋ฉด SSE๊ฐ ๊ตฌํ์ด ๋ ๋จ์ํ๊ณ ํ์ค HTTP ์ธํ๋ผ์ ์นํ์ ์ด๋ค.