Skip to content

Incremental Static Regeneration (ISR) - Page Router ​

ISR이란? ​

ISR은 SSG의 장점은 유지하면서 데이터 최신성 문제를 보완하기 위한 렌더링 방식이다.
빌드 시점에 HTML을 미리 생성하지만, 배포 이후에도 일정 주기로 페이지를 다시 생성할 수 있다.

즉, 정적 페이지의 성능을 유지하면서 데이터를 갱신할 수 있는 방식이다.


Next.js Page Router에서는 getStaticProps의 revalidate 옵션을 통해 ISR을 사용할 수 있다.

tsx
export const getStaticProps = async () => {
  console.log('인덱스 페이지');

  const [allBooks, recoBooks] = await Promise.all([
    fetchBooks(),
    fetchRandomBooks(),
  ]);

  return {
    props: {
      allBooks,
      recoBooks,
    },
    revalidate: 3,
  };
};

왜 ISR이 필요할까? ​

SSG의 구조적인 한계

  • 최신 데이터 반영이 어렵다.
  • 데이터 변경 시 재빌드가 필요하다.

완전히 실시간일 필요는 없지만, 빌드 시점에 데이터가 고정되는 것도 애매한 화면들이 존재한다.
즉, 완전 실시간은 아니지만 최신성이 중요한 화면!

  • 게시글 목록
  • 블로그 목록
  • 뉴스 목록
  • 랭킹 페이지
  • 상품 목록
  • 통계 / 대시보드 요약 화면

이러한 요구를 해결하기 위한 방식이 바로 ISR이다.


ISR의 동작 특성 ​

  • 기본적으로 정적 페이지로 동작
  • 일정 주기 이후 요청 기반 재생성
  • SSR 대비 서버 부담 감소
  • SSG 대비 데이터 최신성 확보

getStaticProps + revalidate ​

ISR은 기존 SSG 코드에 옵션 하나만 추가하면 된다.

tsx
export const getStaticProps = async () => {
  const books = await fetchBooks();

  return {
    props: {
      books,
    },

    revalidate: 60, // 60초마다 갱신
  };
};
  • 이 페이지를 최소 60초 동안 캐싱하고, 이후 새로운 요청이 들어올 때 재생성이 트리거된다.
  • revalidate는 페이지의 유효 기간(TTL)처럼 동작한다.

TTL (Time To Live)

TTL은 캐시의 유효 기간을 의미한다.
설정된 시간이 지나기 전까지는 기존 HTML이 그대로 응답된다.


revalidate 동작 방식 ​

revalidate: 3

  1. 빌드 시점
  • HTML 생성
  • 정적 페이지로 배포
🔍 Next.js 빌드 결과 (ISR 적용)

  1. 배포 이후 요청
  • 캐시된 HTML 응답 (SSG처럼 빠름)
  1. 3초 이후 새로운 요청 발생 시
  • 기존 캐시된 페이지 먼저 응답
  • 서버에서 백그라운드 재생성 수행
  • 생성 완료 후 캐시 교체

즉, 최초 요청까지는 완전한 정적 페이지처럼 동작하고, 이후에는 요청 기반으로 점진적 재생성이 수행된다.


ISR은 SSG와 SSR 사이에서 성능과 최신성의 균형을 맞추기 위한 전략이라고 볼 수 있다!


On-Demand ISR (수동 재생성) ​

위의 ISR은 revalidate 값을 기준으로 동작하는 시간 기반 재생성 방식이다.

하지만 실제 서비스에서는 단순한 시간 주기보다 특정 이벤트 시점에 즉시 갱신이 필요한 경우가 더 많다.

  • 게시글 수정 즉시 반영
  • 관리자가 업데이트 버튼 누르면 갱신
  • 특정 이벤트 발생 시 캐시 무효화

이럴 때 사용하는 방식이 바로 On-Demand ISR이다.
시간 주기가 아닌 사용자의 요청 또는 이벤트를 기준으로 페이지 재생성을 트리거하는 방식이다.


getStaticProps에서 revalidate 제거 ​

시간 기반 ISR 대신 수동 재생성을 사용할 경우, 페이지는 기본적으로 SSG로 고정된다.

tsx
export const getStaticProps = async () => {
  const [allBooks, recoBooks] = await Promise.all([
    fetchBooks(),
    fetchRandomBooks(),
  ]);
  return { props: { allBooks, recoBooks } };
};
  • revalidate 옵션 제거
  • 페이지는 빌드 시점에만 생성되는 순수 SSG

즉, 자동 재생성은 일어나지 않는다.


API Route에서 수동 재생성 ​

페이지 재생성은 별도의 API Route에서 직접 트리거한다.
/api/revalidate.ts

ts
import { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  try {
    await res.revalidate('/');
    return res.json({ revalidate: true });
  } catch (err) {
    console.error(err);
    res.status(500).send('Revalidation Failed');
  }
}

await res.revalidate('/');

  • 지정한 경로 /의 정적 페이지 캐시를 무효화
  • 새로운 HTML 재생성 트리거

즉, 자동 갱신이 아니라 명시적인 트리거 기반 ISR 방식이다.


On-Demand ISR의 핵심 개념 ​

  • 페이지 자체는 SSG로 유지
  • 재생성 시점만 명시적으로 제어
  • CMS 연동, 관리자 액션 등 변경 시점이 명확할 때 적합