Saat Frontend Anda Membutuhkan Server: Membangun Pipeline CI/CD untuk Aplikasi SSR

Anda baru saja menyelesaikan fitur di aplikasi Next.js. Build berhasil di lokal. Anda push ke production. Namun alih-alih halaman yang berfungsi, pengguna melihat layar kosong dengan pemutar berputar. Proses server memang berjalan, tetapi ternyata belum siap menangani permintaan.

Inilah momen Anda menyadari bahwa men-deploy frontend yang di-render di server pada dasarnya berbeda dengan men-deploy situs statis. Pipeline yang berhasil untuk React SPA atau situs Gatsby statis Anda tidak akan cukup lagi.

Perbedaan Inti: Anda Men-deploy Server, Bukan File

Dengan frontend statis, build Anda menghasilkan file HTML, CSS, dan JavaScript. Anda mengunggahnya ke CDN atau bucket penyimpanan, dan selesai. Deployment pada dasarnya adalah operasi penyalinan file.

Dengan Server-Side Rendering (SSR), output build mencakup kode sisi server yang harus berjalan sebagai sebuah proses. Framework seperti Next.js, Nuxt, atau Remix menghasilkan folder yang berisi:

  • Kode JavaScript yang berjalan di server
  • Bundel JavaScript untuk klien
  • Aset statis seperti gambar dan font
  • File entry point (seringnya server.js) yang memulai aplikasi

Pipeline Anda sekarang perlu memperlakukan output ini sebagai aplikasi yang berjalan terus-menerus, bukan sekumpulan file untuk disajikan. Ini mengubah segalanya tentang cara Anda membangun, menguji, dan men-deploy.

Langkah 1: Build dengan Target yang Tepat

Langkah build terlihat mirip dengan frontend statis pada pandangan pertama. Anda menjalankan npm run build atau perintah setara dari framework. Namun outputnya berbeda, dan apa yang Anda lakukan dengannya juga berbeda.

Untuk SSR, output build harus dikemas menjadi sesuatu yang dapat berjalan di server. Jika Anda menggunakan kontainer, ini berarti membuat image Docker yang mencakup:

  • Kode server yang sudah di-build
  • Dependensi runtime (versi Node.js, library sistem)
  • File konfigurasi yang diperlukan saat runtime
  • Skrip entry point

Dockerfile Anda mungkin akan terlihat seperti ini:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY .next ./.next
COPY public ./public
EXPOSE 3000
CMD ["node", ".next/standalone/server.js"]

Detail penting: Anda tidak menyalin seluruh kode sumber. Anda hanya menyalin apa yang diperlukan untuk menjalankan aplikasi. Ini menjaga image tetap kecil dan mengurangi permukaan serangan.

Langkah 2: Health Check Bukan Opsional

Di sinilah banyak pipeline SSR gagal. Kontainer berjalan, proses berjalan, dan semua orang menganggap aplikasi berfungsi. Namun "proses berjalan" tidak sama dengan "aplikasi dapat melayani permintaan".

Aplikasi Anda mungkin berhasil dimulai tetapi gagal merender halaman karena:

  • Koneksi database timeout
  • API eksternal tidak dapat dijangkau
  • Variabel lingkungan hilang
  • Layanan yang diperlukan belum siap

Tambahkan endpoint health check ke aplikasi Anda. Biasanya ini berada di /health atau /api/health dan mengembalikan status 200 ketika aplikasi benar-benar dapat menangani permintaan. Pipeline Anda harus memanggil endpoint ini setelah deployment, sebelum mengarahkan lalu lintas ke versi baru.

Jika health check gagal, hentikan pipeline. Jangan biarkan pengguna melihat halaman error atau status loading tak berujung. Tim menyelidiki, memperbaiki masalah, dan menjalankan pipeline lagi.

Langkah 3: Pilih Strategi Deployment Anda

Ada dua jalur umum untuk men-deploy aplikasi SSR: langsung ke server atau ke dalam kontainer. Masing-masing memiliki implikasi berbeda untuk pipeline Anda.

Diagram alir berikut mengilustrasikan pipeline SSR lengkap, dari build hingga deployment, dengan titik keputusan health check yang kritis.

flowchart TD A[Build SSR App] --> B[Package Artifact] B --> C[Deploy to Server/Cluster] C --> D[Run Health Check] D --> E{Health Check Passes?} E -->|Yes| F[Route Traffic to New Version] E -->|No| G[Stop Pipeline & Alert] F --> H[Track Running Version] G --> I[Investigate & Fix] I --> A H --> J[Monitor & Log]

Deployment Langsung ke Server

Anda menyalin output build ke server, menghentikan proses lama, dan memulai yang baru. Perhatian kritis di sini adalah bagaimana Anda menangani transisi.

Jika Anda mematikan proses lama segera, permintaan yang sedang diproses akan gagal. Pengguna melihat error di tengah tindakan. Solusinya adalah graceful shutdown: server lama berhenti menerima permintaan baru tetapi menyelesaikan pemrosesan permintaan yang sudah berjalan. Setelah selesai, proses keluar dengan bersih. Kemudian server baru dimulai dan mulai menerima lalu lintas.

Skrip pipeline Anda perlu mengkoordinasikan serah terima ini. Ini bisa dilakukan tetapi memerlukan skrip dan pemantauan yang cermat.

Deployment Kontainer

Kontainer memberi Anda lebih banyak kendali. Pipeline membangun image Docker baru, mendorongnya ke registry, dan men-deploy-nya ke platform orkestrasi kontainer Anda.

Berikut adalah Dockerfile minimal yang mengemas aplikasi SSR yang sudah di-build untuk deployment kontainer:

FROM node:20-alpine

WORKDIR /app

# Copy production dependencies
COPY package.json package-lock.json ./
RUN npm ci --only=production

# Copy built server and client assets
COPY .next ./.next
COPY public ./public

# Expose the port the app listens on
EXPOSE 3000

# Start the server
CMD ["node", ".next/standalone/server.js"]

Dengan Kubernetes, ini menjadi rolling update:

  1. Pod baru dimulai dengan image baru
  2. Pod menjalankan health check-nya
  3. Setelah sehat, lalu lintas secara bertahap dialihkan ke pod baru
  4. Pod lama dihentikan setelah menyelesaikan permintaan mereka saat ini

Kubernetes menangani graceful shutdown dan pengalihan lalu lintas secara otomatis. Pipeline Anda hanya perlu memperbarui manifest deployment dengan tag image baru dan menerapkannya.

Langkah 4: Lacak Apa yang Berjalan

Setelah deployment, pipeline Anda harus mencatat versi mana yang berjalan. Simpan hash commit, tag image, atau timestamp deployment di tempat yang dapat diakses. Informasi ini sangat berharga ketika Anda perlu:

  • Roll back ke versi sebelumnya
  • Menyelidiki deployment mana yang memperkenalkan bug
  • Mengorelasikan masalah kinerja dengan rilis tertentu

Pendekatan sederhana: beri tag image Docker Anda dengan hash commit dan simpan pemetaannya di database atau file teks sederhana. Alat pemantauan Anda kemudian dapat mereferensikan data ini saat memberikan peringatan.

Daftar Periksa Praktis untuk Pipeline SSR Anda

Sebelum Anda menyebut pipeline Anda siap production, verifikasi poin-poin ini:

  • Output build dikemas menjadi artefak yang dapat di-deploy (image Docker atau paket server)
  • Endpoint health check ada dan mengembalikan status yang bermakna
  • Pipeline menunggu health check lulus sebelum mengarahkan lalu lintas
  • Pipeline berhenti dan memberi peringatan jika health check gagal
  • Graceful shutdown dikonfigurasi (proses lama menyelesaikan permintaan yang sedang berjalan)
  • Strategi rolling update telah diuji (tanpa downtime selama deployment)
  • Informasi versi disimpan dan dapat diakses setelah deployment
  • Proses rollback didokumentasikan dan diuji

Kesimpulan

Frontend SSR bukanlah situs statis. Ini adalah aplikasi server yang kebetulan merender HTML. Perlakukan pipeline Anda sesuai: build untuk runtime, verifikasi dengan health check, deploy dengan strategi zero-downtime, dan selalu tahu versi mana yang melayani pengguna Anda. Ketika Anda mendapatkan fundamental ini dengan benar, pengguna Anda tidak akan pernah melihat layar kosong. Mereka hanya melihat halaman yang cepat dan berfungsi.