'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로 감싸 스트리밍한다
- 무효화: 16부터
revalidateTag는 두 번째 인자로cacheLife프로필을 받는다. 무효화된 동안 낡은 값을 보여 주며 백그라운드에서 갱신한다. 내가 방금 바꾼 값을 바로 보여 줘야 하면 서버 액션 안에서updateTag를 쓴다
옵트아웃에서 옵트인으로
- 15까지(암묵적 캐싱): 식당에 앉으면 반찬이 다 깔리고, 안 먹을 건 "빼 주세요"라고 해야 했다. 14까지는
fetch가 알아서 캐시돼cache: 'no-store'를 찾아 넣어야 했고, 15에서도 라우트가 자동으로 정적 렌더됐다 - 16(명시적 캐싱): 시킨 것만 나온다. 예측할 수 있어서 의도치 않은 낡은 데이터 버그가 줄어든다
이전 모델의 네 계층은 Next.js 캐시 계층, 요청 하나 안에서만 사는 메모이제이션은 React.cache, 캐시 무효화 일반론은 읽기 캐시 전략.