본문 바로가기
C.W.K.
Stream
Lesson 04 of 09 · published

변경 직후 캐시 무효화하기

~22 min · revalidatePath, revalidateTag, Server Actions

Level 0호기심
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

시간을 기다릴 수 없는 변경이 있어

시간 기반 ISR만 쓰면 편집자가 글을 고친 뒤에도 재검증 창이 끝날 때까지 옛 내용을 볼 수 있어. 요청형 재검증은 변경이 성공한 바로 그 순간 관련 캐시를 무효화해. 데이터 변경과 화면 갱신을 하나의 흐름으로 묶는 거야.

함수무효화 범위
revalidatePath(path, type?)특정 경로. 'page'는 해당 잎만, 'layout'은 그 아래까지 포함해.
revalidateTag(tag)fetch(..., { next: { tags } })로 같은 태그를 붙인 모든 요청을 찾아.

경로와 태그의 관점이 달라

경로 무효화는 “이 화면을 다시 만들어라”에 가깝고, 태그 무효화는 “이 데이터에 기대는 모든 화면을 새로 읽어라”에 가까워. 같은 글이 목록과 상세 화면에 함께 나온다면 태그가 중복 경로를 나열하는 것보다 안전해.

변경이 일어나는 자리에서만 호출해

두 함수는 서버 액션과 라우트 핸들러에서 호출해야 해. Next.js 15는 렌더링 도중 호출하면 오류를 내. 캐시 무효화는 데이터 변경의 부수 효과이지, 같은 입력으로 같은 UI를 만들어야 하는 순수 렌더링의 책임이 아니기 때문이야.

변경 작업마다 어떤 읽기 결과가 영향을 받는지 데이터 이름으로 적어. 글 하나를 고치면 상세·목록·작성자 페이지가 함께 바뀔 수 있으니 공통 post:<id>와 목록 태그를 설계해. 무효화는 DB 쓰기가 성공한 뒤에만 실행하고, 실패했을 때 캐시만 먼저 지우지 않게 해.

큰 태그 하나로 모든 콘텐츠를 무효화하면 구현은 쉬워도 작은 변경마다 전체 캐시가 사라져. 반대로 ID 태그만 쓰면 목록 집계가 오래될 수 있어. 데이터 의존 그래프를 반영한 몇 개의 안정된 태그가 경로를 끝없이 나열하는 것보다 오래 살아. 한 글을 목록·상세·작성자 화면에서 공유하고 path와 tag 무효화를 각각 적용해 봐. 변경 직후 세 화면이 언제 새 값을 읽는지 비교하고, 렌더 도중 함수를 호출했을 때 Next.js 15가 오류를 내는지도 테스트로 고정해.

삭제와 생성도 같은 태그 정책을 거쳐야 해. 수정만 시험하면 목록에 새 항목이 안 나타나거나 삭제한 상세가 캐시에 남는 구멍을 놓쳐. 데이터 수명 주기의 모든 변경을 한 무효화 표에 넣어.

Code

변경 뒤 경로 무효화하기·tsx
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';

export async function publishPost(formData: FormData) {
  const title = formData.get('title') as string;
  const slug = await db.post.create({ data: { title } });

  revalidatePath('/blog');                     // 목록
  revalidatePath(`/blog/${slug}`, 'page');     // 특정 글
  revalidatePath('/blog', 'layout');           // /blog 아래 전부
}
태그 기반 무효화·tsx
// fetch 안에서:
const posts = await fetch('https://api.example.com/posts', {
  next: { tags: ['posts'] },
}).then(r => r.json());

// 액션 안에서:
import { revalidateTag } from 'next/cache';

export async function createPost(formData: FormData) {
  'use server';
  await db.post.create({ data: {/* … */} });
  revalidateTag('posts'); // 'posts' 태그 모든 fetch 무효화
}

External links

Exercise

글을 수정한 뒤 revalidatePath를 호출하는 서버 액션을 만들어. 목록이 시간 간격을 기다리지 않고 다음 요청에서 새 값을 보여 주는지 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.