Membangun Antarmuka (Frontend) Web Aman dengan Next.js & React (Part 3)

Selamat datang kembali di lembar Cyber Journal! Pada seri pamungkas ini, kita akan melengkapi arsitektur pertahanan yang telah kita bangun pada catatan Part 2 sebelumnya. Sampai di titik ini, Anda seharusnya sudah memiliki mesin REST API (Backend) yang tangguh dan kebal terhadap berbagai teknik injeksi data.

Namun, sistem yang aman tidak akan berguna jika tidak bisa dioperasikan oleh pengguna yang sah (seperti guru atau admin LMS). Kita tidak mungkin meminta mereka mengirim data menggunakan terminal atau Thunder Client. Mereka membutuhkan antarmuka visual (Frontend) yang intuitif, namun tetap menghormati protokol keamanan yang ada di belakangnya.

Pada jurnal Part 3 ini, kita akan merajut tampilan Frontend interaktif menggunakan React di dalam ekosistem Next.js. Kita akan membuat form HTML, lalu menghubungkannya secara dinamis ke endpoint /api/kelas yang telah kita amankan.


Berkenalan dengan "use client" di Next.js

Secara default, Next.js App Router merender semua halaman di sisi server (Server Components) demi mereduksi celah keamanan dan meningkatkan performa. Namun, karena kita membutuhkan interaksi langsung dari pengguna (seperti mengisi form dan mengeklik tombol), kita harus memberi tahu Next.js bahwa komponen ini berjalan di sisi peramban (Client-Side).

Caranya sangat elegan, cukup deklarasikan instruksi 'use client'; pada baris paling atas di kode kita.

Langkah Praktis: Membangun Antarmuka & Menampilkan Data

1. Membuat Halaman Manajemen Kelas

Buka VS Code Anda. Di dalam folder app, buatlah direktori baru bernama kelas. Kemudian, di dalam folder kelas tersebut, buat file baru bernama page.tsx. (Jalurnya menjadi: app/kelas/page.tsx).

2. Menulis Kode React (Frontend)

Copy dan paste kode lengkap di bawah ini ke dalam file page.tsx. Kode ini sudah dilengkapi dengan sistem fetching data dari database dan penanganan form pengisian yang sangat mulus.

'use client';

import { useState, useEffect } from 'react';

export default function ManajemenKelas() {
  // State untuk menyimpan daftar kelas dari database
  const [daftarKelas, setDaftarKelas] = useState<any[]>([]);
  
  // State untuk menangani input form
  const [name, setName] = useState('');
  const [sortOrder, setSortOrder] = useState(0);
  const [pesan, setPesan] = useState('');

  // Menjalankan fungsi ambil data saat halaman pertama kali dirender
  useEffect(() => {
    fetchDataKelas();
  }, []);

  // FUNGSI GET: Mengambil data dari API backend kita
  const fetchDataKelas = async () => {
    try {
      const res = await fetch('/api/kelas');
      const result = await res.json();
      if (result.status === 'success') {
        setDaftarKelas(result.data);
      }
    } catch (error) {
      console.error("Gagal mengambil log data kelas", error);
    }
  };

  // FUNGSI POST: Mengirim data payload ke API secara aman
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault(); // Mencegah browser refresh saat tombol diklik
    setPesan('Menyimpan data ke server...');

    try {
      const res = await fetch('/api/kelas', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ 
          name: name, 
          sort_order: Number(sortOrder) 
        }),
      });

      const result = await res.json();

      if (res.ok) {
        setPesan('✅ ' + result.message);
        // Kosongkan form kembali
        setName('');
        setSortOrder(0);
        // Refresh daftar kelas secara otomatis (Real-time update)
        fetchDataKelas(); 
      } else {
        setPesan('❌ Operasi Gagal: ' + result.message);
      }
    } catch (error) {
      setPesan('❌ Terjadi anomali jaringan.');
    }
  };

  return (
    <div style={{ padding: '30px', maxWidth: '600px', margin: '0 auto', fontFamily: 'sans-serif' }}>
      <h1>Terminal Manajemen Kelas</h1>

      {/* FORM INPUT KELAS BARU */}
      <div style={{ background: '#f9f9f9', padding: '20px', borderRadius: '8px', marginBottom: '30px', border: '1px solid #e2e8f0' }}>
        <h3 style={{ marginTop: 0 }}>Tambah Entri Kelas</h3>
        <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
          <input 
            type="text" 
            placeholder="Nama Kelas (Contoh: Kelas 10A)" 
            value={name}
            onChange={(e) => setName(e.target.value)}
            style={{ padding: '10px', borderRadius: '4px', border: '1px solid #cbd5e0' }}
            required
          />
          <input 
            type="number" 
            placeholder="Urutan (Sort Order)" 
            value={sortOrder}
            onChange={(e) => setSortOrder(Number(e.target.value))}
            style={{ padding: '10px', borderRadius: '4px', border: '1px solid #cbd5e0' }}
          />
          <button type="submit" style={{ padding: '12px', background: '#2b6cb0', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', fontWeight: 'bold' }}>
            Kirim Data
          </button>
        </form>
        {/* Tempat Menampilkan Status Respon */}
        {pesan && <p style={{ marginTop: '15px', fontWeight: 'bold', color: '#2d3748' }}>{pesan}</p>}
      </div>

      {/* DAFTAR KELAS DARI DATABASE */}
      <h3>Log Kelas Tersimpan</h3>
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {daftarKelas.map((kelas) => (
          <li key={kelas.id} style={{ padding: '12px', borderBottom: '1px solid #edf2f7', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <span><strong>{kelas.name}</strong></span>
            <span style={{ color: '#718096', fontSize: '0.9em' }}>Urutan: {kelas.sort_order}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

Mengapa Arsitektur Ini Direkomendasikan?

Di balik form yang tampak sederhana ini, terdapat mekanisme modern yang mengedepankan keamanan serta User Experience (UX):

  • Transmisi Asynchronous: Perhatikan fungsi e.preventDefault(). Berbeda dengan web PHP tradisional yang melakukan reload pada seluruh halaman setiap kali tombol ditekan, ekosistem React kita mengirim payload data secara senyap di latar belakang (melalui Fetch API).
  • Sinkronisasi Real-Time: Begitu respon dari server menyatakan sukses (res.ok), kita langsung memanggil kembali fungsi fetchDataKelas(). Daftar kelas di layar akan diperbarui secara instan.
  • Validasi Lapis Ganda: Meskipun HTML Frontend kita memiliki atribut required, lapisan keamanan absolut tetap dijaga oleh API Backend kita. Apapun manipulasi yang dilakukan peretas di sisi peramban (misalnya menghapus atribut required via Inspect Element), server tidak akan pernah bisa ditipu.

Hasil Akhir (Uji Coba Lingkungan Lokal)

Sekarang, nyalakan kembali server lokal Next.js Anda melalui terminal VS Code dengan perintah npm run dev. Buka peramban (browser) dan navigasikan ke alamat: http://localhost:3000/kelas

Cobalah simulasikan input data kelas baru di dalam form, lalu klik "Kirim Data". Jika semua konfigurasi benar, Anda akan melihat notifikasi sukses dan daftar log di bagian bawah akan langsung tersinkronisasi secara otomatis!

Penutup Jurnal Full-Stack

Target misi selesai! Anda baru saja menyelesaikan perakitan arsitektur Full-Stack yang utuh dan tangguh. Mulai dari merancang fondasi database (Prisma ORM), membangun pintu keamanan backend (REST API), hingga membuat antarmuka frontend yang responsif (Next.js/React).

Pendekatan ini adalah standar industri yang digunakan oleh berbagai platform berskala Enterprise. Semoga jurnal berseri ini dapat menjadi cetak biru (blueprint) yang solid untuk membangun LMS atau sistem informasi impian Anda. Teruslah bereksperimen, dan tetap waspada terhadap celah keamanan!

Berlangganan update artikel terbaru via email:

0 Response to "Membangun Antarmuka (Frontend) Web Aman dengan Next.js & React (Part 3)"

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel