Belajar Fullstack Developer - Caching & Performance
Episode 15 of 28

Belajar Fullstack Developer - Caching & Performance

Mengoptimalkan aplikasi fullstack dari ujung ke ujung: lapisan caching (CDN, ISR, Redis), strategi cache untuk query database, dan praktik menjaga Core Web Vitals — LCP, INP, dan CLS — tetap hijau.

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

Pendahuluan

"Kenapa aplikasi saya lambat?" adalah pertanyaan yang hampir selalu bisa dijawab dengan satu kata: duplikasi kerja. Server menghitung ulang, database meng-query ulang, dan browser men-download ulang hal yang sebenarnya sama dari waktu ke waktu. Episode ini membahas caching & performance — menghapus duplikasi kerja itu di setiap lapisan.

Caching adalah topik fullstack murni: ia hidup di CDN (jaringan), ISR (framework), Redis (server), dan browser (client). Kalian tidak bisa optimal hanya dengan satu lapisan — harus semua.

Peta Lapisan Caching

100%

Setiap lapisan menangkap jenis kerja yang berbeda:

LapisanMenyimpanContoh
CDNRespons HTTP statisHTML, gambar, JS/CSS
Framework (ISR)Halaman render/notes, /blog/[slug]
RedisHasil query / objekdaftar note, profil user
BrowserAset HTTPfile JS/CSS (cache-control)

Core Web Vitals: Target Kinerja yang Diukur

Sebelum mengoptimasi, tahu dulu apa yang diukur. Core Web Vitals adalah tiga metrik yang dinilai Google dan pengguna:

MetrikMengukurTarget
LCP (Largest Contentful Paint)Kecepatan konten utama tampil≤ 2,5 detik
INP (Interaction to Next Paint)Responsivitas interaksi≤ 200 ms
CLS (Cumulative Layout Shift)Lompatan tata letak≤ 0,1

LCP adalah metrik paling relevan untuk fullstack: ia diukur dari jaringan sampai render konten utama. CLS paling sering disabotase oleh gambar tanpa dimensi atau konten yang muncul setelah layout — hindari itu sejak awal.

Note

Aturan LCP yang sering dilupakan: konten utama halaman seharusnya datang dari HTML awal, bukan menunggu JavaScript di client. Inilah alasan Server Components (episode 3) dan ISR — HTML jadi dikirim dari server/CDN, browser tidak menunggu JS bundel untuk menampilkan isi.

ISR: Render Sekali, Hidup Panjang

ISR (Incremental Static Regeneration) merender halaman sebagai statis di build, lalu memperbaruinya di background sesuai jadwal — sudah kita singgung di episode 8 dengan revalidate. Contoh halaman publik yang jarang berubah:

Halaman dengan ISR
export const revalidate = 300 // 5 menit
 
export default async function Page() {
  const notes = await prisma.note.findMany({
    where: { published: true },
    orderBy: { createdAt: "desc" },
  })
  return <NoteList notes={notes} />
}

Halaman ini di-render sekali, disajikan dari cache selama 5 menit ke semua pengguna, lalu di-render ulang di background. Database tidak disentuh per request — beban turun drastis tanpa mengorbankan kesegaran yang berarti.

Redis Cache untuk Query

Untuk data dinamis yang tidak bisa di-static (misal daftar per user), cache di Redis menangkap query berulang:

src/lib/cache.ts
import { Redis } from "ioredis"
 
const redis = new Redis({ host: "localhost", port: 6379 })
 
export async function cacheGet<T>(key: string, fn: () => Promise<T>, ttl = 60) {
  const cached = await redis.get(key)
  if (cached) return JSON.parse(cached) as T
 
  const value = await fn()
  await redis.set(key, JSON.stringify(value), "EX", ttl)
  return value
}
Query dengan cache
const notes = await cacheGet("notes:public", () =>
  prisma.note.findMany({ where: { published: true } }),
)

Invalidasi adalah bagian tersulit cache: saat data berubah, cache harus dihapus atau update:

Invalidasi cache saat data berubah
"use server"
 
import { redis } from "@/lib/cache"
 
export async function createNote(formData: FormData) {
  const note = await prisma.note.create({ data: {...} })
  await redis.del("notes:public") // cache basi dihapus
  revalidatePath("/notes")
}

Warning

Pola "cache-then-invalidate" di atas memakai cache-aside (baca cache, miss → isi cache). Kesalahan umum: lupa menghapus cache di semua jalur tulis. Buat daftar key cache dan invalidate di satu tempat — jangan tersebar di banyak server action.

Optimasi Gambar: Sumber Terbesar CLS

Gambar adalah penyebab CLS paling umum dan konstributor LCP terbesar. Di Next.js, gunakan next/image — ia menangani responsive sizing, format modern (WebP/AVIF), dan lazy load secara bawaan:

Gambar dengan next/image
import Image from "next/image"
 
<Image
  src="/static/note-cover.png"
  alt="Sampul catatan"
  width={800}
  height={450}
  priority
/>

Dua aturan emas: selalu isi width & height (mencegah CLS) dan priority hanya untuk gambar LCP (gambar di atas fold) — sisanya lazy load.

Praktik: Optimasi End-to-End

Selesaikan dengan urutan berikut:

  1. Ukur halaman /notes dengan Lighthouse di Chrome DevTools — catat LCP/INP/CLS awal.
  2. Terapkan ISR (revalidate = 300) untuk halaman publik.
  3. Tambahkan Redis cache untuk query daftar note + invalidasi di server action.
  4. Pastikan semua gambar memakai next/image dengan width & height.
  5. Ukur ulang — LCP harus turun signifikan.
Ukur waktu respons
curl -s -o /dev/null -w "%{time_total}s\n" http://localhost:3000/notes

Penutup

Inti yang harus dibawa pulang:

  • Caching = menghilangkan duplikasi kerja di setiap lapisan: CDN, ISR, Redis, browser.
  • Core Web Vitals: LCP ≤ 2,5s, INP ≤ 200ms, CLS ≤ 0,1 — ukur sebelum optimasi.
  • ISR merender statis + regenerasi background — cocok untuk halaman publik.
  • Redis cache-aside menyelamatkan query berulang; jangan lupa invalidasi.
  • next/image dengan width/height mencegah CLS dan mempercepat LCP.

Di episode 16 selanjutnya kita membahas security fullstack — OWASP, XSS, CSRF, injection, CSP, dan secrets — untuk mengamankan aplikasi dari serangan yang paling umum. Sampai jumpa di episode 16!