1. ๋ค์ด๊ฐ๋ฉฐ
๋ฐฑ์๋ ๊ฐ๋ฐ์์๋ ํด๋ผ์ด์ธํธ โ ์๋ฒ ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ์์ด ๋ค์ํฉ๋๋ค.
์ด๋ฒ ๊ธ์์๋ ๊ฐ์ฅ ๋ํ์ ์ธ ๋ค ๊ฐ์ง ํจํด:
- Short Polling (์งง์ ํด๋ง)
- Long Polling (๋กฑ ํด๋ง)
- SSE (Server-Sent Events)
- Pub/Sub (๊ฒ์-๊ตฌ๋
)
์ ๊ตฌ์กฐ๋ถํฐ ์ฅ๋จ์ , ์ค์ ์์ ๊น์ง ์ ๋ฆฌํฉ๋๋ค.
2. Short Polling (์งง์ ํด๋ง)
๊ฐ๋
- "ํด๋ง"์ด๋ ์ผ์ ๊ฐ๊ฒฉ์ผ๋ก ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด ์์
์๋ฃ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ๋ฐฉ์
- ์งง์ ํด๋ง์ ์ฃผ๊ธฐ๊ฐ ๋งค์ฐ ์งง์, ํด๋ผ์ด์ธํธ๊ฐ ๋น ๋ฅด๊ฒ ์ํ๋ฅผ ์ฌํ์ธํจ
- ์ฃผ๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ ์์
์ํ ํ์ธ์ ์ฌ์ฉ
๋์ ํ๋ฆ
- ํด๋ผ์ด์ธํธ๊ฐ ์์
์์ฒญ โ ์๋ฒ๋ ์ฆ์ Job ID ๋ฐํ
- ํด๋ผ์ด์ธํธ๋ Job ID๋ก ์ํ ํ์ธ ์์ฒญ ๋ฐ๋ณต
- ์์
์๋ฃ ์ ์๋ฒ๊ฐ ๊ฒฐ๊ณผ ๋ฐํ
[Client] POST /submit โ { jobId }
[Client] GET /status?jobId=123 โ { progress: 50% }
[Client] GET /status?jobId=123 โ { progress: 100%, result: ... }
์ค์ ์์
- YouTube ์์ ์
๋ก๋
์
๋ก๋ ์์ฒญ ํ, ๋ธ๋ผ์ฐ์ ๊ฐ ์ฃผ๊ธฐ์ ์ผ๋ก ์
๋ก๋ ์งํ๋ฅ ํ์ธ
- ์ด๋ฏธ์ง ์ฒ๋ฆฌ, ๋๊ท๋ชจ ๋ฐ์ดํฐ ๋ณํ ๋ฑ ์ฅ๊ธฐ ์์
๋ชจ๋ํฐ๋ง
์ฅ์
- ๊ตฌํ์ด ๋งค์ฐ ๊ฐ๋จ (๋ฐฑ์๋ยทํ๋ก ํธ์๋ ๋ชจ๋)
- ์ฅ๊ธฐ ์์
์ํ๋ฅผ ์์ ํ๊ฒ ์กฐํ ๊ฐ๋ฅ
- ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐ์ ๋์๋ค๊ฐ ์ฌ์ ์ํด๋ ์ด์ด์ ์ํ ํ์ธ ๊ฐ๋ฅ
๋จ์
- ๋คํธ์ํฌ ๋ญ๋น: ๋๋ถ๋ถ ์์ฒญ์ด "์์ง ์ค๋น ์ ๋จ" ์๋ต
- ํด๋ผ์ด์ธํธ ์๊ฐ ๋ง์ผ๋ฉด ์๋ฒ ๋ถํ ๊ธ์ฆ
- ์๋ต์ด ์ฆ์ ํ์ ์๋ ๊ฒฝ์ฐ์๋ ์์ฒญ ์ ์ก โ ๋น์ฉ ์ฆ๊ฐ
Node.js ์์ (Short Polling)
const express = require('express');
const app = express();
app.use(express.json());
let jobs = {};
app.post('/submit', (req, res) => {
const jobId = Date.now().toString();
jobs[jobId] = 0;
updateJob(jobId);
res.json({ jobId });
});
app.get('/status', (req, res) => {
const jobId = req.query.jobId;
res.json({ progress: jobs[jobId] });
});
function updateJob(jobId) {
if (jobs[jobId] < 100) {
setTimeout(() => {
jobs[jobId] += 10;
updateJob(jobId);
}, 5000);
}
}
app.listen(3000);
3. Long Polling (๋กฑ ํด๋ง)
๊ฐ๋
- ํด๋ง์ ๋ณํ
- ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ๋ณด๋ด๊ณ ๊ฒฐ๊ณผ๊ฐ ์ค๋น๋ ๋๊น์ง ์๋ฒ๊ฐ ์๋ต์ ์ง์ฐ
- Kafka, ์ฑํ
์๋ฒ ๋ฑ์์ ์ฌ์ฉ
๋์ ํ๋ฆ
- ํด๋ผ์ด์ธํธ โ "์์
์ํ ์๋ ค์ค" ์์ฒญ
- ์๋ฒ โ ์์
์๋ฃ ์ ๊น์ง ์๋ต ๋ณด๋ฅ
- ์๋ฃ ์ ์ฆ์ ์๋ต ๋ฐํ
[Client] GET /status?jobId=123 โ (๋๊ธฐ ์ค)
[Server] ์์
์๋ฃ ์ ์๋ต ์ ์ก โ { progress: 100%, result: ... }
์ฅ์
- ๋คํธ์ํฌ ๋ญ๋น ๊ฐ์ (์ก๋ด ์ค์ด๋ฆ)
- ์๋ฒ ์นํ์ โ ๋ถํ์ํ "์์ง ์ ๋จ" ํธ๋ํฝ ์์
๋จ์
- ์ค์๊ฐ์ฑ์ด 100% ๋ณด์ฅ๋์ง ์์ (์๋ต ์งํ ์ ๋ฐ์ดํฐ ๋ฐ์ ๊ฐ๋ฅ)
- ์ฐ๊ฒฐ ์ ์ง ์๊ฐ ๊ธธ์ด์ง โ ์๋ฒ ์์ผ ์ ์
Kafka ์์
- ์๋น์(Consumer)๊ฐ ๋กฑ ํด๋ง์ผ๋ก ํ ํฝ ๊ตฌ๋
- ํ ํฝ์ ๋ฉ์์ง๊ฐ ์์ผ๋ฉด ์๋ต ๋๊ธฐ โ ์ ๋ฉ์์ง ๋์ฐฉ ์ ์๋ต
4. SSE (Server-Sent Events)
๊ฐ๋
- HTTP ๊ธฐ๋ฐ ๋จ๋ฐฉํฅ ์คํธ๋ฆฌ๋ฐ
- ํด๋ผ์ด์ธํธ๊ฐ 1ํ ์์ฒญ โ ์๋ฒ๊ฐ ์ฐ๊ฒฐ ์ ์งํ๋ฉฐ ์ด๋ฒคํธ ์คํธ๋ฆผ ์ ์ก
- Content-Type:
text/event-stream
๋์ ํ๋ฆ
[Client] GET /stream
[Server] data: "msg1"\n\n
[Server] data: "msg2"\n\n
- ๊ฐ ์ด๋ฒคํธ๋
data:๋ก ์์, ๋ ์ค ๋ฐ๊ฟ์ผ๋ก ๊ตฌ๋ถ
- ๋ธ๋ผ์ฐ์
EventSource ๊ฐ์ฒด๋ก ์๋ ํ์ฑ ๊ฐ๋ฅ
์ฅ์
- ์ค์๊ฐ์ฑ โ (ํธ์ ๋ชจ๋ธ๊ณผ ์ ์ฌ)
- HTTP ๊ธฐ๋ฐ์ด๋ผ ๋ฐฉํ๋ฒฝยทํ๋ก์ ํต๊ณผ ์ฉ์ด
- WebSocket๋ณด๋ค ๊ตฌํ ๋จ์
๋จ์
- ๋ธ๋ผ์ฐ์ ๋์ ์ฐ๊ฒฐ ์ ํ(HTTP/1.1 โ ๋๋ฉ์ธ๋น 6๊ฐ)
- ํด๋ผ์ด์ธํธ๊ฐ ์คํ๋ผ์ธ์ด๋ฉด ์ด๋ฒคํธ ์์ ๋ถ๊ฐ
- ํด๋ผ์ด์ธํธ ์ฒ๋ฆฌ ๋ฅ๋ ฅ ์ด๊ณผ ์ ๋ฉ์์ง ์์ค ๊ฐ๋ฅ
Node.js SSE ์์
app.get('/stream', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
let counter = 0;
setInterval(() => {
res.write(`data: ${counter++}\n\n`);
}, 1000);
});
5. Pub/Sub (๊ฒ์-๊ตฌ๋
)
๊ฐ๋
- ๋ฐํ์(Publisher)๊ฐ ๋ฉ์์ง๋ฅผ ํ ํฝ์ ๊ฒ์
- ๊ตฌ๋
์(Subscriber)๋ ๊ด์ฌ ์๋ ํ ํฝ๋ง ์๋น
- ๋ฉ์์ง ๋ธ๋ก์ปค(Kafka, RabbitMQ, Redis Pub/Sub) ์ฌ์ฉ
๋์ ํ๋ฆ
[Publisher] โ [Broker: topic A] โ [Subscriber1]
โ [Subscriber2]
์ฅ์
- ๋์จํ ๊ฒฐํฉ (์๋น์ค ๊ฐ ์ง์ ํธ์ถ ๋ถํ์)
- ์ฌ๋ฌ ์๋น์ ๋์ ์ฒ๋ฆฌ ๊ฐ๋ฅ
- ๋น๋๊ธฐยทํ์ฅ์ฑ ์ฐ์
๋จ์
- ๋ฉ์์ง ์ ๋ฌ ๋ณด์ฅ ๋ฌธ์ (์ค๋ณตยท์ ์ค)
- ๋ธ๋ก์ปค ์ด์ยทํ์ฅ ๋ณต์ก
- ์๋น ์๋ ์ ์ด ํ์ (ํธ์ vs ํด๋ง)
RabbitMQ ์์
channel.assertQueue('jobs');
channel.sendToQueue('jobs', Buffer.from(JSON.stringify({ num: 107 })));
channel.assertQueue('jobs');
channel.consume('jobs', (msg) => {
console.log('Received:', msg.content.toString());
});
6. ๋น๊ต ํ
| ํจํด | ์ค์๊ฐ์ฑ | ๋คํธ์ํฌ ํจ์จ | ๊ตฌํ ๋์ด๋ | ๋ํ ์ฌ๋ก |
|---|
| Short Polling | ์ค๊ฐ | ๋ฎ์ | ๋งค์ฐ ์ฌ์ | ์งํ๋ฅ ์กฐํ |
| Long Polling | ์ค๊ฐโ | ๋์ | ๋ณดํต | Kafka ์๋น์ |
| SSE | ๋์ | ๋์ | ์ฌ์ | ์ค์๊ฐ ์๋ฆผ |
| Pub/Sub | ๋์โ | ๋์โ | ๋์ | ๋ง์ดํฌ๋ก์๋น์ค ์ด๋ฒคํธ |
7. ๋ง์น๋ฉฐ
- ์งง์ ํด๋ง: ๊ฐ๋จํ์ง๋ง ๋คํธ์ํฌ ๋ญ๋น ์ฌํจ
- ๋กฑ ํด๋ง: ํจ์จ์ ์ด์ง๋ง ์์ ์ค์๊ฐ์ ์๋
- SSE: HTTP ๊ธฐ๋ฐ ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ, ๋ธ๋ผ์ฐ์ ์นํ์
- Pub/Sub: ๋๊ท๋ชจ ํ์ฅยท๋ง์ดํฌ๋ก์๋น์ค์ ํ์
๐ ์ํฉยท์๊ตฌ์ฌํญ์ ๋ง์ถฐ ํผํฉ ์ฌ์ฉ์ด ์ผ๋ฐ์
์: ์
๋ก๋ ์ฒ๋ฆฌ โ Pub/Sub๋ก ๋ง์ดํฌ๋ก์๋น์ค ์ฐ๊ฒฐ,
ํด๋ผ์ด์ธํธ ์
๋ฐ์ดํธ๋ SSE๋ก ์ค์๊ฐ ์ ๋ฌ