노트

"use cache"와 Cache Components

use cache Directive and Cache Components

프론트엔드#nextjs · 연결된 개념 11개

쉽게 말하면

'use cache'는 반찬이 알아서 다 깔리던 식당이 시킨 것만 나오는 식당으로 바뀐 것과 같아요. 캐시하고 싶은 함수나 컴포넌트에만 직접 표시를 붙여서, 의도치 않게 낡은 데이터가 보이는 일을 줄여요.

비유의 한계 주문서에 적은 내용이 곧 캐시 키예요. 함수 인자와 바깥에서 가져다 쓴 값이 자동으로 키가 되니 인자는 직렬화할 수 있어야 하고, 쿠키·헤더는 안에서 읽지 말고 바깥에서 인자로 넘겨요.

'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보다 길어야 한다
프로필stalerevalidateexpire
default(지정 안 함)5분15분없음
seconds30초1초1분
hours5분1시간1일
max5분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

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 브라우저 캐시

    한 번 받은 리소스(HTML·CSS·JS·이미지)를 브라우저가 저장해 두었다가 다시 쓰는 기능. 서버가 응답 헤더로 얼마나, 어떻게 캐시할지 알려 주고 브라우저가 그대로 따른다.

  • bfcache(뒤로·앞으로 가기 캐시)

    페이지를 떠날 때 DOM과 JS 힙까지 통째로 메모리에 얼려 두었다가, 뒤로·앞으로 가기에서 그대로 되살리는 브라우저 캐시.

  • Redis (인메모리 저장소)

    Redis는 데이터를 메모리에 두는 키-값 저장소(key-value store)다. 디스크 기반 DB보다 훨씬 빠르지만, 기본적으로 휘발성에 가깝게 다루는 것이 안전하다. 문자열뿐 아니라 리스트·해시·셋·정렬된 셋 같은 자료구조를 명령 하나로 다룬다.

  • Activity와 숨겨진 라우트 보존

    Activity는 React 19.2의 컴포넌트로, UI를 언마운트하지 않고 display: none으로 숨기면서 state와 DOM(Document Object Model)을 보존한다. 숨기는 동안 Effect는 정리(cleanup)된다. 예전 이름은 Offscreen이다.

  • CDN

    CDN(Content Delivery Network)은 세계 곳곳에 둔 서버(엣지, edge)들이 원본 서버(오리진, origin)의 응답을 복사해 두었다가 사용자와 가까운 곳에서 대신 내주는 네트워크다. 물리적 거리가 줄어 응답이 빨라지고, 오리진으로 가는 요청이 줄어 서버 부담도 줄어든다.

보기 옵션