๐Ÿงจ Next.js ์‹ฌํ™” 3์žฅ: Cache Invalidation โ€” ํ™”์„์„ ๋ฌดํšจํ™”ํ•˜๋Š” ์ฒ ํ‡ด

2026๋…„ 4์›” 30์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

revalidatePath, revalidateTag, on-demand ISR๋กœ ์บ์‹œ๋ฅผ ์ •๋ฐ€ํ•˜๊ฒŒ ๋ฌดํšจํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 15๋ถ„ (์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 8๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ์˜์ฒ (์‹ ์ž…): "๊ฒŒ์‹œ๋ฌผ ์ˆ˜์ • ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ  ์„œ๋ฒ„ API์—์„œ DB ์—…๋ฐ์ดํŠธ๊นŒ์ง€ ์„ฑ๊ณตํ–ˆ๋Š”๋ฐ, window.location.reload()๋ฅผ ํ•ด๋„ ์˜ˆ์ „ ๊ธ€์”จ๋งŒ ๋– ์š”. ์ด์ œ ์„œ๋ฒ„ ์บ์‹œ๋ฅผ ๊ฐฑ์‹ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฑด ์•Œ๊ฒ ๋Š”๋ฐ, ์–ด๋–ค API๋ฅผ ๊ณจ๋ผ์•ผ ํ• ๊นŒ์š”?"
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "์ข‹์•„์š”. ์ด์ œ ์งˆ๋ฌธ์ด ํ›จ์”ฌ ์ •ํ™•ํ•ด์กŒ๋„ค์š”. ๊ฒฝ๋กœ๊ฐ€ ๋‚ก์•˜์œผ๋ฉด revalidatePath, ๋ฐ์ดํ„ฐ ์ •์ฒด์„ฑ์ด ๋‚ก์•˜์œผ๋ฉด revalidateTag๋‚˜ updateTag๋ฅผ ๊ฒ€ํ† ํ•˜์„ธ์š”. ๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ์€ ์„œ๋ฒ„ ์บ์‹œ ์ •์ฑ…์„ ๋ฐ”๊พธ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค."

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
์ •์  ์บ์‹œ ๋ฌดํšจํ™”์˜ ํ•„์š”์„ฑ โ†’ ๊ฒฝ๋กœ ๋‹จ์œ„ ๊ฐฑ์‹  โ†’ ํƒœ๊ทธ ๊ธฐ๋ฐ˜ ๊ฐฑ์‹  โ†’ ์‹ค๋ฌด ์•„ํ‚คํ…์ฒ˜

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • CMS/์–ด๋“œ๋ฏผ ์‚ฌ์ดํŠธ์—์„œ [์ €์žฅ] ๋ฒ„ํŠผ์ด ๋ˆŒ๋ฆฌ๋Š” ์ฆ‰์‹œ, ํผ๋ธ”๋ฆญ ์œ ์ €๋“ค์ด ๋ณด๋Š” ๋ฉ”์ธ ์ •์  ์›น์‚ฌ์ดํŠธ์˜ ํŠน์ • ์บ์‹œ๋งŒ ์šฐ์•„ํ•˜๊ฒŒ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ๋Š” ์›นํ›…(Webhook) ์„œ๋ฒ„ API๋ฅผ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฒฝ๋กœ ๋‹จ์œ„ revalidatePath์™€ ๋ฐ์ดํ„ฐ ๋‹จ์œ„ revalidateTag/updateTag๋ฅผ ๊ตฌ๋ถ„ํ•ด, ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์— ํฉ์–ด์ง„ ๋™์ผ ๋ฐ์ดํ„ฐ์˜ Invalidation์„ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€

Next.js App Router ์•„ํ‚คํ…์ฒ˜๋Š” ๊ทน๋‹จ์ ์ธ ์บ์‹ฑ(Caching)์„ ์ „์ œ๋กœ ๋งŒ๋“ค์–ด์กŒ์–ด. ์‹ฌํ™” 1/2์žฅ์—์„œ ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์žฅ ํšจ์œจ์ ์œผ๋กœ ํ‰์ƒ ์–ผ๋ ค๋ฒ„๋ฆด ์ˆ˜ ์žˆ์„๊นŒ๋ฅผ ๋ฐฐ์› ๋‹ค๋ฉด, ์ง€๊ธˆ๋ถ€ํ„ฐ๋Š” ๊ทธ ์–ผ์Œ์„ "๋‚ด๊ฐ€ ์›ํ•  ๋•Œ, ์›ํ•˜๋Š” ๋ถ€๋ถ„๋งŒ" ๊นจ๋Š” ๋ฒ•(Invalidation)์„ ๋ฐฐ์›Œ์•ผ ํ•ด.

๋ฐ์ดํ„ฐ ์ƒ์„ฑ(C), ์ˆ˜์ •(U), ์‚ญ์ œ(D) ์•ก์…˜์ด ์ผ์–ด๋‚ฌ์„ ๋•Œ, ๊ด€๋ จ ํ™”๋ฉด์˜ ์บ์‹œ ์บ์‹œ๋ฅผ ๊ฐฑ์‹ ํ•˜์ง€ ๋ชปํ•˜๋ฉด ์•ฑ์€ ์ˆœ์‹๊ฐ„์— ๋‚ก์€ ์ •๋ณด(Stale Data) ๋งŒ ๋ฑ‰์–ด๋‚ด๋Š” ๋‚ก์€ ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๊ฒŒ ๋ผ. ํ”„๋ก ํŠธ์—”๋“œ์˜ ์ฒด๊ฐ ํ€„๋ฆฌํ‹ฐ์˜ 90%๋Š” ์ด "์ˆ˜์ •ํ•œ ๊ฒŒ ๋ฐ”๋กœ๋ฐ”๋กœ ํ™”๋ฉด์— ๋ฐ˜์˜๋˜๋Š”๊ฐ€?" ์—์„œ ํŒ๊ฐ€๋ฆ„ ๋‚˜์ง€. ์ด๊ฒƒ์ด ๋ฐ”๋กœ ์‹œ๋‹ˆ์–ด์˜ ์บ์‹ฑ ํ†ต์ œ๋ ฅ์ด์•ผ.


๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด? (์น ํŒ ์ง€์šฐ๊ธฐ)

์„ ์ƒ๋‹˜(Next.js)์ด ์น ํŒ์— ์˜ค๋Š˜ ๊ธ‰์‹ ๋ฉ”๋‰ด(Data Cache)๋ฅผ ์ž”๋œฉ ์ ์–ด๋†จ์–ด. ์•„์ด๋“ค์€ ๊ทธ๊ฑธ ๋ณด๊ณ  ํ™˜ํ˜ธํ•˜์ง€.
์˜์ฒ ์ด๊ฐ€ ๊ต๋ฌด์‹ค์— ๊ฐ€์„œ ๊ธ‰์‹ ๋ฉ”๋‰ด๋ฅผ "์ œ์œก๋ณถ์Œ"์—์„œ "๋ˆ๊นŒ์Šค"๋กœ ๋ฐ”๊ฟจ์–ด(DB ์—…๋ฐ์ดํŠธ).

โŒ ๊ณผ๊ฑฐ (๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ)
์˜์ฒ ์ด๊ฐ€ ๋ณต๋„์—์„œ ์•„๋ฌด๋ฆฌ "์ƒˆ๋กœ๊ณ ์นจ! ๋ˆ๊นŒ์Šค์•ผ!" ์†Œ๋ฆฌ์ณ๋ด์•ผ, ์„ ์ƒ๋‹˜์˜ ์น ํŒ์—๋Š” ์—ฌ์ „ํžˆ ์ œ์œก๋ณถ์Œ์ด ์ ํ˜€์žˆ์–ด.

โœ… ํ˜„์žฌ (revalidate ๋ฌดํšจํ™” ๋ช…๋ น)
์˜์ฒ ์ด๊ฐ€ ์„ ์ƒ๋‹˜ ๊ท€์— ๋Œ€๊ณ  "์„ ์ƒ๋‹˜, ์ €๊ธฐ 2๋ฒˆ์งธ ์ค„(๊ฒฝ๋กœ/ํƒœ๊ทธ) ๊ธ‰์‹ ๋ฉ”๋‰ด ๋‚ก์•˜์–ด์š”, ์ข€ ์ง€์›Œ์ฃผ์„ธ์š”!" ๋ผ๊ณ  ์„œ๋ฒ„ ๋‚ด๋ถ€ ๋ช…๋ น๋ง์œผ๋กœ ์†์‚ญ์—ฌ.
์„ ์ƒ๋‹˜์ด ๊ทธ์ œ์•ผ "์–ด์ฟ !" ํ•˜๊ณ  ์ œ์œก๋ณถ์Œ์„ ์ง€์›Œ๋ฒ„๋ ค(Cache Purge). ๋‹ค์Œ ์•„์ด๊ฐ€ ์น ํŒ์„ ๋ดค์„ ๋• ์ œ์œก์ด ์—†์œผ๋‹ˆ ์š”๋ฆฌ์‚ฌํ•œํ…Œ ๋›ฐ์–ด๊ฐ€ "์ƒˆ ๋ฉ”๋‰ด ์ค˜!" ํ•˜๊ณ  ๋ˆ๊นŒ์Šค ๋ฐ์ดํ„ฐ๋ฅผ ์ง„์ •์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ฒŒ ๋˜๋Š” ๊ฑฐ์•ผ.


๐Ÿงฉ revalidatePath: ๊ฒฝ๋กœ ๋‹จ์œ„๋กœ ๋„“๊ฒŒ ๊ฐฑ์‹ ํ•˜๊ธฐ ๐ŸŸข

์ œ์ผ ์ง๊ด€์ ์ธ ๊ฒฝ๋กœ ๊ธฐ์ค€ ๋ฌดํšจํ™” ๋ฐฉ์‹์ด์•ผ.
"ํŠน์ • ์ฃผ์†Œ(๊ฒฝ๋กœ) ์•„๋ž˜์˜ ๋ Œ๋”๋ง ๊ฒฐ๊ณผ์™€ ๊ด€๋ จ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚ก์•˜์œผ๋‹ˆ ๋‹ค์‹œ ๊ฒ€์ฆํ•ด๋ผ" ๋ผ๊ณ  ์„œ๋ฒ„์— ์•Œ๋ ค์ค€๋‹ค.

์‚ฌ์šฉ๋ฒ•

์ฃผ๋กœ ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ/์ˆ˜์ • API(Route Handler app/api/... ๋‚ด๋ถ€)๋‚˜, ๋‹ค์Œ ์žฅ์—์„œ ๋ฐฐ์šธ ์„œ๋ฒ„ ์•ก์…˜ ๋‹จ์—์„œ ์‚ฌ์šฉ ๋ผ.

import { revalidatePath } from 'next/cache'
 
// ์œ ์ €๊ฐ€ ์ƒํ’ˆ์„ ๊ตฌ๋งคํ•œ ์งํ›„ ์‹คํ–‰๋˜๋Š” ์„œ๋ฒ„ ๋กœ์ง์ด๋ผ ๊ฐ€์ •
export async function POST() {
  await db.purchaseItem(...) // 1. DB ์—…๋ฐ์ดํŠธ (์ˆ˜๋Ÿ‰ ๊ฐ์†Œ)
 
  // 2. "/shop" ๊ฒฝ๋กœ์™€ ๊ด€๋ จ๋œ ์บ์‹œ๋ฅผ ๋‹ค์‹œ ๊ฒ€์ฆ ๋Œ€์ƒ์œผ๋กœ ํ‘œ์‹œํ•œ๋‹ค.
  revalidatePath('/shop')
 
  return Response.json({ success: true })
}

์žฅ๋‹จ์ 

  • ์žฅ์ : ๋งค์šฐ ์ง๊ด€์ ์ž„. "๊ทธ๋ƒฅ ๊ทธ ํŽ˜์ด์ง€ ๋‚ก์€ ๊ฑฐ ๋‹ค ์‹น ์ง€์›Œ!"
  • ๋‹จ์ : ๋งŒ์•ฝ /shop ํŽ˜์ด์ง€ ์•ˆ์— ์•„๋ฌด ์ž˜๋ชป ์—†๋Š”(์•ˆ ๋ฐ”๋€) ๊ณต์ง€์‚ฌํ•ญ์ด๋‚˜ ๋ฐฐ๋„ˆ ์บ์‹œ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๋ชจ์กฐ๋ฆฌ ๊ฐ™์ด ๋‚ ์•„๊ฐ€ ๋ฒ„๋ฆผ.
    ๊ทธ๋ฆฌ๊ณ  ๋” ์ค‘์š”ํ•œ ๊ฑด, /shop์—๋„ ์ƒํ’ˆ ๋ชฉ๋ก์ด ์žˆ๊ณ  /mypage/recent์—๋„ ๊ฐ™์€ ์ƒํ’ˆ ๋ชฉ๋ก์ด ์žˆ๋‹ค๋ฉด ๊ฒฝ๋กœ๋ฅผ ๋น ๋œจ๋ฆฌ๊ธฐ ์‰ฝ๋‹ค๋Š” ์ ์ด์•ผ. ์ด๋•Œ๋Š” ๋ฐ์ดํ„ฐ์˜ ์ •์ฒด์„ฑ์— ์ด๋ฆ„ํ‘œ๋ฅผ ๋ถ™์ด๋Š” ํƒœ๊ทธ ๊ธฐ๋ฐ˜ ์ „๋žต์ด ๋” ์•ˆ์ „ํ•˜๋‹ค.

๐ŸŒฑ revalidateTag: ์šฐ์•„ํ•˜๊ณ  ์ •๋ฐ€ํ•œ ์ €๊ฒฉ์ด ๐ŸŸก

Next.js ์ฝ”์–ด ํŒ€์ด ๊ฐ€์žฅ ๋ฏธ๋Š” ์šฐ์•„ํ•˜๊ณ  ์™„๋ฒฝํ•œ ํ•ด๊ฒฐ์ฑ…์ด์•ผ.
"์•„๋ฌด๋ฆฌ ์—ฌ๋Ÿฌ ํŒŒ์ผ๊ณผ ์—ฌ๋Ÿฌ ์ฃผ์†Œ์— ํฉ์–ด์ ธ ์žˆ์–ด๋„, ๊ฐ™์€ '์ด๋ฆ„ํ‘œ(Tag)'๋ฅผ ๋‹ฌ๊ณ  ์žˆ๋Š” ์บ์‹œ๋“ค๋งŒ ๊ณจ๋ผ ๊ฐฑ์‹ ํ•œ๋‹ค!"

1) ๋ช…์ฐฐ ๋ถ™์ด๊ธฐ (๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ)

์ด๊ฑด page.tsx๋“  ์–ด๋””๋“  fetch๋ฅผ ํ•˜๋Š” ๋Œ€์ƒ์—๊ฒŒ ๊ผฌ๋ฆฌํ‘œ๋ฅผ ๋‹ฌ์•„์ฃผ๋Š” ๊ฑฐ์•ผ.

// app/shop/page.tsx
const res = await fetch('https://api.my.com/bestseller', {
  next: { tags: ['bestseller-list'] } // ๐Ÿท๏ธ ์ด ํ†ต์‹  ๊ฒฐ๊ณผ๋ฌผ ๋ฉ์–ด๋ฆฌ ์บ์‹œ์— 'bestseller-list'๋ผ๋Š” ์ด๋งˆํ‘œ๋ฅผ ๋ถ™์ธ๋‹ค!
})
// app/mypage/page.tsx
const res2 = await fetch('https://api.my.com/bestseller?limit=3', {
  next: { tags: ['bestseller-list'] } // ๐Ÿท๏ธ ์™„์ „ ๋‹ค๋ฅธ ํŽ˜์ด์ง€์ธ๋ฐ ์—ฌ๊ธฐ๋„ ๊ฐ™์€ ์ด๋งˆํ‘œ๋ฅผ ๋ถ™์ธ๋‹ค!
})

2) ํƒœ๊ทธ ๊ฐฑ์‹  (๋ฐ์ดํ„ฐ๊ฐ€ ์ˆ˜์ •๋  ๋•Œ)

์–ด๋“œ๋ฏผ ์„œ๋ฒ„์—์„œ ๋ฒ ์ŠคํŠธ์…€๋Ÿฌ ์ƒํ’ˆ 1๊ฐœ๋ฅผ ๊ฐ•์ œ๋กœ ๊ต์ฒด(์ˆ˜์ •)ํ–ˆ์–ด. ์ด๋•Œ ์›นํ›…(Webhook) ์„œ๋ฒ„์—์„œ ๋‹จ ํ•œ ๋ฐœ์˜ ์ด์•Œ๋งŒ ์˜๋ฉด ๋ผ.

import { revalidateTag } from 'next/cache'
 
export async function POST() {
  await db.updateBestseller(...)
 
  // stale-while-revalidate ๋ฐฉ์‹์œผ๋กœ ํƒœ๊ทธ๊ฐ€ ๋ถ™์€ ์บ์‹œ๋ฅผ ๊ฐฑ์‹ ํ•œ๋‹ค.
  revalidateTag('bestseller-list', 'max')
 
  return Response.json({ success: true })
}

์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฉ๊ธˆ ์ˆ˜์ •ํ•œ ๊ฐ’์„ ์ฆ‰์‹œ ๋ด์•ผ ํ•˜๋Š” Server Action ํ๋ฆ„์ด๋ผ๋ฉด updateTag('bestseller-list')๊ฐ€ ๋” ์–ด์šธ๋ฆด ์ˆ˜ ์žˆ๋‹ค. revalidateTag('tag', 'max')๋Š” ๋‚ก์€ ๊ฐ’์„ ์ž ๊น ๋ณด์—ฌ์ฃผ๋ฉด์„œ ๋ฐฐ๊ฒฝ์—์„œ ์ƒˆ ๊ฐ’์„ ์ค€๋น„ํ•˜๋Š” stale-while-revalidate ์„ฑ๊ฒฉ์— ๊ฐ€๊น๋‹ค.


๐Ÿ›ก๏ธ router.refresh(): ์œ ์ € ๋ธŒ๋ผ์šฐ์ € ์†์‚ด ์ง€์šฐ๊ธฐ ๐Ÿ”ด

์œ„ ๋‘ ๊ฐœ์˜ ๋ฌดํšจํ™” API(Path, Tag)์€ ์ „๋ถ€ ์„œ๋ฒ„(Node.js ๋‹จ) ์—์„œ๋งŒ ๋ฐœ๋™ ๊ฐ€๋Šฅํ•œ ๋ฌด๊ธฐ๋“ค์ด์•ผ.
ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ(use client) ๋‚ด๋ถ€์˜ onClick ์ด๋ฒคํŠธ์—์„œ๋Š” ์ € ๋ฌดํšจํ™” API๋ฅผ ํ•จ๋ถ€๋กœ ๋ˆ„๋ฅผ ์ˆ˜๊ฐ€ ์—†์–ด! (๋ณด์•ˆ ์ •์ฑ… ์ƒ)

ํด๋ผ์ด์–ธํŠธ ๋‹จ์—์„œ ์ง์ ‘ "๋‚˜ ๋ฐฉ๊ธˆ ๊ธ€ ์ง€์› ๋Š”๋ฐ, ํ™”๋ฉด ์ข€ ์ฆ‰์‹œ ๋‹ค์‹œ ๊ทธ๋ ค์ฃผ๋ผ!" ๋ผ๊ณ  ๋ช…๋ นํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ?
๋ฐ”๋กœ Next.js์˜ ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํ„ฐ ํ›…์— ๋‹ฌ๋ฆฐ refresh() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์•ผ.

'use client'
import { useRouter } from 'next/navigation'
 
export default function DeleteButton({ id }) {
  const router = useRouter()
 
  const handleDelete = async () => {
    // 1. ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ฐฑ์—”๋“œ ์‚ญ์ œ API ํ˜ธ์ถœ
    await fetch(`/api/posts/${id}`, { method: 'DELETE' });
 
    // 2. ๋ธŒ๋ผ์šฐ์ €์•ผ! ๋„ค ๋ฉ”๋ชจ๋ฆฌ(Router Cache)์— ๋„์›Œ๋‘” ๋‚ก์€ HTML ๊ป๋ฐ๊ธฐ๋ฅผ ๋ฒ„๋ ค!
    // ๊ทธ๋ฆฌ๊ณ  ๊ฐ•์ œ๋กœ ํ™”๋ฉด ๊นœ๋นก์ž„(F5) ์—†์ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์„œ๋ฒ„๊ฐ€ ์ฃผ๋Š” ์ตœ์‹  ๋ Œ๋”๋ง ๋ทฐ๋ฅผ ๋‹ค์‹œ ๊ธ์–ด์™€!
    router.refresh();
  }
 
  return <button onClick={handleDelete}>๊ธ€ ์‚ญ์ œ</button>
}

๐Ÿ”ฅ ์ฃผ์˜ํ•  ์ !
router.refresh() ๋Š” ์‹ฌํ™” 1์žฅ์—์„œ ๋ฐฐ์šด [๊ฐ€์žฅ ์ฒซ ๋ฒˆ์งธ ๋ฐฉ์–ด๋ง‰์ธ ๋ธŒ๋ผ์šฐ์ € ๋‹จ์˜ Router Cache] ๋งŒ์„ ๋ถ€์ˆด๋ฒ„๋ฆฌ๋Š” ํ–‰์œ„์•ผ!
๋งŒ์•ฝ ๋ฐฑ์—”๋“œ ์‚ญ์ œ API(DELETE) ์ชฝ์— revalidatePath('/posts')๋‚˜ ๊ด€๋ จ ํƒœ๊ทธ ๊ฐฑ์‹ ์ด ์—†๋‹ค๋ฉด?
refresh()๊ฐ€ ์„œ๋ฒ„์— ๋‹ค์‹œ ์š”์ฒญํ•ด๋„ ์„œ๋ฒ„๊ฐ€ ์—ฌ์ „ํžˆ ๋‚ก์€ ์บ์‹œ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋จผ์ € ์„œ๋ฒ„ ์บ์‹œ๋ฅผ ๋ฌดํšจํ™”ํ•˜๊ณ , ํ•„์š”ํ•  ๋•Œ ํ˜„์žฌ ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์„ ๋‹ค์‹œ ์š”์ฒญํ•œ๋‹ค ๋Š” ์ˆœ์„œ๋ฅผ ๊ธฐ์–ตํ•˜์ž.


๐Ÿ’ฅ ์—๋Ÿฌ ํ•ด๊ฒฐ ์นดํƒˆ๋กœ๊ทธ

์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋œจ๋ฉด Ctrl+F๋กœ ๊ฒ€์ƒ‰ํ•ด๋ด.

โŒ revalidateTag is not a function ํ˜น์€ ์„œ๋ฒ„ ์—๋Ÿฌ

์›์ธ: revalidateTag ๋“ฑ ์บ์‹œ ๊ฐฑ์‹  ์ง€์‹œ์ž๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ("use client") ์˜์—ญ์—์„œ import ํ•ด์„œ ๋ฒ„ํŠผ onClick ํ•จ์ˆ˜ ์•ˆ์—์„œ ์ง์ ‘ ๋ถ€๋ฅด๋ ค๊ณ  ์‹œ์ „ํ–ˆ์Œ.
ํ•ด๊ฒฐ์ฑ…: ์ด ๊ฐฑ์‹  ํ•จ์ˆ˜๋Š” ๋ณด์•ˆ์ƒ ์˜ค๋กœ์ง€ [์„œ๋ฒ„ ์•ก์…˜(Server Actions)] ์ด๋‚˜ Route Handlers(app/api/) ํ™˜๊ฒฝ๊ฐ™์ด ์•ˆ์ „ํ•œ Node.js ์˜์—ญ ๋‚ด๋ถ€์—์„œ๋งŒ ์ˆ˜์ž…(next/cache)ํ•ด์„œ ๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค.

โŒ ํƒœ๊ทธ ์ €๊ฒฉ์ด์„ ์ˆ๋Š”๋ฐ๋„ ๊ธ€์”จ๊ฐ€ ์•ˆ ๋ณ€ํ•ด์š”!

์›์ธ: fetch ํ†ต์‹ ๋ถ€์— { next: { tags: ['my-tag'] } } ๊ผฌ๋ฆฌํ‘œ๋ฅผ ๋ช…์‹œํ•˜๋Š” ๊ฑธ ๊นœ๋นกํ–ˆ๊ฑฐ๋‚˜ ์˜คํƒ€๊ฐ€ ๋‚œ ๊ฒฝ์šฐ.
ํ•ด๊ฒฐ์ฑ…: ์„œ๋ฒ„ ์บ์‹œ ๋คํ”„ ๋กœ๊ทธ๋ฅผ ์ฐ์–ด๋ณด๊ณ (logging: { fetches: { fullUrl: true } } config ํ™œ์šฉ), ๋‚ด๊ฐ€ ํƒ€๊ฒฉํ•œ ํƒœ๊ทธ ์ด๋ฆ„ํ‘œ์˜ ์ฒ ์ž๊ฐ€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š”์ง€ ์ ๊ฒ€ํ•  ๊ฒƒ.


๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

๋ฌดํšจํ™” ์ˆ˜๋‹จ์ž‘๋™ ์œ„์น˜์žฅ๋‹จ์  ๋ฐ ์‚ฌ์šฉ์ฒ˜
revalidatePath(์ฃผ์†Œ)์„œ๋ฒ„ (Node.js)URL ํ•˜๋‚˜ ๊ธฐ๋ฐ˜์˜ ์ „๋ฉด ํ†ต์งธ ํ™”์„ ๋ฌดํšจํ™”. ๋‹จ์ˆœํ•˜์ง€๋งŒ ์ •๋ฐ€ ํƒ€๊ฒฉ ๋ถˆ๊ฐ€.
revalidateTag(ํƒœ๊ทธ๋ช…)์„œ๋ฒ„ (Node.js)์ด๋ฆ„ํ‘œ๊ฐ€ ๋ถ™์€ ์บ์‹œ๋“ค๋งŒ ๊ฐ™์€ ํƒœ๊ทธ์˜ ์บ์‹œ๋ฅผ ํ•จ๊ป˜ ๊ฐฑ์‹ ํ•จ. ๊ณ ๋‚œ๋„ ์•„ํ‚คํ…์ฒ˜ ํ•„์ˆ˜ ์„ค๊ณ„
router.refresh()ํด๋ผ์ด์–ธํŠธ (๋ธŒ๋ผ์šฐ์ €)์„œ๋ฒ„๊ฐ€ ๋ฑ‰์–ด๋‚ด๋Š” ๋‚ก์€ RSC Payload๋ฅผ ํ˜„์žฌ ๋ธŒ๋ผ์šฐ์ € ํƒญ์—์„œ ์ง€์›Œ๋ฒ„๋ฆฌ๊ณ  UI ์žฌ์š”์ฒญ ๊ฐฑ์‹  (ํ™”๋ฉด ์•ˆ ๊นœ๋นก์ž„)

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ์ƒํ’ˆ ๊ฐ€๊ฒฉ์ด ํ™ˆ, ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ, ๊ด€์‹ฌ ์ƒํ’ˆ ํŽ˜์ด์ง€์— ๋ชจ๋‘ ๋…ธ์ถœ๋œ๋‹ค. ๊ด€๋ฆฌ์ž ์ €์žฅ ํ›„ ์–ด๋–ค ๋ฌดํšจํ™”๊ฐ€ ๊ฐ€์žฅ ์ •๋ฐ€ํ• ๊นŒ?

โœ… ์ •๋‹ต: ์ƒํ’ˆ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ง€์ ์— ๊ฐ™์€ ํƒœ๊ทธ๋ฅผ ๋ถ™์ด๊ณ  revalidateTag('product-10', 'max') ๋˜๋Š” ์ฆ‰์‹œ ๋งŒ๋ฃŒ๊ฐ€ ํ•„์š”ํ•˜๋ฉด updateTag('product-10')๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: revalidatePath('/')๋Š” ํ™ˆ ๊ฒฝ๋กœ๋งŒ ๊ฒจ๋ƒฅํ•œ๋‹ค. ๊ฐ™์€ ์ƒํ’ˆ์ด ์—ฌ๋Ÿฌ ๊ฒฝ๋กœ์— ํฉ์–ด์ ธ ์žˆ์œผ๋ฉด ๊ฒฝ๋กœ ๊ธฐ์ค€ ๋ฌดํšจํ™”๋Š” ๋น ๋œจ๋ฆฌ๊ธฐ ์‰ฝ๋‹ค. ํƒœ๊ทธ๋Š” ๋ฐ์ดํ„ฐ์˜ ์ •์ฒด์„ฑ์„ ๊ธฐ์ค€์œผ๋กœ ๋ฌถ์œผ๋ฏ€๋กœ ๋” ์ •๋ฐ€ํ•˜๋‹ค. ๋‹ค๋งŒ ์ตœ์‹  Cache Components ๋ชจ๋ธ์—์„œ revalidateTag๋Š” stale-while-revalidate ์„ฑ๊ฒฉ์ด๋ฏ€๋กœ, ๋ฐฉ๊ธˆ ์ˆ˜์ •ํ•œ ๊ด€๋ฆฌ์ž๊ฐ€ ์ฆ‰์‹œ ์ƒˆ ๊ฐ’์„ ๋ด์•ผ ํ•œ๋‹ค๋ฉด Server Action ์•ˆ์—์„œ updateTag๋ฅผ ๊ณ ๋ คํ•œ๋‹ค.


Q2. router.refresh()๋ฅผ ๋ˆŒ๋ €๋Š”๋ฐ๋„ ํ™”๋ฉด์ด ๋‚ก์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ฐ›๋Š”๋‹ค. ๋ฌด์—‡์„ ์˜์‹ฌํ•ด์•ผ ํ• ๊นŒ?

โœ… ์ •๋‹ต: ํด๋ผ์ด์–ธํŠธ Router Cache๋งŒ ๋น„์› ๊ณ , ์„œ๋ฒ„์˜ Data Cache๋‚˜ Full Route Cache๊ฐ€ ์•„์ง ๊ฐฑ์‹ ๋˜์ง€ ์•Š์•˜์„ ๊ฐ€๋Šฅ์„ฑ์ด๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: router.refresh()๋Š” ํ˜„์žฌ ๋ธŒ๋ผ์šฐ์ € ์„ธ์…˜์˜ RSC Payload๋ฅผ ๋‹ค์‹œ ์š”์ฒญํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค. ํ•˜์ง€๋งŒ ์„œ๋ฒ„๊ฐ€ ์—ฌ์ „ํžˆ ๋‚ก์€ ์บ์‹œ๋ฅผ ๋“ค๊ณ  ์žˆ์œผ๋ฉด ์‚ฌ์šฉ์ž๋Š” ๊ฐ™์€ ๋‚ก์€ ์‘๋‹ต์„ ๋‹ค์‹œ ๋ฐ›๋Š”๋‹ค. ์˜ํ˜ธ๊ฐ€ ๋งํ•œ ์ˆœ์„œ๋Š” "์„œ๋ฒ„ ์บ์‹œ๋ฅผ ๋ฌดํšจํ™”ํ•˜๊ณ , ํ•„์š”ํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ ํ™”๋ฉด์„ ์ƒˆ๋กœ ์š”์ฒญํ•œ๋‹ค"์ด๋‹ค.


Q3. ์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„: ๊ธ€ ์ž‘์„ฑ Server Action์ด ์„ฑ๊ณตํ•˜๋ฉด ๋ฐฉ๊ธˆ ์ž‘์„ฑํ•œ ๊ธ€ ์ƒ์„ธ๋กœ ์ด๋™ํ•œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ž๊ธฐ ๊ธ€์„ ๋ฐ”๋กœ ๋ด์•ผ ํ•œ๋‹ค๋ฉด ์–ด๋–ค ๊ฐฑ์‹  ์ „๋žต์ด ์–ด์šธ๋ฆด๊นŒ?

โœ… ์ •๋‹ต: ๊ธ€ ๋ชฉ๋ก/์ƒ์„ธ์— ๋ถ™์€ ํƒœ๊ทธ๋ฅผ ์ฆ‰์‹œ ๋งŒ๋ฃŒํ•˜๋Š” updateTag ๋˜๋Š” ํ•„์š”ํ•œ ๊ฒฝ๋กœ์˜ revalidatePath๋ฅผ Server Action ์•ˆ์—์„œ ํ˜ธ์ถœํ•œ ๋’ค ์ด๋™ํ•œ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ์ž‘์„ฑ ์งํ›„๋Š” read-your-own-writes๊ฐ€ ์ค‘์š”ํ•œ ์ˆœ๊ฐ„์ด๋‹ค. ๋ฐฐ๊ฒฝ ๊ฐฑ์‹ ์œผ๋กœ ์ž ๊น ๋‚ก์€ ๋ชฉ๋ก์„ ๋ณด์—ฌ์ค˜๋„ ๋˜๋Š” CMS ๊ณต๊ฐœ ๋ชฉ๋ก๊ณผ ๋‹ค๋ฅด๋‹ค. Server Action์€ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ๊ณผ ์บ์‹œ ๊ฐฑ์‹ ์„ ๊ฐ™์€ ์„œ๋ฒ„ ํ๋ฆ„์— ๋‘˜ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์„ฑ๊ณต ์กฐ๊ฑด์ด ํ™•์ •๋œ ๋’ค ํƒœ๊ทธ๋‚˜ ๊ฒฝ๋กœ๋ฅผ ๊ฐฑ์‹ ํ•ด์•ผ ํ•œ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜์€ ์บ์‹œ ๋ฌดํšจํ™”๋ฅผ "์บ์‹œ๋ฅผ ๋„๋Š” ์ผ"๋กœ ๋ณด๋ฉด ์•ˆ ๋œ๋‹ค๋Š” ๊ฑธ ๋ฐฐ์› ๋‹ค. revalidatePath๋Š” ํ™”๋ฉด ๊ฒฝ๋กœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋„“๊ฒŒ ์น˜๊ณ , ํƒœ๊ทธ ๊ธฐ๋ฐ˜ ๊ฐฑ์‹ ์€ ๋ฐ์ดํ„ฐ์˜ ์ •์ฒด์„ฑ์„ ๊ธฐ์ค€์œผ๋กœ ์ข๊ฒŒ ์นœ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฉ๊ธˆ ๋ฐ”๊พผ ๊ฐ’์„ ๋ฐ”๋กœ ๋ด์•ผ ํ•˜๋Š”์ง€, ์กฐ๊ธˆ ๋Šฆ๊ฒŒ ๋ฐ˜์˜๋˜์–ด๋„ ๋˜๋Š”์ง€์— ๋”ฐ๋ผ ์„ ํƒ๋„ ๋‹ฌ๋ผ์ง„๋‹ค.

๐Ÿ’ก "๋ฌดํšจํ™” ์ „๋žต์€ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ฝ”๋“œ์˜ ์ผ๋ถ€๋‹ค. ์ €์žฅ ๋กœ์ง๊ณผ ๋–จ์–ด์ ธ ์žˆ์œผ๋ฉด ์–ธ์  ๊ฐ€ ๋‚ก์€ ํ™”๋ฉด์ด ์ƒ๊ธด๋‹ค."

๋‹ค์Œ๋ถ€ํ„ฐ๋Š” ์ €์žฅ ์•ก์…˜์„ ๋งŒ๋“ค ๋•Œ PR ์„ค๋ช…์— "์–ด๋–ค ์บ์‹œ๋ฅผ ๊ฐฑ์‹ ํ•˜๋Š”๊ฐ€"๋ฅผ ๊ผญ ์ ๊ฒ ๋‹ค. ํ™ˆ๋งŒ ์ƒˆ๋กœ๊ณ ์นจํ•ด์„œ ํ†ต๊ณผ์‹œํ‚ค๋Š” ๋Œ€์‹ , ๊ฐ™์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์ˆจ์–ด ์žˆ๋Š” ๋ชฉ๋ก๊ณผ ์ƒ์„ธ, ๋‚ด ํŽ˜์ด์ง€๊นŒ์ง€ ๊ฐ™์ด ๊ฒ€์ฆํ•ด์•ผ๊ฒ ๋‹ค.

๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ