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 상태 모니터링 등 서버 → 클라이언트 단방향 시나리오에 적합합니다.