정적·동적·ISR·스트리밍을 골라 쓴다.
렌더링 전략
같은 페이지라도 언제 HTML을 만드느냐에 따라 속도와 신선함이 달라집니다. App Router는 정적 생성, 동적 렌더링, 시간 기반 재생성(ISR), 그리고 스트리밍을 모두 지원합니다. 각각이 언제 적합한지 감을 잡아 봅시다.
학습 목표
- 정적(SSG)·동적(SSR)·ISR의 차이를 이해한다.
dynamic,revalidate라우트 설정으로 전략을 고른다.generateStaticParams로 동적 경로를 미리 생성한다.- Suspense로 화면을 스트리밍한다.
정적 생성 (SSG)
기본적으로 데이터에 동적 요소가 없으면 Next.js는 빌드 시점에 HTML을 미리 생성합니다. 요청 때마다 만들 필요가 없어 가장 빠르고, CDN으로 뿌리기 좋습니다. 이 강좌 사이트의 마크다운 글들도 빌드 때 정적으로 만들어집니다.
// 별다른 설정 없이 정적: 빌드 시 HTML 생성
export default async function Page() {
const data = await fetch('https://api.example.com/info', {
cache: 'force-cache',
}).then((r) => r.json());
return <h1>{data.title}</h1>;
}
동적 렌더링 (SSR)
쿠키, 헤더, searchParams, no-store fetch 등 요청에 따라 달라지는 것을 쓰면 자동으로 요청마다 렌더하는 동적 페이지가 됩니다. 강제로 동적을 지정할 수도 있습니다.
// 라우트 전체를 동적으로
export const dynamic = 'force-dynamic';
export default async function Page() {
const data = await fetch(url, { cache: 'no-store' }).then((r) => r.json());
return <pre>{JSON.stringify(data)}</pre>;
}
| 값 | 의미 |
|---|---|
'auto'(기본) | 코드에 따라 자동 판단 |
'force-static' | 항상 정적으로 |
'force-dynamic' | 항상 요청마다 렌더 |
ISR — 시간 기반 재생성
정적의 빠름과 동적의 신선함 사이 절충안입니다. revalidate 초가 지나면 백그라운드에서 새 버전을 만들어 교체합니다. 방문자는 항상 캐시된 빠른 페이지를 보고, 데이터는 주기적으로 갱신됩니다.
// 이 라우트를 60초마다 재생성
export const revalidate = 60;
export default async function Page() {
const posts = await getPosts();
return <List posts={posts} />;
}
💡 TIP — 뉴스 목록, 상품 가격처럼 "초 단위 실시간은 아니지만 자주 갱신되면 좋은" 데이터에 ISR이 딱 맞습니다.
generateStaticParams — 동적 경로 미리 만들기
[slug] 같은 동적 경로도 빌드 시점에 미리 생성할 수 있습니다. generateStaticParams 로 어떤 값들을 미리 만들지 알려 줍니다.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((p) => ({ slug: p.slug })); // [{slug:'a'}, {slug:'b'}]
}
export default async function Post({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return <article>{post.title}</article>;
}
이렇게 하면 각 글이 빌드 때 정적 HTML로 생성됩니다.
스트리밍 (Suspense)
페이지의 느린 부분만 나중에 흘려보내고, 빠른 부분은 즉시 보여줄 수 있습니다. <Suspense> 로 느린 컴포넌트를 감싸면 됩니다.
import { Suspense } from 'react';
export default function Page() {
return (
<>
<h1>대시보드</h1> {/* 즉시 표시 */}
<Suspense fallback={<p>매출 불러오는 중...</p>}>
<SlowSales /> {/* 준비되면 스트리밍 */}
</Suspense>
</>
);
}
⚠️ 주의 —
loading.tsx(4강)는 사실 페이지 전체를<Suspense>로 감싸는 것입니다. 페이지 일부만 지연시키고 싶으면 직접<Suspense>를 쓰세요.
요약
- 기본은 정적 생성(SSG)이며 가장 빠르다.
- 요청 의존 데이터를 쓰면 동적(SSR)이 되고,
dynamic으로 강제할 수 있다. revalidate로 ISR을 만들어 빠름과 신선함을 절충한다.generateStaticParams로 동적 경로를 미리 생성하고,Suspense로 일부만 스트리밍한다.
연습문제
export const revalidate = 30을 둔 페이지를 만들고, 30초 후 데이터가 갱신되는지 확인하세요.searchParams를 읽는 페이지가 왜 동적이 되는지 설명해 보세요.- 글 목록 5개에 대해
generateStaticParams로 정적 페이지를 생성해 보세요. - 페이지의 느린 차트 부분만
<Suspense>로 감싸 먼저 보이는 부분과 나중에 보이는 부분을 나눠 보세요.
힌트 — 동적 전환을 일으키는 신호는
cookies(),headers(),searchParams,no-storefetch 등입니다. ISR은 라우트 파일에export const revalidate = N한 줄이면 됩니다.
💡 연습문제 풀이
불러오는 중…
댓글 0
“Next.js” 강좌에 대한 댓글입니다.