Belajar Fullstack Developer - Privacy & Compliance
Episode 20 of 28

Belajar Fullstack Developer - Privacy & Compliance

Membangun aplikasi yang menghormati privasi pengguna: memahami prinsip GDPR, menetapkan cookie consent yang benar, enkripsi data pribadi, serta memenuhi hak pengguna atas data (akses, hapus, ekspor).

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Data pribadi pengguna bukan sekadar baris di database — ia milik pengguna, dipinjamkan ke aplikasi kalian. Episode ini membahas privacy & compliance: bagaimana membangun fitur yang menghormati kepemilikan itu, dengan GDPR sebagai standar dunia yang paling dikenal dan paling banyak menetapkan aturan.

Ini bukan topik "hukum" yang abstrak — hampir semuanya adalah keputusan teknik yang konkret: cookie apa yang dipasang sebelum consent, bagaimana mengenkripsi data, dan bagaimana menjalankan hak hapus data. Mari bedah satu per satu.

Prinsip GDPR yang Terasa di Kode

GDPR menetapkan prinsip yang punya padanan langsung di implementasi:

Prinsip GDPRArtiPadanan Teknis
Lawful basisSetiap pemrosesan punya dasarConsent dialog yang jelas
Data minimizationKumpulkan hanya yang perluSkema tanpa kolom berlebih
Storage limitationSimpan tidak lebih lama dari perluRetensi & hapus terjadwal
Privacy by designPrivasi dari awal, bukan tambahanEnkripsi sejak skema

Kunci yang sering keliru dipahami: GDPR mengatur "legal grounds" — consent hanyalah salah satunya. Yang harus kalian lakukan: tandai dengan jelas di sistem kalian, data mana yang diproses dengan dasar apa.

Banner consent yang "kamu klik untuk membuangnya" adalah pelanggaran paling umum. Pola yang benar:

  1. Sebelum consent: pasang hanya cookie esensial (session auth) — tidak ada analytics, tidak ada tracking.
  2. Saat consent: pisahkan tujuan — analytics vs marketing — dengan mekanisme opt-in terpisah.
  3. Setelah consent: catat siapa, kapan, dan pilihan apa (audit trail).
src/components/cookie-consent.tsx
"use client"
 
export function CookieConsent() {
  const [decision, setDecision] = useState(null)
 
  if (decision) {
    document.cookie = `consent=${JSON.stringify(decision)}; max-age=${60*60*24*365}`
  }
 
  if (!decision) {
    return (
      <div className="fixed bottom-4 inset-x-4 bg-white border p-4 rounded-lg shadow">
        <p className="text-sm">Kami memakai cookie untuk mengingat sesi login
          (esensial) dan, jika kalian setuju, untuk analitik.</p>
        <div className="mt-3 flex gap-2">
          <button onClick={() => setDecision({ analytics: false })}>
            Hanya esensial
          </button>
          <button onClick={() => setDecision({ analytics: true })}>
            Setujui analitik
          </button>
        </div>
      </div>
    )
  }
}

Aturan emasnya: jangan muat Google Analytics (atau script apa pun yang tracking) sebelum consent "Setujui". Kalau kalian memuatnya lebih dulu, kalian sudah melanggar.

Warning

Ingat, cookie consent ini berlaku untuk tracking cookie. Cookie esensial (session, keamanan) sah tanpa consent — bedanya: ia diperlukan untuk fungsi inti. Saat ragu, tanya diri: "apakah fungsi inti aplikasi rusak tanpa cookie ini?" Jika ya, ia esensial.

Enkripsi Data Pribadi

Dua jenis enkripsi yang wajib dibedakan:

JenisContohTujuan
In transitHTTPS/TLSMelindungi saat berpindah jaringan
At restEnkripsi diskMelindungi file database itu sendiri

Untuk kolom sangat sensitif (token, data identitas), tambahkan field-level encryption — data terenkripsi di database sehingga membacanya mentah pun tidak berguna:

src/lib/encrypt.ts
import crypto from "node:crypto"
 
const KEY = Buffer.from(process.env.FIELD_KEY, "base64")
 
export function encryptField(value: string) {
  const iv = crypto.randomBytes(12)
  const cipher = crypto.createCipheriv("aes-256-gcm", KEY, iv)
  const encrypted = Buffer.concat([cipher.update(value), cipher.final()])
  return `${iv.toString("base64")}:${encrypted.toString("base64")}`
}

Prisma sendiri sudah menangani hashing password dengan aman (episode 6). Enkripsi field ini khusus untuk data yang perlu dibaca ulang oleh aplikasi tapi sebaiknya tidak terbaca mata telanjang di DB.

Hak Pengguna atas Data: Akses, Hapus, Ekspor

GDPR memberi pengguna hak yang harus dijalankan oleh sistem, bukan hanya dibalas email manual:

HakArtiFitur Teknis
Access"Data apa yang kalian simpan tentang saya?"Endpoint/portal menampilkan data pribadi
Erasure"Hapus data saya"Endpoint hapus lengkap + semua turunan
Portability"Berikan data saya dalam format standar"Ekspor JSON/CSV
Rectification"Perbaiki data yang salah"Profil editable

Contoh endpoint hapus akun yang benar — menghapus data, bukan menandai akun:

src/app/api/account/route.ts
export async function DELETE() {
  const session = await auth()
  await prisma.$transaction([
    prisma.note.deleteMany({ where: { authorId: session.user.id } }),
    prisma.session.deleteMany({ where: { userId: session.user.id } }),
    prisma.user.delete({ where: { id: session.user.id } }),
  ])
  return Response.json({ ok: true })
}

Dan ekspor data dalam format standar — sesuatu yang harus ada untuk compliance:

src/app/api/export/route.ts
export async function GET() {
  const session = await auth()
  const [user, notes] = await Promise.all([
    prisma.user.findUnique({ where: { id: session.user.id } }),
    prisma.note.findMany({ where: { authorId: session.user.id } }),
  ])
  return Response.json({ user, notes }, {
    headers: { "Content-Disposition": 'attachment; filename="data.json"' },
  })
}

Tip

Hak "hapus data" terdengar sederhana tapi menggali masalah arsitektur: siapa pemilik data? Catatan di database relational saling merujuk (user → notes → comments). Hapus akun berarti hapus semua turunan — gunakan $transaction (seperti contoh) agar hapusnya atomik: semua atau tidak sama sekali. Data yang tersisa setelah "akun dihapus" adalah pelanggaran yang paling sering diaudit.

Retention: Simpan Sepanjang Perlu

"Data minimization" punya padanan operasional: retensi. Simpan data pribadi selama masih berguna, lalu hapus. Contoh job terjadwal (episode 13) untuk menghapus data yang sudah basi:

Cron hapus sesi basi
import { logger } from "@/lib/logger"
 
export async function cleanupExpiredSessions() {
  const cutoff = new Date(Date.now() - 1000 * 60 * 60 * 24 * 30) // 30 hari
  const { count } = await prisma.session.deleteMany({
    where: { expiresAt: { lt: cutoff } },
  })
  logger.info({ count }, "sesi kedaluwarsa dibersihkan")
}

Praktik: Aplikasi yang Komplian

Selesaikan dengan urutan berikut:

  1. Tandai di kode: cookie mana yang esensial vs tracking; jangan muat tracking sebelum consent.
  2. Pasang CookieConsent dan pastikan analytics tidak dimuat sebelum "Setujui".
  3. Enkripsi kolom sensitif dengan field-level encryption (misal nomor telepon).
  4. Tambahkan endpoint hapus akun (transaksi lengkap) dan ekspor data JSON.
  5. Tambahkan cron pembersih data basi; log semua tindakan data pribadi.
Uji ekspor data
curl -s http://localhost:3000/api/export -H "Cookie: session=..."

Penutup

Inti yang harus dibawa pulang:

  • Privasi bukan fitur tambahan — ia keputusan desain (privacy by design).
  • Cookie: esensial tanpa consent, tracking hanya setelah consent.
  • Enkripsi in transit (HTTPS) dan at rest; field-level untuk data sangat sensitif.
  • Hak pengguna adalah fitur: akses, hapus (transaksional), ekspor, perbaiki.
  • Terapkan retensi: simpan sepanjang perlu, hapus sisanya, log semuanya.

Di episode 21 selanjutnya kita memasuki era AI-fullstack — membangun LLM & RAG apps dengan streaming respons, embedding, dan vector database. Sampai jumpa di episode 21!

Belajar Fullstack Developer - Privacy & Compliance | Belajar Fullstack