본문 바로가기
Q&A2026년 8월 7일1분 읽기

App Router에서 fetch가 계속 캐시된 옛날 데이터를 주는데 no-store를 넣어도 그대로예요

n
next_taeyang
조회 584댓글 3

Next 14 App Router로 대시보드를 만들고 있는데요, 서버 컴포넌트에서 API를 호출해서 그리는 페이지가 자꾸 옛날 데이터를 보여줍니다. 관리자가 값을 바꿔도 이 페이지엔 반영이 안 되고, 심지어 재배포를 해야 그때 값으로 한 번 갱신되고 또 고정돼요.

캐시 문제 같아서 fetch에 옵션을 줬습니다.

const res = await fetch(`${API}/stats`, { cache: 'no-store' })

이렇게 no-store를 넣었는데도 여전히 옛날 값이에요. 로컬 dev에선 잘 바뀌는데 배포(build) 후에만 이럽니다. 뭔가 캐시 레이어가 여러 개 있는 것 같은데 감이 안 잡히네요.

댓글 3

김김영삼2개월 전
"배포 후에만, 재배포해야 갱신"이 결정적 단서예요. 그 페이지가 정적으로(빌드 타임에) 렌더돼서 HTML이 통째로 고정된 겁니다. Next App Router엔 캐시 레이어가 여러 층인데, fetch의 no-store는 그중 데이터 캐시만 끄는 거고, 페이지 자체가 정적으로 판정되는 Full Route Cache는 별개예요. 라우트가 정적이면 빌드 때 그린 결과가 그대로 서빙돼서 fetch 옵션이 뭐든 재배포 전까진 안 바뀝니다.

그래서 이 라우트를 동적 렌더로 강제해야 해요. 해당 page.tsx 맨 위에 export const dynamic = 'force-dynamic'을 넣으세요. 그럼 매 요청마다 서버에서 새로 렌더합니다. 값이 자주 안 바뀌면 force-dynamic 대신 export const revalidate = 60처럼 초 단위 재검증(ISR)을 걸어도 되고요. 관리자 액션 직후 즉시 반영이 필요하면 그 액션에서 revalidatePath로 콕 집어 무효화하는 방법도 있습니다.

하나 더 확인할 게 있는데, 혹시 이 stats 호출을 fetch가 아니라 axios 같은 라이브러리로 하고 계신 건 아니죠? Next의 캐시/no-store는 자기네 확장된 fetch에만 걸리는 거라, axios로 부르면 no-store고 뭐고 Next가 관여를 안 해요. 그래서 라우트 정적 판정이랑 겹치면 지금 증상이 딱 나옵니다.
nnext_taeyang(guest)2개월 전
헉 맞아요 저 stats 호출은 axios로 하고 있어요. 사내 공통 axios 인스턴스에 인터셉터가 붙어 있어서 그거 재활용하려고요. 그럼 no-store가 애초에 안 먹었던 거네요. 이 경우엔 그냥 라우트에 force-dynamic만 걸면 되는 건가요, 아니면 axios 부분도 뭘 바꿔야 하나요? 인터셉터 때문에 fetch로 다 바꾸긴 좀 부담스러워서요.
김김영삼2개월 전
네 그게 원인이었네요. 정리하면 캐시 제어를 하는 주체가 두 갈래예요. 하나는 라우트가 정적이냐 동적이냐(Full Route Cache), 다른 하나는 개별 데이터 요청 캐시(Data Cache)인데, axios는 후자에 아예 못 들어갑니다.

axios를 유지하실 거면 데이터 캐시는 포기하고 라우트 레벨로만 제어하세요. 즉 page에 force-dynamic(또는 revalidate)을 걸면 매 요청마다 서버 렌더가 돌면서 axios 호출도 그때그때 새로 나갑니다. 이 조합이면 axios 코드는 안 건드려도 됩니다. 관리자 변경 즉시 반영까지 원하면 그 변경 처리하는 곳에서 revalidatePath로 이 경로를 무효화해주면 되고요. 데이터 캐시의 세밀한 재검증(태그 기반 revalidate 같은)까지 쓰고 싶어질 때 그때 fetch로 옮기는 걸 검토하시면 됩니다.
Ctrl+Enter로 등록