๐Ÿš€ Next.js ์‹ฌํ™” 1์žฅ: Caching Deep Dive โ€” 4์ค‘ ์บ์‹œ ์•„ํ‚คํ…์ฒ˜ ํŒŒํ—ค์น˜๊ธฐ

๐Ÿ“‹ ๊ฐœ์š”

4์ค‘ ์บ์‹œ ์•„ํ‚คํ…์ฒ˜(Request Memoization, Data Cache, Full Route Cache, Router Cache)๋ฅผ ์™„์ „ํžˆ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


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

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

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

  • ์˜์ฒ (์‹ ์ž…): "์–ด? ๊ด€๋ฆฌ์ž ์–ด๋“œ๋ฏผ ํŽ˜์ด์ง€์—์„œ ๊ณต์ง€์‚ฌํ•ญ ์ œ๋ชฉ์„ ๋ผˆ ๋น ์ง€๊ฒŒ '๊ธด๊ธ‰ ํŒจ์น˜ 2.0'์œผ๋กœ ์ˆ˜์ •ํ•˜๊ณ  DB ์ €์žฅ(UPDATE)๊นŒ์ง€ ์™„๋ฒฝํ•˜๊ฒŒ ์„ฑ๊ณต์‹œ์ผฐ์–ด์š”! ๊ทธ๋Ÿฐ๋ฐ ๋ฉ”์ธ ํ™”๋ฉด์œผ๋กœ ๋Œ์•„๊ฐ€์„œ ์ƒˆ๋กœ๊ณ ์นจ(F5)์„ ์•„๋ฌด๋ฆฌ ๋ˆŒ๋Ÿฌ๋„ ์˜ˆ์ „ ์ œ๋ชฉ์ธ '๊ธด๊ธ‰ ํŒจ์น˜ 1.0'๋งŒ ๊ณ„์† ๋– ์š”! ๋ฌด๋ ค 30๋ถ„์ด๋‚˜ ์ง€๋‚œ ์ง€๊ธˆ๋„์š”. ํšŒ์‚ฌ ์ปดํ“จํ„ฐ๊ฐ€ ๋ฏธ์นœ ๊ฑด๊ฐ€์š”?!"
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "์˜์ฒ  ๋‹˜... Next.js์˜ ์•…๋งˆ ๊ฐ™์€ 'Data Cache'์™€ 'Full Route Cache'๋ผ๋Š” ์ด์ค‘ ์ž๋ฌผ์‡ ๋ฅผ ์•ˆ ํ’€์—ˆ์ž–์•„์š”! Next.js ์„œ๋ฒ„๋Š” ํ•œ ๋ฒˆ ๊ธ์–ด์˜จ ์ •์ (Static) ๋ฐ์ดํ„ฐ๋ฅผ ์–ต์ง€๋กœ ๋•Œ๋ ค ๋ถ€์ˆ˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ง€์ ค์ฒ˜๋Ÿผ ์˜์›ํžˆ ๋ณด์กดํ…Œ์ดํ”„๋ฅผ ํ‹€์–ด์ค€๋‹ค๊ตฌ์š”!"

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
Next.js ์บ์‹œ์˜ ์•…๋ช… โ†’ 4๊ฐ€์ง€ ์บ์‹œ ๊ณ„์ธต ์™„๋ฒฝ ๋ถ„ํ•ด โ†’ ๊ฐ ์บ์‹œ์˜ ์ดˆ๊ธฐํ™”(Invalidate) ์›๋ฆฌ

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

  • ๋‚ด ์ฝ”๋“œ๊ฐ€ ์–ด๋А ๊ณ„์ธต์˜ ์บ์‹œ์— ๋ฌถ์—ฌ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ๋Š”์ง€, "๋ฒ”์ธ ๋ ˆ์ด์–ด"๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์ง€๋ชฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Vercel ๋นŒ๋“œ ์‹œ ํ™”๋ฉด ์˜†์— ๋œจ๋Š” โ—‹ (Static) ๊ณผ ฦ’ (Dynamic) ์˜ ์ง„์งœ ๋‚ด๋ถ€ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

Next.js 13+ ๋ฒ„์ „(App Router)์ด ์ฒ˜์Œ ๋“ฑ์žฅํ–ˆ์„ ๋•Œ ์ „ ์„ธ๊ณ„ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋“ค์ด "์บ์‹œ ๊ดด๋ฌผ"์ด๋ผ๊ณ  ์š•์„ ํผ๋ถ€์—ˆ์–ด.
์ด์ „ ๋ฒ„์ „์ด "ํ•„์š”ํ•  ๋•Œ ์•Œ์•„์„œ ์บ์‹ฑํ•ด์ค˜" ์˜€๋‹ค๋ฉด, App Router๋Š” "๊ธฐ๋ณธ๊ฐ’์ด ๋ฌด์กฐ๊ฑด ์˜๊ตฌ ์บ์‹ฑ์ด์•ผ! ์บ์‹ฑํ•˜๊ธฐ ์‹ซ์œผ๋ฉด ๋‚˜๋ฅผ ์„ค๋“ํ•ด๋ด(opt-out)!" ๋ผ๋Š” ๊ทน๋‹จ์ ์ธ ์บ์‹œ ์šฐ์„ ์ฃผ์˜(Cache by default)๋กœ ๋ฐ”๋€Œ์—ˆ๊ฑฐ๋“ .

์™œ ์ด๋ ‡๊ฒŒ๊นŒ์ง€ ๋…ํ•˜๊ฒŒ ๋ฐ”๊ฟจ์„๊นŒ? ์„œ๋ฒ„ ๋ Œ๋”๋ง(SSR) ์œ„์ฃผ์˜ ์›น์€ ๊ตฌ์กฐ์ ์œผ๋กœ ๋งค์šฐ ๋น„์‹ธ๊ณ  ๋А๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์ด์•ผ. ์‚ฌ์šฉ์ž๊ฐ€ 1๋งŒ ๋ช… ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„๊ฐ€ 1๋งŒ ๊ฐœ์˜ HTML ๊ณต์žฅ์„ ๋Œ๋ฆฌ๊ณ  DB๋ฅผ ์ฐŒ๋ฅด๋ฉด Vercel ์š”๊ธˆ์ด 100๋ฐฐ๋กœ ํญ์ฃผํ•  ํ…Œ๋‹ˆ๊นŒ.

ํ•˜์ง€๋งŒ ์ด๊ฑธ ์™„๋ฒฝํ•˜๊ฒŒ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋ฉด, ์˜์ฒ ์ด์ฒ˜๋Ÿผ ํ•˜๋ฃจ ์ข…์ผ DB ๊ฐ’์ด ์—…๋ฐ์ดํŠธ ์•ˆ ๋˜๋Š” ์ง€์˜ฅ์—์„œ ํ—ค๋งค๊ฒŒ ๋ผ.
Next.js๋Š” ๋‹จ์ผ ์บ์‹œ๊ฐ€ ์•„๋‹ˆ๋ผ 4๊ฐœ์˜ ์บ์‹œ ๊ณ„์ธต(Layer)์ด ๋งˆ์น˜ ์–‘ํŒŒ ๊ป์งˆ์ฒ˜๋Ÿผ ์„œ๋กœ๋ฅผ ๋‘˜๋Ÿฌ์‹ธ๊ณ  ์žˆ์–ด. ์ด 4์ค‘ ์‰ด๋“œ์˜ ๋ฉ˜ํƒˆ ๋ชจ๋ธ์„ ๋จธ๋ฆฟ์†์— ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์ด ์‹œ๋‹ˆ์–ด์˜ ๊ฐ€์žฅ ์œ„๋Œ€ํ•œ ์—ฌ์ •์˜ ์‹œ์ž‘์ด์•ผ.


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

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด? (์˜์ˆ˜๋„ค ํ–„๋ฒ„๊ฑฐ ๊ณต์žฅ์˜ 4์ค‘ ์Šคํ”ผ๋“œ ์‹œ์Šคํ…œ)

์†๋‹˜์ด "๋น…๋งฅ ํ•˜๋‚˜!" ์ฃผ๋ฌธํ–ˆ์„ ๋•Œ ๊ณต์žฅ ๋‚ด๋ถ€์—์„œ ์ผ์–ด๋‚˜๋Š” 4๋‹จ๊ณ„ ์ตœ์ ํ™”:

  • 1๋‹จ๊ณ„ (Router Cache - ๋‚ด ์Šค๋งˆํŠธํฐ): "์–ด? ๋‚˜ ๋ฐฉ๊ธˆ ๋น…๋งฅ ์‹œ์ผฐ์—ˆ๋Š”๋ฐ. ๋ฐฐ๋‹ฌ ์•ˆ ์‹œํ‚ค๊ณ  ๋‚ด ์ฃผ๋จธ๋‹ˆ์—์„œ ์•„๊นŒ ์‹œํ‚จ ๋น…๋งฅ(์‚ฌ์ง„) ๊บผ๋‚ด์„œ ๋ด์•ผ์ง•~" (์ƒˆ๋กœ๊ณ ์นจ ์ „๊นŒ์ง€ ์œ ์ง€)
  • 2๋‹จ๊ณ„ (Request Memoization - ํ•œ ๋ฒˆ ๋ฐฐ๋‹ฌํ•  ๋•Œ): "์ด๋ฒˆ์—” ์ง„์งœ ๋ฐฐ๋‹ฌ ์‹œ์ผฐ์–ด. ์ฃผ๋ฐฉ์žฅ์ด ๊ณ ๊ธฐ ๊ตฝ๋Š”๋ฐ, ๋‚ด๊ฐ€ ๊ณ ๊ธฐ(fetch) 2๋ฒˆ ๊ฐ–๋‹ค ๋‹ฌ๋ผ๊ณ  ํ•ด๋„ ์ฃผ๋ฐฉ์žฅ์ด ๋ฌด์‹œํ•˜๊ณ  ํ•œ ๋ฒˆ ๊ตฌ์šด ๊ณ ๊ธฐ๋ฅผ ๋ฐ˜๋ฐ˜ ๋‚˜๋ˆ ์คŒ!"
  • 3๋‹จ๊ณ„ (Data Cache - ์ฃผ๋ฐฉ์˜ ์˜๊ตฌ ๋ƒ‰๋™๊ณ ): "์–ด์ œ ๋“ค์–ด์˜จ ํŠน์ œ ์†Œ์Šค(DB ๊ฒฐ๊ณผ)๋ฅผ ์˜๊ตฌ ๋ƒ‰๋™๊ณ ์— ๋ณด๊ด€ํ•ด๋’€์–ด. ๋‚ด์ผ ์†๋‹˜์ด ์™€๋„, ๋ชจ๋ ˆ ์™€๋„ ์•ˆ ๋ฒ„๋ฆฌ๊ณ  ๋˜‘๊ฐ™์€ ์†Œ์Šค๋ฅผ ๊ณ„์† ๋ฟŒ๋ฆด ๊ฑฐ์•ผ!"
  • 4๋‹จ๊ณ„ (Full Route Cache - ์ง„์—ด์žฅ์˜ ๋ชจํ˜• ํ–„๋ฒ„๊ฑฐ): "์•„์˜ˆ ์†Œ์Šค, ๋นต, ์•ผ์ฑ„๋ฅผ ๋‹ค ์กฐ๋ฆฝํ•ด๋‘” ์™„์ œํ’ˆ ๋น…๋งฅ(HTML ํŒŒ์ผ ์ž์ฒด)์„ ๊ฐ€๊ฒŒ ์•ž์— ์œ ๋ฆฌ์žฅ์‹์œผ๋กœ ๊ฑธ์–ด๋’€์–ด! ์ฃผ๋ฌธ ์˜ค๋ฉด ์ฃผ๋ฐฉ์žฅ ์•ˆ ๊นจ์šฐ๊ณ  ์ € ๋ชจํ˜• ๋ฐ”๋กœ ๋˜์ ธ์คŒ ใ…‹ใ…‹ใ…‹"

๐Ÿงฉ 1๋‹จ๊ณ„: Request Memoization (์ˆ˜๋ช… 1์ดˆ์˜ ํ•จ์ˆ˜ ์บ์‹œ) ๐ŸŸข

์ด๊ฑด ์ด๋ฏธ guide 6์žฅ์—์„œ ๋งˆ์Šคํ„ฐํ•œ ๋‚ด์šฉ์ด์•ผ. ๊ฐ€๋ณ๊ฒŒ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž.

ํŠน์ง• ์š”์•ฝ

  • ๋ชฉ์ : ๊นŠ๊ณ  ๋ณต์žกํ•œ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ ํ•˜์œ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋˜‘๊ฐ™์ด ๋˜ ํ˜ธ์ถœํ•  ๋•Œ, ๋„คํฌ์›Œํฌ ๋ถ€ํ•˜ ๋ฐฉ์ง€ (Props Drilling ํšŒํ”ผ)
  • ๋Œ€์ƒ: ์˜ค๋กœ์ง€ GET ๋ฐฉ์‹์˜ fetch() ํ•จ์ˆ˜. (๊ทธ๋ฆฌ๊ณ  ์ˆ˜๋™์œผ๋กœ ๋ž˜ํ•‘ํ•œ React.cache)
  • ์ˆ˜๋ช…: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™”๋ฉด์„ ๊ฐฑ์‹ ํ•˜๋Š” ๋”ฑ ๊ทธ "ํ•œ ๋ฒˆ์˜ ๋ Œ๋”๋ง ํ„ด(Lifecycle)" ๋™์•ˆ๋งŒ ์‚ด์•„์žˆ์Œ.
  • ์ดˆ๊ธฐํ™”(Revalidation) ๋ฐฉ๋ฒ•: ์—†์Œ. ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์ด ๋”ฑ ๋๋‚˜๋Š” 1์ดˆ ๋’ค์— ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์—ฐ๊ธฐ์ฒ˜๋Ÿผ ์‚ฌ๋ผ์ง.

๐ŸŒฑ 2๋‹จ๊ณ„: Data Cache (DB ์ฟผ๋ฆฌ์˜ ์˜๊ตฌ ๋ณด์กด) ๐ŸŸก

์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ๊ฐ€ ์ง„์งœ ๊ณจ์น˜ ์•„ํ”ˆ ๋†ˆ๋“ค์˜ ์‹œ์ž‘์ด์•ผ. ์˜์ฒ ์ด๊ฐ€ 30๋ถ„ ๋™์•ˆ ์ฃฝ์–ด๋ผ ๊ณ ์ƒํ•œ ๋ฒ”์ธ์ด์ง€.

ํŠน์ง•

  • ๋ชฉ์ : ๊ฐ™์€ URL๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด์˜ค๋Š” ์™ธ๋ถ€ DB๋‚˜ API ํ†ต์‹  ๊ฒฐ๊ณผ๋ฅผ ๋””์Šคํฌ(์„œ๋ฒ„) ์–ด๋”˜๊ฐ€์— ๊ฑฐ๋ฏธ์ค„์ฒ˜๋Ÿผ ์˜์›ํžˆ ๊ฐ€๋‘ฌ๋ฒ„๋ฆผ.
  • ๋Œ€์ƒ: fetch() ์‘๋‹ต๊ฐ’ ๊ทธ ์ž์ฒด.
  • ์ˆ˜๋ช…: ์˜๊ตฌ์ (Permanent). ๊ฐœ๋ฐœ์ž๊ฐ€ ์„œ๋ฒ„๋ฅผ ๋‚ด๋ ธ๋‹ค ์ผœ๊ฑฐ๋‚˜ ๊ฐ•์ œ๋กœ ์ง€์šฐ๊ธฐ ์ „๊นŒ์ง€ 10๋…„์ด ์ง€๋‚˜๋„ ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฑ‰์Œ!

โŒ ์˜์ฒ ์ด์˜ ํ•จ์ • (๊ธฐ๋ณธ ์บ์‹œ ์ง€์˜ฅ)

// app/notice/page.tsx
export default async function NoticeInfo() {
  // ๐Ÿ’ฃ ๋”์ฐํ•œ ํ•จ์ •: Next 13, 14 ์—์„œ๋Š” ์ด๊ฑธ ์ ๋Š” ์ˆœ๊ฐ„ "ํ‰์ƒ ์บ์‹œ" ๋กœ ๋™์ž‘ํ•จ!
  // (Next 15๋ถ€ํ„ฐ๋Š” ๋‹คํ–‰ํžˆ no-store๋กœ ์ •์ฑ…์ด ์™„ํ™”๋˜์—ˆ์ง€๋งŒ 14 ๋ฒ ์ด์Šค ๋ ˆ๊ฑฐ์‹œ๋Š” ์กฐ์‹ฌํ•ด์•ผ ํ•จ)
  const res = await fetch('https://db.api/api/notice/urgent');
  const data = await res.json();
  return <div>{data.title}</div>
}

โœ… ์ œ์–ด๊ถŒ ๋˜์ฐพ๊ธฐ (Opting out)

์ˆ˜์ฒœ ๊ฐ€์ง€์˜ "์บ์‹œ ์•ˆ ํ•ด!" ํƒˆ์ถœ ๋ฐฉ๋ฒ•์ด ์žˆ์–ด.

1) ๋ถ€๋ถ„ ํƒˆ์ถœ (Fetch ๋ ˆ๋ฒจ)
fetch(url, { cache: 'no-store' }) // Next.js์•ผ, ์ œ๋ฐœ ์ด ์ฟผ๋ฆฌ๋งŒํผ์€ ๋งค๋ฒˆ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ€์ ธ์˜ค๋ ด!

2) ํŒŒ์ผ ์ „์ฒด ํƒˆ์ถœ (Segment Configuration)

export const dynamic = 'force-dynamic' // ์ด ํŒŒ์ผ(page) ๋‚ด์˜ ๋ชจ๋“  ๋กœ์ง ์บ์‹œ ๊ฑฐ๋ถ€!
 
export default async function Page() { ... }

3) ์‹œ๊ฐ„์ œ ๋ณด๊ด€ (ISR - Time based Revalidation)
fetch(url, { next: { revalidate: 3600 } }) // Next.js์•ผ, ์ด๊ฑฐ 1์‹œ๊ฐ„(3600์ดˆ) ์นด์šดํŠธ๋‹ค์šดํ•˜๊ณ  ์บ์‹œ ํ๊ธฐํ•ด.


๐Ÿ›ก๏ธ 3๋‹จ๊ณ„: Full Route Cache (์™„์„ฑ๋œ HTML์˜ ํ™”์„ํ™”) ๐Ÿ”ด

Data Cache๊ฐ€ "DB ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ(JSON)"๋ฅผ ์กฐ๊ฐ์กฐ๊ฐ ์–ผ๋ ค๋‘๋Š” ๊ฑฐ๋ผ๋ฉด, Full Route Cache๋Š” ๋” ๊ทน๊ฐ•์˜ ๋ณ‘๊ธฐ์•ผ.
์•„์˜ˆ ํ™”๋ฉด ์ „์ฒด HTML(JS๋ฒˆ๋“ค ํฌํ•จ) ๋ฉ์–ด๋ฆฌ๋ฅผ ํ†ต์งธ๋กœ ๋นŒ๋“œ ํƒ€์ž„์— ๊ตฌ์›Œ์„œ ํ™”์„์œผ๋กœ ๋งŒ๋“ค์–ด๋ฒ„๋ ค!

ํŠน์ง•

  • ๋ชฉ์ : ์„œ๋ฒ„ ๊นกํ†ต CPU๋ฅผ ์•„๋ผ๊ธฐ ์œ„ํ•ด, 1์ดˆ์˜ ์ง€์—ฐ๋„ ์—†์ด ๋ฒˆ๊ฐœ์ฒ˜๋Ÿผ ํ™”๋ฉด์„ ์‘๋‹ตํ•˜๊ธฐ ์œ„ํ•œ ์™„์ œํ’ˆ ๋ณด๊ด€.
  • ์ˆ˜๋ช…: ์˜๊ตฌ์ . ๋‹ค์Œ ๋นŒ๋“œ(Build)๋ช…๋ น npm run build๋ฅผ ์ƒˆ๋กœ ์น˜๊ธฐ ์ „๊นŒ์ง€ ์ฃฝ์ง€ ์•Š์Œ.
  • ์กฐ๊ฑด: 2๋‹จ๊ณ„์˜ Data Cache๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์‚ด์•„์žˆ๋Š”(Static) ์†์„ฑ์˜ ํŽ˜์ด์ง€.

๐Ÿ”„ Static vs Dynamic ๋ฃจํŠธ ํŒ๋…๊ธฐ

ํ„ฐ๋ฏธ๋„์—์„œ npm run build๋ฅผ ์ณค์„ ๋•Œ ๋œจ๋Š” ์„ฑ์ ํ‘œ๋ฅผ ์ž˜ ๋ด์•ผ ํ•ด.

Route (app)                       Size     First Load JS
โ”Œ โ—‹ /                             154 B    86.6 kB
โ”œ โ—‹ /about                        154 B    86.6 kB
โ”” ฦ’ /profile/[id]                 154 B    86.6 kB
 
โ—‹  (Static)   prerendered as static HTML (uses no initial data)
ฦ’  (Dynamic)  server-rendered on demand using Node.js
  • โ—‹ (Static) ํŒ์ •์˜ ๋น„๋ฐ€: ๋‚ด ํŽ˜์ด์ง€ ์•ˆ์— no-store๊ฐ€ ๊ฑธ๋ฆฐ fetch๋„ ์—†๊ณ , ๋ธŒ๋ผ์šฐ์ € ์‹ค์‹œ๊ฐ„ ํ—ค๋” cookies(), headers(), searchParams (?id=1) ์ชผ๊ฐ€๋ฆฌ๋„ ๋‹จ ํ•œ ๊ฐœ๋„ ์“ฐ์ง€ ์•Š์•˜์„ ๋•Œ ํš๋“! ์ด ์ถ•๋ณต๋ฐ›์€ ํŒŒ์ผ์€ Full Route Cache์— ํ™”์„์œผ๋กœ ์˜๊ตฌ ์ €์žฅ๋˜๋ฉฐ ๋น›์˜ ์†๋„๋กœ ์„œ๋น™๋œ๋‹ค.
  • ฦ’ (Dynamic) ํŒ์ •: ๋กœ๊ทธ์ธ ์ธ์ฆ์„ ์œ„ํ•ด cookies().get('token')์„ ํ•œ ์ค„์ด๋ผ๋„ ์“ด ์ˆœ๊ฐ„, ์•„... ๋ˆ„๊ตฐ์ง€ ๋งค๋ฒˆ ํ™•์ธํ•ด์•ผ ํ•˜๋„ค? ํ•˜๋ฉด์„œ ํ™”์„์„ ๋ฐ•์‚ด ๋‚ด๊ณ  ๋งค๋ฒˆ SSR(์ฆ‰์„ ๋ Œ๋”๋ง)๋กœ ๊ฐ•๋“ฑ๋‹นํ•œ๋‹ค.

[์˜์ฒ ์ด ์—๋Ÿฌ์˜ ์ง„์ƒ ๊ทœ๋ช…]
์˜์ฒ ์ด์˜ ๊ณต์ง€์‚ฌํ•ญ ํŽ˜์ด์ง€๋Š” ์™„๋ฒฝํ•œ โ—‹ Static ํŒŒ์ผ์ด์—ˆ์–ด. DB ๊ฐ’ ์—…๋ฐ์ดํŠธ ๋กœ์ง(Admin)๋งŒ ๋งŒ๋“ค์–ด ๋†จ์ง€, ์œ ์ €๊ฐ€ ๋ณด๋Š” ์ด ๋ผ์šฐํŠธ์˜ HTML ๋ฉ์–ด๋ฆฌ ํ™”์„(Full Route Cache)๊ณผ JSON(Data Cache)์„ ๋ง์น˜๋กœ ๊นจ๋ถ€์ˆด์„œ ๋‹ค์‹œ ๊ตฌ์šฐ๋ผ๋Š” ๋ช…๋ น์–ด(revalidatePath)๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•˜๋˜ ๊ฑฐ์•ผ!


๐Ÿšฅ 4๋‹จ๊ณ„: Router Cache (ํด๋ผ์ด์–ธํŠธ ํƒญ ๋ฉ”๋ชจ๋ฆฌ) ๐ŸŸฃ

์„œ๋ฒ„๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์œ„ 3๊ฐœ์˜ ์บ์‹œ์™€ ๋‹ค๋ฅด๊ฒŒ, ๋งˆ์ง€๋ง‰ ๋ฐฉ์–ด์„ ์€ ์‚ฌํŒŒ๋ฆฌ/ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ € ์† ์‚ฌ์šฉ์ž ์ปดํ“จํ„ฐ์˜ ๋ฉ”๋ชจ๋ฆฌ(RAM) ์•ˆ์— ์‚ด์•„์žˆ์–ด.

๋ธŒ๋ผ์šฐ์ €๋Š” ๋˜‘๋˜‘ํ•˜๊ณ  ๊ฒŒ์œผ๋ฅด๋‹ค

Next.js App Router๋กœ ๋„ค๋น„๊ฒŒ์ด์…˜(Link ํƒœ๊ทธ ํด๋ฆญ)์„ ํ•˜๋ฉด, ๊ธฐ์กด ๋ฆฌ์•กํŠธ์ฒ˜๋Ÿผ ์„œ๋ฒ„๋กœ ์•ˆ ๊ฐ€๊ณ  ๋ธŒ๋ผ์šฐ์ € ์•ˆ์— ๊ฟ์ณ๋‘” ๋ Œ๋”๋ง ์กฐ๊ฐ ๋ชจ์Œ(RSC Payload)์„ ๊บผ๋‚ด์จ.

  • ์ˆ˜๋ช…: ๋ธŒ๋ผ์šฐ์ € ํƒญ์„ X๋กœ ๋‹ซ๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจ(F5)์„ ๋ˆ„๋ฅด๊ธฐ ์ „๊นŒ์ง€(๋˜๋Š” 30์ดˆ~5๋ถ„ ๋‚ด์™ธ ๋ฉ”๋ชจ๋ฆฌ ์ •์ฑ… ๋งŒ๋ฃŒ ์ „๊นŒ์ง€).
  • ์ด ๋…€์„ ๋•Œ๋ฌธ์— "๋‚˜ ๋ฐฉ๊ธˆ ๊ธ€ ์ผ๋Š”๋ฐ ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ˆŒ๋ €๋”๋‹ˆ ์˜›๋‚  ๋ชฉ๋ก ๋œจ๋„ค?" ๊ฐ™์€ ๋ฒ„๊ทธ์„ฑ ์ฒด๊ฐ์ด ๊ฐ„ํ—์ ์œผ๋กœ ๋ฐœ์ƒํ•ด.

๐Ÿ”จ ๋ถ€์ˆ˜๊ธฐ (Invalidation)

์ด ์บ์‹œ๊ฐ€ ๋ฐฉํ•ด๊ฐ€ ๋  ๋•Œ๋Š” ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ "๋„ค์ž„ํŽœ์œผ๋กœ ์“ด ๋ฉ”๋ชจ ์ˆ˜์ฒฉ ์ฐข์–ด๋ฒ„๋ ค!"๋ผ๊ณ  ๋ช…๋ นํ•ด์•ผ ํ•ด.

'use client'
import { useRouter } from 'next/navigation'
 
export default function ReRouteButton() {
  const router = useRouter()
  return <button onClick={() => router.refresh()}>๊ฐ•์ œ ๊ฐฑ์‹ (๋ธŒ๋ผ์šฐ์ € ์บ์‹œ ๋ถ€์ˆ˜๊ธฐ)!</button>
}

router.refresh() ๋Š” ํ™”๋ฉด ๋ฐฑ์ง€ํ™” ์—†์ด ์˜ค์ง ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์„œ๋ฒ„์˜ ์ตœ์‹  RSC Payload๋งŒ ์ƒˆ๋กœ ์‹น ๋‚ด๋ ค๋ฐ›์•„ ์น˜ํ™˜ํ•˜๋Š” ๋งˆ๋ฒ•์˜ ๊ณ ๊ธ‰ ํ›…์ด์•ผ.


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

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

โŒ Dynamic server usage: cookies ์—๋Ÿฌ๊ฐ€ ๋œจ๋ฉด์„œ ๋นŒ๋“œ๊ฐ€ ํญํŒŒ๋ผ์š”!

์›์ธ: generateStaticParams ์™€ ๊ฐ™์ด '๋ฏธ๋ฆฌ ์ •์ ์œผ๋กœ(Static) ๊ตฌ์›Œ๋‘๋ ค๊ณ  ์ž‘์ •'ํ•œ ํŒŒ์ผ ๊ณต๊ฐ„ ๋‚ด๋ถ€์—์„œ, cookies() ๋‚˜ URL ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ™์ด ์‹ค์‹œ๊ฐ„ ๋Ÿฐํƒ€์ž„์—๋งŒ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์–ต์ง€๋กœ ๊บผ๋‚ด ์“ฐ๋ ค๋‹ค ๋ชจ์ˆœ์ด ๋ฐœ์ƒํ•œ ๊ฒƒ.
ํ•ด๊ฒฐ์ฑ…: ํŽ˜์ด์ง€์— export const dynamic = 'force-dynamic' ์„ ๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธํ•˜์—ฌ ์ด ํŒŒ์ผ์€ ์ ˆ๋Œ€ ํ™”์„(Static)์œผ๋กœ ๋‚จ์„ ์ˆ˜ ์—†๋Š” ์šด๋ช…์ž„์„ Next.js์—๊ฒŒ ์ž๋ฐฑํ•ด๋ผ.


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

์ด ๊ฑฐ๋Œ€ํ•œ 4์ค‘ ์žฅ๋ฒฝ์˜ ๋ฐฉ์–ด ์ˆœ์„œ! ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™”๋ฉด์„ ๋‹ฌ๋ผ๊ณ  ์กฐ๋ฅด๋ฉด...

๊ป์งˆ ๊ณ„์ธต ์ˆœ์„œํŠ•๊ฒจ๋‚ด๋Š” ๋‹จ๊ณ„์บ์‹œ ์œ„์น˜๋ฌด๋ ฅํ™”(๋ถ€์ˆ˜๊ธฐ) ๋ฐฉ๋ฒ•
1๋ฒˆ ๋ฐฉ์–ด๋ง‰Router Cache: "์•ผ ๋ธŒ๋ผ์šฐ์ €์•ผ, ๋„ˆ ์•„๊นŒ ์ด๊ฑฐ ๋ฐฉ๋ฌธํ–ˆ์—ˆ์ž–์•„. ๋‚ด RAM์— ์ €์žฅ๋œ ๊ฑฐ ์ค„๊ฒŒ."ํด๋ผ์ด์–ธํŠธ (๋ธŒ๋ผ์šฐ์ €)router.refresh() / ํŽ˜์ด์ง€ ์ฐฝ ๋‹ซ๊ธฐ / F5 ๊ฐ•์ œ ์„ธ๋กœ๊ณ ์นจ
2๋ฒˆ ๋ฐฉ์–ด๋ง‰Full Route Cache: (์„œ๋ฒ„ ๋„๋‹ฌ) "์–ด? ๋„ˆ๊ฐ€ ์ฐพ๋Š” HTML, ๋‚ด๊ฐ€ ์ €๋ฒˆ ๋นŒ๋“œ ๋•Œ ์™„์ œํ’ˆ ํ™”์„์œผ๋กœ ๊ตฌ์›Œ๋†จ์ง€!"์„œ๋ฒ„ (๋””์Šคํฌ)์žฌ๋นŒ๋“œ(build) / revalidatePath ์„œ๋ฒ„์•ก์…˜ ํƒ€๊ฒฉ
3๋ฒˆ ๋ฐฉ์–ด๋ง‰Request Memoization: (์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ์ค‘) "์•— ๋˜‘๊ฐ™์€ fetch ๋„ค? ์ฃผ๋ฐฉ์žฅ ๊ณ ์ƒํ•˜์ง€ ๋ง๊ณ  ๋‚ด ๊ธฐ์–ต(๋ฉ”๋ชจ๋ฆฌ)์—์„œ ๋ณต์‚ฌํ•ด์ค„๊ฒŒ."์„œ๋ฒ„ (๋ฉ”๋ชจ๋ฆฌ ๋‹จ๋ฐœ์„ฑ)์กฐ์น˜ ํ•„์š” ์—†์Œ (1 ๋Ÿฐํƒ€์ž„ ์ข…๋ฃŒ ์‹œ ์ž๋™ ํ๊ธฐ)
4๋ฒˆ ๋ฐฉ์–ด๋ง‰Data Cache: "๊ฒฐ๊ตญ ์ง„์งœ ๋ฐ์ดํ„ฐ ์ฐŒ๋ฅด๋ ค ์™”๋„ค? ๊ทผ๋ฐ ๊ทธ API ๊ฒฐ๊ณผ๊ฐ’ 5์‹œ๊ฐ„ ๋™์•ˆ ์–ผ๋ ค๋‘” ๋‚ด DB ์บ์‹œ ์ฐฝ๊ณ ์—์„œ ์ค„๊ฒŒ ใ…‹ใ…‹ใ…‹"์„œ๋ฒ„ (๋””์Šคํฌ)fetch { cache: no-store } / revalidateTag ๋“ฑ

๐Ÿ’ก ์‹œ๋‹ˆ์–ด์˜ ๋ฉ˜ํƒˆ ๋ชจ๋ธ
๋นŒ๋“œ ์‹œ โ—‹ (Static) ๋กœ ๊ตณ์–ด๋ฒ„๋ฆฐ ๋ผ์šฐํŠธ๋Š” Data Cache๋ฅผ ๋ถ€์ˆ˜๊ธฐ ์ „๊นŒ์ง€ ์ ˆ๋Œ€ ์ž๊ธฐ ์Šค์Šค๋กœ ๊ฐฑ์‹ ๋˜์ง€ ์•Š๋Š”๋‹ค.
"๋ฐ์ดํ„ฐ๊ฐ€ ์•ˆ ๋ฐ”๋€Œ์–ด์š”!" ๋ผ๋Š” ์งˆ๋ฌธ์— ๋‹นํ™ฉํ•˜์ง€ ๋ง๊ณ , ๊ฐ€์žฅ ๋จผ์ € Vercel ๋นŒ๋“œ ํ„ฐ๋ฏธ๋„ ์ฐฝ์„ ์—ด๊ณ  ๊ทธ ํŽ˜์ด์ง€๊ฐ€ โ—‹ ์ธ์ง€ ฦ’ ์ธ์ง€๋ถ€ํ„ฐ ์ƒ‰์ถœํ•˜๋ผ!


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

๋ฐฐ์› ์œผ๋ฉด ํ•œ ๋ฒˆ ๋น„ํ‹€์–ด์„œ ํ™•์ธํ•ด๋ด์•ผ ํ•ด.

Q1. ์˜์ฒ ์ด๊ฐ€ ํšŒ์› ์ „์šฉ "๋‚ด ํ”„๋กœํ•„ ํŽ˜์ด์ง€(/profile/me)"๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด ํŽ˜์ด์ง€ ์ตœ์ƒ๋‹จ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” const token = cookies().get('session_id') ๋ฅผ ํ˜ธ์ถœํ•ด ์œ ์ €์˜ ์ฟ ํ‚ค๋ฅผ ๋œฏ์–ด๋ณด๊ณ  ์žˆ์—ˆ๋‹ค. ์˜์ฒ ์ด๋Š” ์ด ๋ Œ๋”๋ง ์—ฐ์‚ฐ ๋น„์šฉ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ์ด ํŽ˜์ด์ง€๋ฅผ Full Route Cache ํ™”์„์œผ๋กœ ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌ์›Œ๋ฒ„๋ฆฌ๊ณ  ์‹ถ๋‹ค! ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ?

โœ… ์ •๋‹ต: ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ตฌ์กฐ์ ์œผ๋กœ ์„ค๊ณ„๊ฐ€ ํ‹€๋ ค๋จน์—ˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ๋‚ด ํ”„๋กœํ•„(me)์ฒ˜๋Ÿผ ์œ ์ €๋งˆ๋‹ค, ์ ‘์†ํ•œ ๋ธŒ๋ผ์šฐ์ € ์ฟ ํ‚ค ํ—ค๋”๊ฐ’์— ๋”ฐ๋ผ ์™„์ „ํžˆ ํ™”๋ฉด์ด ๋‹ฌ๋ผ์ง€๋Š” ๊ณ ๋„์˜ ๊ฐœ์ธํ™” ํŽ˜์ด์ง€๋Š” ์ ˆ๋Œ€๋กœ Static(์ •์ ) HTML๋กœ ํ•œ ๋ฒˆ๋งŒ ๊ตฌ์›Œ์„œ ์บ์‹ฑ์„ ๋จน์ผ ์ˆ˜ ์—†๋‹ค. cookies() ๋“ฑ Dynamic Function์ด ์ฐํžŒ ์ˆœ๊ฐ„ ์ด ํŽ˜์ด์ง€๋Š” ๋ฌด์กฐ๊ฑด ๋งค ์ ‘์† ์‹œ๋งˆ๋‹ค ์ƒˆ๋กœ ์กฐ๋ฆฝ๋˜๋Š” ฦ’ (Dynamic) ๋ Œ๋”๋ง์œผ๋กœ ๊ฐ•๋“ฑ๋‹นํ•ด ์ž‘๋™ํ•ด์•ผ ์ •์ƒ์ด๋‹ค. ์ด๊ฑธ ์–ต์ง€๋กœ ์ •์ ์œผ๋กœ ๋ฌป์–ด๋ฒ„๋ฆฌ๋ ค ํ•œ๋‹ค๋ฉด ๋ˆ„๊ตฐ๊ฐ€์˜ ๊ฐœ์ธ์ •๋ณด๊ฐ€ ์ „ ์„ธ๊ณ„ ์œ ์ €์—๊ฒŒ ํ‰์ƒ ๊ณต์œ ๋˜๋Š” ๋Œ€ํ˜• ์ฐธ์‚ฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

Q2. ์˜ํ˜ธ ๋ฆฌ๋“œ๊ฐ€ ๋ฉ”์ธ ๋‰ด์Šค ํ™ˆํŽ˜์ด์ง€(/news)๋ฅผ ๊ตฌ์ถ•ํ–ˆ๋‹ค. ์ด๊ฑด ๋‰ด์Šค๋‹ˆ๊นŒ ์ •์ ์œผ๋กœ ๊ตฌ์›Œ(Full Route Cache - Static) ์‘๋‹ต ์†๋„๋ฅผ ๊ด‘์†์œผ๋กœ ๋ฝ‘์•˜๋Š”๋ฐ, ํŽธ์ง‘ํŒ€์—์„œ ํ•˜๋ฃจ์— ํŠน์ข…์ด ํ„ฐ์งˆ ๋•Œ๋งˆ๋‹ค ๋‰ด์Šค ๋ชฉ๋ก DB๊ฐ€ ์ˆ˜์‹œ๋กœ ๋ฐ”๋€Œ์–ด์•ผ ํ•œ ๋‹จ๋‹ค. ์˜ํ˜ธ๋Š” ์ด ๋Œ€๊ทœ๋ชจ ๋‰ด์Šค ์ •์  ํ™”์„์„ ์–ด๋–ป๊ฒŒ, ์–ด๋А ์ฃผ๊ธฐ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์ชผ๊ฐœ๊ณ  ๋‹ค์‹œ ๊ตฝ๊ฒŒ ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ธํŒ…ํ•ด์•ผ ์šฐ์•„ํ• ๊นŒ?

โœ… ์ •๋‹ต: ๊ฐ€์žฅ ์šฐ์•„ํ•œ ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ์ฒซ์งธ, ์‹œ๊ฐ„ ํƒ€์ž„์•„์›ƒ ์บ์‹ฑ (ISR): revalidate: 60 ์†์„ฑ์„ ๋จน์—ฌ์„œ, ๋”ฑ 60์ดˆ๋งˆ๋‹ค ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ๋‚ก์€ ํ™”์„์„ ๋ถ€์ˆ˜๊ณ  ์ƒˆ ๋‰ด์Šค ํ™”์„ HTML๋กœ ๊ต์ฒดํ•œ๋‹ค. ํŽธ์ง‘ํŒ€์€ 1๋ถ„ ์ •๋„์˜ ๋”œ๋ ˆ์ด๋งŒ ์ฒด๊ฐํ•˜๋ฉฐ ํ™˜ํ˜ธํ•œ๋‹ค. ๋‘˜์งธ, ๊ณ ์˜ค๊ธ‰ ์ˆ˜๋™ ์ €๊ฒฉ ๋ฌดํšจํ™” (On-demand Revalidation): Admin CMS ์„œ๋ฒ„์—์„œ [๊ธฐ์‚ฌ ์ˆ˜์ •] ๋ฒ„ํŠผ์ด ๋ˆŒ๋ฆฌ๋Š” ๊ทธ ์ฐฐ๋‚˜์˜ ์ˆœ๊ฐ„์—๋งŒ Next.js ์„œ๋ฒ„๋กœ API ์›นํ›… ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๋‚ ๋ ค, ์„œ๋ฒ„ ์•ก์…˜์˜ revalidatePath('/news') ๋งˆ๋ฒ• ํ•จ์ˆ˜๋ฅผ ํ—ˆ๊ณต์— ์˜๊ฒŒ ๋งŒ๋“ ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ํ‰์†Œ์—๋Š” ์™„๋ฒฝํ•œ Static ์†๋„๋กœ ๊ฟ€์„ ๋นจ๋‹ค๊ฐ€, ์˜ค์ง ํŽธ์ง‘์ž์˜ ์กฐ๋ฆฌ ๋„๊ตฌ๊ฐ€ ๋ˆŒ๋ฆฐ ๊ทธ ์ˆœ๊ฐ„์—๋งŒ ํ™”์„ ์žฅ๋ง‰์ด ๋ฐ•์‚ด ๋‚˜๋Š” ๊ถ๊ทน์˜ ์•„ํ‚คํ…์ฒ˜ ์™„์„ฑ! (์ด ๋งˆ์ˆ ์„ ์‹ฌํ™” 3์žฅ์—์„œ ๋” ๊นŠ๊ฒŒ ํŒ” ๊ฒƒ์ด๋‹ค.)


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

์˜ค๋Š˜์€ ์ •๋ง ๋„ฅ์ŠคํŠธ์˜ ๊ฑฐ๋Œ€ํ•œ '์บ์‹ฑ ์žฅ๋ฒฝ' ์•ž์— ์„  ๊ธฐ๋ถ„์ด์—ˆ์–ด. ๊ทธ๋™์•ˆ ๋‹จ์ˆœํžˆ "์บ์‹œ๊ฐ€ ์žˆ์–ด์„œ ๋นจ๋ผ์š”"๋ผ๊ณ ๋งŒ ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, ๊ทธ ์ด๋ฉด์— Router, Full Route, Request Memoization, Data Cache๋ผ๋Š” 4์ค‘ ํ•„ํ„ฐ๊ฐ€ ์žˆ์—ˆ๋‹ค๋‹ˆ!

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "๋ฐ์ดํ„ฐ๊ฐ€ ์•ˆ ๋ฐ”๋€๋‹ค๋ฉด ๋นŒ๋“œ ์‹œ์ ์— ํ™”์„(Static)์œผ๋กœ ๊ตฝ๊ณ , ์œ ์ €๋งˆ๋‹ค ๋‹ค๋ฅธ ์ •๋ณด๋Š” ๋™์ (Dynamic)์œผ๋กœ ๋ Œ๋”๋งํ•˜์—ฌ ๋ณด์•ˆ๊ณผ ์„ฑ๋Šฅ์„ ๋ชจ๋‘ ์žก์ž!"

์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋ธŒ๋ผ์šฐ์ € RAM๋ถ€ํ„ฐ ์„œ๋ฒ„ ๋””์Šคํฌ๊นŒ์ง€ ๋‹จ๊ณ„๋ณ„๋กœ ์บ์‹ฑ์ด ์ž‘๋™ํ•˜๋Š” ์›๋ฆฌ๋ฅผ ์„ค๋ช…ํ•ด ์ฃผ์‹ค ๋•Œ, ํŒŒํŽธํ™”๋˜์–ด ์žˆ๋˜ ์ง€์‹๋“ค์ด ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์ง€๋Š” ์งœ๋ฆฟํ•จ์„ ๋А๊ผˆ์–ด. "ํ™”์„์ด ๋œ HTML์€ ๋ถ€์ˆ˜๊ธฐ ์ „๊นŒ์ง€ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค"๋Š” ๋ง์”€์„ ๊ฐ€์Šด์— ์ƒˆ๊ธฐ๋ฉฐ... ์˜ค๋Š˜ ๋จธ๋ฆฌ๋ฅผ ๋„ˆ๋ฌด ๋งŽ์ด ์ผ๋”๋‹ˆ ๋‹น๋ถ„์ด ํ•„์š”ํ•ด. ์ง‘์— ๊ฐ€๋Š” ๊ธธ์— ๋‹ฌ์ฝคํ•œ ๋งˆ์นด๋กฑ์ด๋ผ๋„ ์ข€ ์‚ฌ ๊ฐ€์•ผ์ง€. ๋‚ด์ผ์€ ๋” 'ํšจ์œจ์ ์ธ' ์บ์‹ฑ ์ „๋žต์„ ์งœ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ฒ ์–ด! ๐Ÿฃ


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