재검증(Revalidating)

재검증(Revalidating)

재검증(revalidation)은 캐시된 데이터를 갱신하는 과정이에요. 캐시된 응답을 계속 빠르게 제공하면서도 콘텐츠가 신선하게 유지되게 해 주죠. Next.js에는 크게 두 가지 전략이 있어요. 정해진 시간 후 자동 갱신하는 시간 기반(time-based)과, 어떤 변경 뒤 수동으로 무효화하는 온디맨드(on-demand) 방식이에요.

출처: https://nextjs.org/docs/app/getting-started/revalidating

두 가지 재검증 전략

  • 시간 기반 재검증cacheLife를 써서 정해진 시간이 지나면 캐시된 데이터를 자동으로 새로고침해요.
  • 온디맨드 재검증 — 변경(뮤테이션) 후 revalidateTag, updateTag, revalidatePath로 캐시된 데이터를 수동으로 무효화해요.

cacheLife

cacheLife는 캐시된 데이터가 얼마나 오래 유효한지를 정해요. use cache 스코프 안에서 사용해요.

import { cacheLife } from 'next/cache'

export async function getProducts() {
  'use cache'
  cacheLife('hours')
  return db.query('SELECT * FROM products')
}

cacheLife는 프로파일 이름이나 커스텀 설정 객체를 받아요.

프로파일 stale revalidate expire
default 5m 15m never
seconds 30s 1s 60s
minutes 5m 1m 1h
hours 5m 1h 1d
days 5m 1d 1w
weeks 5m 1w 30d
max 5m 30d 1y

세밀한 제어가 필요하면 객체를 넘겨요.

'use cache'
cacheLife({
  stale: 3600, // 1시간 후 stale 로 간주
  revalidate: 7200, // 2시간 후 재검증
  expire: 86400, // 1일 후 만료
})

seconds 프로파일, revalidate: 0, 또는 expire가 5분 미만이면 캐시는 "단기 수명"으로 간주돼요. 단기 수명 캐시는 자동으로 프리렌더에서 제외되고 대신 동적 홀이 돼요.

cacheTag

cacheTag는 캐시된 데이터에 태그를 붙여서 온디맨드로 무효화할 수 있게 해줘요.

import { cacheTag } from 'next/cache'

export async function getProducts() {
  'use cache'
  cacheTag('products')
  return db.query('SELECT * FROM products')
}

태그가 붙으면 revalidateTagupdateTag로 캐시를 무효화할 수 있어요.

revalidateTag

revalidateTag는 stale-while-revalidate 시맨틱으로 태그 캐시를 무효화해요 — 신선한 콘텐츠가 백그라운드에서 로드되는 동안 옛 콘텐츠를 즉시 제공해요. 블로그 글·상품 카탈로그처럼 갱신에 약간의 지연이 허용되는 콘텐츠에 적합해요.

import { revalidateTag } from 'next/cache'

export async function updateUser(id: string) {
  // 데이터 변경
  revalidateTag('user', 'max') // 권장: stale-while-revalidate
}

같은 태그를 여러 함수에 재사용해 한 번에 모두 재검증할 수 있어요. revalidateTag는 Server Action이나 Route Handler에서 호출해요. 두 번째 인자는 신선한 콘텐츠가 생성되는 동안 옛 콘텐츠를 얼마나 오래 서빙할지 정해요. 'max'를 쓰면 가장 긴 stale 창을 줘요.

updateTag

updateTag는 read-your-own-writes 시나리오를 위해 캐시된 데이터를 즉시 만료시켜요 — 사용자가 옛 콘텐츠 대신 자신의 변경을 바로 볼 수 있게 해요. revalidateTag와 달리 Server Actions에서만 사용할 수 있어요.

updateTag revalidateTag
위치 Server Actions 전용 Server Actions와 Route Handlers
동작 캐시 즉시 만료 Stale-while-revalidate
용도 Read-your-own-writes (사용자가 자신의 변경을 봄) 백그라운드 새로고침 (약간의 지연 허용)
import { updateTag } from 'next/cache'
import { redirect } from 'next/navigation'

export async function createPost(formData: FormData) {
  const post = await db.post.create({
    data: {
      title: formData.get('title'),
      content: formData.get('content'),
    },
  })

  updateTag('posts')
  redirect(`/posts/${post.id}`)
}

revalidatePath

revalidatePath는 특정 라우트 경로의 모든 캐시된 데이터를 무효화해요. 어떤 태그가 연관됐는지 모르는 채 라우트를 재검증하고 싶을 때 써요.

import { revalidatePath } from 'next/cache'

export async function updateUser(id: string) {
  // 데이터 변경
  revalidatePath('/profile')
}

가능하면 태그 기반 재검증(revalidateTag/updateTag)을 경로 기반보다 선호해요. 더 정밀하고 과잉 무효화를 피할 수 있어요.

무엇을 캐시해야 하나

런타임 데이터에 의존하지 않고, 한동안 캐시에서 서빙해도 괜찮은 데이터를 캐시해요. use cachecacheLife로 그 동작을 표현하면 돼요. 시간 기반 재검증이 필요 없는 콘텐츠(CMS 데이터 등)는 cacheTag와 긴 cacheLife(예: max)를 써서 정적 셸에 유지하고, 콘텐츠가 바뀌면 웹훅 등으로 revalidateTag를 호출하게 설정해요.

더 알아보기