Caching
Next.js의 가장 핵심적인 기능 중 하나는 Caching입니다. Next.js는 다양한 캐싱 전략을 통해 페이지 로딩 속도를 향상시키고, 서버 부하를 줄이며, 효율적인 데이터 관리를 제공합니다.
💡 Next.js의 캐싱 방식
Next.js는 크게 두 가지 방식으로 캐싱을 수행합니다.
- Full Route Cache (빌드 시점 캐싱)
- Data Cache (요청 시점 캐싱)
이 두 가지 방식 fetch 함수를 기반으로 동작하며, Next.js의 fetch API는 브라우저의 fetch API를 확장한 기능을 제공합니다.
1️⃣ Full Route Cache
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을 최종적으로 생성하게 됩니다.
🔹 Full Route Cache 무효화 (Revalidation)
데이터가 변경되었거나 실시간 정보가 필요한 경우, revalidate 옵션을 통해 캐시를 무효화할 수 있습니다. (IRS)
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 }, // 60초마다 캐시 갱신
});
2️⃣ Data Cache
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();
3️⃣ Request Memoization
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(); // 두 번째 호출에서는 첫 번째 결과를 재사용
✔️ 이 기능은 서버 컴포넌트의 렌더링 과정에서만 유지되며, 요청이 완료되면 캐시는 초기화됩니다.
4️⃣ Next.js 캐싱 관련 주요 메서드
🔹 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;
}
✔️ generateStaticParams는 동적 경로를 미리 생성하여 정적 페이지로 빌드할 수 있도록 도와줍니다.
🔹 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)에서 활용됩니다.
✔️ 데이터 변경 시 전체 페이지를 갱신할 필요 없이 특정 경로만 갱신할 수 있어 효율적입니다.