"매 어디서 render 매 (server/build/client/edge), 매 언제 (request/build/runtime), 매 무엇 (static/dynamic) — 매 이 3 축 의 매 trade-off". 매 2026 매 hybrid (RSC + Streaming SSR + ISR + Edge) 매 default — Next.js / Nuxt / Remix / SvelteKit / Astro 매 모두 mix.
매 핵심
매 5 가지 strategy
CSR (Client-Side Rendering): empty HTML + JS bundle → browser 매 render. SPA classic.
SSR (Server-Side Rendering): per-request HTML on server.
SSG (Static Site Generation): build-time HTML.
ISR (Incremental Static Regeneration): SSG + on-demand or time-based revalidate.
RSC (React Server Components, 2023~): server-only component, zero JS ship for that part.
매 핵심 metric
TTFB: server response time.
FCP / LCP: 매 visible content speed.
TTI: 매 interactive — hydration cost.
INP: 매 interaction latency (FID 의 후속, Core Web Vitals 2024+).
JS bundle: 매 작을 수록 hydration fast.
매 응용
Marketing site → SSG / ISR (Astro, Next).
E-commerce → ISR + dynamic SSR for cart.
Dashboard / admin → CSR or SSR with auth.
Blog → SSG + ISR.
Real-time chat → CSR + SSE/WebSocket.
News site → ISR (revalidate every 60s).
💻 패턴
Next.js App Router (RSC + streaming)
// app/products/[id]/page.tsx — RSC, fetched on server, no JS shipped
exportdefaultasyncfunctionProductPage({params}:{params:{id: string}}){constproduct=awaitdb.product.findUnique({where:{id: params.id}});return(<article><h1>{product.name}</h1><Suspensefallback={<Skeleton/>}><ReviewsproductId={params.id}/></Suspense></article>);}
ISR with revalidate
// Next.js — 매 60s 마다 background regenerate
exportconstrevalidate=60;exportdefaultasyncfunctionPosts() {constposts=awaitfetch("https://api.example.com/posts").then((r)=>r.json());return<ul>{posts.map((p)=><likey={p.id}>{p.title}</li>)}</ul>;}
// app/dashboard/page.tsx
exportdefaultfunctionDashboard() {return(<><Header/><Suspensefallback={<Skel/>}><SlowWidget/></Suspense><Suspensefallback={<Skel/>}><AnotherSlow/></Suspense></>);}// 매 shell 먼저 flush, 매 widget ready 시 stream
Edge runtime (Vercel / Cloudflare Workers)
// Next.js — Edge 매 cold start 매 0 에 가까움
exportconstruntime="edge";exportasyncfunctionGET(req: Request){constcountry=req.headers.get("x-vercel-ip-country")??"US";returnnewResponse(`Hello from ${country}`);}
Astro islands
---
// 매 default static, 매 island 만 hydrate
import Counter from "../components/Counter.tsx";
const posts = await fetch("...").then((r) => r.json());
---
<html>
<body>
{posts.map((p) => <article><h2>{p.title}</h2></article>)}
<Counter client:visible /> <!-- viewport entry 시 hydrate -->
</body>
</html>
On-demand revalidation
// Next.js — webhook 받으면 매 즉시 invalidate
import{revalidateTag,revalidatePath}from"next/cache";exportasyncfunctionPOST(req: Request){const{tag}=awaitreq.json();revalidateTag(tag);returnResponse.json({ok: true});}
매 결정 기준
상황
Approach
Static marketing
SSG / Astro
Blog with comments
SSG + CSR comments
News feed (changes hourly)
ISR (revalidate=3600)
Product page + cart
RSC + Client island for cart
Real-time dashboard
CSR + WebSocket
Geo-personalized
Edge SSR
Authenticated app
SSR with auth + RSC
기본값: Next.js App Router (RSC default) + Suspense streaming + ISR for content. 매 client component 매 조심히.