캐시를 명시적으로 켜는 use cache와 그 대가
문제 발생
목록 페이지가 매 요청마다 같은 쿼리를 반복했습니다. 캐시를 켜려고 use cache를 붙였더니 빌드가 이런 오류로 멈췄습니다.
Error: Route used "cookies" inside "use cache".세션을 읽어 개인화된 부분이 그 안에 함께 있었기 때문입니다.
원인 분석
use cache는 순수 함수를 전제로 합니다. Next.js 문서는 이 디렉티브를 라우트·React 컴포넌트·함수를 캐시 가능하다고 표시하는 것으로 설명하고, 사용하려면 next.config.ts에 cacheComponents를 켜야 하며 캐시되는 함수와 컴포넌트는 async여야 한다고 적습니다.
캐시 키가 무엇인지도 명시돼 있습니다 — 빌드 ID, 함수 ID, 그리고 직렬화 가능한 인자입니다. 여기에 중요한 성질이 하나 더 있습니다 — 캐시된 함수가 바깥 스코프의 변수를 참조하면 그 변수들이 자동으로 캡처되어 인자로 바인딩되고, 캐시 키의 일부가 됩니다.
그래서 요청별 데이터는 금지됩니다. 문서가 분명히 합니다 — 캐시된 함수와 컴포넌트는 cookies(), headers(), searchParams 같은 런타임 API에 접근할 수 없으며, 그 제약은 호출 스택을 따라갑니다. 캐시된 함수가 부르는 헬퍼가 그걸 읽어도 같은 오류가 납니다.
해결 방안
- 요청 데이터는 캐시 밖에서 읽어 인자로 넘깁니다. 문서가 권장 패턴으로 제시하는 방식입니다.
export default async function Page() {
const theme = (await cookies()).get("theme")?.value ?? "light";
return <Feed theme={theme} />; // Feed 안에서 'use cache'
}- 수명을 호출 지점에 명시합니다.
cacheLife를 생략하면default프로필이 적용되고 수명이 코드에서 보이지 않습니다.
async function getPosts() {
"use cache";
cacheLife("hours");
return db.posts.findMany();
}-
무효화가 필요하면 태그를 답니다.
cacheTag로 표시하고 뮤테이션에서updateTag/revalidateTag로 무효화합니다. -
직렬화 제약을 확인합니다. 문서가 나열하듯 클래스 인스턴스, 함수(패스스루 제외), Symbol, URL 인스턴스는 인자로 쓸 수 없습니다.
-
자체 호스팅과 서버리스의 차이를 감안합니다. 문서 표 그대로 서버리스에서는 인메모리 캐시 항목이 요청 간에 유지되지 않는 것이 보통이고, 자체 호스팅에서는 유지됩니다. 이 저장소처럼 Docker로 직접 띄우는 구성은 후자입니다.
-
캐시 전략은 한 번에 정합니다. 이 저장소는 sitemap을
force-dynamic으로 되돌린 이력이 있습니다 — 캐시를 다시 켤 때는 그 결정과 함께 재검토합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.