페이지를 열 때마다 서버가 그 순간의 데이터를 담아 HTML을 만들어 보내준다. Next.js App Router에서는 fetch에 cache: 'no-store'를 지정하는 것만으로 이 동작을 활성화할 수 있다. 캐시된 스냅샷이 아닌 살아있는 데이터가 필요할 때 SSR은 가장 직접적인 해답이다.
SSR(Server Side Rendering)은 클라이언트의 요청이 도착할 때마다 서버에서 HTML을 새로 생성해 응답하는 렌더링 방식이다. Next.js App Router에서는 이를 Dynamic Rendering이라고도 부른다.
브라우저 Next.js 서버 데이터 소스
│ │ │
│── GET /page ──> │ │
│ │── fetch(url) ──> │
│ │<── 최신 데이터 ── │
│ │ (HTML 생성) │
│<── 완성된 HTML ── │ │
│ (hydration) │ │
빌드 타임이 아닌 런타임에 HTML이 만들어지므로, 응답마다 다른 내용을 담을 수 있다.
App Router에서 SSR을 적용하는 가장 간단한 방법은 fetch 옵션에 cache: 'no-store'를 전달하는 것이다.
// app/dashboard/page.tsx
export default async function DashboardPage() {
const res = await fetch('https://api.example.com/stats', {
cache: 'no-store', // 요청마다 새로 fetch
});
const data = await res.json();
return <StatsView data={data} />;
}
cache: 'no-store' — HTTP Cache-Control의 no-store와 동일한 의미. Next.js는 이 옵션을 보고 해당 페이지를 Dynamic으로 전환한다.
cookies, headers, searchParams 같은 Dynamic API를 사용해도 페이지가 자동으로 Dynamic Rendering으로 전환된다.
import { cookies } from 'next/headers';
export default async function ProfilePage() {
const cookieStore = cookies(); // 이것만으로도 SSR 전환
const token = cookieStore.get('auth_token')?.value;
const res = await fetch('https://api.example.com/me', {
headers: { Authorization: `Bearer ${token}` },
});
const user = await res.json();
return <Profile user={user} />;
}
| 방식 | HTML 생성 시점 | 데이터 신선도 | SEO | 서버 부하 |
|---|---|---|---|---|
| CSR | 브라우저 (런타임) | 요청마다 | 취약 | 낮음 |
| SSG | 빌드 타임 | 빌드 시점 고정 | 우수 | 없음 |
| ISR | 빌드 + 주기적 재생성 | 설정한 주기 | 우수 | 낮음 |
| SSR | 서버 (런타임) | 항상 최신 | 우수 | 높음 |
장점
단점
데이터 변경 주기가 길거나 사용자 구분이 필요 없다면 SSG나 ISR이 더 효율적이다. SSR은 "항상 최신, 항상 개인화"가 요구될 때 꺼내는 카드다.