Server-Sent Events
서버에서 클라이언트로 단방향 이벤트 스트리밍
서버에서 클라이언트로 단방향 이벤트 스트리밍
Server-Sent Events(SSE)는 서버에서 클라이언트로 단방향 실시간 데이터를 스트리밍하는 웹 표준 기술입니다. HTTP 연결을 유지하면서 서버가 이벤트를 푸시할 수 있어, WebSocket보다 간단하게 실시간 기능을 구현할 수 있습니다.
SSE는 text/event-stream MIME 타입을 사용하며, 'data:', 'event:', 'id:', 'retry:' 필드로 구성된 텍스트 프로토콜입니다. 브라우저의 EventSource API로 손쉽게 연결하고, 자동 재연결 기능이 내장되어 있습니다.
SSE의 대표적인 사용 사례로는 실시간 알림, 주식 시세, 소셜 미디어 피드, AI 챗봇 응답 스트리밍 등이 있습니다. ChatGPT와 같은 LLM 서비스들이 토큰 단위로 응답을 스트리밍할 때 SSE를 활용합니다.
WebSocket과 비교하면 SSE는 단방향 통신만 지원하지만, HTTP/2에서 멀티플렉싱이 가능하고 기존 HTTP 인프라(프록시, 로드밸런서)와 호환됩니다. 양방향 통신이 필요 없다면 SSE가 더 효율적입니다.
// Node.js/Express SSE 서버
import express from 'express';
const app = express();
app.get('/api/events', (req, res) => {
// SSE 헤더 설정
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
// 연결 ID 전송
const clientId = Date.now();
res.write(`id: ${clientId}\n`);
res.write(`event: connected\n`);
res.write(`data: {"clientId": "${clientId}"}\n\n`);
// 주기적으로 데이터 전송
const interval = setInterval(() => {
const data = JSON.stringify({
time: new Date().toISOString(),
price: Math.random() * 100
});
res.write(`data: ${data}\n\n`);
}, 1000);
// 연결 종료 처리
req.on('close', () => {
clearInterval(interval);
res.end();
});
});
// Next.js App Router SSE
// app/api/chat/route.ts
export async function GET(request: Request) {
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
// AI 응답 시뮬레이션
const tokens = ['안녕', '하세요', '!', ' ', '무엇을', ' ', '도와', '드릴까요', '?'];
for (const token of tokens) {
const data = `data: ${JSON.stringify({ token })}\n\n`;
controller.enqueue(encoder.encode(data));
await new Promise(r => setTimeout(r, 100));
}
controller.enqueue(encoder.encode('data: [DONE]\n\n'));
controller.close();
}
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
},
});
}
// 클라이언트 EventSource 사용
function ChatStream() {
const [messages, setMessages] = useState('');
useEffect(() => {
const eventSource = new EventSource('/api/chat');
eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
eventSource.close();
return;
}
const { token } = JSON.parse(event.data);
setMessages(prev => prev + token);
};
eventSource.onerror = () => eventSource.close();
return () => eventSource.close();
}, []);
return <div>{messages}</div>;
}
기술 미팅에서:
"AI 응답 타이핑 효과는 SSE로 구현하면 돼요. 토큰 단위로 스트리밍하면 ChatGPT처럼 실시간으로 글자가 나타나요. WebSocket보다 구현도 쉽고요."
기술 면접에서:
"SSE와 WebSocket의 차이점은요?" - "SSE는 서버→클라이언트 단방향, WebSocket은 양방향입니다. SSE는 HTTP 프로토콜을 사용해서 기존 인프라 호환성이 좋고, 자동 재연결도 지원됩니다."
코드 리뷰에서:
"SSE 연결이 끊어졌을 때 재연결 로직 추가하세요. EventSource는 자동 재연결하지만, 컴포넌트 언마운트 시 close() 호출 안 하면 메모리 누수 생겨요."