Server-Sent Events (SSE)
서버 → 클라이언트 단방향 실시간 스트림 · EventSource API
연결 안 됨
수신 이벤트: 0개
| 재연결: 0회
시스템 정보
대기
- 서버 시각
- --:--:--
- PHP 버전
- —
- 메모리 사용량
- —
- 누적 틱
- —
큐 현황
대기
—
대기 중
—
실패
실시간 알림
대기
연결 후 알림이 여기에 표시됩니다.
이벤트 로그
연결 시작 버튼을 클릭하면 이벤트 로그가 여기에 표시됩니다.
서버 측 — app/Controllers/Examples/Sse.php
public function stream(): void
{
@set_time_limit(120);
@ini_set('output_buffering', 'off');
// ① SSE 필수 헤더
header('Content-Type: text/event-stream; charset=UTF-8');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // nginx 버퍼링 비활성화
if (ob_get_level()) ob_end_flush();
$lastId = (int)($_SERVER['HTTP_LAST_EVENT_ID'] ?? 0);
$db = \Config\Database::connect();
for ($tick = 0; $tick < 30; $tick++) {
if (connection_aborted()) break;
$id = $lastId + $tick + 1;
// ② 이벤트 전송 형식
// id: {고유 ID}
// event: {이벤트 타입}
// data: {JSON 문자열}
// (빈 줄 2개로 이벤트 종료)
echo "id: {$id}\n";
echo "event: system\n";
echo 'data: ' . json_encode([
'time' => date('H:i:s'),
'memory' => round(memory_get_usage(true) / 1024 / 1024, 1),
]) . "\n\n";
flush(); // ③ 즉시 전송
sleep(2); // ④ 2초 대기 후 다음 이벤트
}
// ⑤ 재연결 유도 (max ticks 후 연결 종료)
echo "event: reconnect\ndata: {}\n\n";
flush();
}
클라이언트 측 — EventSource API
// ① EventSource 생성
const es = new EventSource('/examples/sse/stream');
// ② 기본 message 이벤트
es.onmessage = (e) => {
const data = JSON.parse(e.data);
console.log('message:', data);
};
// ③ 커스텀 이벤트 타입 수신
es.addEventListener('system', (e) => {
const data = JSON.parse(e.data);
document.getElementById('sysTime').textContent = data.time;
});
es.addEventListener('queue', (e) => {
const data = JSON.parse(e.data);
document.getElementById('qPending').textContent = data.pending;
});
es.addEventListener('notify', (e) => {
const data = JSON.parse(e.data);
showNotification(data.message);
});
// ④ 재연결 이벤트 (서버가 연결을 끊으면 자동 재연결)
es.addEventListener('reconnect', () => {
es.close();
setTimeout(() => reconnect(), 1000);
});
// ⑤ 에러 처리 (자동 재연결 내장)
es.onerror = (e) => {
console.warn('SSE 오류 — 자동 재연결 시도 중...');
};
// ⑥ 연결 종료
function stopSSE() {
es.close();
}
SSE 이벤트 형식
# 기본 message 이벤트
data: Hello World
# 이벤트 ID 포함
id: 42
data: {"key": "value"}
# 커스텀 이벤트 타입
id: 43
event: system
data: {"time": "12:34:56"}
# 재연결 간격 설정 (밀리초)
retry: 3000
data: reconnecting...
# 주석 (keepalive용)
: ping
nginx 프록시 설정
location /examples/sse/stream {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
# SSE 필수 설정
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
# 헤더 전달
proxy_set_header Connection '';
proxy_set_header X-Accel-Buffering no;
}
주의: nginx 기본 버퍼링을 끄지 않으면 이벤트가 묶음으로 전달됩니다.
| 항목 | SSE | WebSocket |
|---|---|---|
| 통신 방향 | 서버 → 클라이언트 (단방향) | 양방향 |
| 프로토콜 | HTTP/1.1, HTTP/2 | ws:// / wss:// |
| 구현 난이도 | 낮음 (표준 HTTP) | 높음 (별도 서버 필요) |
| 자동 재연결 | 브라우저 내장 | 직접 구현 필요 |
| Last-Event-ID | 내장 지원 | 없음 |
| 바이너리 전송 | 불가 (텍스트 전용) | 가능 |
| 서버 부하 | 연결당 HTTP 스레드/커넥션 유지 | 경량 WebSocket 서버 |
| 적합한 용도 | 실시간 피드, 알림, 로그 스트리밍 | 채팅, 게임, 양방향 통신 |
| CI4 지원 | 기본 PHP로 구현 가능 | 별도 패키지 필요 (Ratchet 등) |
언제 SSE를 선택할까?
실시간 읽기만 필요하다면 SSE가 훨씬 간단합니다. 대시보드 갱신, 알림 푸시, 로그 스트리밍, Queue 상태 모니터링 등 서버 → 클라이언트 단방향 시나리오에 적합합니다.
실시간 읽기만 필요하다면 SSE가 훨씬 간단합니다. 대시보드 갱신, 알림 푸시, 로그 스트리밍, Queue 상태 모니터링 등 서버 → 클라이언트 단방향 시나리오에 적합합니다.