๐Ÿš€ Next.js 2์žฅ: App Router ํ•ด๋ถ€ํ•™ (ํŒŒ์ผ ์‹œ์Šคํ…œ ๋ผ์šฐํŒ…)

๐Ÿ“‹ ๊ฐœ์š”

App Router์˜ ํŒŒ์ผ ์‹œ์Šคํ…œ ๋ผ์šฐํŒ… ๊ตฌ์กฐ์™€ layout, page, loading, error ํŒŒ์ผ์˜ ์—ญํ• ์„ ํŒŒํ—ค์นฉ๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


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

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

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
ํŒŒ์ผ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์˜ ์›๋ฆฌ โ†’ Layout ๊ณผ Template ์˜ ์ฐจ์ด โ†’ Route Group โ†’ ์‹ค์ „ ์‹ค์Šต ( ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ์˜ˆ์ œ )

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

  • layout.tsx ์™€ template.tsx ๋ฅผ ์ƒํ™ฉ์— ๋งž๊ฒŒ ๊ณจ๋ผ์„œ ์“ธ ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒํƒœ(State) ๊ฐ€ ์œ ์ง€๋˜์–ด์•ผ ํ•˜๋Š” UI ์™€ ์ƒˆ๋กœ ๊ฐฑ์‹ ๋˜๋Š” UI ๋ฅผ ํด๋” ๊ตฌ์กฐ๋กœ ๋ถ„๋ฆฌ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ผ์šฐํŠธ ๊ทธ๋ฃน์„ ํ™œ์šฉํ•ด ๋ณต์žกํ•œ ๋Œ€์‹œ๋ณด๋“œ์™€ ์œ ์ € ํŽ˜์ด์ง€์˜ ๋ ˆ์ด์•„์›ƒ์„ ์šฐ์•„ํ•˜๊ฒŒ ๊ณ ๋ฆฝ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

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

  • ๐Ÿฃ ์˜์ฒ  ( ์‹ ์ž… ): "๋ฆฌ๋“œ ๋‹˜! ์Šคํ„ฐ๋”” ์นดํ…Œ๊ณ ๋ฆฌ ํƒญ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ํ™”๋ฉด์ด ๋œ์ปน๊ฑฐ๋ฆฌ๋ฉด์„œ, ํž˜๋“ค๊ฒŒ ๊ฒ€์ƒ‰ํ•ด๋‘” ์‚ฌ์ด๋“œ๋ฐ” ํ•„ํ„ฐ๊ฐ’์ด ์ž๊พธ ์ดˆ๊ธฐํ™”๋ผ์š”! ์œ ์ž… ๊ฒฝ๋กœ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค ์ฃฝ๋Š” ๊ฑด๊ฐ€์š”?"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, ๊ทธ๊ฑด ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๋ฐฐ๊ฒฝ(Layout) ๊ณผ ๋ณ€ํ•˜๋Š” ์ฃผ์ธ๊ณต(Page) ์„ ํ•œ ํŒŒ์ผ์— ๋‹ค ๋•Œ๋ ค ๋„ฃ์–ด์„œ ๊ทธ๋ž˜์š”. ๋ฆฌ์•กํŠธ ๋ผ์šฐํ„ฐ ์‹œ์ ˆ์˜ ๊ด€์Šต์„ ์•„์ง ๋ชป ๋ฒ„๋ฆฌ์…จ๊ตฐ์š”."
  • ๐Ÿ‘” ์˜์ˆ˜ ( PM ): "์˜์ฒ  ์”จ, ํ•„ํ„ฐ๊ฐ€ ๋งค๋ฒˆ ์ดˆ๊ธฐํ™”๋˜๋ฉด ์œ ์ €๋“ค์ด ์Šคํ„ฐ๋”” ์ฐพ๋‹ค๊ฐ€ ํ™”๋‚˜์„œ ์•ฑ ์ง€์šธ๊ฑธ์š”? ์ด๊ฑฐ ๋น„์ฆˆ๋‹ˆ์Šค์ ์œผ๋กœ ์น˜๋ช…์ ์ž…๋‹ˆ๋‹ค."
  • ๐ŸŽจ ์˜์ˆ™ ( UX ): "๋งž์•„์š”. ๊ทธ๋ฆฌ๊ณ  ํƒญ์„ ๋„˜๊ธธ ๋•Œ๋งˆ๋‹ค ํ™”๋ฉด์ด ์Šˆ์›…~ ์˜ˆ์˜๊ฒŒ ๋‚˜ํƒ€๋‚˜๋ฉด ์ข‹๊ฒ ๋Š”๋ฐ, layout ์€ ์ ˆ๋Œ€๋กœ ๋‹ค์‹œ ์•ˆ ๊ทธ๋ ค์ง„๋‹ค๋ฉด์„œ์š”? ์ œ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ์–ด์ฉŒ์ฃ ?"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์ž, ๋‹ค๋“ค ์ฃผ๋ชฉ! ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ์—๊ฒ Layout ๊ณผ Template, ๊ทธ๋ฆฌ๊ณ  Route Group ์ด๋ผ๋Š” ๋„๊ตฌ๊ฐ€ ์žˆ๋Š” ๊ฒ๋‹ˆ๋‹ค. ์˜ค๋Š˜ ์ด ๋…€์„๋“ค์˜ ํ•ด๋ถ€ํ•™์„ ์ œ๋Œ€๋กœ ํŒŒํ—ค์ณ ๋ณด์ฃ ."

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

์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ("๊ฐœ๋ฐœ์ž ์Šคํ„ฐ๋”” ๋งค์นญ ์•ฑ") ํ”„๋กœ์ ํŠธ ์ƒํ™ฉ์ด์•ผ.

์˜์ฒ (์ƒˆ๋กœ ์˜จ ์ฃผ๋‹ˆ์–ด) ์ด๊ฐ€ ์Šคํ„ฐ๋”” ์ฐพ๊ธฐ ํŽ˜์ด์ง€(/studies) ์—์„œ ๋„ค๋น„๊ฒŒ์ด์…˜ ํƒญ(ํ”„๋ก ํŠธ์—”๋“œ ๋ชจ์ง‘, ๋ฐฑ์—”๋“œ ๋ชจ์ง‘) ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ํ™”๋ฉด ์ „์ฒด๊ฐ€ ๊นœ๋นก์ด๋ฉด์„œ ์‚ฌ์ด๋“œ๋ฐ” ํ•„ํ„ฐ๊นŒ์ง€ ์ดˆ๊ธฐํ™”๋˜๋Š” ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ์–ด. ์˜์ฒ ์ด๋Š” React Router ์ฒ ํ•™๋Œ€๋กœ ๊ฑฐ๋Œ€ํ•œ App.tsx ์—์„œ ๋ชจ๋“  ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋ ค๋‹ค ์œ ์ง€๋ณด์ˆ˜ ์ง€์˜ฅ(Maintenance Nightmare) ์— ๋น ์ง€๊ณ  ๋งŒ ๊ฑฐ์•ผ.

์˜ํ˜ธ(FE ๋ฆฌ๋“œ) ๊ฐ€ ํ•œ์ˆจ์„ ์‰ฌ๋ฉฐ ๋‹ค๊ฐ€์™”์–ด. "์˜์ฒ ์•„, Next.js App Router ์˜ layout.tsx ๊ฐ€ ์™œ ์กด์žฌํ•˜๋Š”์ง€ ์•„์ง ๋ชจ๋ฅด๋Š”๊ตฌ๋‚˜. ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๋ฐฐ๊ฒฝ๊ณผ ๋ณ€ํ•˜๋Š” ์ฃผ์ธ๊ณต์„ ํŒŒ์ผ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์•ผ ํ•ด."

Next.js App Router ๋Š” "ํด๋” ์ด๋ฆ„์ด ๊ณง URL ์ด๊ณ , ํŒŒ์ผ ์ด๋ฆ„์ด ๊ณง ์—ญํ• ์ด๋‹ค" ๋ผ๋Š” ๊ฐ•๋ ฅํ•œ ์›์น™ ์ œ๊ณตํ•ด. ํŠน์ •ํ•œ ์˜ˆ์•ฝ์–ด ํŒŒ์ผ๋“ค(layout.tsx, template.tsx, loading.tsx) ์˜ ๋™์ž‘ ๋ฐฉ์‹๊ณผ ์ƒ์กด ์ฃผ๊ธฐ(์ƒ๋ช…์ฃผ๊ธฐ) ๋ฅผ ๋ชจ๋ฅด๋ฉด, ๋ถˆํ•„์š”ํ•œ ์žฌ๋ Œ๋”๋ง์— ์˜์›ํžˆ ๊ณ ํ†ต๋ฐ›๊ฒŒ ๋  ๊ฑฐ์•ผ.


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

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
์šฐ๋ฆฌ๊ฐ€ ์‚ฌ๋Š” ์ง‘์„ ๋– ์˜ฌ๋ ค๋ณด์ž. ๊ฑฐ์‹ค์˜ ๋ฒฝ์ง€๋Š” ๊ทธ๋Œ€๋กœ์ธ๋ฐ, TV ์† ํ™”๋ฉด๋งŒ ๋ฐ”๋€Œ๋Š” ๊ฒƒ๊ณผ ๊ฑฐ์‹ค ์ธํ…Œ๋ฆฌ์–ด ์ „์ฒด๋ฅผ ๋งค๋ฒˆ ๋‹ค ๋œฏ์–ด๊ณ ์น˜๋Š” ๊ฒƒ ์ค‘ ๋ฌด์—‡์ด ๋” ํšจ์œจ์ ์ผ๊นŒ?

๐Ÿ–ผ๏ธ ๋งˆํŠธ๋ฃŒ์‹œ์นด ์ธํ˜•๊ณผ ์•ก์ž ๋น„์œ 

Next.js ์˜ ๋ผ์šฐํŒ… ๊ตฌ์กฐ๋Š” ๊ฒน๊ฒน์ด ์Œ“์ธ ์•ก์ž ํ”„๋ ˆ์ž„ ๊ณผ ๊ฐ™์•„.

  • layout.tsx: ๊ฐ€์žฅ ๋ฐ”๊นฅ์ชฝ์˜ ํŠผํŠผํ•œ ๋‚˜๋ฌด ์•ก์ž ํ‹€. ์•ˆ์˜ ๊ทธ๋ฆผ์„ ์•„๋ฌด๋ฆฌ ๋ฐ”๊ฟ” ๋ผ์›Œ๋„, ๋ฐ”๊นฅ ์•ก์ž ํ‹€์€ ์ „ํ˜€ ์›€์ง์ด๊ฑฐ๋‚˜ ๋ถ€์„œ์ง€์ง€ ์•Š์•„. (์ƒํƒœ๊ฐ€ ์˜์›ํžˆ ์œ ์ง€๋จ)
  • page.tsx: ์•ก์ž ์† ์•Œ๋งน์ด ๊ทธ๋ฆผ. URL ์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ๊ต์ฒด๋˜๋Š” ํ•ต์‹ฌ ๋‚ด์šฉ์ด์•ผ.
  • template.tsx: ์ƒ๊น€์ƒˆ๋Š” layout ๊ณผ ๋น„์Šทํ•œ๋ฐ, ๊ทธ๋ฆผ ๊ต์ฒดํ•  ๋•Œ๋งˆ๋‹ค ์•ก์ž ํ‹€๊นŒ์ง€ ํŒŒ์‚ฌ์‚ญ ๋ถ€์ˆด๋ฒ„๋ฆฌ๊ณ  ์ƒˆ ์•ก์ž๋กœ ๋‹ค์‹œ ๊ฐˆ์•„ ๋ผ์šฐ๋Š” ์˜ˆ๋ฏผํ•œ ๋…€์„ ์ด์•ผ. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹คํ–‰์ด๋‚˜ ์ดˆ๊ธฐํ™”๋ฅผ ๊ฐ•์ œํ•  ๋•Œ ์“ฐ์ง€.

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?
"๋ ˆ์ด์•„์›ƒ์€ ํŠผํŠผํ•œ ์šฐ๋ฆฌ ์ง‘ ๋ฒฝ์ด์•ผ. ์šฐ๋ฆฌ๊ฐ€ ๊ฑฐ์‹ค์—์„œ ๋ฐฉ์œผ๋กœ ๊ฐ€๋„ ๋ฒฝ์€ ๊ทธ๋Œ€๋กœ์ง€? ํ•˜์ง€๋งŒ ํ…œํ”Œ๋ฆฟ์€ ์šฐ๋ฆฌ๊ฐ€ ๋ฐฉ์— ๋“ค์–ด๊ฐˆ ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ์ƒˆ๋กœ ํ„ฐ๋œจ๋ ค์ฃผ๋Š” ํ™”๋ คํ•œ ํญ์ฃฝ ๊ฐ™์€ ๊ฑฐ์•ผ!"

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
Layout ์€ ๋ณด์กด, Template ์€ ์žฌ์ƒ์„ฑ์ด๋‹ค.


๐Ÿ“„ page.tsx์™€ layout.tsx์˜ ๊ณต์ƒ ๊ด€๊ณ„ ๐ŸŸข

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • Next.js ๊ฐ€ ํ™”๋ฉด์„ ์กฐ๋ฆฝํ•  ๋•Œ ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๊ฐ€ ์–ด๋–ป๊ฒŒ ์œ ์ง€๋˜๋Š”์ง€ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒํƒœ ๋ณด์กด์„ ์œ„ํ•ด ์™œ Layout ์„ ์จ์•ผ ํ•˜๋Š”์ง€ ๊ธฐ์ˆ ์ ์œผ๋กœ ์ฆ๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.

1. page.tsx: ๋ผ์šฐํŠธ์˜ ์œ ์ผํ•œ ์ฃผ์ธ๊ณต

ํ•ด๋‹น ์ฃผ์†Œ(/studies)๋กœ ์ ‘๊ทผํ–ˆ์„ ๋•Œ ๊ทธ๋ ค์ง€๋Š” ๋ฉ”์ธ UI์•ผ.

2. layout.tsx: ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๋“ ๋“ ํ•œ ๋ฐฐ๊ฒฝ

์ž์‹ ๋ผ์šฐํŠธ ์‚ฌ์ด๋ฅผ ์ด๋™ํ•  ๋•Œ ์ƒํƒœ(State)๋ฅผ ๋ณด์กดํ•˜๊ณ  ๋ฆฌ๋ Œ๋”๋ง์„ ์ฐจ๋‹จํ•ด์ฃผ๋Š” ์ผ๋“ฑ ๊ณต์‹ ์ด์•ผ. ์˜์ฒ ์ด์˜ ๊ณ ๋ฏผ์ด ์—ฌ๊ธฐ์„œ ํ•ด๊ฒฐ๋˜์ง€.

โŒ ์ˆœ์ง„ํ•œ ์ฝ”๋“œ (Naive Approach)
ํŽ˜์ด์ง€ ํŠธ๋žœ์ง€์…˜ ๋กœ์ง์„ page.tsx ์ตœ์ƒ๋‹จ ๋ถ€๋ชจ์—์„œ ๋ฌด๋ฆฌํ•˜๊ฒŒ ์กฐ์ž‘ํ•ด ํ™”๋ฉด ๊นœ๋นก์ž„ ์œ ๋ฐœ.

โœ… ์šฐ์•„ํ•œ ์ฝ”๋“œ ( Pro Approach ) - ์˜ํ˜ธ์˜ ํ•ด๊ฒฐ์ฑ…

// app/studies/layout.tsx
// ๐Ÿฆ ์˜ํ˜ธ: "์ด ํŒŒ์ผ์€ URL์ด ๋ฐ”๋€Œ์–ด๋„ ์ ˆ๋Œ€ ๋‹ค์‹œ ๋ Œ๋”๋˜์ง€ ์•Š๊ณ  ๊ตณ๊ฑดํžˆ ๋ฒ„ํŒ๋‹ˆ๋‹ค."
 
export default function StudiesLayout({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex">
      {/* ๐Ÿฃ ์˜์ฒ : "์™€! ์ด์ œ ํ•„ํ„ฐ์— ์ ์–ด๋‘” ๊ฒ€์ƒ‰์–ด๊ฐ€ ์•ˆ ์‚ฌ๋ผ์ ธ์š”!" */}
      <nav>๊ฐ•๋ ฅํ•œ ์Šคํ„ฐ๋”” ๊ฒ€์ƒ‰ ์‚ฌ์ด๋“œ๋ฐ” ํ•„ํ„ฐ (์ƒํƒœ ์œ ์ง€๋จ)</nav>
      
      {/* ๐Ÿฆ ์˜ํ˜ธ: "children ์ž๋ฆฌ์˜ page.tsx๋งŒ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๊ต์ฒด๋˜๋Š” ๊ฒ๋‹ˆ๋‹ค." */}
      <main className="flex-1">{children}</main>
    </div>
  )
}

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
Page ๋Š” ๋งค๋ฒˆ ๋ฐ”๋€Œ๋Š” ์ฃผ์ธ๊ณต, Layout ์€ ๊ทธ ์ฃผ์ธ๊ณต์„ ์ง€์ผœ์ฃผ๋Š” ๋ฌด๋Œ€ ๋ฐฐ๊ฒฝ์ด๋‹ค.


๐Ÿงฉ layout.tsx vs template.tsx โ€” ๋ฌด์—‡์ด ๋‹ค๋ฅผ๊นŒ? ๐ŸŸก

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
์˜์ˆ™(๋””์ž์ด๋„ˆ)์ด๊ฐ€ ์š”๊ตฌํ–ˆ์–ด. "์Šคํ„ฐ๋”” ํƒญ ํŽ˜์ด์ง€ ์ด๋™ํ•  ๋•Œ๋งˆ๋‹ค ํ™”๋ฉด์ด ์™ผ์ชฝ์—์„œ ์Šˆ์›…~ ๋‚˜ํƒ€๋‚˜๊ฒŒ (Framer Motion) ํ•ด์ฃผ์„ธ์š”! ๊ทธ๋ฆฌ๊ณ  ์ด๋™ํ•  ๋•Œ๋งˆ๋‹ค ํŽ˜์ด์ง€ ๋ทฐ(GA) ์ด๋ฒคํŠธ๋„ ์˜์•„์ฃผ์‹œ๊ณ ์š”."
์ด๊ฑธ ์ ˆ๋Œ€ ๋ฆฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š” ์ฒ ๋ฒฝ๋ฐฉ์–ด layout.tsx์— ๋ถ™์—ฌ๋‘๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

์ด๊ฒŒ ๋ญ”๊ฐ€?

template.tsx๋Š” ๋ฌธ๋ฒ•์ ์œผ๋กœ layout๊ณผ ์™„์ „ ๋™์ผ({ children } ํŒจํ„ด)ํ•˜์ง€๋งŒ, ์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ๋ผ์šฐํŠธ๋กœ ์ง„์ž…/๋ณ€๊ฒฝํ•  ๋•Œ๋งˆ๋‹ค DOM ์ž์ฒด๋ฅผ ์ฐข๊ณ  ์ƒˆ๋กœ ์ƒ์„ฑ(Re-mount)ํ•ด๋ฒ„๋ฆฌ๋Š” ํŒŒ์ผ์ด์•ผ.

์‹ค๋ฌด์—์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉ๋˜๋Š”๊ฐ€?

useEffect ๋ฅผ ์ด์šฉํ•ด '๊ฒฝ๋กœ ๋ฐฉ๋ฌธ ์‹œ 1ํšŒ ๋ฐœ๋™' ํ•˜๋Š” ๋กœ์ง์ด๋‚˜ ํŠธ๋žœ์ง€์…˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ถ™์ผ ๋•Œ ์™„๋ฒฝํ•ด.

// app/studies/template.tsx
'use client' // ๐ŸŽจ ์˜์ˆ™: "์• ๋‹ˆ๋ฉ”์ด์…˜์€ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋Œ์•„๊ฐ€์•ผ ํ•˜๋‹ˆ๊นŒ์š”!"
import { useEffect } from 'react'
 
export default function StudyTemplate({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    // ๐Ÿฆ ์˜ํ˜ธ: "ํƒญ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ์—ฌ๊ธฐ๊ฐ€ ๋ฐœ๋™๋˜๋Š” ๋งˆ๋ฒ•์„ ๋ณด์„ธ์š”."
    console.log('๐Ÿ“Œ ํŽ˜์ด์ง€ ๋ทฐ ์ „ํ™˜ ์ด๋ฒคํŠธ ์ˆ˜์ง‘ ๋ฐœ๋™!')
  }, [])
 
  return <div className="animate-slide-in">{children}</div>
}

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
ํ•„ํ„ฐ ๊ฐ’์„ ์ง€ํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด Layout, ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค ๋ถˆ๊ฝƒ๋†€์ด๋ฅผ ํ„ฐ๋œจ๋ฆฌ๊ณ  ์‹ถ๋‹ค๋ฉด Template ์ด๋‹ค.


๐Ÿ”€ layout.tsx + template.tsx ํ•จ๊ป˜ ์“ฐ๋ฉด? ๐ŸŸก

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ๊ฐ™์€ ํด๋”์— layout.tsx์™€ template.tsx๊ฐ€ ๊ณต์กดํ•  ๋•Œ ๋ Œ๋” ์ˆœ์„œ๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค
  • ๋‘ ํŒŒ์ผ์˜ ์—ญํ• ์„ ๋ถ„๋ฆฌํ•ด์„œ ์‹ค๋ฌด์— ์‘์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

๋‘˜์ด ๊ณต์กดํ•  ์ˆ˜ ์žˆ์–ด?

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜๋ฉด ์™„๋ฒฝํ•˜๊ฒŒ ๊ณต์กด ๊ฐ€๋Šฅํ•ด. Next.js๋Š” ๊ฐ™์€ ํด๋”์— layout.tsx์™€ template.tsx๊ฐ€ ๋ชจ๋‘ ์žˆ์œผ๋ฉด ์ด ์ˆœ์„œ๋Œ€๋กœ ๊ฐ์‹ธ์„œ ๋ Œ๋”ํ•ด:

layout.tsx
  โ””โ”€ template.tsx
       โ””โ”€ page.tsx

์ฆ‰ layout์ด ๊ฐ€์žฅ ๋ฐ”๊นฅ์ชฝ, template์ด ๊ทธ ์•ˆ์ชฝ, page๊ฐ€ ์•Œ๋งน์ด ๊ตฌ์กฐ์•ผ.

์–ธ์ œ ์ด๋ ‡๊ฒŒ ์“ธ๊นŒ?

์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ์ƒํ™ฉ์ด์•ผ. ์˜ํ˜ธ๊ฐ€ ์Šคํ„ฐ๋”” ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€์— ๋‘ ๊ฐ€์ง€๋ฅผ ๋™์‹œ์— ์›ํ•ด:

  • ๊ฒ€์ƒ‰ ํ•„ํ„ฐ ์‚ฌ์ด๋“œ๋ฐ” ์ƒํƒœ๋ฅผ URL ์ด๋™ ์ค‘์—๋„ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค (โ†’ layout)
  • ํƒญ์„ ๋ˆŒ๋Ÿฌ ์ƒˆ ์Šคํ„ฐ๋”” ๋ชฉ๋ก์œผ๋กœ ์ด๋™ํ•  ๋•Œ๋งˆ๋‹ค ํŽ˜์ด์ง€ ์ง„์ž… ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ GA ์ด๋ฒคํŠธ๋ฅผ ์ด์•ผ ํ•œ๋‹ค (โ†’ template)
// app/studies/layout.tsx โ† ๋ฐ”๊นฅ์ชฝ (์˜๊ตฌ ์ƒ์กด, ์ƒํƒœ ๋ณด์กด)
export default function StudiesLayout({ children }) {
  return (
    <div className="flex">
      <Sidebar />  {/* URL์ด ๋ฐ”๋€Œ์–ด๋„ ์ด ์‚ฌ์ด๋“œ๋ฐ”๋Š” ์ ˆ๋Œ€ ์‚ฌ๋ผ์ง€์ง€ ์•Š์Œ */}
      <main>{children}</main>
    </div>
  )
}
// app/studies/template.tsx โ† ์•ˆ์ชฝ (ํƒญ ์ด๋™๋งˆ๋‹ค ๊ฐ•์ œ ์žฌ๋งˆ์šดํŠธ)
'use client'
import { useEffect } from 'react'
 
export default function StudiesTemplate({ children }) {
  useEffect(() => {
    // ํƒญ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค(URL ๋ณ€๊ฒฝ ์‹œ) ์—ฌ๊ธฐ๊ฐ€ ๋ฐœ๋™๋จ โ† template์ด๊ธฐ ๋•Œ๋ฌธ
    sendGAEvent('page_view', { path: window.location.pathname })
  }, [])
 
  return (
    <div className="animate-fade-in">  {/* ์Šˆ์›…~ ํŽ˜์ด๋“œ์ธ ์• ๋‹ˆ๋ฉ”์ด์…˜ */}
      {children}
    </div>
  )
}
// app/studies/[category]/page.tsx โ† ์•Œ๋งน์ด (์นดํ…Œ๊ณ ๋ฆฌ๋ณ„๋กœ ๊ต์ฒด๋จ)
export default function StudyListPage({ params }) {
  return <StudyList category={params.category} />
}

๊ฒฐ๊ณผ:

  • ์‚ฌ์ด๋“œ๋ฐ” ํ•„ํ„ฐ ์ƒํƒœ โ†’ layout์ด ๋ฒ„ํ…จ์ค˜์„œ URL ์ด๋™ ํ›„์—๋„ ๋ณด์กด โœ…
  • ํŽ˜์ด๋“œ์ธ ์• ๋‹ˆ๋ฉ”์ด์…˜ + GA ์ด๋ฒคํŠธ โ†’ template์ด ๋งค๋ฒˆ ์žฌ๋งˆ์šดํŠธ๋˜๋ฉฐ ๋ฐœ๋™ โœ…

๋ Œ๋” ์ˆœ์„œ ์ •๋ฆฌ

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
๋งŒ์•ฝ layout ์•ˆ์— template ์ด ์žˆ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ๋ฐ˜๋Œ€๋กœ template ์ด layout ์„ ๊ฐ์‹ธ๊ณ  ์žˆ๋‹ค๋ฉด ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ๊นŒ? (ํžŒํŠธ: ์ƒํƒœ ์œ ์ง€)

[URL: /studies/frontend ์ง„์ž…]
1. layout.tsx ๋งˆ์šดํŠธ (์ตœ์ดˆ 1ํšŒ, ์ดํ›„ ๋ณด์กด)
2. template.tsx ๋งˆ์šดํŠธ (์ง„์ž…๋งˆ๋‹ค ์ƒˆ๋กœ ๋งˆ์šดํŠธ)
3. page.tsx ๋ Œ๋” (URL ๋ณ€๊ฒฝ๋งˆ๋‹ค ๊ต์ฒด)
 
[URL: /studies/backend ์ด๋™ ์‹œ]
1. layout.tsx โ†’ ์œ ์ง€ (์‚ฌ์ด๋“œ๋ฐ” ์ƒํƒœ ์‚ด์•„์žˆ์Œ) โœ…
2. template.tsx โ†’ ํŒŒ๊ดด ํ›„ ์ƒˆ๋กœ ๋งˆ์šดํŠธ (์• ๋‹ˆ๋ฉ”์ด์…˜/GA ๋ฐœ๋™) ๐Ÿ”„
3. page.tsx โ†’ ์ƒˆ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๊ต์ฒด ๐Ÿ”„

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
Layout ์€ ์˜๊ตฌ์ ์ธ ๊ทผ๊ฑฐ์ง€, Template ์€ ๊ทธ ์•ˆ์—์„œ ํƒญ๋งˆ๋‹ค ์ƒˆ๋กœ ์ง€์–ด์ง€๋Š” ํ…ํŠธ์™€ ๊ฐ™๋‹ค.


๐ŸŽญ ๋ผ์šฐํŠธ ๊ทธ๋ฃน (folder)์˜ ๋งˆ๋ฒ• ๐ŸŸก

์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ์—๋Š” ์ผ๋ฐ˜ ์œ ์ €๋“ค์ด ๋ณด๋Š” **"์Šคํ† ์–ด/์Šคํ„ฐ๋”” ๋ฉ”์ธ ์˜์—ญ"**๊ณผ ์˜์ˆ˜๋งŒ ๋“ค์–ด๊ฐ€๋Š” "๊ด€๋ฆฌ์ž ์–ด๋“œ๋ฏผ(admin)" ์˜์—ญ์ด ์žˆ์–ด.
์ด ๋‘˜์€ ์‚ฌ์ด๋“œ๋ฐ” ๋ชจ์–‘, ํ—ค๋”๊นŒ์ง€ 180๋„ ๋‹ค๋ฅด๊ฒŒ ์ƒ๊ฒผ์ง€.

โŒ ์ˆœ์ง„ํ•œ ์ฝ”๋“œ (Naive Approach)
๋ฃจํŠธ ์ตœ์ƒ๋‹จ app/layout.tsx ํŒŒ์ผ ๋‚ด์—์„œ ๊ธธ๊ณ  ์ง€์ €๋ถ„ํ•œ if (pathname.includes('admin')) return AdminLayout ํ˜•ํƒœ์˜ ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ฅผ ๋ฐ•์•„๋ฒ„๋ฆฌ๋Š” ๊ตฌ์‹œ๋Œ€์  ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ. ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ์˜ค์—ผ ๋ฐœ์ƒ.

โœ… ์šฐ์•„ํ•œ ์ฝ”๋“œ ( Pro Approach ) - ๋ผ์šฐํŠธ ๊ทธ๋ฃน ์ ์šฉ
ํด๋” ์ด๋ฆ„ ์•ž๋’ค์— ๊ด„ํ˜ธ () ๋ฅผ ๋ฌถ์–ด์ฃผ์–ด "URL ๊ฒฝ๋กœ์—์„œ๋Š” ์™„์ „ํžˆ ์ƒ๋žต" ํ•˜๋ฉด์„œ "์กฐ์ง์ ์ธ ํŒŒ์ผ ๊ทธ๋ฃน" ์„ ๋งŒ๋“ค์–ด๋‚ด.

app/
 โ”œโ”€ (consumer)/
 โ”‚   โ”œโ”€ layout.tsx      // ๐ŸŽจ ์˜์ˆ™: "์ผ๋ฐ˜ ์œ ์ €์šฉ ๊น”๋”ํ•œ ๋ ˆ์ด์•„์›ƒ"
 โ”‚   โ”œโ”€ page.tsx        // url: '/'
 โ”‚   โ””โ”€ studies/page.tsx// url: '/studies'
 โ”‚
 โ””โ”€ (admin)/
     โ”œโ”€ layout.tsx      // ๐Ÿ‘” ์˜์ˆ˜: "์‹œ์ปค๋จผ ์–ด๋“œ๋ฏผ ์ „์šฉ ์‚ฌ์ด๋“œ๋ฐ” ๋ ˆ์ด์•„์›ƒ!"
     โ””โ”€ dashboard/page.tsx // url: '/dashboard'

์ด ๊ตฌ์กฐ ๋•๋ถ„์—, ์™„๋ฒฝํžˆ ๊ณ ๋ฆฝ๋œ ์ตœ์ƒ์œ„ ๋ ˆ์ด์•„์›ƒ์„ 2๊ฐœ ์ด์ƒ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ฒŒ ๋ผ!

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
Route Group ์€ URL ์— ํ”์ ์„ ๋‚จ๊ธฐ์ง€ ์•Š๋Š” ์œ ๋ น ํด๋”์ด์ž, ๋ ˆ์ด์•„์›ƒ ๋ถ„๋ฆฌ ์ˆ˜๊ฑฐํ•จ์ด๋‹ค.


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

โŒ You cannot define a route with the same specificity...

์›์ธ: (consumer)/page.tsx ์™€ (admin)/page.tsx ๋ฅผ ๋‘˜ ๋‹ค ๋งŒ๋“ค์–ด ๋ฒ„๋ ธ์Œ.
๊ด„ํ˜ธ ๋ ˆ์ด์–ด๋Š” URL์—์„œ ๋ฌด์‹œ๋˜๋ฏ€๋กœ, ๋‘ ํŽ˜์ด์ง€ ๋ชจ๋‘ ์ธํ„ฐ๋„ท ์ฃผ์†Œ๋กœ๋Š” / ์ตœ์ƒ์œ„๋ฅผ ๋ฐ”๋ผ๋ณด๊ฒŒ ๋˜์–ด ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•ด.
ํ•ด๊ฒฐ์ฑ…: ๊นŠ์ด๊ฐ€ ๊ฒน์น˜๋Š” ๋ฃจํŠธ ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… ์‹œ์—๋Š” ๊ทธ๋ฃน ์ค‘ ํ•˜๋‚˜์—๋งŒ ๋ฐฐ์น˜ํ•˜๊ฑฐ๋‚˜ ํด๋” ๊ฒฝ๋กœ๋ฅผ ๋ช…์‹œํ•ด์•ผ ํ•ด.


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

์˜ค๋Š˜ ๋ฐฐ์šด ํ•ต์‹ฌ์„ ํ•œ๋ˆˆ์— ์ •๋ฆฌํ•ด๋ณผ๊นŒ? ์‹ค๋ฌด์—์„œ ๊ธธ์„ ์žƒ์—ˆ์„ ๋•Œ ์ด๊ฒƒ๋งŒ ๋ด๋„ ๋ผ.

๐Ÿ“‹ ํŠน์ˆ˜ ํŒŒ์ผ๋ช… ์—ญํ•  ์š”์•ฝ

ํŠน์ˆ˜ ํŒŒ์ผ๋ช…์ฃผ๋œ ๋ชฉ์ ๊ฒฝ๋กœ ์ด๋™ ์‹œ ๋งˆ์šดํŠธ ์œ ์ง€ ์—ฌ๋ถ€
layout.tsx๊ตฌ์กฐ ์œ ์ง€, ์žฌ๋ Œ๋”๋ง ๋ฐฉ์ง€ (์ƒํƒœ ๋ณด์กด)โœ… ์œ ์ง€๋จ (์™„๋ฒฝ ๋ฐฉ์–ด)
template.tsxํŽ˜์ด์ง€ ์ง„์ž… ์• ๋‹ˆ๋ฉ”์ด์…˜, ๊ฐ•์ œ ์ดˆ๊ธฐํ™”โŒ ํŒŒ๊ดด ํ›„ ์ƒˆ๋กญ๊ฒŒ ๋งˆ์šดํŠธ
page.tsxURL ์˜ ์ตœ์ข… ์ฃผ์ธ๊ณต(๋ฉ”์ธ ์ฝ˜ํ…์ธ )-
(folder)URL ์— ์˜ํ–ฅ ์•ˆ ์ฃผ๊ณ  ๋ ˆ์ด์•„์›ƒ ๊ทธ๋ฃนํ•‘ ๋ถ„๋ฆฌ-

โš ๏ธ ์ ˆ๋Œ€ ํ•˜์ง€ ๋ง ๊ฒƒ ( ์•ˆํ‹ฐ ํŒจํ„ด )

์ƒํ™ฉโŒ ๋‚˜์œ ์˜ˆโœ… ์ข‹์€ ์˜ˆ
๋ ˆ์ด์•„์›ƒ ๋ถ„๊ธฐpathname ์ฒดํฌํ•ด์„œ ๊ฑฐ๋Œ€ํ•œ if ๋ฌธ ๋Œ๋ฆฌ๊ธฐRoute Group ์œผ๋กœ ๋ ˆ์ด์•„์›ƒ ๋ฌผ๋ฆฌ์  ๋ถ„๋ฆฌ
์ƒํƒœ ์œ ์ง€๋ฐ์ดํ„ฐ ๋ณด์กด์ด ํ•„์š”ํ•œ๋ฐ template ์“ฐ๊ธฐ๋ฐ˜๋“œ์‹œ Layout ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ๋ Œ๋”๋ง ์ฐจ๋‹จ
ํŽ˜์ด์ง€ ๊ตฌ์กฐํ•œ ํŒŒ์ผ์— ๋ชจ๋“  ๋ฉ”๋‰ด์™€ ๋‚ด์šฉ์„ ๋‹ค ๋„ฃ๊ธฐ์ƒ์† ๊ตฌ์กฐ์— ๋งž์ถฐ layout ๊ณผ page ๋กœ ์ ์ ˆํžˆ ์ชผ๊ฐœ๊ธฐ

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

Q1. ์˜์ˆ™(๋””์ž์ด๋„ˆ) ์ด๊ฐ€ ์š”๊ตฌํ–ˆ๋‹ค. "์œ ์ €๊ฐ€ ๋ฌธ์˜ ํผ ์ž‘์„ฑ ์ค‘์— ๋’ค๋กœ ๊ฐ€๊ธฐ๋ฅผ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํƒญ์œผ๋กœ ๋„˜์–ด๊ฐ”๋‹ค ์™€๋„, ์ ์–ด๋‘์—ˆ๋˜ ๊ธ€์ž๋“ค์ด ์ ˆ๋Œ€(!!!) ๋‚ ์•„๊ฐ€์ง€ ์•Š๊ณ  ์œ ์ง€๋˜์–ด์•ผ ํ•ด์š”." ์˜์ฒ ์ด๋Š” ์ด ํผ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฃจํŠธ ์ƒ๋‹จ ๊ตฌ์กฐ๋ฅผ ์–ด๋–ค ํŒŒ์ผ ํฌ๋งท์œผ๋กœ ์„ธํŒ…ํ•ด์•ผ ํ• ๊นŒ? layout.tsx ์™€ template.tsx ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ณ ๋ฅด๊ณ  ์ด์œ ๋ฅผ ์„ค๋ช…ํ•ด๋ผ.

โœ… ์ •๋‹ต: layout.tsx

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: ํผ ๋ฐ์ดํ„ฐ(State) ๋ฅผ ๋ณด์กดํ•˜๋ ค๋ฉด URL ์ด๋™ ๊ฐ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํŒŒ๊ดด(Unmount) ๋˜์ง€ ์•Š์•„์•ผ ํ•ด. layout.tsx ๋Š” ์ž์‹ ๊ฒฝ๋กœ๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ์ƒํƒœ๋ฅผ ๋๊นŒ์ง€ ์ง€์ผœ์ฃผ๋Š” ์ฒ ๋ฒฝ ๋ฐฉ์–ด๋ง‰ ์—ญํ• ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์•ผ.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, template.tsx ๋ฅผ ์“ฐ๋Š” ์ˆœ๊ฐ„ ์œ ์ €๊ฐ€ ํƒญ์„ ๋ˆ„๋ฅด์ž๋งˆ์ž ์ •์„ฑ๊ป ์“ด ๊ธ€์ž๊ฐ€ ์•ˆ๊ฐœ์ฒ˜๋Ÿผ ์ฆ๋ฐœํ•ด ๋ฒ„๋ฆด ๊ฑฐ์˜ˆ์š”!"
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "๊ธˆ์ชฝ๊ฐ™์€ ์œ ์ € ๋ฐ์ดํ„ฐ๋Š” ๋ ˆ์ด์•„์›ƒ์— ๋งก๊ฒจ๋ผ."

Q2. ์˜ํ˜ธ(๋ฆฌ๋“œ) ๊ฐ€ ํŠน์ • ํŒŒ์ผ ํŠธ๋ฆฌ ์•ˆ์— (auth) ๋ผ๋Š” ํด๋”๋ฅผ ํŒ ๊ณ  ๋‚ด๋ถ€๋กœ ๋กœ๊ทธ์ธ์„ ์˜ฎ๊ฒผ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ผ์šฐํŠธ ๊ทธ๋ฃน์„ ์ƒ์„ฑํ•จ์œผ๋กœ์จ ์–ป๋Š” ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ "์•„ํ‚คํ…์ฒ˜์  ์žฅ์ "์€ ๋ฌด์—‡์ธ์ง€ URL ์ œ์–ด์˜ ๊ด€์ ์ด ์•„๋‹Œ layout ์˜ ๊ด€์ ์—์„œ ์„ค๋ช…ํ•˜๋ผ.

โœ… ์ •๋‹ต: ํŠน์ • ๋ชฉ์ (์ธ์ฆ, ์–ด๋“œ๋ฏผ ๋“ฑ) ์— ๋งž๊ฒŒ ๋ผˆ๋Œ€(Layout) ๋ฅผ ์™„์ „ํžˆ ์ชผ๊ฐœ์„œ ๊ณ ๋ฆฝ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ: ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€(์—ฌ๋ฐฑ ๊ฐ€๋“) ์™€ ๋ฉ”์ธ ๋Œ€์‹œ๋ณด๋“œ(์‚ฌ์ด๋“œ๋ฐ” ๋ณต์žก) ์˜ ๋ ˆ์ด์•„์›ƒ์„ ํ•˜๋‚˜์˜ ๋ถ€๋ชจ ๋ฐ‘์—์„œ ์ง€์ €๋ถ„ํ•œ ์กฐ๊ฑด๋ฌธ ์—†์ด ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.
  • ๋น„์ฆˆ๋‹ˆ์Šค ๊ฐ€์น˜: ์ธ์ฆ ์˜์—ญ์—๋งŒ ํŠน๋ณ„ํ•œ ๋ณด์•ˆ ํ—ค๋”๋‚˜ ๋กœ์ง์„ ์ ์šฉํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์ˆ˜์›”ํ•ด์ ธ์„œ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์šฐ์•„ํ•ด์ง€์ง€.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "๋ผ์šฐํŠธ ๊ทธ๋ฃน์€ ๋ ˆ์ด์•„์›ƒ์˜ ๊ตฌ์›์ž๋‹ค."

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

์˜ค๋Š˜ App Router์˜ ๊ตฌ์กฐ๋ฅผ ๋ฐฐ์šฐ๋ฉด์„œ ๋„ฅ์ŠคํŠธ์˜ ๊ฑฐ๋Œ€ํ•œ ๋ณด๋ฌผ์„ ๋ฐœ๊ฒฌํ•œ ๊ธฐ๋ถ„์ด์•ผ! ํŠนํžˆ '๊ณต์œ ๋˜๋Š” ๋ฐ˜์ฐฌํ†ต(Layout)' ๊ณผ '๋งค๋ฒˆ ์ƒˆ๋กœ ์ฐจ๋ฆฌ๋Š” ๊ตญ๋ฐฅ(Page)' ์˜ ์กฐํ™”๋Š” ์ •๋ง ์˜ˆ์ˆ ์ด๋”๋ผ๊ณ .

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "๋ณ€ํ•˜๋Š” ๊ฒƒ(Page) ๊ณผ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ(Layout) ์„ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์˜ ์ผ์†์„ ๋œ์–ด์ฃผ์ž!"

๊ทธ๋™์•ˆ ํŽ˜์ด์ง€๋ฅผ ์˜ฎ๊ธธ ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  ๊ฑธ ๋‹ค ์ง€์› ๋‹ค ๋‹ค์‹œ ๊ทธ๋ ธ๋˜ ๋‚ด ์ง€๋‚œ๋‚ ๋“ค์ด ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์˜ ํŒฉํญ๊ณผ ํ•จ๊ป˜ ์Šค์ณ ์ง€๋‚˜๊ฐ”์ง€๋งŒ... ๋ญ ์–ด๋•Œ! ์ด์ œ๋ผ๋„ ์ œ๋Œ€๋กœ ๋œ ๊ตฌ์กฐ๋ฅผ ์•Œ์•˜์œผ๋‹ˆ ๋์ง€! ์˜ค๋Š˜ ์ง‘์ค‘๋ ฅ ์ข‹์•˜๋‹ค. ์ง‘์— ๊ฐ€์„œ ์‹œ์›ํ•œ ๋งฅ์ฃผ ํ•œ ์บ” ๋”ฐ๊ณ  ํ‘น ์‰ฌ์–ด์•ผ๊ฒ ์–ด. ๋‚ด์ผ์€ ๋‚ด๊ฐ€ ์˜ค๋Š˜ ๋ฐฐ์šด ๋ ˆ์ด์•„์›ƒ ์„ค๊ณ„ ๋Šฅ๋ ฅ์„ ๋ฝ๋‚ด์„œ ์˜ํ˜ธ ๋‹˜์„ ๊นœ์ง ๋†€๋ผ๊ฒŒ ํ•ด๋“œ๋ ค์•ผ์ง€! ๐Ÿฃ


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