์ž‘์„ฑ: 2026-03-04 05:39:06์ˆ˜์ •: 2026-03-04 05:39:06

Nuxt ๋Œ€์‹œ๋ณด๋“œ ํ”„๋กœ์ ํŠธ ์‹œ๋ฆฌ์ฆˆ (4): ๋Œ€์‹œ๋ณด๋“œ ๋ ˆ์ด์•„์›ƒ ๊ตฌํ˜„

์ด์ œ ๋””์ž์ธ ์‹œ์Šคํ…œ๊ณผ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ์ค€๋น„๋˜์—ˆ์œผ๋‹ˆ, ๋ณธ๊ฒฉ์ ์œผ๋กœ ๋Œ€์‹œ๋ณด๋“œ์˜ ๋ผˆ๋Œ€์ธ **๋ ˆ์ด์•„์›ƒ(Layout)**์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Naive UI์˜ ๋ ˆ์ด์•„์›ƒ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.


1. ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ ๊ตฌ์กฐ (layouts/default.vue)

์‚ฌ์ด๋“œ๋ฐ”, ํ—ค๋”, ์ฝ˜ํ…์ธ  ์˜์—ญ์œผ๋กœ ๋‚˜๋‰œ ํ‘œ์ค€ ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

<template>
  <n-layout has-sider h-screen>
    <!-- ์‚ฌ์ด๋“œ๋ฐ” -->
    <n-layout-sider
      bordered
      collapse-mode="width"
      :collapsed-width="64"
      :width="240"
      :collapsed="appStore.isSidebarCollapsed"
      show-trigger
      @collapse="appStore.toggleSidebar"
      @expand="appStore.toggleSidebar"
    >
      <div class="h-16 flex-center font-bold text-lg overflow-hidden whitespace-nowrap">
        {{ appStore.isSidebarCollapsed ? 'D' : 'Dashboard' }}
      </div>
      <n-menu :options="menuOptions" />
    </n-layout-sider>
 
    <n-layout>
      <!-- ํ—ค๋” -->
      <n-layout-header bordered class="h-16 flex items-center justify-between px-6">
        <div class="i-carbon-menu text-xl cursor-pointer" @click="appStore.toggleSidebar" />
        <div class="flex items-center gap-4">
          <span>{{ authStore.user.name }}</span>
          <n-avatar round :src="authStore.user.avatar" />
        </div>
      </n-layout-header>
 
      <!-- ์ฝ˜ํ…์ธ  ์˜์—ญ -->
      <n-layout-content content-style="padding: 24px;" class="bg-gray-50/50">
        <slot />
      </n-layout-content>
    </n-layout>
  </n-layout>
</template>
 
<script setup>
import { useAppStore } from '@/stores/app'
import { useAuthStore } from '@/stores/auth'
 
const appStore = useAppStore()
const authStore = useAuthStore()
 
const menuOptions = [
  { label: 'ํ™ˆ', key: 'home', icon: () => h('div', { class: 'i-carbon-home' }) },
  { label: 'ํ†ต๊ณ„', key: 'stats', icon: () => h('div', { class: 'i-carbon-chart-line' }) },
  { label: '์„ค์ •', key: 'settings', icon: () => h('div', { class: 'i-carbon-settings' }) },
]
</script>

2. ๋ ˆ์ด์•„์›ƒ์˜ ํ•ต์‹ฌ ํฌ์ธํŠธ

  1. has-sider: n-layout ์•ˆ์— ์‚ฌ์ด๋“œ๋ฐ”๊ฐ€ ํฌํ•จ๋  ๋•Œ ๋ฐ˜๋“œ์‹œ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋Š” ์†์„ฑ์ž…๋‹ˆ๋‹ค.
  2. slot: ๊ฐ ํŽ˜์ด์ง€์˜ ๋‚ด์šฉ์ด ๋“ค์–ด๊ฐˆ ์ž๋ฆฌ์ž…๋‹ˆ๋‹ค.
  3. ๋ฐ˜์‘ํ˜• ๋Œ€์‘: ์‚ฌ์ด๋“œ๋ฐ” ์ ‘ํž˜ ์ƒํƒœ๋ฅผ Pinia์™€ ์—ฐ๋™ํ•˜์—ฌ ํ—ค๋”์˜ ๋ฒ„ํŠผ์ด๋‚˜ ์‚ฌ์ด๋“œ๋ฐ” ์ž์ฒด ํŠธ๋ฆฌ๊ฑฐ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. UnoCSS๋กœ ์—ฌ๋ฐฑ๊ณผ ๋ฐฐ๊ฒฝ ์ฒ˜๋ฆฌ

content-style ์™ธ์—๋„ UnoCSS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋””ํ…Œ์ผํ•œ ์Šคํƒ€์ผ์„ ์žก์Šต๋‹ˆ๋‹ค.

  • h-screen: ์ „์ฒด ํ™”๋ฉด ๋†’์ด ์ฐจ์ง€.
  • bg-gray-50/50: ์ฝ˜ํ…์ธ  ์˜์—ญ์— ์‚ด์ง ํšŒ์ƒ‰๋น› ๋ฐฐ๊ฒฝ์„ ์ฃผ์–ด ์นด๋“œ๊ฐ€ ๋‹๋ณด์ด๊ฒŒ ํ•จ.

4. ๊ฒฐ๋ก 

๊น”๋”ํ•˜๊ณ  ์ „๋ฌธ์ ์ธ ๋Œ€์‹œ๋ณด๋“œ ๋ ˆ์ด์•„์›ƒ์ด ์™„์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! ์ด์ œ ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋กœ ๋Œ€์‹œ๋ณด๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€์— ์ฐจํŠธ์™€ ๋ฐ์ดํ„ฐ ํ‘œ๋ฅผ ์ฑ„์›Œ ๋„ฃ์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.