Next.js에서는 성능을 개선하고 비용을 줄이기 위해 다양한 캐시를 지원한다. Request Memoization (요청 메모이제이션), Data Cache (데이터 캐시), Full Route Cache (전체 경로 캐시), Client Router Cache (클라이언트 라우터 캐시)를 통해 Next.js의 캐싱 메커니즘, 이를 구성하는 데 사용할 수 있는 API, 이들이 서로 상호 작용하는 방식에 대해 자세히 살펴보자.
Next.js는 동일한 요청을 한 번만 수행하도록 자동으로 메모이제이션(기억)한다.
예를 들어, fetch를 동일한 URL로 여러 번 호출하면 Next.js는 한 번만 요청을 수행하고, 그 결과를 재사용한다.
async function getData() {
const res = await fetch('https://example.com/api/data');
return res.json();
}
// getData()를 여러 번 호출해도 Next.js는 동일한 요청을 중복 수행하지 않는다.
export default async function Page() {
const data1 = await getData(); // 실제 요청 수행
const data2 = await getData(); // 캐시된 결과를 사용
return <div>{data1.someValue}</div>;
}
MISS.HIT, 함수를 실행하지 않고도 메모리에서 데이터가 반환된다.App Router에서는 React Server Component의 도입으로 각각의 컴포넌트에서 데이터를 직접 요청할 수 있게 되었다. 때문에 하나의 페이지에서 여러 컴포넌트가 존재하는 경우에, 서로 다른 컴포넌트에서 동일한 데이터를 필요로 하는 경우가 발생할 수 밖에 없어졌다.
이를 위해서 Next.js 동일한 요청을 한 번만 수행하도록 자동으로 메모이제이션하고, 이는 동일한 데이터 요청을 중복으로 보내는 것을 방지하여 성능을 최적화한다.
fetch()를 사용하여 캐시를 활성화할 수 있으며, 동일한 요청은 다시 실행되지 않고 캐시된 응답이 반환된다.
async function getData() {
const res = await fetch('https://example.com/api/data', { cache: 'force-cache' });
return res.json();
}
fetch렌더링 중에 옵션이 포함된 요청이 처음 'force-cache'호출되면 Next.js는 캐시된 응답이 있는지 데이터 캐시를 확인한다.cache옵션이 정의되지 않았거나 를 사용하지 않음 { cache: 'no-store' })의 경우 결과는 항상 데이터 소스에서 가져와서 메모화된다.next.revalidate 옵션을 사용하면 일정 시간마다 새로운 데이터를 가져오도록 설정할 수 있다.
async function getData() {
const res = await fetch('https://example.com/api/data', { next: { revalidate: 60 } });
return res.json();
}
fetch() 요청이 처음 호출될 때, 외부 데이터 소스에서 데이터를 가져와 데이터 캐시(Data Cache) 에 저장된다.PURGE.MISS가 발생하며, 외부 데이터 소스에서 다시 데이터를 가져와 데이터 캐시에 저장한다.Next.js는 정적 페이지(Static Pages)를 생성할 때 전체 페이지를 빌드 타임(또는 재검증 시)에 렌더링하고 캐싱하여 빠르게 제공할 수 있도록 한다. 이렇게 하면 매 요청마다 서버에서 다시 렌더링하는 대신 미리 캐싱된 경로를 제공할 수 있어 페이지 로딩 속도가 빨라진다.
정적 경로가 생성될 때(Static Rendering)
HTML과 RSC Payload를 서버 캐시에 저장사용자가 페이지 요청
HTML과 RSC Payload 반환 (SSG)데이터 재검증(Revalidation) 또는 배포 발생 (ISR )
즉, Full Route Cache는 SSG(정적 생성)된 페이지를 캐싱하고, SSR(동적 요청)된 페이지는 캐싱 없이 렌더링하며, ISR(점진적 재생성)된 페이지는 특정 조건에서 캐시를 업데이트하는 방식으로 동작한다.
Next.js의 Client-side Router Cache는 경로 세그먼트(Route Segment)의 RSC 데이터를 클라이언트 메모리에 저장하여 성능을 최적화하는 기능이다.
사용자가 네비게이션할 때, Next.js는 방문한 경로 세그먼트를 캐싱하고 사용자가 이동할 가능성이 높은 경로를 미리 가져온다.
| 구분 | Request Memoization | Data Cache | Full Route Cache | Client Router Cache |
|---|---|---|---|---|
| 설명 | 동일한 요청이 같은 렌더링 패스에서 중복 실행되지 않도록 메모이제이션 (자동 캐싱) | fetch() 요청을 캐싱하여 데이터 소스 요청을 줄임 | 정적으로 생성된 페이지(HTML, RSC Payload)를 서버에서 캐싱 | 클라이언트에서 네비게이션 시 경로 세그먼트를 캐싱 |
| 캐싱 위치 | 서버 메모리 (렌더링 중 유지됨) | 서버 (데이터 저장) | 서버 (페이지 저장) | 클라이언트 (브라우저 메모리) |
| 기본 캐싱 여부 | ✅ 기본 활성화 | ❌ (Next.js 15부터 no-store 기본값) | ✅ 기본 활성화 (정적 페이지) | ✅ 기본 활성화 (레이아웃, 로딩 상태) |
| 데이터 보존 기간 | 렌더링 종료 시 캐시 삭제 | 서버가 동작하는 동안 지속 | 배포 시 삭제되거나 재검증 시 업데이트 | 세션 동안 유지되며, 페이지 새로고침 시 삭제 |
| 캐시 무효화 방법 | 렌더링 패스가 끝나면 자동 삭제 | next.revalidate를 사용한 데이터 재검증 또는 cache: 'no-store' 설정 | 배포 또는 revalidatePath() 호출 | router.refresh(), cookies.set(), cookies.delete() 호출 |
| 적용 대상 | React 렌더링 단계 내에서 동일한 요청 | API 요청(fetch) 응답 데이터 | 정적으로 렌더링된 페이지 | 네비게이션 중 이전 및 예상되는 경로 |
| 주요 특징 | - 하나의 페이지를 렌더링하는 동안 같은 fetch() 요청을 중복 실행하지 않음 | - fetch() 호출을 캐싱하여 데이터 요청 최적화 - cache: 'force-cache' 설정 필요 | - 정적 페이지를 서버에서 캐싱하여 성능 최적화 - 배포 시 삭제됨 | - 네비게이션 성능 개선 - 브라우저 상태 및 React 상태 유지 |
📚 Next.js 공식문서
🎥 한 입 크기로 잘라먹는 Next.js