캐싱 전략 총정리
Next.js는 성능 최적화를 위해 다양한 캐싱 메커니즘을 제공합니다. 이 글에서는 4가지 캐시의 동작 원리와 제어 방법을 알아봅니다.
캐싱 종류 한눈에 보기
섹션 제목: “캐싱 종류 한눈에 보기”| 종류 | 대상 | 장소 | 목적 | 기간 |
|---|---|---|---|---|
| Request Memoization | fetch 함수의 return 값 | 서버 | React Component tree에서 데이터의 재사용 | request 생명주기 |
| Data Cache | Data | 서버 | 유저 요청이나 deployment에 의해 저장된 데이터 | 영구적 |
| Full Route Cache | HTML, RSC Payload | 서버 | 렌더링 비용 감소 및 성능 향상 | 영구적 |
| Router Cache | RSC Payload | 클라이언트 | 네비게이션에 의한 서버 요청 감소 | 세션 또는 정해진 시간 동안 |
Request Memoization
섹션 제목: “Request Memoization”정확히 동일한 요청일 때 캐싱됩니다 (headers 구성이 다르면 캐싱 미발생).
fetch의 cache 프로퍼티
섹션 제목: “fetch의 cache 프로퍼티”// 가능한 한 캐싱fetch('url', { cache: 'force-cache' })
// 이 요청은 캐싱되지 않음 → 항상 새로운 데이터fetch('url', { cache: 'no-store' })fetch의 next.revalidate
섹션 제목: “fetch의 next.revalidate”fetch("url", { next: { revalidate: 60, // 60초마다 재검증 },});페이지 전체 revalidate 설정
섹션 제목: “페이지 전체 revalidate 설정”export const revalidate = 5; // 5초마다 재검증
export default function SomeComponent() { // ...}dynamic 옵션
섹션 제목: “dynamic 옵션”// 항상 재요청 (캐싱되지 않음)export const dynamic = "force-dynamic";noStore (컴포넌트 단위)
섹션 제목: “noStore (컴포넌트 단위)”페이지 내 일부 컴포넌트만 캐싱을 비활성화할 때 사용합니다.
import { unstable_noStore as noStore } from "next/cache";
export default async function Component() { noStore(); const result = await db.query(/* ... */); // ...}참고: Next.js 15에서
unstable_noStore는 deprecated되었습니다. 대신import { connection } from "next/server"를 사용하고, 컴포넌트 내에서await connection()을 호출합니다.
Full Route Cache
섹션 제목: “Full Route Cache”npm run build 시 사용됩니다. 동적 라우팅을 사용한 경우 동적 페이지로 빌드되나, 나머지는 가능한 한 정적 페이지로 빌드됩니다.
revalidatePath()
섹션 제목: “revalidatePath()”가능한 한 많은 부분을 캐싱하면서 필요할 때만 업데이트된 데이터를 얻을 수 있습니다.
import { revalidatePath } from "next/cache";
revalidatePath('/feed', 'page'); // 특정 페이지 재검증revalidatePath('/', 'layout'); // 모든 페이지 재검증revalidateTag()
섹션 제목: “revalidateTag()”fetch 요청에 태그를 추가하고, 태그 기반으로 캐시를 초기화합니다.
// 데이터 페칭 시 태그 추가fetch("url", { next: { tags: ["msg"] },});
// 데이터 변경 시 태그로 캐시 초기화async function updateHandler() { // ...데이터 변경 revalidateTag("msg"); redirect("/");}Custom Fetch (DB 직접 접근 시)
섹션 제목: “Custom Fetch (DB 직접 접근 시)”직접 데이터베이스에 접근하는 경우 fetch를 사용하지 않으므로, React의 cache 함수를 이용합니다.
React.cache()
섹션 제목: “React.cache()”import { cache } from "react";
export const getMsg = cache(function getMsg() { return db.collection.find("doc");});React.cache() 주의사항
섹션 제목: “React.cache() 주의사항”Object.is로 얕은 비교를 하므로, 인라인 객체를 인자로 전달하면 항상 cache miss가 발생합니다.
// Anti-pattern: 매번 새 객체 → cache missconst getUser = cache(async (params) => { return await db.user.findUnique({ where: { id: params.uid } });});getUser({ uid: 1 });getUser({ uid: 1 }); // 다시 쿼리 실행
// 올바른 패턴: 원시값을 인자로 사용const getUser = cache(async (uid) => { return await db.user.findUnique({ where: { id: uid } });});getUser(1);getUser(1); // cache hit!- Next.js의
fetch는 자동으로 요청 중복 제거가 적용되므로React.cache()래핑이 불필요 - DB 쿼리, 인증 확인, 파일시스템 작업 등 fetch가 아닌 비동기 작업에는
React.cache()가 필수적
unstable_cache + React.cache 조합
섹션 제목: “unstable_cache + React.cache 조합”import { unstable_cache as nextCache } from "next/cache";import { cache } from "react";
export const getMsg = nextCache( cache(function getMsg() { // 요청을 cache return db.collection.find("doc"); }), ["message"], // cache를 구분하기 위한 키 { tags: ["msg"] } // revalidateTag용 태그);
function someUpdateFunc() { // ...데이터 변경 revalidateTag("msg"); // msg 태그의 cache 초기화}참고:
unstable_cache는 Next.js 15에서use cache지시문으로 대체되었습니다.
Cross-Request LRU 캐싱
섹션 제목: “Cross-Request LRU 캐싱”React.cache()는 단일 요청 내에서만 작동합니다. 여러 요청에 걸쳐 데이터를 공유하려면 LRU 캐시를 사용합니다.
import { LRUCache } from "lru-cache";
const cache = new LRUCache({ max: 1000, ttl: 5 * 60 * 1000, // 5분});
export async function getUser(id) { const cached = cache.get(id); if (cached) return cached;
const user = await db.user.findUnique({ where: { id } }); cache.set(id, user); return user;}- Vercel Fluid Compute 환경에서는 동일 인스턴스를 여러 요청이 공유하므로 LRU 캐시가 효과적
- 전통적인 serverless 환경에서는 Redis 같은 외부 캐시를 고려
| 캐시 종류 | 제어 방법 |
|---|---|
| Request Memoization | cache: 'no-store', noStore() |
| Data Cache | revalidate, revalidateTag() |
| Full Route Cache | revalidatePath(), dynamic |
| Router Cache | 클라이언트 네비게이션 시 자동 |
| Custom (DB 등) | React.cache(), unstable_cache |
다음 글에서는 데이터 페칭과 Server Action을 살펴봅니다.