Dalam 15 menit ke depan, kita akan merakit sebuah sistem real-time berbeban tinggi dari nol sampai berjalan mulus, langsung dari ruang kendali infrastruktur kita. Lupakan teori kelas pengantar; mari kita bedah bagaimana menangani lonjakan jutaan frame WebSocket dengan Vue.js Composition API dan Pinia tanpa membuat browser klien mengalami memory leak atau UI stutter.
1. Menu Kita Hari Ini & Alat yang Perlu Disiapkan
Pagi ini di meja kerja kita sudah tersusun rapi bahan-bahan kelas enterprise: Node.js dengan native ws untuk backend WebSocket berkecepatan tinggi, Vue 3 Composition API bersama Pinia untuk manajemen state reaktif, serta Nginx sebagai reverse proxy yang siap mengamankan upgrade header WebSocket kita. Bayangkan arsitektur ini seperti jalur pipa air bertekanan tinggi; jika katup dan sambungannya tidak presisi, airnya akan meluap dan merusak komponen di sekitarnya.
Pastikan di mesin lokal Anda sudah terpasang Node.js versi 20 LTS, Vue CLI atau Vite, serta Redis untuk menangani sinkronisasi pub/sub lintas instance backend. Kita tidak akan membuang waktu membahas instalasi dasar, jadi mari kita langsung masuk ke blueprint perakitannya.
2. Peta Jalan Singkat: Apa yang Sedang Kita Rakit
Sebelum kita menyalakan terminal, mari kita petakan alur perjalanan data kita. Kita akan mulai dengan menyiapkan fondasi Reactivity Transform pada WebSocket stream agar setiap paket data yang masuk dari server langsung memicu perubahan state UI secara instan tanpa overhead berlebih. Dari sana, kita akan mengalirkan data tersebut melalui Pinia Store Integration yang bertindak sebagai dispatcher terpusat, memastikan komponen-komponen Vue kita tetap bersih dan decoupled.
[!TIP] Kunci dari arsitektur real-time yang sehat adalah memisahkan lapisan socket transport dari lapisan business logic di state manager. Jangan pernah langsung memanggil instance WebSocket di dalam komponen UI.
Langkah terakhir dari perjalanan kilat kita adalah mengupas tuntas Memory Management di Vue 3, di mana kita akan membersihkan event listener dan memutus koneksi socket saat komponen di-unmount agar RAM browser tidak perlahan-lahan bengkak hingga crash.
3. Langkah 1: Mempersiapkan Fondasi & Environment
Mari kita mulai dengan meracik fondasi server WebSocket yang efisien di Node.js. Buat file server.js di direktori backend Anda dan masukkan konfigurasi dasar untuk menangani koneksi mentah berkecepatan tinggi:
const { WebSocketServer } = require('ws');
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Enterprise WebSocket Gateway Active\n');
});
const wss = new WebSocketServer({ server });
wss.on('connection', (ws) => {
console.log('[INFO] Klien baru terhubung ke edge node.');
ws.on('message', (message) => {
// Broadcast dummy data real-time ke semua klien
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(`Echo: ${message}`);
}
});
});
ws.on('close', () => {
console.log('[INFO] Klien terputus secara bersih.');
});
});
server.listen(8080, () => {
console.log('[SYSTEM] WebSocket Gateway berjalan di port 8080');
});
Jalankan dengan perintah node server.js. Perhatikan respons log di terminal Anda; tulisan tersebut adalah tanda pertama bahwa gerbang komunikasi real-time kita sudah siap menerima hantaman trafik.
4. Langkah 2: Meracik Konfigurasi Inti Tanpa Basa-basi
Sekarang kita berpindah ke sisi frontend Vue 3. Di sinilah keajaiban Reactivity Transform pada WebSocket Stream dan Pinia Store Integration berpadu. Kita buat file src/stores/useRealtimeStore.js untuk mengelola state koneksi dan aliran data masuk:
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useRealtimeStore = defineStore('realtime', () => {
const socket = ref(null);
const messages = ref([]);
const isConnected = ref(false);
function connect(url) {
socket.value = new WebSocket(url);
socket.value.onopen = () => {
isConnected.value = true;
console.log('[VUE] Terhubung ke WebSocket server.');
};
socket.value.onmessage = (event) => {
// Masukkan data langsung ke reaktif array dengan batasan buffer
if (messages.value.length > 100) messages.value.shift();
messages.value.push(event.data);
};
socket.value.onclose = () => {
isConnected.value = false;
console.warn('[VUE] Koneksi terputus. Menjadwalkan reconnect...');
setTimeout(() => connect(url), 3000);
};
}
function sendMessage(payload) {
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
socket.value.send(payload);
}
}
return { socket, messages, isConnected, connect, sendMessage };
});
[!IMPORTANT] Perhatikan pembatasan array
messages.value.length > 100. Pada aplikasi enterprise, mengabaikan ukuran buffer array reaktif adalah penyebab nomor satu browser mengalami memory exhaustion dalam hitungan jam.
5. Langkah 3: Sentuhan Akhir Keamanan & Optimasi
Di dunia nyata, kita tidak pernah menyambungkan WebSocket langsung ke port backend tanpa pengaman. Kita butuh Nginx di depan untuk menangani SSL/TLS termination dan Upgrade Header. Berikut adalah blok konfigurasi Nginx yang wajib Anda pasang:
server {
listen 443 ssl http2;
server_name ws.perusahaan.com;
ssl_certificate /etc/letsencrypt/live/ws.perusahaan.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ws.perusahaan.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Timeout penunjang koneksi persisten
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
Uji konfigurasi dengan nginx -t dan reload service Nginx Anda. Jalur pipa data kini sudah terenkripsi dan terlindung di balik benteng proxy yang kokoh.
6. Tes Rasa: Memastikan Semuanya Berjalan Sempurna
Mari kita integrasikan store Pinia tadi ke dalam komponen Vue (src/components/LiveFeed.vue) untuk melihat hasil racikan kita:
<script setup>
import { onMounted, onUnmounted } from 'vue';
import { useRealtimeStore } from '@/stores/useRealtimeStore';
const store = useRealtimeStore();
onMounted(() => {
store.connect('wss://ws.perusahaan.com');
});
onUnmounted(() => {
if (store.socket) {
store.socket.close();
}
});
</script>
<template>
<div class="p-6 font-sans">
<div :class="store.isConnected ? 'bg-green-500' : 'bg-red-500'" class="w-4 h-4 rounded-full inline-block mr-2"></div>
<span>Status: {{ store.isConnected ? 'Online' : 'Offline' }}</span>
<ul class="mt-4 border p-4 rounded bg-gray-50 h-64 overflow-y-auto">
<li v-for="(msg, index) in store.messages" :key="index" class="text-sm py-1">
{{ msg }}
</li>
</ul>
</div>
</template>
[!TIP] Implementasi
onUnmounteddi atas adalah wujud nyata dari Memory Management di Vue 3. Hook ini memastikan socket ditutup secara eksplisit saat komponen dihancurkan, mencegah orphan connection yang menguras resource server.
7. Pertolongan Pertama: Jika Terjadi Kendala Kecil
Jika Anda mendapati koneksi WebSocket langsung terputus dengan kode 1006 sesaat setelah dibuka, biasanya Nginx memutus koneksi karena proxy_read_timeout terlalu pendek atau ada aturan WAF/Cloudflare yang memblokir header Upgrade. Pastikan Anda menonaktifkan fitur caching Cloudflare untuk subdomain WebSocket (ws.*) karena proxy edge mereka secara default tidak mendukung koneksi persisten HTTP/1.1 Upgrade pada paket gratis.