Incremental Static Regeneration (ISR)
예제
Next.js를 사용하면 사이트를 빌드한 후 정적 페이지를 생성하거나 업데이트할 수 있습니다. Incremental Static Regeneration (ISR)을 사용하면 전체 사이트를 재빌드할 필요 없이 페이지별로 정적 생성을 사용할 수 있습니다. ISR을 사용하면 정적 페이지의 장점을 유지하면서 수백만 개의 페이지로 확장할 수 있습니다.
알아두면 좋은 점:
edge
runtime은 현재 ISR과 호환되지 않지만,cache-control
헤더를 수동으로 설정하여stale-while-revalidate
를 활용할 수 있습니다.
ISR을 사용하려면, revalidate
prop을 getStaticProps
에 추가하세요:
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
// 이 함수는 빌드 시 서버 측에서 호출됩니다.
// 재검증이 활성화되고 새로운 요청이 들어오면,
// 서버리스 함수에서 다시 호출될 수 있습니다.
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js는 페이지 재생성을 시도합니다:
// - 요청이 들어올 때
// - 최대 10초마다 한 번씩
revalidate: 10, // 초 단위
}
}
// 이 함수는 빌드 시 서버 측에서 호출됩니다.
// 경로가 생성되지 않은 경우,
// 서버리스 함수에서 다시 호출될 수 있습니다.
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
// 게시물을 기반으로 사전 렌더링할 경로를 가져옵니다.
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// 빌드 시 이러한 경로만 사전 렌더링합니다
// 경로가 존재하지 않는 경우, { fallback: 'blocking' }은
// 온디맨드로 페이지를 서버 렌더링합니다.
return { paths, fallback: 'blocking' }
}
export default Blog
빌드 시 사전 렌더링된 페이지에 요청을 하면 처음에는 캐시된 페이지가 표시됩니다.
- 초기 요청 후 10초 이내에 발생한 페이지에 대한 모든 요청은 캐시되며 즉시 처리됩니다.
- 10초가 지난 후의 다음 요청은 여전히 캐시된 (오래된) 페이지를 표시합니다.
- Next.js는 백그라운드에서 페이지 재생성을 트리거합니다.
- 페이지가 성공적으로 생성되면, Next.js는 캐시를 무효화하고 업데이트된 페이지를 표시합니다. 백그라운드 재생성이 실패하면, 이전 페이지가 그대로 유지됩니다.
생성되지 않은 경로로 요청이 들어오면, Next.js는 첫 번째 요청에 대해 페이지를 서버 렌더링합니다. 이후의 요청은 캐시에서 정적 파일을 제공합니다. Vercel의 ISR은 캐시를 전역적으로 유지하고 롤백을 처리합니다 (opens in a new tab).
알아두면 좋은 점: 업스트림 데이터 제공자가 기본적으로 캐싱을 활성화했는지 확인하세요. 비활성화해야 할 수도 있습니다(예:
useCdn: false
). 그렇지 않으면 재검증이 ISR 캐시를 업데이트하기 위해 새로운 데이터를 가져올 수 없습니다. 캐싱은 (요청된 엔드포인트에 대해)Cache-Control
헤더를 반환할 때 CDN에서 발생할 수 있습니다.
On-Demand Revalidation
revalidate
시간을 60
으로 설정하면, 모든 방문자는 1분 동안 동일한 버전의 사이트를 보게 됩니다. 캐시를 무효화하는 유일한 방법은 1분이 지난 후 누군가 해당 페이지를 방문하는 것입니다.
v12.2.0
부터 Next.js는 특정 페이지에 대한 Next.js 캐시를 수동으로 삭제할 수 있는 온디맨드 Incremental Static Regeneration을 지원합니다. 이를 통해 다음과 같은 경우에 사이트를 더 쉽게 업데이트할 수 있습니다:
- 헤드리스 CMS의 콘텐츠가 생성되거나 업데이트된 경우
- 전자상거래 메타데이터 변경 (가격, 설명, 카테고리, 리뷰 등)
getStaticProps
내부에서 온디맨드 재검증을 사용하기 위해 revalidate
를 지정할 필요가 없습니다. revalidate
를 생략하면, Next.js는 기본값인 false
(재검증 없음)를 사용하고 revalidate()
가 호출될 때만 온디맨드로 페이지를 재검증합니다.
알아두면 좋은 점: Middleware는 온디맨드 ISR 요청에 대해 실행되지 않습니다. 대신, 재검증하려는 정확한 경로에서
revalidate()
를 호출하세요. 예를 들어pages/blog/[slug].js
가 있고/post-1
->/blog/post-1
의 rewrite가 있는 경우,res.revalidate('/blog/post-1')
를 호출해야 합니다.
Using On-Demand Revalidation
먼저 Next.js 앱에서만 알 수 있는 비밀 토큰을 생성합니다. 이 비밀 토큰은 재검증 API 라우트에 대한 무단 접근을 방지하는 데 사용됩니다. 다음 URL 구조로 (수동으로 또는 웹훅으로) 경로에 접근할 수 있습니다:
https://<your-site.com>/api/revalidate?secret=<token>
다음으로, 애플리케이션에 비밀 토큰을 환경 변수로 추가합니다. 마지막으로, 재검증 API 라우트를 생성합니다:
export default async function handler(req, res) {
// 이 요청이 유효한지 확인하기 위해 비밀 토큰을 확인합니다.
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' })
}
try {
// 이 경로는 rewritten 경로가 아닌 실제 경로여야 합니다.
// 예: "/blog/[slug]"의 경우 "/blog/post-1"이어야 합니다.
await res.revalidate('/path-to-revalidate')
return res.json({ revalidated: true })
} catch (err) {
// 오류가 발생한 경우
// Next.js는 마지막으로 성공적으로 생성된 페이지를 계속 표시합니다.
return res.status(500).send('Error revalidating')
}
}
데모 (opens in a new tab)에서 온디맨드 재검증이 동작하는 것을 확인하고 피드백을 제공하세요.
Testing on-Demand ISR during development
next dev
로 로컬에서 실행할 경우, 모든 요청에 대해 getStaticProps
가 호출됩니다. 온디맨드 ISR 구성이 올바른지 확인하려면 프로덕션 빌드를 생성하고 프로덕션 서버를 시작해야 합니다:
$ next build
$ next start
그런 다음, 정적 페이지가 성공적으로 재검증되었는지 확인할 수 있습니다.
Error handling and revalidation
백그라운드 재생성을 처리할 때 getStaticProps
내부에 오류가 발생하거나, 수동으로 오류를 발생시키면 마지막으로 성공적으로 생성된 페이지가 계속 표시됩니다. 다음 후속 요청 시, Next.js는 getStaticProps
호출을 다시 시도합니다.
export async function getStaticProps() {
// 이 요청에서 예기치 않은 오류가 발생하면,
// Next.js는 현재 표시된 페이지를 무효화하지 않고
// 다음 요청 시 getStaticProps를 다시 시도합니다.
const res = await fetch('https://.../posts')
const posts = await res.json()
if (!res.ok) {
// 서버 오류가 발생한 경우,
// 다음 요청이 성공할 때까지 캐시가 업데이트되지 않도록
// 반환하는 대신 오류를 발생시키는 것이 좋습니다.
throw new Error(`Failed to fetch posts, received status ${res.status}`)
}
// 요청이 성공하면 게시물을 반환하고
// 10초마다 재검증합니다.
return {
props: {
posts,
},
revalidate: 10,
}
}
Self-hosting ISR
Incremental Static Regeneration (ISR)은 next start
를 사용할 때 자체 호스팅된 Next.js 사이트에서 기본적으로 작동합니다.
Next.js 자체 호스팅에 대해 자세히 알아보세요.
Version History
버전 | 변경 사항 |
---|---|
v14.1.0 | 커스텀 cacheHandler 안정화 |
v12.2.0 | 온디맨드 ISR 안정화 |
v12.1.0 | 온디맨드 ISR 추가 (베타) |
v12.0.0 | Bot-aware ISR fallback (opens in a new tab) 추가 |
v9.5.0 | Base Path 추가 |