Kamu punya bug di JavaScript. Kamu tambah console.log di sini, console.log di sana. Reload. Masih salah. Tambah lagi. Reload lagi. Lima belas menit kemudian kamu masih belum tahu variabel mana yang nilainya aneh.
Ini bukan cara debug yang efisien — ini cara buang waktu.
Browser modern punya debugger yang jauh lebih powerful dari console.log. Artikel ini bahas cara debug aplikasi JavaScript di browser secara proper: pakai breakpoint, inspect variable live, trace call stack, sampai monitor network request yang mencurigakan.
Semua contoh di sini pakai Chrome DevTools, tapi Firefox Developer Tools punya fitur yang hampir identik.
Buka DevTools dan Kenali Layoutnya
Tekan F12 atau Ctrl+Shift+I (Mac: Cmd+Option+I). Yang paling sering kamu butuhkan untuk debug JavaScript:
- Console — output log, jalankan expression secara ad-hoc
- Sources — ini tempat debugging sesungguhnya; kamu set breakpoint di sini
- Network — monitor semua HTTP request/response
- Performance — profiling kalau app kamu lemot
Tab Sources adalah yang paling underused. Kebanyakan dev cuma buka Console, padahal Sources-lah yang bikin debugging jadi 10x lebih cepat.
Breakpoint: Ganti console.log Sekarang Juga
Breakpoint artinya kamu bilang ke browser: "Hei, kalau eksekusi sampai baris ini, berhenti dulu. Jangan lanjut."
Cara set breakpoint:
- Buka tab Sources
- Di panel kiri, cari file JavaScript kamu (biasanya di folder
src/atau langsung di root) - Klik nomor baris yang kamu mau inspect — akan muncul titik biru
- Reload halaman atau trigger action yang memanggil kode itu
Browser akan pause tepat di baris itu. Kamu bisa hover variabel untuk lihat nilainya langsung, atau lihat panel Scope di sebelah kanan untuk semua variabel yang accessible di titik itu.
Contoh skenario: kamu punya fungsi kalkulasi harga yang hasilnya salah.
function hitungTotal(harga, qty, diskon) {
const subtotal = harga * qty;
const potongan = subtotal * (diskon / 100);
const total = subtotal - potongan;
return total;
}
const hasil = hitungTotal(50000, 3, 10);
console.log(hasil); // harusnya 135000, tapi kok 0?
Daripada tambah console.log di setiap baris, set breakpoint di baris const subtotal = harga * qty;. Pas browser pause, kamu bisa lihat nilai harga, qty, diskon di panel Scope. Kalau salah satu undefined, kamu langsung tahu masalahnya ada di pemanggilan fungsi, bukan di logika hitungnya.
Step Over, Step Into, Step Out — Ini yang Bikin Beda
Pas browser pause di breakpoint, ada beberapa tombol navigasi di toolbar debugger:
- Resume (
F8) — lanjut eksekusi sampai breakpoint berikutnya - Step Over (
F10) — eksekusi baris ini, lompat ke baris berikutnya tanpa masuk ke dalam fungsi yang dipanggil - Step Into (
F11) — kalau baris ini memanggil fungsi lain, masuk ke dalam fungsi itu - Step Out (
Shift+F11) — selesaikan fungsi sekarang, kembali ke caller
Contoh penggunaan Step Into:
function validasiEmail(email) {
return email.includes('@') && email.includes('.');
}
function daftarUser(nama, email) {
if (!validasiEmail(email)) { // <-- set breakpoint di sini
throw new Error('Email tidak valid');
}
// lanjut proses...
}
daftarUser('Budi', 'budigmail.com');
Kalau kamu pause di baris if (!validasiEmail(email)) terus tekan Step Into, browser akan masuk ke dalam fungsi validasiEmail dan kamu bisa lihat step by step kenapa hasilnya false.
Conditional Breakpoint untuk Kasus Spesifik
Ini fitur yang jarang dipakai tapi sangat berguna. Bayangkan kamu punya loop yang iterasi 500 kali, tapi bug-nya cuma muncul di iterasi ke-347. Kamu nggak mau pause 347 kali.
Solusinya: conditional breakpoint.
Cara set:
- Klik kanan nomor baris (bukan klik biasa)
- Pilih Add conditional breakpoint
- Ketik kondisinya, misalnya
i === 347atauuser.id === 'abc123'
Browser hanya akan pause kalau kondisi itu true.
const orders = fetchOrders(); // array 500 item
orders.forEach((order, i) => {
const total = kalkulasiOrder(order); // set conditional breakpoint di sini: i === 347
simpanKeDB(order.id, total);
});
Gotcha yang gue temuin: kalau kondisinya ada typo atau variabelnya belum ada di scope, breakpoint-nya akan diabaikan tanpa error. Kalau conditional breakpoint kamu nggak pernah trigger, cek dulu ekspresinya di Console apakah valid.
Watch Expression dan Live Edit
Di panel Watch (sebelah kanan di tab Sources), kamu bisa tambah expression yang terus dipantau nilainya setiap browser pause. Misalnya kamu mau monitor orders.length atau currentUser?.role sepanjang debugging session.
Klik tombol + di panel Watch, ketik expression-nya. Setiap kamu step ke baris berikutnya, nilai expression itu akan update otomatis.
Satu lagi fitur keren: live edit. Di tab Sources, kamu bisa langsung edit kode JavaScript yang lagi jalan di browser. Klik di dalam kode, edit, tekan Ctrl+S. Perubahan langsung aktif tanpa reload. Ini berguna banget untuk coba-coba fix tanpa harus balik ke editor, save, reload.
Catatan: live edit ini cuma sementara. Reload halaman dan perubahan hilang. Pastikan kamu copy fix-nya ke file asli.
Debug dengan Console yang Lebih dari Sekedar .log
Selama kamu di Console, ada beberapa method yang lebih informatif dari console.log biasa:
// Tampilkan object sebagai tabel — bagus untuk array of objects
const users = [
{ id: 1, nama: 'Andi', role: 'admin' },
{ id: 2, nama: 'Budi', role: 'user' },
];
console.table(users);
// Grouping log supaya nggak berantakan
console.group('Proses Checkout');
console.log('Validasi item...');
console.log('Hitung ongkir...');
console.groupEnd();
// Ukur berapa lama sebuah operasi
console.time('fetchData');
const data = await fetchFromAPI();
console.timeEnd('fetchData'); // output: fetchData: 342ms
// Stack trace — tahu fungsi mana yang manggil fungsi ini
console.trace('Dipanggil dari mana?');
console.table ini underrated banget. Kalau kamu debug data array, jauh lebih enak dibanding nge-log object mentah yang harus di-expand satu-satu.
Network Tab untuk Debug API dan Fetch
Kalau bug-nya bukan di logika JavaScript tapi di data yang datang dari server, kamu butuh Network tab.
Buka Network tab, lakukan action yang trigger fetch/axios call, lihat request yang muncul. Klik salah satu request untuk lihat:
- Headers — method, URL, request headers, response headers
- Payload — request body yang dikirim
- Response — data yang balik dari server
- Timing — berapa lama tiap fase (DNS, connect, TTFB, download)
Skenario umum: kamu kirim POST request tapi server selalu return error 400.
async function buatOrder(orderData) {
const response = await fetch('/api/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(orderData),
});
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}
Di Network tab, klik request POST ke /api/orders, buka tab Payload — lihat persis data apa yang dikirim. Sering kali masalahnya sesederhana field yang namanya typo atau value yang undefined karena object-nya belum ter-populate dengan benar.
Gotcha: kalau kamu pakai fetch dan server return status 4xx atau 5xx, fetch tidak throw error otomatis — kamu harus cek response.ok atau response.status manual. Beda dengan axios yang langsung throw. Ini sumber bug yang sering bikin bingung.
Source Map: Debug Kode yang Sudah Di-built
Kalau kamu pakai bundler (Vite, Webpack, Parcel), kode yang jalan di browser sudah di-minify dan di-bundle. Nama variabelnya jadi a, b, c. Susah dibaca.
Solusinya: source map. Source map adalah file yang memetakan kode hasil build ke kode sumber aslinya.
Pastikan development build kamu generate source map. Di Vite:
// vite.config.js
export default {
build: {
sourcemap: true, // aktifkan untuk production build juga kalau perlu debug
},
};
Kalau source map aktif, DevTools akan otomatis menampilkan kode sumber asli (TypeScript, JSX, dsb.) di tab Sources, bukan kode yang sudah di-bundle. Breakpoint pun bisa di-set di file sumber asli.
Jangan aktifkan source map di production kalau kamu nggak mau user bisa baca source code asli kamu. Atau pakai hidden source map yang cuma bisa diakses internal.
Apa yang Gue Lakuin Sekarang
Sejak gue stop pakai console.log sebagai satu-satunya alat debug, waktu yang gue habiskan untuk trace bug turun signifikan. Workflow yang sekarang gue pakai:
- Reproduksi bug dulu — pastikan gue bisa trigger bug-nya secara konsisten sebelum mulai debug
- Set breakpoint di titik masuk yang paling logis, bukan langsung di mana gue curiga ada bug
- Step Into ke fungsi-fungsi yang dipanggil, sambil monitor panel Scope
- Cek Network tab kalau ada data dari server yang terlibat
- Conditional breakpoint kalau bug-nya cuma muncul di kondisi tertentu
Satu hal yang gue rekomendasiin: coba debug satu bug kecil yang kamu punya hari ini pakai breakpoint, bukan console.log. Bukan karena console.log jelek — tapi karena breakpoint kasih kamu konteks yang jauh lebih lengkap di satu titik waktu, tanpa harus tambah dan hapus kode terus-menerus.
Kalau kamu mau lanjut, pelajari juga this guide on systemd service unit file tutorial untuk memahami bagaimana menjalankan aplikasi JavaScript di background dengan proper process management — itu topik tersendiri yang cukup dalam.