WebSocket untuk Notifikasi Real-Time

16 min readIntermediate
WebSocketRealtimeSpringBackend

Di artikel Anatomi Protokol HTTP, kita belajar bahwa HTTP adalah protokol request-response: setiap percakapan selalu dimulai dari klien. Itu bagus untuk mengambil data, tapi jadi masalah saat Anda butuh notifikasi real-time — chat, live harga saham, atau pemberitahuan order baru yang masuk ke layar kasir. Bagaimana caranya server memberi tahu klien tanpa klien bertanya dulu?

Jawaban klasiknya adalah WebSocket: protokol yang membuka koneksi dua arah yang persisten antara klien dan server, sehingga data bisa mengalir ke dua arah kapan saja. Artikel ini membahas kenapa HTTP tidak cukup untuk real-time, perbandingan polling vs SSE vs WebSocket, cara kerja handshake, implementasi di Spring Boot memakai STOMP, jebakan-jebakan yang sering muncul di produksi, dan studi kasus notifikasi order.

Masalahnya: HTTP Tidak Bisa "Menelepon" Klien

Konteks dasar HTTP: klien mengirim request, server membalas response, lalu koneksi bisa ditutup. Server tidak pernah berinisiatif mengirim data. Konsekuensinya, untuk hal-hal yang berubah tanpa ditekan klien — pesan baru di chat, harga yang bergerak tiap detik, atau order masuk — klien tidak akan pernah tahu kecuali ia sendiri yang bertanya.

Coba bayangkan skenario notifikasi order baru di e-commerce. Saat pelanggan berhasil checkout, kasir/moderator harus melihat order itu muncul sendiri di layar. Dengan HTTP murni:

  Klien (Kasir)                        Server
      |                                  |
      | 1. GET /orders (apa ada yang baru?) |
      |--------------------------------->|
      | 2. "Tidak ada." 200 OK []       |
      |<---------------------------------|
      | ...menunggu beberapa detik...     |
      |                                  |
      | 3. GET /orders (apa ada yang baru?) |
      |--------------------------------->|
      | 4. "Tidak ada lagi." 200 OK []   |
      |<---------------------------------|

Server hanya menjawab pertanyaan; ia tidak pernah menelepon klien. Padahal real-time justru butuh arah sebaliknya: server memanggil klien. Di sinilah kita butuh pendekatan baru.

Tiga Pendekatan untuk "Push" ke Klien

Ada beberapa cara untuk meniru real-time di atas HTTP, dan satu cara yang benar-benar berbeda. Masing-masing punya tradeoff.

Polling: Klien Bertanya Berulang-Ulang

Pendekatan paling sederhana: klien memakai setInterval untuk memanggil GET /orders tiap beberapa detik. Jika ada data baru, langsung dirender.

  • Kelebihan: gampang, berjalan di mana saja, tidak butuh infrastruktur baru.
  • Kekurangan: boros. Sebagian besar request menjawab "tidak ada data baru", tapi tetap memakan bandwidth, CPU server, dan beban database. Makin cepat interval-nya, makin "realtime"-nya, tapi makin mahal juga.
  • Tradeoff besar: latensi maksimum = interval polling. Kalau poll tiap 5 detik, notifikasi bisa telat hingga 5 detik — dan di antara itu Anda membayar banyak request kosong.

Long Polling: Server Menahan Jawaban

Variasi polling: klien mengirim request, dan server menahan respons selama masih belum ada data baru (misalnya 30 detik). Begitu data datang (atau timeout), server membalas, dan klien langsung membuat request baru.

  • Kelebihan: latensi rendah (data terkirim begitu tersedia), masih berjalan di atas HTTP biasa.
  • Kekurangan: lebih rumit untuk ditangani (timeout, koneksi hang, race condition saat dua data datang bersamaan), dan tiap siklus tetap membuat request HTTP baru dengan overhead header. Server yang tahan banyak koneksi panjang ini juga harus diperlakukan khusus di level load balancer.
  • Tradeoff: lebih hemat daripada polling murni, tapi bukan komunikasi dua arah yang alami — masih berupa request-response yang dipanjangkan.

SSE (Server-Sent Events): Satu Arah Lewat HTTP

SSE adalah API browser yang memakai satu koneksi HTTP panjang untuk mengalirkan data dari server ke klien saja. Klien tidak bisa mengirim data lewat koneksi yang sama.

  • Kelebihan: sederhana, otomatis reconnect bawaan, jalan di atas HTTP (tidak perlu protokol baru), bisa lewat proxy/CDN.
  • Kekurangan: satu arah — jika klien perlu mengirim pesan, harus lewat request HTTP biasa; koneksi HTTP punya batas jumlah (browser membatasi ~6 koneksi HTTP per host).
  • Tradeoff: sempurna untuk notifikasi satu arah (berita, feed), tapi kurang cocok untuk chat atau game yang butuh dua arah.

WebSocket: Dua Arah Penuh

WebSocket adalah protokol terpisah yang berjalan di atas TCP. Sekali terhubung, klien dan server sama-sama bisa mengirim kapan saja tanpa memulai percakapan baru — inilah full-duplex.

PendekatanArahLatensiOverhead per pesanKompleksitasCocok untuk
PollingKlien → serverTinggi (sesuai interval)Tinggi (HTTP baru tiap kali)RendahFitur yang jarang berubah
Long PollingKlien → server (jawaban ditahan)RendahSedang (header HTTP tiap siklus)SedangFallback untuk browser lama
SSEServer → klienRendahRendah (satu koneksi HTTP)RendahNotifikasi satu arah, feed
WebSocketDua arah, kapan sajaSangat rendahRendah (frame kecil, tanpa header HTTP)TinggiChat, game, kolaborasi, live harga

Aturan praktis: gunakan pendekatan termurah yang cukup. Notifikasi sesekali → SSE. Percakapan dua arah yang sering → WebSocket. Dan jika Anda hanya butuh mengambil data berkala, polling biasa atau bahkan cache HTTP mungkin sudah jauh lebih masuk akal.

Apa itu WebSocket?

WebSocket adalah protokol komunikasi yang menyediakan koneksi persisten dan dua arah (full-duplex) di atas koneksi TCP. Berbeda dengan HTTP yang tiap pesan membawa header berat dan "lupa" sesi sebelumnya, WebSocket mempertahankan satu koneksi yang bisa dipakai bolak-balik tanpa henti.

Data dikirim dalam frame — unit data kecil dengan header minimal — sehingga overhead-nya jauh lebih ringan daripada request HTTP penuh. Protokol ini dijelaskan secara lengkap oleh dokumentasi WebSockets API di MDN, dan ini contoh membuka koneksi dari JavaScript:

const ws = new WebSocket("ws://api.example.com/ws");

ws.onopen = () => {
  console.log("Koneksi terbuka");
  ws.send("Halo dari browser!");          // kirim pesan
};

ws.onmessage = (event) => {
  console.log("Data dari server:", event.data); // terima pesan
};

ws.onclose = () => {
  console.log("Koneksi ditutup, coba reconnect nanti");
};

Handshake: Naik Kelas dari HTTP

Bagian yang membingungkan banyak orang: WebSocket bukan HTTP, tapi koneksi WebSocket dimulai sebagai request HTTP. Klien mengirim request HTTP biasa dengan header Upgrade: websocket. Jika server setuju, ia membalas 101 Switching Protocols, dan kedua pihak "naik kelas" dari HTTP ke WebSocket di atas TCP koneksi yang sama.

  Klien                                       Server
    |  1. GET /ws (Upgrade: websocket,       |
    |     Sec-WebSocket-Key: ...)            |
    |--------------------------------------->|
    |  2. HTTP/1.1 101 Switching Protocols   |
    |     Upgrade: websocket                 |
    |     Sec-WebSocket-Accept: ...          |
    |<---------------------------------------|
    |  ==== koneksi TCP tetap terbuka ====   |
    |  sekarang bicara pakai frame WebSocket  |
    |  3. frame "Halo server"  ------------>|
    |  4. frame "Halo klien"  <------------  |
    |  5. frame ... (bolak-balik, bebas)      |

Detail handshake:

  1. Klien mengirim request GET ke endpoint WebSocket (misal /ws) dengan header Connection: Upgrade dan Upgrade: websocket, plus Sec-WebSocket-Key (random string).
  2. Server memvalidasi, lalu membalas status 101 Switching Protocols dengan header Sec-WebSocket-Accept yang dihitung dari key klien.
  3. Sejak saat itu, protokol berganti: HTTP tidak dipakai lagi, komunikasi berjalan lewat frame WebSocket hingga salah satu pihak menutup koneksi.

Kenapa lewat HTTP? Karena infrastruktur web (port, firewall, proxy) sudah paham HTTP. Memulai handshake sebagai HTTP membuat WebSocket bisa melewati port 443 yang sudah terbuka, dan server bisa memakai logika HTTP yang ada (misalnya autentikasi) sebelum melakukan upgrade.

HTTP vs WebSocket

AspekHTTPWebSocket
Arah komunikasiSatu arah per request-responseDua arah penuh, kapan saja
KoneksiDibuka dan ditutup per request (kecuali keep-alive)Satu koneksi persisten untuk seluruh sesi
OverheadHeader besar di tiap pesanFrame kecil, header minimal
StateStateless (tiap request independen)Stateful (koneksi menyimpan konteks)
InisiatifSelalu dimulai klienKlien atau server bisa memulai kirim
Cocok untukREST API, ambil data, halaman webChat, notifikasi real-time, live harga
AutentikasiHeader Authorization mudah dibawaTidak otomatis — harus ditangani sendiri

Implementasi di Spring Boot dengan STOMP

Di Java/Spring, memakai WebSocket mentah (lewat TextWebSocketHandler) itu mungkin, tapi Anda akan menghabiskan banyak waktu membuat routing pesan manual. Solusi yang umum di produksi adalah STOMP di atas WebSocket.

Apa itu STOMP?

STOMP (Simple Text Oriented Messaging Protocol) adalah protokol pesan sederhana berbasis teks yang berjalan di atas WebSocket. Ia menambahkan hal-hal yang WebSocket mentah tidak punya:

  • subscribe / unsubscribe — klien bisa "berlangganan" ke topik, dan server mengirim pesan ke semua subscriber topik itu.
  • destination (alamat topik) — pesan dirutekan ke tujuan bernama, seperti "/topic/notifications".
  • Semantik publish-subscribe — mirip pola event bus, tapi di level jaringan.

Dengan kata lain, WebSocket memberi Anda "kabel"; STOMP memberi Anda "aturan lalu lintas" di atasnya.

Spring Boot menyediakan spring-boot-starter-websocket yang membawa Spring WebSocket + STOMP sekaligus. Struktur prefix yang umum dipakai:

PrefixDipakai untukArahContoh
/appKlien mengirim pesan ke server (diproses @MessageMapping)Klien → server/app/chat.send
/topicServer mengirim pesan broadcast ke semua subscriberServer → klien/topic/notifications
/userServer mengirim pesan khusus ke satu userServer → klien/user/{id}/queue/orders
/queuePesan privat yang ditujukan per-userServer → klien/user/{id}/queue/orders

Konfigurasi Broker Sederhana

Buat kelas konfigurasi yang mengimplementasikan WebSocketMessageBrokerConfigurer. Inti yang wajib dipahami: enableSimpleBroker untuk mengaktifkan broker in-memory sederhana, dan setApplicationDestinationPrefixes untuk menandai bahwa pesan ber-prefix /app adalah pesan aplikasi yang harus diproses controller.

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").setAllowedOriginPatterns("*");
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic", "/queue");
        registry.setApplicationDestinationPrefixes("/app");
        registry.setUserDestinationPrefix("/user");
    }
}

Yang terjadi:

  1. addEndpoint("/ws") — endpoint handshake WebSocket. Klien menghubungi /ws untuk "naik kelas" ke WebSocket.
  2. enableSimpleBroker("/topic", "/queue") — server punya broker in-memory untuk topik /topic/... (broadcast) dan /queue/... (per-user).
  3. setApplicationDestinationPrefixes("/app") — pesan dari klien dengan alamat /app/... diteruskan ke method @MessageMapping.
  4. setUserDestinationPrefix("/user") — prefix untuk tujuan per-user.

Mengirim Notifikasi ke Semua (Broadcast)

Setelah konfigurasi di atas, membuat notifikasi broadcast semudah memanggil SimpMessagingTemplate. Anda bisa memanggilnya dari mana saja — termasuk dari service setelah transaksi database sukses, bukan hanya dari controller WebSocket.

import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    private final SimpMessagingTemplate messagingTemplate;

    public NotificationService(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    public void broadcastAnnouncement(String message) {
        NotificationPayload payload = new NotificationPayload("announcement", message);
        messagingTemplate.convertAndSend("/topic/notifications", payload);
    }
}

Klien berlangganan topik itu lewat STOMP:

// StompJS — pendekatan umum di browser
const socket = new WebSocket("ws://localhost:8080/ws");
const stompClient = Stomp.over(socket);

stompClient.connect({}, () => {
    stompClient.subscribe("/topic/notifications", (message) => {
        const data = JSON.parse(message.body);
        renderNotification(data);
    });
});

Mengirim Notifikasi ke User Tertentu

Broadcast tidak selalu tepat. Notifikasi "order Anda berhasil dibuat" harus sampai ke satu user tertentu, bukan semua orang. Di sinilah tujuan per-user berperan. Ada dua cara:

Cara 1 — convertAndSendToUser dari service:

public void notifyUser(Long userId, NotificationPayload payload) {
    // "/user/{userId}/queue/orders" -> alamat privat untuk user ini
    messagingTemplate.convertAndSendToUser(
        String.valueOf(userId),
        "/queue/orders",
        payload
    );
}

Cara 2 — @MessageMapping dengan @SendToUser:

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendToUser;
import org.springframework.stereotype.Controller;

@Controller
public class OrderController {

    @MessageMapping("/order.status")
    @SendToUser("/queue/orders")
    public OrderStatus statusRequest(Long orderId) {
        return orderService.findStatus(orderId);
    }
}

Di sisi klien, langganan per-user memakai alamat /user/queue/orders — Spring yang menerjemahkannya menjadi /user/{principalId}/queue/orders berdasarkan identitas yang terautentikasi:

stompClient.subscribe("/user/queue/orders", (message) => {
    showNewOrder(JSON.parse(message.body));
});

Contoh Handler Server Sederhana

Kombinasi lengkap: klien mengirim pesan lewat /app/..., server memproses, lalu mem-broadcast hasilnya ke /topic/.... Pola ini adalah fondasi chat dan papan notifikasi.

@Controller
public class ChatController {

    private final SimpMessagingTemplate messagingTemplate;

    public ChatController(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @MessageMapping("/chat.send")
    public void handleChatMessage(ChatMessage message) {
        // proses validasi / simpan ke database bila perlu
        chatMessageService.save(message);
        // kirim ke semua subscriber topik chat
        messagingTemplate.convertAndSend("/topic/chat", message);
    }
}

Satu method @MessageMapping dengan return value langsung juga bisa memakai @SendTo agar Spring mengirim hasilnya ke topik tertentu tanpa SimpMessagingTemplate:

@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) {
    return new Greeting("Halo, " + message.getName() + "!");
}

Keamanan Singkat: Membatasi Endpoint /ws

Koneksi WebSocket yang terbuka tanpa autentikasi adalah celah serius — siapa pun bisa berlangganan topik yang seharusnya privat. Dengan Spring Security, batasi endpoint handshake agar hanya user yang terautentikasi yang bisa terhubung, misalnya lewat WebSocketMessageBrokerConfigurer dengan SimpUserRegistry dan channel interceptor, atau filter di level HTTP.

Pendekatan paling mudah: pastikan handshake /ws (yang diawali sebagai request HTTP) melewati filter autentikasi, lalu di configureMessageBroker gunakan interceptor untuk memeriksa principal:

import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.Message;
import org.springframework.security.core.Authentication;

@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
    registration.interceptors(new ChannelInterceptor() {
        @Override
        public Message<?> preSend(Message<?> message, MessageChannel channel) {
            StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
            if (StompCommand.CONNECT.equals(accessor.getCommand())) {
                Authentication auth = (Authentication) accessor.getUser();
                if (auth == null) {
                    throw new IllegalStateException("Tidak ada autentikasi pada handshake");
                }
            }
            return message;
        }
    });
}

Jika handshake ditolak, browser menerima status 403 Forbidden dan koneksi gagal terbuka — persis seperti request REST yang diblokir.

Dua lapis pengaman: lapis pertama memastikan hanya user terautentikasi yang bisa membuka koneksi (handshake). Lapis kedua memastikan user hanya bisa subscribe ke topik yang menjadi haknya (otorisasi per topik). Lapis kedua sering dilupakan dan berujung kebocoran data privat antar-user.

Jebakan & Praktik yang Wajib Diketahui

Autentikasi Tidak Otomatis di Browser

Koneksi WebSocket dari browser tidak membawa header Authorization seperti fetch. Headers HTTP hanya dipakai saat handshake, dan browser tidak mengizinkan Anda menetapkan header kustom pada new WebSocket(...). Solusi yang umum dipakai:

  1. Token di query parameternew WebSocket("wss://.../ws?token=" + jwt), lalu di server ekstrak dan validasi saat handshake (bisa lewat HandshakeInterceptor). Sederhana, tapi token bocor ke log server (query string sering tercatat).
  2. Header STOMP — STOMP punya fase CONNECT sendiri yang membawa header, jadi token bisa dikirim di header CONNECT setelah koneksi WebSocket terbuka. Ini lebih aman dan umum dipakai library seperti StompJS.
  3. Cookie session — karena handshake adalah request HTTP, cookie yang sudah ada ikut terkirim; server bisa mengautentikasi dari cookie session.

Satu Koneksi = Satu Mahal

Tiap klien yang terhubung memegang satu koneksi TCP yang tetap terbuka, lengkap dengan memori untuk buffer dan state di server. Pada aplikasi single-instance hal ini masih wajar, tapi begitu klien ribuan dan instance Anda bertambah jadi beberapa, masalah muncul: broker in-memory (enableSimpleBroker) hanya hidup di satu instance.

   Klien A ──► Instance 1 ──► broker in-memory (topik X)
   Klien B ──► Instance 2 ──► broker in-memory (topik X) ✗ tidak tahu pesan
   Pesan baru masuk ke Instance 1 → Klien B di Instance 2 tidak menerima

Klien yang terhubung ke Instance 2 tidak akan menerima pesan yang di-broadcast dari Instance 1. Solusinya: ganti broker in-memory dengan broker eksternal (RabbitMQ, ActiveMQ Artemis, atau Redis pub/sub) yang menjadi "otak" bersama semua instance. Di Spring, itu tinggal mengganti enableSimpleBroker dengan enableStompBrokerRelay.

Reconnect & Heartbeat

Koneksi WebSocket bisa putus — jaringan berubah, proxy timeout, server restart. Klien yang tidak menyiapkan reconnect akan diam saja sampai halaman di-refresh. Praktik yang umum:

  • Reconnect otomatis di klien (StompJS punya reconnectDelay, atau library seperti backoff).
  • Heartbeat ping/pong — kedua sisi mengirim sinyal kecil secara berkala agar koneksi yang mati terdeteksi cepat, bukan menunggu timeout yang lama. Di STOMP Spring, atur heartbeat saat handshake atau di StompHeaderAccessor.
  • Tangani onclose di server — lepaskan resource, dan (jika diperlukan) catat putusnya koneksi untuk log atau membership.

Skala Horizontal: Simple Broker vs Broker Eksternal

AspekSimple Broker (in-memory)Broker Eksternal (RabbitMQ/ActiveMQ)
Jumlah instance1 sajaBanyak (horizontal scaling)
Komunikasi antar-instanceTidak adaAda (melalui broker bersama)
Konfigurasi di SpringenableSimpleBroker(...)enableStompBrokerRelay(...)
Kemampuan pesanTerbatas (ingat semua di memory)Persistent queue, clustering, routing lanjutan
Kapan dipakaiPrototipe, aplikasi kecilProduksi multi-instance, pesan harus dijamin

Kapan JANGAN Pakai WebSocket

WebSocket bukan jawaban universal. Pertimbangkan lagi jika:

  • Notifikasinya sesekali — satu email pengingat per hari cukup dikirim via SSE atau bahkan polling; tidak perlu koneksi permanen.
  • Anda hanya butuh request-response biasa — ambil daftar user, buat order, update profil: tetap pakai HTTP REST. WebSocket menambah kompleksitas besar tanpa manfaat.
  • Klien sangat beragam dan lama — dukungan WebSocket di browser sudah luas, tapi beberapa proxy/network korporat memblokir upgrade; SSE sebagai fallback sering menyelamatkan.
  • Anda butuh reliabilitas transaksi ketat — WebSocket adalah saluran cepat tapi fire-and-forget; untuk pesan yang tidak boleh hilang, pertimbangkan menyimpan ke database dan memakai WebSocket hanya sebagai notifikasi.

Prinsip utama: gunakan teknologi sesederhana mungkin yang memenuhi kebutuhan. WebSocket dipakai bukan karena "keren", tapi karena ada kebutuhan dua arah yang persisten dengan latensi rendah.

Studi Kasus: Notifikasi Order Baru di E-Commerce

Mari satukan semua konsep dalam satu skenario: toko online tempat kasir/moderator harus melihat order baru muncul langsung di layar, plus pelanggan menerima konfirmasi status order.

  1. Pelanggan checkout → request POST /api/orders biasa (tetap HTTP — ini request-response normal) → order tersimpan di database.
  2. Service memanggil SimpMessagingTemplate setelah transaksi sukses:
    • convertAndSendToUser(moderatorId, "/queue/orders", payload) → notifikasi muncul di layar kasir.
    • convertAndSend("/topic/orders.new", payload) → dashboard ops yang menampilkan semua order live ikut ter-update.
  3. Kasir mengubah status order (misal "Sedang Diproses") → server memanggil convertAndSendToUser(customerId, "/queue/orders", status) → pelanggan yang berlangganan /user/queue/orders langsung melihat status terbaru tanpa me-refresh halaman.

Alur lengkapnya kira-kira begini:

   Pelanggan            Server (Spring)          Kasir / Moderator
      │                       │                        │
      │ POST /api/orders      │                        │
      ├──────────────────────►│                        │
      │                       │ simpan ke DB           │
      │                       ├──────────┐             │
      │                       │          │ convertAndSendToUser
      │                       │          ├────────────►│  (muncul di layar)
      │                       │          │             │
      │                       │          │ convertAndSend("/topic/orders.new")
      │                       │          ├────────────►│  (dashboard live)
      │                       │                        │
      │  convertAndSendToUser │                        │
      │◄──────────────────────┤   (status order)       │

Kunci keberhasilannya: data sumber tetap di database (sumber kebenaran), WebSocket hanya saluran pengumuman. Jika koneksi putus, data tetap aman di database dan bisa diambil ulang lewat REST biasa — WebSocket tidak pernah dijadikan satu-satunya tempat penyimpanan.

Ringkasan

HTTP adalah protokol request-response yang tidak bisa mengirim data tanpa diminta — untuk itu kita butuh pendekatan lain. Polling sederhana tapi boros, long polling menahan respons tapi rumit, SSE cocok untuk satu arah di atas HTTP, dan WebSocket memberikan koneksi persisten dua arah dengan latensi rendah. Di Spring Boot, STOMP di atas WebSocket (@MessageMapping, @SendTo, SimpMessagingTemplate, prefix /app//topic//user) membuat routing pesan dan notifikasi per-user jadi mudah. Ingat tiga jebakan besar: autentikasi tidak otomatis di browser, broker in-memory tidak bisa dipakai multi-instance (butuh broker eksternal), dan koneksi harus ditangani reconnect-nya. Dan yang terpenting: pilih WebSocket hanya ketika kebutuhan dua arah yang persisten benar-benar ada — untuk selebihnya, SSE atau REST biasa sering kali sudah lebih dari cukup.

Lanjut membaca

← Back to technical articles