ISR 캐싱 전략, 지금 이 화면에 써도 되는가?

결론부터 쓰면, ISR은 "같은 URL을 많은 사용자가 공유하고, 콘텐츠가 주기적으로만 바뀌는" 화면에 쓸 때 트레이드오프가 가장 잘 맞는다. 사용자별로 결과가 달라지는 화면(장바구니, 추천, 권한)에 통째로 ISR을 씌우면 캐시 적중률이 떨어지고 오히려 복잡도만 늘어난다. 2026년 기준 App Router 환경에서 이 판단은 revalidate 시간 하나로 끝나지 않고, 온디맨드 재검증·CDN 저장소·upstream 캐시까지 같이 설계해야 한다.

핵심 정리

  • ISR은 콘텐츠 갱신 주기가 느리고 다수 사용자가 같은 URL을 보는 화면(상품 상세, 블로그, 문서)에 적합하다.
  • revalidate: 60은 "60초마다 자동 갱신"이 아니라 "만료 후 다음 요청이 들어올 때 재생성 시작"이다. 출처: Next.js 공식 문서
  • CDN 캐시 적중 TTFB 약 80ms, 캐시 미스 재생성 약 300ms라는 수치는 특정 CDN·애플리케이션 조건의 관측값이며 보편 수치가 아니다. 출처
  • upstream API가 별도 캐시를 쓰면 ISR 재생성이 성공해도 화면이 그대로인 장애가 생길 수 있다.
  • 분산 배포에서는 HTML·RSC payload의 TTL 불일치, 캐시 저장소의 일관성 모델까지 함께 설계해야 한다.

판단은 보통 세 축에서 갈린다. 첫째는 콘텐츠 갱신 주기와 개인화 정도, 둘째는 revalidate 간격과 갱신 정확도의 트레이드오프, 셋째는 배포 환경(단일 서버인지 분산 엣지인지)에 따른 캐시 저장소 선택이다. 이 세 축을 먼저 나눠놓고 수치를 붙이는 게 순서다.

콘텐츠 갱신 주기와 개인화, 무엇부터 따져야 하나?

먼저 "이 URL을 몇 명이 같이 보는가"를 묻는 게 먼저다. 상품 상세, 뉴스 목록, 블로그, 문서 페이지처럼 동일 URL을 다수가 공유하는 화면은 ISR의 캐시 적중 이득을 그대로 받는다. 반대로 요청자별 권한·지역·장바구니·추천 결과가 핵심인 화면은 전체 페이지를 ISR로 캐싱하면 캐시 키가 사실상 무효화돼 미스율만 올라간다. 이럴 땐 캐시 가능한 공통 셸과 개인화 영역을 분리하거나 SSR을 쓰는 편이 맞다. 서버 컴포넌트도 여기서 오해가 잦은데, 서버 컴포넌트는 렌더링 위치를 서버로 옮기는 방식이지 자동으로 페이지 전체를 ISR로 만들어주지 않는다. 내부 데이터의 캐시 정책과 경로의 재검증 정책은 따로 설계해야 한다.

revalidate 시간을 길게 잡을까 짧게 잡을까?

여기서부터는 갱신 정확도와 캐시 효율의 트레이드오프다. revalidate: 60으로 설정하면 게시 직후 최대 약 60초 동안 이전 버전이 노출될 수 있다. 출처: Next.js 공식 문서 간격을 너무 짧게 잡으면 캐시 적중률이 떨어져 원본 서버 CPU 사용률이 올라가고, 너무 길게 잡으면 콘텐츠가 오래된 상태로 노출되는 시간이 늘어난다. Next.js 공식 문서는 매우 짧은 간격보다 상대적으로 긴 재검증 간격을 권장하고, 즉시성이 필요하면 간격을 줄이는 대신 온디맨드 재검증을 쓰라고 안내한다. 출처 직접 겪어보니 재검증 간격을 1분 단위로 세분화하는 것보다, 변경이 드문 화면은 10분~1시간 단위로 넉넉히 잡고 정확한 즉시성이 필요한 소수 경로만 웹훅으로 따로 처리하는 쪽이 운영 부담이 적었다.

온디맨드 재검증으로 바꾸면 운영 부담은 줄어드나?

줄어들 수는 있지만 공짜는 아니다. CMS 발행 웹훅과 revalidatePath()를 연결하면 특정 URL만 무효화할 수 있고, revalidateTag()를 쓰면 공유 데이터 단위로 여러 경로를 한 번에 갱신할 수 있다. 출처: Next.js 공식 문서 문제는 "무효화하면 즉시 모든 사용자에게 새 콘텐츠가 보인다"는 기대다. 이 문장은 CDN 캐시, RSC payload 캐시, upstream API 캐시까지 전부 같은 TTL로 움직일 때만 성립한다. 실제로는 CDN에서 HTML과 RSC 응답을 서로 다른 TTL로 캐싱하거나 무효화 시점이 어긋나면, 페이지 전체 이동과 클라이언트 내비게이션에서 서로 다른 콘텐츠가 보이는 경우가 생긴다. 출처: Next.js 공식 문서 온디맨드 재검증을 도입할 때는 웹훅 코드만 짜는 게 아니라 Vary 헤더, CDN 캐시 키, prefetch 응답까지 같이 확인하는 작업이 뒤따른다.

분산 배포 환경에서 ISR 캐시 저장소는 무엇을 감수해야 하나?

플랫폼마다 캐시 저장소의 일관성 모델이 다르다는 점을 감수해야 한다. OpenNext의 Cloudflare 문서에 따르면 ISR 캐시 저장소로 R2, Workers KV, Workers Static Assets를 쓸 수 있는데, R2는 단일 리전에 저장되어 지역별 접근 지연이 생길 수 있고 Workers KV는 eventual consistency 때문에 즉시 일관성이 필요한 용도에는 주의해야 한다. Workers Static Assets는 빌드 시 정적 자산용이라 재검증 자체를 지원하지 않아 시간 기반 ISR 저장소로는 쓸 수 없다. 출처: OpenNext 공식 문서 여러 리전에서 동시에 재생성이 걸리면 중복 렌더링이 발생할 수 있어, OpenNext는 재검증 요청을 큐로 조정해 중복을 줄이는 구성을 제공한다. 단일 리전 배포라면 이런 문제를 덜 신경써도 되지만, 글로벌 트래픽을 받는 서비스라면 캐시 저장소 선택 자체가 설계 결정이 된다.

실측 TTFB와 비용 수치는 어떻게 해석해야 하나?

이 수치들은 일반 보장값이 아니라 특정 조건의 관측값으로 읽어야 한다. 2025년 6월 게시된 한 사례 글은 CDN 캐시 적중 시 ISR의 TTFB를 약 80ms, 캐시 미스 후 원본 재생성 시 약 300ms, 캐시 없는 SSR을 약 200~800ms 범위로 제시했다. 출처 같은 자료는 일일 10만 요청 조건에서 SSR이 ISR보다 컴퓨팅 비용이 10~50배 높을 수 있다고도 설명하지만, 인스턴스 사양과 요청당 렌더링 비용이 공개되지 않은 추정치라 참고 수치로만 쓰는 게 맞다. 재현 가능한 비교를 하려면 최소한 Next.js 버전, CDN 제품·지역, 페이지 HTML 크기, 원본 API 응답시간, 캐시 적중·미스 비율을 같이 기록해야 한다. "ISR은 빠르다"보다 HIT TTFB, MISS TTFB, stale 응답 비율, 재생성 실패율을 나눠서 보는 쪽이 실제 사용자 경험을 더 정확히 설명한다.

ISR 도입에서 가장 자주 놓치는 함정은 무엇인가?

가장 흔한 함정은 "재생성은 성공했는데 화면이 그대로"인 경우다. Next.js 공식 문서는 upstream 데이터 제공자가 별도의 CDN·API 캐시를 쓰면 ISR 재생성 시에도 오래된 데이터를 다시 받아 새 페이지가 갱신되지 않을 수 있다고 경고한다. 출처: Next.js 공식 문서 ISR 캐시만 지우고 CMS API의 Cache-Control이나 SDK의 CDN 캐시를 그대로 두면 이런 상황이 재현된다. 또 하나는 가용성 우선 설계를 과신하는 것이다. Next.js 재검증 시스템은 엄격한 일관성보다 가용성을 우선해서, 캐시 쓰기가 실패해도 응답은 반환되고 다음 요청에서 다시 렌더링을 시도한다. 출처: Next.js 공식 문서 다만 캐시 읽기 실패를 내부에서 그대로 throw하면 단순 캐시 미스가 아니라 렌더링 오류로 번질 수 있다. 장애 회고를 남길 때는 캐시 읽기 실패, 캐시 쓰기 실패, 재생성 실패, upstream API 실패를 하나로 뭉뚱그리지 말고 별도 지표로 분리해 기록하는 편이 다음 장애 때 도움이 됐다.

결국 어떤 상황에 ISR을 고르나?

정리하면 축은 세 개다. 콘텐츠 갱신 주기가 느리고 다수 사용자가 같은 URL을 공유한다면 ISR이 출발점이고, 개인화·권한이 핵심이면 SSR이나 셸 분리를 먼저 검토한다. revalidate 간격은 너무 짧게 잡기보다 넉넉히 잡고, 정확한 즉시성이 필요한 경로만 온디맨드 재검증으로 따로 뺀다. 분산 배포라면 캐시 저장소의 일관성 모델(R2 단일 리전, Workers KV의 eventual consistency)을 먼저 확인한 뒤 TTL 정책을 정한다. 어느 쪽이든 "빠르다"는 결과가 아니라 HIT/MISS TTFB와 재생성 실패율을 따로 재는 작업이 선행돼야 다음 선택의 근거가 남는다.