tutorial

  1. Studi Kasus: Layanan Chat Real-Time

97. Studi Kasus: Layanan Chat Real-Time

Sebagai seorang engineer, tantangan membangun layanan chat real-time selalu menarik dibahas. Menghadirkan komunikasi dua arah dengan latensi rendah, skalabilitas, serta keamanan memadai, merupakan kombinasi yang kompleks namun sangat fundamental di era digital saat ini. Pada artikel ini, saya akan membedah studi kasus membangun layanan chat real-time dengan websocket menggunakan Node.js dan React, lengkap dengan contoh kode, simulasi, serta ilustrasi alur data. Semoga bermanfaat!


1. Latar Belakang: Mengapa Real-Time Chat?

Dewasa ini, real-time chat service kian merajalela: dari aplikasi messaging, customer support, hingga fitur komentar live pada event streaming. Solusi real-time menjadi kunci produktivitas dan keterlibatan pengguna. Pada kasus ini, kita ingin membangun layanan chat sederhana, group chat dengan fitur sebagai berikut:

  • Setiap user dapat join ke satu atau lebih room
  • Pesan dikirim dan diterima secara real-time
  • Chat history disimpan
  • Handle user join/leave notification

2. Arsitektur Singkat

Arsitektur yang akan kita gunakan sebagai berikut:

  • Client: React (websocket client)
  • Server: Node.js with Express & ws (WebSocket library)
  • Database: MongoDB (penyimpanan chat history & user-room relation)

Diagram arsitektur:

flowchart LR
    User1 --Socket--> Server
    User2 --Socket--> Server
    Server --REST(GET/POST)--> MongoDB
    Server --Socket Broadcast--> User1
    Server --Socket Broadcast--> User2

Server jadi “relay” dua-arah: melayani koneksi socket dari klien, meneruskan pesan ke pengguna lain di room yang sama, dan menyimpan chat ke database.

3. Desain Skema Data

Berikut contoh skema MongoDB koleksi chat:

FieldTipeKeterangan
_idObjectIdPrimary key
room_idStringID ruang
senderStringUser yang mengirim pesan
messageStringIsi pesan
created_atISODateTimestamp waktu dikirim

Dan relasi user ke room (dengan koleksi user_rooms):

FieldTipeKeterangan
_idObjectIdPrimary key
user_idStringID user
room_idStringID ruang

4. Implementasi Backend dengan Node.js

Kita mulai dengan server WebSocket.

// index.js
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const { MongoClient } = require('mongodb');

const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

// Koneksi MongoDB
const mongoClient = new MongoClient('mongodb://localhost:27017');
let chatCollection;
mongoClient.connect().then(client => {
    const db = client.db('realtime_chat_demo');
    chatCollection = db.collection('chats');
});

// Map user ke ws
const rooms = {};

wss.on('connection', (ws) => {
    ws.on('message', async (data) => {
        const msg = JSON.parse(data);
        if (msg.type === 'JOIN') {
            ws.room = msg.room_id;
            // Register socket ke room
            rooms[ws.room] = rooms[ws.room] || [];
            rooms[ws.room].push(ws);
            ws.send(JSON.stringify({ type: 'NOTIF', text: `Welcome to room ${ws.room}` }));
        }
        else if (msg.type === 'CHAT') {
            // Simpan ke MongoDB
            await chatCollection.insertOne({
                room_id: ws.room,
                sender: msg.sender,
                message: msg.message,
                created_at: new Date()
            });
            // Broadcast ke seluruh ws di room tsb
            (rooms[ws.room] || []).forEach(client => {
                if (client.readyState === WebSocket.OPEN) {
                    client.send(JSON.stringify({
                        type: 'CHAT',
                        sender: msg.sender,
                        message: msg.message,
                        room_id: ws.room
                    }));
                }
            });
        }
    });
    ws.on('close', () => {
        if (ws.room && rooms[ws.room]) {
            rooms[ws.room] = rooms[ws.room].filter(client => client !== ws);
        }
    });
});

server.listen(9000, () => {
    console.log('Listening on port 9000');
});

Penjelasan Cepat

  • Saat client mengirim tipe JOIN, server menambahkan socket ke room tertentu.
  • Jika pesan CHAT, server menyimpan ke database & meneruskan pesan ke seluruh ws di room tersebut.
  • Broadcast dilakukan lokal pada memori server (untuk yang skala kecil hingga menengah).

5. Client Side: React + WebSocket

Komponen React untuk connect dan chat secara real-time.

// ChatRoom.jsx
import React, { useState, useEffect, useRef } from 'react';

const WS_URL = 'ws://localhost:9000';

export default function ChatRoom({ user, room_id }) {
    const [messages, setMessages] = useState([]);
    const [input, setInput] = useState('');
    const ws = useRef(null);

    useEffect(() => {
        ws.current = new WebSocket(WS_URL);
        ws.current.onopen = () => {
            ws.current.send(JSON.stringify({ type: 'JOIN', room_id }));
        };
        ws.current.onmessage = (e) => {
            const msg = JSON.parse(e.data);
            if (msg.type === 'CHAT' || msg.type === 'NOTIF') {
                setMessages((prev) => [...prev, msg]);
            }
        };
        return () => ws.current.close();
    }, [room_id]);

    const sendMessage = () => {
        ws.current.send(JSON.stringify({
            type: 'CHAT',
            sender: user,
            message: input
        }));
        setInput('');
    };

    return (
        <div>
            <ul>
                {messages.map((msg, i) =>
                    <li key={i}><b>{msg.sender || 'System'}</b>: {msg.message || msg.text}</li>
                )}
            </ul>
            <input value={input} onChange={e => setInput(e.target.value)} />
            <button onClick={sendMessage}>Send</button>
        </div>
    );
}

Simulasi Penggunaan

  1. Jalankan server.
  2. Buka dua tab browser, join ke room_id yang sama, misal “room1”.
  3. Tulis dan kirim pesan, pesan akan muncul di kedua tab secara real-time.

6. Penanganan Skala: Broadcast dan Distribusi

Sistem awal di atas cukup untuk puluhan hingga ratusan user aktif. Namun, pada skala ribuan koneksi, memori dan proses broadcast socket harus didistribusi.

Prinsipnya:

  • Jika server websocket lebih dari satu, relay pesan memakai message broker (misal Redis Pub/Sub).
  • Pengiriman pesan antar node server dilakukan secara sinkron, agar user room yang berbeda server tetap real-time.

Diagram alur dengan Redis sebagai pub/sub:

sequenceDiagram
    participant UserA as Client A
    participant Node1 as WebSocket Node 1
    participant Node2 as WebSocket Node 2
    participant Redis as Redis Pub/Sub

    UserA->>Node1: Send message (room1)
    Node1->>Redis: Publish (room1, message)
    Redis->>Node2: Subscribe (room1, message)
    Node2->>UserB: Forward message

Dengan arsitektur seperti ini, setiap server hanya perlu broadcast ke client yang terkoneksi padanya, dan data lintas node tetap sinkron.


7. Tabel Pro & Kontra Solusi WebSocket Sederhana

AspekKelebihanKekurangan
Real-timeLatency sangat rendahPotensi bottleneck di satu node
SederhanaImplementasi mudah, cepat developmentPerlu redesign untuk high scale
SkalabilitasMudah scaling horizontal dengan pub/subTambahan infra message broker
Store Chat HistoryBebas integrasi DB NoSQLPerlu pembatasan growth DB
KeamananMudah ditambah authentication/authorization via ws handshakePerlu secure channel (wss, token, dsb)

8. Kesimpulan

Membangun layanan chat real-time membutuhkan pemahaman komponen networking, database, hingga skalabilitas sistem. Studi kasus di atas memberikan gambaran dasar implementasi chat berbasis websocket, dari backend hingga client. Tentu saja, pada industri atau skala produksi, dibutuhkan concern lebih pada aspek otentikasi, rate-limit, message retention, logging, monitoring, dan lain-lain. Tetapi, fondasi yang terbangun dari studi kasus ini cukup kokoh untuk dikembangkan menjadi produk chat yang scalable!

Bagaimana dengan arsitektur real-time chat yang pernah kamu bangun? Jangan ragu diskusikan di kolom komentar!


Happy coding, and keep sharing knowledge!

comments powered by Disqus