Tutorial SSE (Server-Sent Events): Solusi Real-time Efisien

Aditya Y PradhanaAditya Y Pradhana/
Tutorial SSE ServerSent Events Solusi Realtime Efisien
Tutorial SSE ServerSent Events Solusi Realtime Efisien

Ringkasan

  • Apa itu SSE: Mengenal Server-Sent Events sebagai alternatif WebSocket yang lebih ringan untuk komunikasi satu arah (Server ke Client).
  • Kelebihan Utama: Mengapa SSE lebih unggul untuk sistem antrian, notifikasi, dan live-feed berkat fitur auto-reconnect dan protokol HTTP standar.
  • Implementasi Next.js: Panduan langkah demi langkah membangun sistem panggilan antrian real-time menggunakan App Router dan EventEmitter.
  • Strategi Skalabilitas: Cara meningkatkan performa sistem dari local emitter ke Redis Pub/Sub untuk lingkungan produksi multi-instance.

Saat membangun aplikasi real-time, banyak developer yang secara refleks langsung memilih WebSocket. Memang, WebSocket sangat powerful, tetapi seringkali menjadi overkill untuk kebutuhan yang sederhana. Bayangkan Anda sedang membangun sistem Display TV Panggilan Antrian; apakah TV tersebut perlu mengirim data balik ke server secara terus-menerus? Tentu tidak. TV hanya perlu "mendengarkan" kapan server memanggil nomor antrian berikutnya.

Di sinilah Server-Sent Events (SSE) hadir sebagai pahlawan yang tidak terduga. SSE adalah standar komunikasi yang memungkinkan server mendorong (push) data ke browser secara real-time melalui koneksi HTTP yang tetap terbuka. Bagi Anda yang mencari solusi yang lebih simpel, efisien, dan ramah infrastruktur, SSE adalah jawabannya.

Apa Itu Server-Sent Events (SSE)?

Server-Sent Events adalah teknologi yang memungkinkan server mengirimkan data ke web page secara otomatis tanpa perlu client meminta data tersebut berulang kali (polling). Berbeda dengan HTTP request biasa yang bersifat request-response, SSE menciptakan aliran data (stream) yang tetap terbuka.

SSE bekerja di atas protokol HTTP standar. Browser menggunakan API EventSource untuk membuka koneksi ke server, dan server merespons dengan header khusus text/event-stream. Selama koneksi ini terbuka, server dapat mengirimkan pesan kapan saja dalam format teks tertentu.

Perbandingan Mendalam: SSE vs WebSocket

Untuk membantu Anda menentukan pilihan, mari kita bedah perbedaan keduanya melalui tabel berikut:

Fitur SSE (Server-Sent Events) WebSocket
Arah Komunikasi Satu arah (Server → Client) Dua arah (Bi-directional / Full-duplex)
Protokol Standar HTTP / HTTP/2 TCP kustom (Diawali HTTP Upgrade)
Fitur Reconnect Otomatis bawaan browser Manual (Harus koding retry mechanism)
Dukungan Proxy/Firewall Sangat ramah (Tanpa konfigurasi khusus) Perlu konfigurasi khusus di Edge/Proxy
Format Data Teks (UTF-8) Teks dan Biner (Binary)
Penggunaan Terbaik Live status board, notifikasi, feed update, AI streaming Chat app, game multiplayer, collaborative tools

Mengapa SSE Lebih Cocok untuk Sistem Antrian Online?

Dalam skenario Display TV Panggilan Antrian, kebutuhan utamanya adalah menampilkan nomor yang dipanggil secara instan saat petugas menekan tombol panggil di konsol operator. Berikut adalah alasan mengapa SSE jauh lebih unggul untuk kasus ini:

1. Komunikasi Satu Arah yang Efisien

Display TV hanya bertindak sebagai "pendengar" (read-only broadcast). TV tidak perlu mengirim data apa pun kembali ke server melalui koneksi stream. Menggunakan WebSocket untuk kebutuhan ini ibarat menggunakan jalan tol dua arah untuk kendaraan yang hanya berjalan satu arah; tidak efisien dan memboroskan sumber daya server.

2. Arsitektur yang Lebih Sederhana

Dengan SSE, Anda tidak perlu mengubah seluruh paradigma komunikasi aplikasi Anda. Proses perubahan data (mutation), seperti memanggil nomor antrian, tetap menggunakan route HTTP standar (misalnya POST /api/queue/call). Server kemudian cukup menyiarkan (broadcast) peristiwa tersebut via SSE kepada semua client yang terhubung.

3. Auto-Reconnect Bawaan

Koneksi jaringan di area publik (seperti ruang tunggu) seringkali tidak stabil. Jika koneksi TV terputus sejenak, objek EventSource bawaan browser akan secara otomatis mencoba menyambung ulang tanpa perlu Anda menulis logika retry yang kompleks di sisi frontend.

4. Ramah Infrastruktur dan Firewall

Karena berjalan di atas HTTP biasa, SSE tidak akan terblokir oleh firewall perusahaan atau load balancer yang ketat. WebSocket seringkali membutuhkan konfigurasi khusus pada proxy (seperti Nginx atau AWS ALB) untuk menangani protocol upgrade, sementara SSE bekerja "out-of-the-box".

Panduan Implementasi: Next.js App Router (Route Handler)

Mari kita praktikkan. Kita akan membangun sistem sederhana di mana operator memicu panggilan, dan Display TV menerima update tersebut secara real-time.

Langkah 1: Setup Event Emitter (lib/queue-events.ts)

Karena kita berada di lingkungan Node.js, kita membutuhkan cara untuk mengirim sinyal dari satu route (Operator) ke route lain (SSE Stream). Kita akan menggunakan EventEmitter.

import { EventEmitter } from 'events';

// Menghindari duplikasi instance saat development hot-reload di Next.js
const globalForEvents = global as unknown as { queueEmitter: EventEmitter };

export const queueEmitter =
  globalForEvents.queueEmitter || new EventEmitter();

if (process.env.NODE_ENV !== 'production') {
  globalForEvents.queueEmitter = queueEmitter;
}

Langkah 2: Buat Endpoint SSE Stream (app/api/queue/events/route.ts)

Endpoint ini akan menjaga koneksi tetap terbuka dan mengirimkan data setiap kali ada event 'call'.

import { queueEmitter } from '@/lib/queue-events';

export const dynamic = 'force-dynamic';

export async function GET() {
  const encoder = new TextEncoder();

  const stream = new ReadableStream({
    start(controller) {
      // Handler saat ada pemanggilan antrian baru
      const onQueueChange = (data: any) => {
        // Format SSE harus diawali dengan 'event:' dan diakhiri dengan dua newline '\n\n'
        const payload = `event: queue\ndata: ${JSON.stringify(data)}\n\n`;
        controller.enqueue(encoder.encode(payload));
      };

      // Dengarkan event 'call' dari event emitter
      queueEmitter.on('call', onQueueChange);

      // Tangani saat client/browser terputus untuk mencegah memory leak
      return () => {
        queueEmitter.off('call', onQueueChange);
      };
    },
  });

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache, no-transform',
      'Connection': 'keep-alive',
    },
  });
}

Langkah 3: Trigger Event dari Operator (app/api/queue/call/route.ts)

Saat petugas memanggil nomor, kita mengirim request POST yang kemudian akan memicu queueEmitter.

import { NextResponse } from 'next/server';
import { queueEmitter } from '@/lib/queue-events';

export async function POST(request: Request) {
  const body = await request.json();
  const { counterId, displayNumber } = body;

  // 1. Simpan/Update status di Database Anda di sini...
  // await db.queue.update({ ... })

  // 2. Broadcast perubahan data ke semua subscriber SSE
  queueEmitter.emit('call', {
    type: 'call',
    counterId,
    displayNumber,
    timestamp: Date.now(),
  });

  return NextResponse.json({ success: true });
}

Langkah 4: Konsumsi SSE di Display TV (React Client Component)

Di sisi frontend, kita menggunakan EventSource untuk mendengarkan stream data.

'use client';

import { useEffect, useState } from 'react';

export default function DisplayTV() {
  const [currentCall, setCurrentCall] = useState<any>(null);

  useEffect(() => {
    // Buka koneksi SSE ke endpoint server
    const eventSource = new EventSource('/api/queue/events');

    // Mendengarkan event spesifik dengan nama 'queue'
    eventSource.addEventListener('queue', (event) => {
      const data = JSON.parse(event.data);
      if (data.type === 'call') {
        setCurrentCall(data);
        // Tips: Tambahkan fungsi Text-to-Speech (Web Speech API) di sini
        // window.speechSynthesis.speak(new SpeechSynthesisUtterance(`Nomor antrian ${data.displayNumber}, silahkan menuju loket ${data.counterId}`));
      }
    });

    eventSource.onerror = (err) => {
      console.error('SSE Error, reconnecting...', err);
    };

    return () => {
      eventSource.close();
    };
  }, []);

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-slate-900 text-white">
      <h1 className="text-4xl font-bold mb-8">PANGGILAN ANTRIAN</h1>
      {currentCall ? (
        <div className="text-center bg-slate-800 p-12 rounded-2xl shadow-2xl border border-slate-700">
          <p className="text-2xl text-slate-400 mb-2">NOMOR ANTRIAN</p>
          <p className="text-9xl font-black text-amber-400 tracking-wider">
            {currentCall.displayNumber}
          </p>
          <p className="text-3xl mt-6 text-slate-300">
            MENUJU LOKET: <span className="font-bold text-white">{currentCall.counterId}</span>
          </p>
        </div>
      ) : (
        <p className="text-slate-500 text-xl">Menunggu Panggilan Berikutnya...</p>
      )}
    </div>
  );
}

Catatan Skalabilitas: Mengatasi Keterbatasan Multi-Instance

Jika aplikasi Anda berkembang dan mulai menggunakan multi-instance server (misalnya menggunakan Kubernetes, PM2 Cluster, atau Vercel Serverless), Anda akan menghadapi masalah serius dengan EventEmitter.

Masalahnya: EventEmitter beroperasi secara in-process. Artinya, jika petugas memanggil antrian di Instance A, namun Display TV terhubung ke Instance B, maka TV tersebut tidak akan pernah menerima notifikasi karena event hanya berputar di dalam memori Instance A.

Solusinya: Redis Pub/Sub. Anda harus mengganti EventEmitter lokal dengan sistem pub/sub eksternal. Alurnya menjadi seperti ini:

  1. Operator mengirim request ke /api/queue/call → Server mempublikasikan pesan ke channel Redis 'queue_channel'.
  2. Setiap instance server yang menjalankan SSE stream berlangganan (subscribe) ke channel Redis 'queue_channel'.
  3. Saat Redis menerima pesan, semua instance server akan meneruskannya ke client SSE masing-masing.

Keuntungan menggunakan Redis adalah Anda tidak perlu mengubah kode di sisi frontend (Display TV), karena format data yang dikirim tetap sama.

Kesimpulan

SSE adalah solusi yang elegan untuk kebutuhan real-time satu arah. Dengan implementasi yang tepat, Anda bisa mengurangi beban server, menyederhanakan kode, dan meningkatkan stabilitas aplikasi. Untuk sistem antrian, notifikasi dashboard, atau streaming respons AI, SSE adalah pilihan yang jauh lebih rasional dibandingkan WebSocket.

Solusi yang relevan

Service

Jasa Pembuatan Website

Pembuatan website custom yang cepat, modern, dan siap jualan.

Lihat Solusi →

Dapatkan Artikel Terbaru!

Berlangganan newsletter kami untuk mendapatkan tips dan insight menarik langsung ke inbox Anda.

Kami tidak akan pernah membagikan email Anda (No Spam).