๐งจ Next.js ์ฌํ 3์ฅ: Cache Invalidation โ ํ์์ ๋ฌดํจํํ๋ ์ฒ ํด
๐ ๊ฐ์
revalidatePath, revalidateTag, on-demand ISR๋ก ์บ์๋ฅผ ์ ๋ฐํ๊ฒ ๋ฌดํจํํ๋ ๋ฐฉ๋ฒ์ ๋ค๋ฃน๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ
revalidatePath: ๋ฌด๋ฆฌํ๊ฒ ๋๋ผ๋ก ๋ด๋ ค์ฐ๊ธฐ ๐ข - ๐ฑ
revalidateTag: ์ฐ์ํ๊ณ ์ ๋ฐํ ์ ๊ฒฉ์ด ๐ก - ๐ก๏ธ
router.refresh(): ์ ์ ๋ธ๋ผ์ฐ์ ์์ด ์ง์ฐ๊ธฐ ๐ด - ๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 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 ์ค๋ช ์ "์ด๋ค ์บ์๋ฅผ ๊ฐฑ์ ํ๋๊ฐ"๋ฅผ ๊ผญ ์ ๊ฒ ๋ค. ํ๋ง ์๋ก๊ณ ์นจํด์ ํต๊ณผ์ํค๋ ๋์ , ๊ฐ์ ๋ฐ์ดํฐ๊ฐ ์จ์ด ์๋ ๋ชฉ๋ก๊ณผ ์์ธ, ๋ด ํ์ด์ง๊น์ง ๊ฐ์ด ๊ฒ์ฆํด์ผ๊ฒ ๋ค.