'use cache'는 Next.js 16의 Cache Components 모델에서 async 함수나 컴포넌트의 반환값을 캐시하라고 명시하는 지시어(Directive)다. 아무것도 표시하지 않으면 매 요청 실행되고, 캐시하고 싶은 곳에만 직접 붙인다(옵트인, Opt-in).
import { cacheLife, cacheTag } from 'next/cache'
export async function getPromotions() {
'use cache'
cacheLife('hours') // 수명
cacheTag('promotions') // 무효화 단위 → revalidateTag('promotions', 'max')
return db.promotion.findMany()
}- 켜는 법:
next.config.ts에cacheComponents: true. 이 설정에서는 부분 사전 렌더링(PPR)이 기본 동작이 된다 - 붙이는 곳: 데이터 함수(데이터 단위 캐시), 컴포넌트·페이지(UI 단위 캐시), 파일 맨 위(모든 export)
- 캐시 키: 함수 인자와 바깥에서 가져다 쓴 값이 자동으로 키가 된다. 입력이 다르면 다른 항목이 생긴다. 그래서 인자는 직렬화할 수 있어야 한다
- 쿠키·헤더 같은 요청 시점 API(Application Programming Interface)는 캐시 함수 안에서 직접 읽지 않는다. 바깥(캐시하지 않는 컴포넌트)에서 값을 꺼내 인자로 넘긴다
- 매 요청 새로워야 하는 데이터는 캐시하지 말고 Suspense로 감싸 스트리밍한다
- 저장소:
cacheHandlers를 따로 두지 않으면 서버 프로세스마다 메모리 LRU 캐시에 담는다. 크기는cacheMaxMemorySize(기본 50MB, 2026 기준)로 정하고, 차면 가장 오래 안 쓴 항목부터 밀려난다(LRU 캐시와 캐시 계층 비교). 자체 호스팅에서는 요청 사이에 남지만 서버리스에서는 인스턴스가 바뀌어 거의 남지 않는다. 여러 인스턴스가 함께 쓰거나 재시작 뒤에도 남겨야 하면cacheHandlers로 Redis 같은 외부 저장소를 붙인다. 이때 방출 정책은 그 핸들러가 정한다 - 무효화: 16부터
revalidateTag는 두 번째 인자로cacheLife프로필을 받는다. 무효화된 동안 낡은 값을 보여 주며 백그라운드에서 갱신한다. 내가 방금 바꾼 값을 바로 보여 줘야 하면 서버 액션 안에서updateTag를 쓴다
cacheLife 프로필 (2026 기준)
cacheLife는 세 가지 시간으로 캐시 수명을 정한다.
stale: 클라이언트 라우터가 서버에 묻지 않고 캐시를 바로 쓰는 시간revalidate: 이 시간이 지난 뒤 요청이 오면 캐시를 먼저 내주고 서버가 백그라운드에서 다시 만든다(ISR과 비슷하다)expire: 이 시간 동안 요청이 없으면 다음 요청은 새 결과가 만들어질 때까지 기다린다.revalidate보다 길어야 한다
| 프로필 | stale | revalidate | expire |
|---|---|---|---|
default(지정 안 함) | 5분 | 15분 | 없음 |
seconds | 30초 | 1초 | 1분 |
hours | 5분 | 1시간 | 1일 |
max | 5분 | 30일 | 1년 |
이 밖에 minutes·days·weeks가 있고, next.config.ts에서 프로필을 새로 정의하거나 덮어쓸 수 있다.
- 중첩: 캐시된 컴포넌트가 다른 캐시된 함수를 쓰면, 바깥에 명시한
cacheLife가 안쪽보다 길든 짧든 우선한다. 바깥에 지정하지 않으면default가 적용되고, 안쪽 수명이 더 짧을 때만 바깥도 짧아진다. 그래서 문서는 모든'use cache'에cacheLife를 명시하라고 권한다 - 클라이언트 캐시 비우기: 서버 액션에서
revalidateTag·revalidatePath·updateTag·refresh를 부르면stale과 상관없이 클라이언트 캐시 전체가 즉시 비워진다
옵트아웃에서 옵트인으로
- 15까지(암묵적 캐싱): 식당에 앉으면 반찬이 다 깔리고, 안 먹을 건 "빼 주세요"라고 해야 했다. 14까지는
fetch가 알아서 캐시돼cache: 'no-store'를 찾아 넣어야 했고, 15에서도 라우트가 자동으로 정적 렌더됐다 - 16(명시적 캐싱): 시킨 것만 나온다. 예측할 수 있어서 의도치 않은 낡은 데이터 버그가 줄어든다
이전 모델의 네 계층은 Next.js 캐시 계층, 요청 하나 안에서만 사는 메모이제이션은 React.cache, 캐시 무효화 일반론은 읽기 캐시 전략.
출처: Next.js - Caching · Next.js - cacheLife · Next.js - use cache: Runtime caching considerations · Next.js - cacheHandlers · Next.js - cacheMaxMemorySize