Next.js Pages Router ๋ ˆ๊ฑฐ์‹œ๋ฅผ ๋„˜์–ด, App Router ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ ๋‹ค์‹œ ์„ค๊ณ„ํ•˜๊ธฐ

minyoung
  • #Next.js
  • #App Router
  • #React
  • #Frontend

๋“ค์–ด๊ฐ€๋ฉฐ

์•ˆ๋…•ํ•˜์„ธ์š”, ๋…ธ๋จธ์Šค FE ๊ฐœ๋ฐœ์ž ๊น€๋ฏผ์˜์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ์— ๋…ธ๋จธ์Šค์—์„œ ์ƒˆ๋กœ์šด ์ด์ปค๋จธ์Šค ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ๊ธฐ๊ฐ„์€ ์•ฝ ํ•œ ๋‹ฌ ์ •๋„์˜€๊ณ , ์š”๊ตฌ์‚ฌํ•ญ ์ƒ๋‹น์ˆ˜๊ฐ€ ๊ธฐ์กด fromm store์™€ ์œ ์‚ฌํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•œ ๋‹ฌ์ด๋ผ๋Š” ๊ธฐ๊ฐ„ ์•ˆ์— ๋ชจ๋“  ๊ฒƒ์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋งŒ๋“ค๊ธฐ๋ณด๋‹ค๋Š”, ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๋Š” ํŽธ์ด ํšจ์œจ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•˜๋ฉด Next.js Pages Router ๊ธฐ๋ฐ˜์˜ ๋ ˆ๊ฑฐ์‹œ ๊ตฌ์กฐ๊นŒ์ง€ ํ•จ๊ป˜ ์œ ์ž…๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ํŒ€์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉํ–ฅ์„ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

โ€œ๊ธฐ์กด ์ฝ”๋“œ๋Š” ์ฐธ์กฐ ์ž์‚ฐ์œผ๋กœ ํ™œ์šฉํ•˜๋˜, App Router ๊ตฌ์กฐ์— ๋งž์ถฐ ์žฌ์„ค๊ณ„ํ•˜๊ณ  ํ•„์š”ํ•œ ๋ฆฌํŒฉํ† ๋ง๊นŒ์ง€ ํ•จ๊ป˜ ์ง„ํ–‰ํ•œ๋‹ค.โ€

๋‹จ์ˆœํ•œ ์ฝ”๋“œ ๋ณต์‚ฌ๊ฐ€ ์•„๋‹ˆ๋ผ, ๊ฒ€์ฆ๋œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ์žฌํ™œ์šฉํ•˜๋ฉด์„œ๋„ ์ƒˆ๋กœ์šด ์•„ํ‚คํ…์ฒ˜์™€ ํŒ€ ์ปจ๋ฒค์…˜์— ๋งž๊ฒŒ ๋‹ค์‹œ ๊ตฌํ˜„ํ•˜๋Š” ์ „๋žต์ด์—ˆ์Šต๋‹ˆ๋‹ค.

1. Pages Router vs App Router: ํ•ต์‹ฌ ์ฐจ์ด์ 

์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „, ํŒ€ ๋‚ด์—์„œ App Router์˜ ํ•ต์‹ฌ ๋ณ€ํ™”๋ฅผ ๋จผ์ € ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ผ์šฐํŒ… ๊ตฌ์กฐ

# Pages Router (๊ธฐ์กด fromm store)
pages/
โ”œโ”€โ”€ _app.tsx
โ”œโ”€โ”€ _document.tsx
โ”œโ”€โ”€ index.tsx
โ”œโ”€โ”€ goods/
โ”‚   โ””โ”€โ”€ [id].tsx

# App Router (์ƒˆ ํ”„๋กœ์ ํŠธ)
app/
โ”œโ”€โ”€ layout.tsx
โ”œโ”€โ”€ page.tsx
โ”œโ”€โ”€ goods/
โ”‚   โ””โ”€โ”€ [id]/
โ”‚       โ””โ”€โ”€ page.tsx

Pages Router์—์„œ๋Š” _app.tsx์™€ _document.tsx๊ฐ€ ์ „์—ญ ์—ญํ• ์„ ๋‹ด๋‹นํ–ˆ์ง€๋งŒ, App Router์—์„œ๋Š” layout.tsx๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๊ณ„์ธต์ ์ธ ๋ ˆ์ด์•„์›ƒ ๊ตฌ์„ฑ์ด ๊ฐ€๋Šฅํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ณ€ํ™”

๊ตฌ๋ถ„Pages RouterApp Router
๊ธฐ๋ณธ ์ปดํฌ๋„ŒํŠธClient ComponentServer Component
๋ฐ์ดํ„ฐ ํŽ˜์นญgetServerSideProps,ย getStaticPropsServer Component ๋‚ด๋ถ€ย async/await
๋ฉ”ํƒ€๋ฐ์ดํ„ฐnext/headgenerateMetadata

๊ฐ€์žฅ ํฐ ๋ณ€ํ™”๋Š” Server Component๊ฐ€ ๊ธฐ๋ณธ๊ฐ’์ด ๋˜์—ˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

๊ธฐ์กด Pages Router์—์„œ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰๋˜๋Š” Client Component ์ค‘์‹ฌ์ด์—ˆ์ง€๋งŒ, App Router์—์„œ๋Š” ๋ณ„๋„์˜ 'use client' ์„ ์–ธ์ด ์—†๋Š” ํ•œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

Server Component๋Š” ๋ธŒ๋ผ์šฐ์ €๋กœ ์ „๋‹ฌ๋˜๋Š” JavaScript ์–‘์„ ์ค„์ผ ์ˆ˜ ์žˆ๊ณ , ์„œ๋ฒ„์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด ์ƒํƒœ(useState), ์ดํŽ™ํŠธ(useEffect), ๋ธŒ๋ผ์šฐ์ € API(window, document), ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ(onClick ๋“ฑ)๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋‹ค๋งŒ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Server Component์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๊ธฐ๋ณด๋‹ค, ๋Œ€๋ถ€๋ถ„์˜ ๋ฐ์ดํ„ฐ ํŽ˜์นญ์„ ํด๋ผ์ด์–ธํŠธ์˜ TanStack Query์— ์œ„์ž„ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. Server Component๋Š” API ํ—ค๋” ์ดˆ๊ธฐํ™”, ์ธ์ฆ ๊ฐ€๋“œ ๋ž˜ํ•‘, ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋“ฑ ์ตœ์†Œํ•œ์˜ ์„œ๋ฒ„ ์ž‘์—…๋งŒ ๋‹ด๋‹นํ•˜๊ณ , ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜๊ณผ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋Š” Client Component์—์„œ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ณ€ํ™”๋กœ ์ธํ•ด ๋‹จ์ˆœํžˆ ๋ผ์šฐํŒ… ๋ฐฉ์‹๋งŒ ๋‹ฌ๋ผ์ง„ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, โ€œ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„œ๋ฒ„์—์„œ ์‹คํ–‰ํ•  ๊ฒƒ์ธ๊ฐ€โ€๊นŒ์ง€ ํ•จ๊ป˜ ๊ณ ๋ คํ•˜๋Š” ๊ตฌ์กฐ์  ์„ค๊ณ„๊ฐ€ ์ค‘์š”ํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

useRouter API ๋ณ€ํ™”

// Pages Router
import { useRouter } from 'next/router';
const router = useRouter();
router.pathname   // โœ…
router.query      // โœ…
router.push()     // โœ…

// App Router
import { useRouter } from 'next/navigation';
const router = useRouter();
router.pathname   // โŒ โ†’ usePathname()
router.query      // โŒ โ†’ useSearchParams()
router.push()     // โœ…

๊ธฐ์กด Pages Router์—์„œ ์ต์ˆ™ํ–ˆ๋˜ API ์ผ๋ถ€๊ฐ€ ์ œ๊ฑฐ๋˜๋ฉด์„œ, ํ”„๋กœ์ ํŠธ ์ „๋ฐ˜์— ๊ฑธ์ณ ๋ผ์šฐํŒ… ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์„ค๊ณ„ํ•  ํ•„์š”๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

2. App Router ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ ์žฌ์„ค๊ณ„

์ƒํ™ฉ

์ƒˆ ํ”„๋กœ์ ํŠธ์˜€์ง€๋งŒ ๋‹ค์Œ ์กฐ๊ฑด๋“ค์ด ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๊ธฐ์กด fromm store์™€ ๋„๋ฉ”์ธ ๋กœ์ง์ด ๋งค์šฐ ์œ ์‚ฌ
  • ์ฃผ๋ฌธ, ๊ฒฐ์ œ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋“ฑ ๊ฒ€์ฆ๋œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ด๋ฏธ ์กด์žฌ
  • ํ•˜์ง€๋งŒ ๊ธฐ์กด ๊ตฌ์กฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•˜๋ฉด Pages Router ํŒจํ„ด๊นŒ์ง€ ํ•จ๊ป˜ ์œ ์ž…๋จ

์ฆ‰, โ€œ๋กœ์ง์€ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์ง€๋งŒ ๊ตฌ์กฐ๋Š” ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ์ง€ ์•Š์€โ€ ์ƒํ™ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์ ‘๊ทผ ๋ฐฉ์‹

๊ธฐ์กด ๊ตฌ์กฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•˜์ง€ ์•Š๊ณ , App Router ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์™€ ์ƒˆ๋กœ์šด ํŒ€ ์ปจ๋ฒค์…˜์— ๋งž์ถฐ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์„ค๊ณ„ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ ์•„๋ž˜ ํ•ญ๋ชฉ๋“ค์„ ์ค‘์ ์ ์œผ๋กœ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

  • App Router ๊ธฐ๋ฐ˜ ํด๋” ๊ตฌ์กฐ ์ •๋ฆฝ
  • Server / Client Component ์—ญํ•  ๋ถ„๋ฆฌ
  • ๊ณตํ†ต ๋ผ์šฐํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค ์žฌ์„ค๊ณ„
  • ์Šคํƒ€์ผ ์‹œ์Šคํ…œ ๋ฐ ํ† ํฐ ์ฒด๊ณ„ ๊ฐœ์„ 
  • ์ „์—ญ ๋ ˆ์ด์•„์›ƒ๊ณผ Provider ์ฑ…์ž„ ๋ถ„๋ฆฌ

์‹ค์ œ ์›Œํฌํ”Œ๋กœ์šฐ

  1. ๊ธฐ์กด ์„œ๋น„์Šค ๊ตฌ์กฐ ๋ฐ ๋„๋ฉ”์ธ ํ๋ฆ„ ๋ถ„์„
  2. App Router + ํŒ€ ์ปจ๋ฒค์…˜ ๊ธฐ์ค€ ์ •์˜
  3. ๊ธฐ๋Šฅ ๋‹จ์œ„๋กœ ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์— ๋งž์ถฐ ๊ตฌํ˜„
  4. ๊ตฌํ˜„ ๊ณผ์ •์—์„œ ๊ณตํ†ต ํŒจํ„ด๊ณผ ์ •์ฑ… ์ •๋ฆฌ

์ด ๊ณผ์ •์„ ํ†ตํ•ด ๋‹จ์ˆœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์•„๋‹ˆ๋ผ, ์ƒˆ ํ”„๋กœ์ ํŠธ ๊ธฐ์ค€์— ๋งž๋Š” ๊ตฌ์กฐ์™€ ๊ฐœ๋ฐœ ๋ฌธํ™”๋ฅผ ํ•จ๊ป˜ ์ •๋ฆฝํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

3. ๊ตฌ์ฒด์ ์œผ๋กœ ๊ฐœ์„ ํ•œ ๋ถ€๋ถ„๋“ค

์ƒˆ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๋ฉด์„œ, ๊ธฐ์กด์— ๊ฐœ์„ ํ•˜๊ณ  ์‹ถ์—ˆ๋˜ ๋ถ€๋ถ„๋“ค๋„ ํ•จ๊ป˜ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

3.1 useRouter ํ˜ธํ™˜ ๋ ˆ์ด์–ด ๊ตฌ์„ฑ + safeBack() ์ถ”๊ฐ€

๊ธฐ์กด Pages Router์˜ ์‚ฌ์šฉ ํŒจํ„ด์€ ์ตœ๋Œ€ํ•œ ์œ ์ง€ํ•˜๋˜, ์ƒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” safeBack() ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • safeBack()ย โ†’ SNS, ์™ธ๋ถ€ ๋ธŒ๋ผ์šฐ์ € ๋“ฑ์—์„œ ์ง์ ‘ ์œ ์ž…๋œ ์‚ฌ์šฉ์ž๊ฐ€ ๋’ค๋กœ๊ฐ€๊ธฐ ์‹œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์™ธ๋ถ€ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ
safeBack() {
    const prevPath = sessionStorage.getItem(StorageKey.prevPath);

    if (prevPath) {
        sessionStorage.removeItem(StorageKey.prevPath);
        appRouter.back();
    } else {
        appRouter.push(withLocale(Path.Home));
    }
}

์ผ๋ฐ˜์ ์ธ router.back()์€ ์ด์ „ ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๋ธŒ๋ผ์šฐ์ € ๋™์ž‘์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์™ธ๋ถ€ ์œ ์ž… ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋น„์Šค ๋ฐ”๊นฅ์œผ๋กœ ์ด๋™ํ•˜๊ฑฐ๋‚˜ ๋นˆ ํ™”๋ฉด์„ ๋งˆ์ฃผํ•˜๋Š” UX ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚ด๋ถ€ ์ด๋™ ์ด๋ ฅ์ด ์—†๋Š” ๊ฒฝ์šฐ ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ด๋™์‹œํ‚ค๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณด์™„ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ธฐ์กด ์ฝ”๋“œ์˜ ๋ผ์šฐํ„ฐ ์‚ฌ์šฉ ๋ฐฉ์‹์„ ์œ ์ง€ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ณผ์ •์—์„œ ๋Œ€๊ทœ๋ชจ ์ˆ˜์ • ์—†์ด ์ ์ง„์ ์œผ๋กœ App Router ๊ตฌ์กฐ์— ์ ์‘ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

3.2 Server / Client Component ๊ฒฝ๊ณ„ ๋ช…ํ™•ํ™”

๊ธฐ์กด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํด๋ผ์ด์–ธํŠธ ๊ธฐ๋ฐ˜์ด์—ˆ์ง€๋งŒ, ์ƒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Server Component๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ทœ์น™์„ ์ •๋ฆฝํ–ˆ์Šต๋‹ˆ๋‹ค.

app/[M]/order/
โ”œโ”€โ”€ page.tsx              # Server Component
โ””โ”€โ”€ OrderClientPage.tsx   # Client Component
// page.tsx โ€” Server Component ('use client' ์—†์Œ)
const OrderPage = async () => {
    return (
        <SigninRequiredGuard>
            <OrderClientPage />
        </SigninRequiredGuard>
    );
};
// OrderClientPage.tsx โ€” Client Component
'use client';

const OrderClientPage: FC = () => {
    const { data } = useGetOrderInfo();
    return (...);
};

๋‹ค๋งŒ ํ˜„์žฌ ๊ตฌ์กฐ์—์„œ๋Š” Server Component์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์„œ๋ฒ„ ๋ Œ๋”๋ง์˜ ํ•ต์‹ฌ ์ด์ ์„ ์ถฉ๋ถ„ํžˆ ํ™œ์šฉํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๋ณด๊ธฐ๋Š” ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด fromm store์˜ TanStack Query ๊ธฐ๋ฐ˜ ํด๋ผ์ด์–ธํŠธ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ App Router ์œ„์— ์•ˆ์ •์ ์œผ๋กœ ์˜ฌ๋ฆฌ๋Š” ๊ฒƒ์ด ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์˜ ์šฐ์„  ๋ชฉํ‘œ์˜€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ํ™œ์šฉ์€ ์ดํ›„ ๊ฐœ์„  ๊ณผ์ œ๋กœ ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค.

3.3 z-index ํ† ํฐ ์ฒด๊ณ„ ๊ฐœ์„ 

๊ธฐ์กด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” z-index๋ฅผ modalZIndex๋ผ๋Š” ๋‹จ์ผ ๊ธฐ์ค€๊ฐ’์„ ๋‘๊ณ , ํ•„์š”์— ๋”ฐ๋ผ CSS ๋ณ€์ˆ˜์— ๋”ํ•˜๊ฑฐ๋‚˜ ๋นผ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

// ๊ธฐ์กด ํ”„๋กœ์ ํŠธ: ๋‹จ์ผ ๊ธฐ์ค€๊ฐ’ ์ •์˜
zIndex: {
    modalZIndex: { value: 10 }
}
// ์‚ฌ์šฉ์ฒ˜์—์„œ ๊ธฐ์ค€๊ฐ’์— ๋”ํ•˜๊ฑฐ๋‚˜ ๋นผ์„œ ๋ ˆ์ด์–ด ์กฐ์ ˆ
zIndex: 'calc(var(--z-index-modal-z-index) + 2)'   // ๋“œ๋กญ๋‹ค์šด
zIndex: 'calc(var(--z-index-modal-z-index) + 10)'  // ๋“œ๋กญ๋‹ค์šด ๋ชฉ๋ก

ํ•˜๋‚˜์˜ ๊ธฐ์ค€๊ฐ’์— ์˜์กดํ•˜๋‹ค ๋ณด๋‹ˆ +2, +10 ๊ฐ™์€ ๋งค์ง ๋„˜๋ฒ„๊ฐ€ ๊ณณ๊ณณ์— ์ƒ๊ฒผ๊ณ , ๋ ˆ์ด์–ด ๊ฐ„ ์šฐ์„ ์ˆœ์œ„๋ฅผ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ: ์—ญํ• ๋ณ„ ํ† ํฐ ๋ถ„๋ฆฌ

์ƒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ฐ ๋ ˆ์ด์–ด์˜ ์—ญํ• ์— ๋งž๋Š” ํ† ํฐ์„ ๊ฐœ๋ณ„ ์ •์˜ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

tokens: {
    zIndex: {
        local: { value: 1 },       // ๋ฐฐ๋„ˆ ์ปจํŠธ๋กค, ์บ๋Ÿฌ์…€ ์ธ๋””์ผ€์ดํ„ฐ
        backdrop: { value: 9 },     // ํ—ค๋”, ๋ชจ๋‹ฌ ๋ฐฑ๋“œ๋กญ
        modalZIndex: { value: 10 }, // ๋ชจ๋‹ฌ, ๋‹ค์ด์–ผ๋กœ๊ทธ
        overlay: { value: 11 },     // ๋ชจ๋‹ฌ ์œ„ ์˜ค๋ฒ„๋ ˆ์ด (๊ฒ€์ƒ‰์ฐฝ ๋“ฑ)
        loading: { value: 20 }      // ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ
    }
}
// Before: ๋งค์ง ๋„˜๋ฒ„ ์˜์กด
zIndex: 'calc(var(--z-index-modal-z-index) + 2)'

// After: ์˜๋ฏธ ๊ธฐ๋ฐ˜ ํ† ํฐ
zIndex: 'overlay'

local(1) < backdrop(9) < modal(10) < overlay(11) < loading(20) ์ฒด๊ณ„๋กœ ์ •๋ฆฌ๋˜๋ฉด์„œ, ์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๋ ˆ์ด์–ด ์—ญํ•  ๊ธฐ์ค€์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๊ณ , ์Šคํƒ€์ผ ์ถฉ๋Œ๊ณผ ์šฐ์„ ์ˆœ์œ„ ํ˜ผ๋ž€์ด ํฌ๊ฒŒ ์ค„์–ด๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

3.4 _app.tsx ์—ญํ•  ๋ถ„๋ฆฌ

๊ธฐ์กด _app.tsx๋Š” ์„œ๋ฒ„ ๋กœ์ง๊ณผ ํด๋ผ์ด์–ธํŠธ ๋กœ์ง์ด ํ˜ผ์žฌ๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ƒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ด๋ฅผ layout.tsx์™€ providers.tsx๋กœ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

// layout.tsx โ€” Server Component
const RootLayout = ({ children }: { children: ReactNode }) => {
    const locale = getLang();
    const currency = getCurrency();
    const messages = messagesMap[locale] ?? messagesMap.ko;

    setApiHeaders({ lang: locale, currency });

    return (
        <html lang={locale}>
            <body>
                <Providers locale={locale} messages={messages} currency={currency}>
                    {children}
                </Providers>
            </body>
        </html>
    );
};
// providers.tsx โ€” Client Component
'use client';

const Providers = ({ children, locale, messages, currency }: Props) => {
    const [queryClient] = useState(() => new QueryClient({ ... }));

    return (
        <NextIntlClientProvider locale={locale} messages={messages}>
            <JotaiProvider>
                <QueryClientProvider client={queryClient}>
                    <InitHeaders locale={locale} currency={currency}>
                        <ErrorBoundary>
                            <CustomApp>
                                <AuthReadyGate>{children}</AuthReadyGate>
                            </CustomApp>
                        </ErrorBoundary>
                    </InitHeaders>
                </QueryClientProvider>
            </JotaiProvider>
        </NextIntlClientProvider>
    );
};

๊ฐœ์„  ํฌ์ธํŠธ

  • ์„œ๋ฒ„/ํด๋ผ์ด์–ธํŠธ ์ฑ…์ž„ ๋ช…ํ™• ๋ถ„๋ฆฌ
  • Provider ์ดˆ๊ธฐํ™” ์ฑ…์ž„ ์ง‘์ค‘
  • AuthReadyGate๋ฅผ ํ†ตํ•œ ์ธ์ฆ ์ค€๋น„ ์ „ ๋กœ๋”ฉ ์ฒ˜๋ฆฌ๋กœ ํ™”๋ฉด ๊นœ๋นก์ž„ ๊ฐ์†Œ

3.5 Panda CSS: breakpoints vs conditions ์ •๋ฆฌ

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์Šคํƒ€์ผ ์‹œ์Šคํ…œ์—์„œ๋„ ํ˜ผ๋ž€ ์š”์†Œ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

breakpoints์™€ conditions๋ฅผ ๋™์‹œ์— ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

๋ฌธ์ œ ์ƒํ™ฉ

breakpoints: {
    tablet: '760px',
    pc: '1000px'
}

conditions: {
    tablet: '@media (min-width: 760px) and (max-width: 999px)',
    pc: '@media (min-width: 1000px)'
}

์ด๋ฆ„์€ ๊ฐ™์ง€๋งŒ ์‹ค์ œ ์˜๋ฏธ๊ฐ€ ๋‹ฌ๋ž์Šต๋‹ˆ๋‹ค.

๊ตฌ๋ถ„breakpointsconditions
๋ฐฉ์‹๋ชจ๋ฐ”์ผ ํผ์ŠคํŠธ (min-widthย ๋ˆ„์ )์ปค์Šคํ…€ ๋ฒ”์œ„ ์ง€์ •
์‚ฌ์šฉ ํ˜•ํƒœ{ base, tablet, pc }{ _tablet, _pc }
tablet ์˜๋ฏธ760pxย ์ด์ƒ ์ „์ฒด760px ~ 999pxย ํ•œ์ •

๊ฐ™์€ tablet์ด๋ผ๋Š” ์ด๋ฆ„์ด ๋งฅ๋ฝ์— ๋”ฐ๋ผ ์ „ํ˜€ ๋‹ค๋ฅธ ๋ฒ”์œ„๋กœ ๋™์ž‘ํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ตœ์ข… ๋ฐฉํ–ฅ

์—ฌ๋Ÿฌ ๋ฐฉ์•ˆ์„ ๊ฒ€ํ† ํ•œ ๋์—, conditions๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  breakpoints ์ค‘์‹ฌ์œผ๋กœ ํ†ต์ผํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด์œ :

  • ๋ชจ๋ฐ”์ผ ํผ์ŠคํŠธ ์ „๋žต์˜ ์ผ๊ด€์„ฑ ํ™•๋ณด
  • Panda CSS ๊ธฐ๋ณธ ์ฒ ํ•™๊ณผ ๋ถ€ํ•ฉ
  • ํŒ€ ๋‚ด ํ˜ผ๋ž€ ๊ฐ์†Œ
  • ๋ฐ˜์‘ํ˜• ์Šคํƒ€์ผ ํŒจํ„ด ๋‹จ์ˆœํ™”

์ดˆ๊ธฐ์—๋Š” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋น„์šฉ์ด ์žˆ์—ˆ์ง€๋งŒ, ์žฅ๊ธฐ์ ์œผ๋กœ๋Š” ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์ด ๋” ์ค‘์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋Š” ๋‹จ์ˆœํžˆ Pages Router์—์„œ App Router๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ์ž‘์—…์ด ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ ˆ๊ฑฐ์‹œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ๊ฒƒ์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  ์ƒˆ๋กœ์šด ๊ตฌ์กฐ๋ฅผ ์–ด๋–ค ๊ธฐ์ค€์œผ๋กœ ์„ค๊ณ„ํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์— ๋” ๊ฐ€๊นŒ์› ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ํ›„๋ฐ˜์—๋Š” ์„œ๋ฒ„/ํด๋ผ์ด์–ธํŠธ ์—ญํ•  ๋ถ„๋ฆฌ, ๋ผ์šฐํŒ… ๊ทœ์น™, ์Šคํƒ€์ผ ํ† ํฐ ์ •์ฑ… ๋“ฑ์ด ์ž๋ฆฌ ์žก์œผ๋ฉด์„œ ์ดํ›„ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์—์„œ๋„ ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ฐ˜์ด ๋งˆ๋ จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

App Router ํ™˜๊ฒฝ์—์„œ๋Š” Server Component, Client Component, ๋ผ์šฐํŒ…, ๋ ˆ์ด์•„์›ƒ, ์Šคํƒ€์ผ ์‹œ์Šคํ…œ์ด ๋ชจ๋‘ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ดˆ๊ธฐ ์•„ํ‚คํ…์ฒ˜์™€ ์ปจ๋ฒค์…˜ ์„ค๊ณ„๊ฐ€ ํ”„๋กœ์ ํŠธ ์ „์ฒด์˜ ์ƒ์‚ฐ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์— ํฐ ์˜ํ–ฅ์„ ์ค๋‹ˆ๋‹ค.

์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ตฌ์ถ•ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด๋ผ๋ฉด, ๊ธฐ๋Šฅ ๊ตฌํ˜„๋งŒ ์šฐ์„ ํ•˜๊ธฐ๋ณด๋‹ค ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ์™€ ๊ฐœ๋ฐœ ๊ธฐ์ค€์„ ๋จผ์ € ์ •๋ฆฌํ•˜๋Š” ์ ‘๊ทผ๋„ ์ถฉ๋ถ„ํžˆ ๊ฐ€์น˜ ์žˆ๋Š” ์„ ํƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ