Next.js의 가장 핵심적인 기능 중 하나는 Caching입니다. Next.js는 다양한 캐싱 전략을 통해 페이지 로딩 속도를 향상시키고, 서버 부하를 줄이며, 효율적인 데이터 관리를 제공합니다.
💡 Next.js의 캐싱 방식
Next.js는 크게 두 가지 방식으로 캐싱을 수행합니다.
- Full Route Cache (빌드 시점 캐싱)
- Data Cache (요청 시점 캐싱)
이 두 가지 방식 fetch
함수를 기반으로 동작하며, Next.js의 fetch
API는 브라우저의 fetch
API를 확장한 기능을 제공합니다.
Next.js는 빌드 시점에 페이지를 렌더링하고 그 결과를 캐싱하는 Full Route Cache 기능을 제공합니다. 이 방식은 SSG(Static Site Generation)와 함께 동작하며, 미리 생성된 HTML과 데이터를 빠르게 제공하여 페이지 로딩 속도를 최적화합니다.
React Server Component Payload 생성
서버에서 React Server Component를 렌더링 하고, 스트리밍에 최적화된 데이터 형식으로 변환합니다.
HTML 생성
Next.js는 생성된 *Payload와 클라이언트 컴포넌트의 JavaScript를 결합하여 최종 HTML을 완성합니다.
캐싱 적용
빌드 시점에 생성된 HTML이 CDN에 배포되고, 이후 동일한 페이지 요청이 발생하면 캐시된 HTML을 반환합니다.
💡 Payload
Next.js에서 React Server Component Payload 는 서버에서 렌더링된 컴포넌트의 데이터와 상태 정보를 포함한 특별한 형식의 데이터입니다. 이 데이터를 기반으로 클라이언트에서 HTML을 최종적으로 생성하게 됩니다.
데이터가 변경되었거나 실시간 정보가 필요한 경우, revalidate
옵션을 통해 캐시를 무효화할 수 있습니다. (IRS)
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 }, // 60초마다 캐시 갱신
});
Data Cache는 fetch
요청을 통해 가져온 API 데이터를 캐싱하는 방식입니다. 이 방식은 API 요청 수를 줄이고 응답 속도를 높이기 위해 사용됩니다.
fetch
함수를 사용하면 자동으로 데이터가 캐싱
fetch는 cache
옵션 값을 지정하지 않으면 기본적으로 { cache: 'force-cache' }
입니다.
캐싱된 데이터는 revalidate
옵션을 통해 일정 시간마다 갱신할 수 있음
{ cache: 'no-store' }
옵션을 사용하면 캐잇 없이 항상 새로운 데이터를 가져옴
// default
const res = await fetch('https://api.example.com/data'); // { cache: "force-cache" }와 동일
const data = await res.json();
// 데이터 캐싱 (3600초 동안 유지)
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 },
});
const data = await res.json();
// 캐싱 없이 항상 새로운 데이터를 가져오기
const res = await fetch('https://api.example.com/data', {
cache: 'no-store',
});
const data = await res.json();
Request Memoization은 서버 컴포넌트 렌더링 중 동일한 fetch
요청이 여러 번 발생해도, 실제 네트워크 요청은 한 번만 실행되도록 최적화하는 기능입니다.
async function getCommonData() {
const res = await fetch('https://api.example.com/common-data');
return res.json();
}
const dataA = await getCommonData();
const dataB = await getCommonData(); // 두 번째 호출에서는 첫 번째 결과를 재사용
✔️ 이 기능은 서버 컴포넌트의 렌더링 과정에서만 유지되며, 요청이 완료되면 캐시는 초기화됩니다.
generateStaticParams
정적 경로를 생성하여 빌드 시점에 캐싱을 적용할 수 있습니다.
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then((res) => res.json());
return posts.map((post) => ({ slug: post.slug }));
}
export default function Page({ params }) {
const { slug } = params;
}
✔️ generateStaticParam
s는 동적 경로를 미리 생성하여 정적 페이지로 빌드할 수 있도록 도와줍니다.
tags
특정 데이터에 태그를 부여하여 캐시를 관리할 수 있습니다.
fetch('https://api.example.com/products', {
next: {
revalidate: 60, // 60초마다 갱신
tags: ['product', 'category'],
},
});
✔️ 태그를 활용하면 특정 데이터 그룹을 손쉽게 캐시 무효롸할 수 있습니다.
revalidate
ISR(Incremental Static Regeneration) 방식으로 캐시를 일정 주기로 갱신합니다.
fetch('/api/data', {
next: { revalidate: 10 },
});
✔️ revalidate
를 사용하면 페이지나 데이터의 자동 갱신 주기를 설정할 수 있습니다.
revalidateTags
태그를 기반으로 특정 데이터를 무효화하고 갱신합니다.
import { revalidateTag } from 'next/cache';
async function updateProduct() {
revalidateTag('product');
return { message: 'Product data updated' };
}
✔️ revalidateTag
는 특정 태그에 연결된 데이터를 즉시 갱신할 때 유용합니다.
revalidatePath
특정 경로의 Full Route Cache를 무효화하고 갱신할 수 있습니다.
import { revalidatePath } from 'next/cache';
async function updatePage() {
revalidatePath('/products');
return { message: 'Product page cache updated' };
}
✔️ revalidatePath('/경로')
를 사용하면 해당 경로의 정적 페이지가 다시 생성됩니다.
✔️ 주로 SSG(Static Site Generation) 및 ISR(Incremental Static Regeneration)에서 활용됩니다.
✔️ 데이터 변경 시 전체 페이지를 갱신할 필요 없이 특정 경로만 갱신할 수 있어 효율적입니다.