Next.js가 기본적으로 제공하는 기능

  • Data cache – Next.js 13+ 버전에서는 모든 fetch() 호출이 요청별 메모리 캐시에 저장됩니다. revalidate 옵션을 추가하면 설정된 간격 후에 데이터를 새로 고치도록 런타임에 지시할 수 있으며, 이를 통해 빈번한(hot) API 호출을 필요한 경우에만 새로 수행하는(cold) 방식으로 전환할 수 있습니다.
  • Full-route cache – 프레임워크는 렌더링된 HTML과 전체 라우트에 필요한 데이터를 유지합니다. 재방문자는 서버가 재렌더링을 건너뛰기 때문에 즉각적인 화면 전환을 경험할 수 있습니다.
  • ISR – 기존 버전이 계속 트래픽을 처리하는 동안 백그라운드에서 정적 페이지가 재생성됩니다. 이를 통해 콘텐츠가 변경될 때마다 전체 빌드를 다시 하지 않고도 대규모 사이트를 정적으로 유지할 수 있습니다.
  • Server Component cache() – React의 cache 헬퍼는 단일 요청이 지속되는 동안 비용이 많이 드는 계산이나 데이터베이스 연결을 메모이제이션하여, 페이지의 컴포넌트 트리 내부에서 중복 작업이 발생하는 것을 방지합니다.

이러한 캐시들은 "첫 번째 요청(first-hit)" 문제를 해결하지만, 여전히 Node 프로세스 내부에 존재합니다. 서버와 데이터베이스를 보호하려면 캐싱 범위를 외부로 확장해야 합니다.

추가할 수 있는 외부 계층

계층 저장 내용 대표적인 도구 도움을 주는 방식
CDN 정적 에셋, HTML, API JSON Cloudflare, Akamai, AWS CloudFront 콘텐츠를 에지(edge) 위치로 이동시켜 사용자까지의 왕복 시간(round-trip)을 단축합니다
Reverse proxy Node에 도달하기 전의 전체 페이지 응답 Nginx, Varnish 캐시된 페이지를 직접 제공하여 Next.js 인스턴스의 부하를 줄입니다
Application-level cache 비용이 많이 드는 DB 쿼리 또는 API 호출 결과 Redis, Memcached 서버 재시작 후에도 유지되며 여러 앱 인스턴스 간에 공유할 수 있는 빠른 키-값(key-value) 저장소를 제공합니다

각 계층은 원본 데이터베이스에서 더 멀리 떨어져 있습니다. 따라서 한 계층에서 캐시 미스(miss)가 발생하면 다음 계층으로 이어지며, 궁극적으로는 반드시 필요한 경우에만 데이터베이스에 도달하게 됩니다.

캐시 최신 상태 유지하기

캐시 무효화(Invalidation)는 많은 팀이 어려워하는 부분입니다. 다음 세 가지 실용적인 패턴이 효과적입니다.

  • Time-based (TTL) – 캐시 항목에 고정된 만료 시간을 할당합니다. 설정이 간단하지만 타이머가 만료될 때까지 오래된 데이터를 제공할 수 있습니다.
  • Event-driven – 콘텐츠가 변경될 때 웹훅(webhook)을 보내는 CMS 또는 데이터 소스에 연결합니다. 웹훅이 호출되면 관련 캐시 항목을 삭제(purge)합니다.
  • Tag-based – 일련의 fetch 호출 그룹에 논리적 태그(예: product-list)를 붙입니다. 해당 그룹의 항목이 변경되면 revalidateTag('product-list') 한 번의 호출로 해당 태그를 공유하는 모든 항목을 삭제할 수 있습니다.

이러한 접근 방식을 혼합하면 데이터의 최신성과 캐시 적중률(cache-hit rate) 사이의 균형을 맞출 수 있습니다.

대부분의 팀에 적합한 계층 구조

  1. Browser cache – 정적 에셋(CSS, JS, 이미지)에 긴 max-age를 설정하여 사용자의 기기가 네트워크에 다시 요청하지 않도록 합니다.
  2. CDN – 에지 노드가 전체 HTML 페이지와 API JSON을 캐싱하며, Next.js에서 설정한 Cache-Control 헤더를 준수합니다.
  3. Reverse proxy – Nginx 또는 Varnish 인스턴스가 Next.js 서버 앞에 위치하여, 거의 변경되지 않는 라우트에 대해 캐시된 응답을 제공합니다.
  4. Next.js internal cache – 프레임워크의 데이터 및 라우트 캐시가 요청별 메모이제이션과 ISR을 처리합니다.
  5. Application cache – Redis가 쿼리 파라미터나 태그를 키로 사용하여 무거운 데이터베이스 쿼리 결과를 저장합니다.
  6. Database – 최종적인 신뢰할 수 있는 원천(source of truth)으로, 상위 모든 계층에서 캐시 미스가 발생했을 때만 쿼리됩니다.

요청이 들어오면, 어떤 계층이 응답할 때까지 이 목록을 따라 내려갑니다. 가장 빠른 응답이 선택되며, 해당 응답은 향후 재사용을 위해 체인을 타고 다시 위로 기록됩니다.

종합하기

  1. API 라우트에 Cache-Control 헤더 설정하기
  2. CDN 구성하기 – HTML 및 JSON 엔드포인트에 대해 에지 캐싱을 활성화하고, CDN이 Cache-Control을 준수하는지 확인합니다.
  3. Reverse proxy 배포하기 – Next.js 서버 앞에 Nginx를 배치합니다.
  4. Redis 추가하기 – 비용이 많이 드는 데이터베이스 쿼리 결과를 캐싱합니다.
  5. 컴포넌트에서 revalidateTag 사용하기revalidateTag를 호출하여 특정 태그에 대한 Next.js 내부 캐시를 삭제합니다.

요약

Next.js 내부의 단일 캐시는 첫 번째 요청을 빠르게 만들지만, 진정한 확장성은 체계적인 스택(browser → CDN → reverse proxy → framework → Redis → database)에서 나옵니다. 각 계층을 구성하고 시간, 이벤트, 태그를 통해 무효화를 처리하며 지표를 모니터링하세요. 그러면 백엔드가 트래픽 급증을 견뎌내는 동안 지연 시간(latency)은 낮게 유지될 것입니다.