레이아웃

레이아웃 (Layouts)

여러 페이지에 걸쳐 반복되는 UI 패턴, 예를 들어 헤더·푸터·사이드바 같은 공통 뼈대가 있다면 매 페이지마다 붙여 넣기보다 한 곳에 모아 재사용하고 싶을 거예요. Nuxt는 이런 공통 UI 패턴을 재사용 가능한 레이아웃으로 뽑아내는 layouts 프레임워크를 제공해요.

출처: 공식문서

본문

app/layouts/ 디렉터리에 두는 컴포넌트는 성능을 위해 사용할 때 비동기 임포트로 자동 로드돼요.

레이아웃 켜기

레이아웃은 app.vue<NuxtLayout>을 추가하면 켜져요.

<!-- app/app.vue -->
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

레이아웃을 사용하는 방법은 여러 가지예요.

  • 페이지에서 definePageMetalayout 속성 설정
  • <NuxtLayout>name prop 설정
  • 라우트 규칙(route rules)에서 appLayout 속성 설정

레이아웃 이름은 kebab-case로 정규화되어 someLayoutsome-layout이 돼요. 레이아웃을 지정하지 않으면 app/layouts/default.vue가 사용돼요. 애플리케이션에 레이아웃이 하나뿐이라면 app.vue를 쓰는 걸 권장해요.

다른 컴포넌트와 달리 레이아웃은 레이아웃 전환 시 트랜지션을 적용할 수 있도록 단일 루트 엘리먼트를 가져야 하고, 그 루트 엘리먼트는 <slot />이면 안 돼요.

기본 레이아웃

~/layouts/default.vue를 추가해요.

<!-- app/layouts/default.vue -->
<template>
  <div>
    <p>Some default layout content shared across all pages</p>
    <slot />
  </div>
</template>

레이아웃 파일에서 페이지의 내용은 <slot /> 컴포넌트에 표시돼요.

이름 있는 레이아웃

-| layouts/
---| default.vue
---| custom.vue

그런 다음 페이지에서 custom 레이아웃을 이렇게 사용해요.

<!-- pages/about.vue -->
<script setup lang="ts">
declare module 'nuxt/app' {
  interface NuxtLayouts {
    'custom': unknown
  }
}

// ---cut---

definePageMeta({
  layout: 'custom',
})
</script>

<NuxtLayout>name 속성으로 모든 페이지의 기본 레이아웃을 직접 바꿀 수도 있어요.

<!-- app/app.vue -->
<script setup lang="ts">
// API 호출이나 로그인 상태에 따라 고를 수 있어요
const layout = 'custom'
</script>

<template>
  <NuxtLayout :name="layout">
    <NuxtPage />
  </NuxtLayout>
</template>

중첩 디렉터리에 레이아웃이 있으면, 레이아웃 이름은 자신의 경로 디렉터리와 파일 이름을 기준으로 만들어지고 중복 세그먼트는 제거돼요. 헷갈림을 피하려면 레이아웃 파일 이름이 그 이름과 일치하도록 하는 걸 권장해요.

레이아웃 동적 변경

setPageLayout 헬퍼로 레이아웃을 동적으로 바꿀 수도 있어요.

<!-- app/pages/index.vue -->
<script setup lang="ts">
declare module 'nuxt/app' {
  interface NuxtLayouts {
    'custom': unknown
  }
}

// ---cut---

function enableCustomLayout () {
  setPageLayout('custom')
}

definePageMeta({
  layout: false,
})
</script>

<template>
  <div>
    <button @click="enableCustomLayout">
      Update layout
    </button>
  </div>
</template>

특정 경로에 대한 레이아웃은 라우트 규칙의 appLayout 속성으로도 지정할 수 있어요(v4.3+). 각 페이지 파일에서 관리하기보다 설정으로 중앙 관리하고 싶을 때, 또는 대응되는 페이지 컴포넌트가 없는 경로(여러 경로에 걸쳐 매칭될 수 있는 catchall 페이지 같은)에 적용하고 싶을 때 유용해요.

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // 특정 경로에 레이아웃 지정
    '/admin': { appLayout: 'admin' },
    // 여러 경로에 레이아웃 지정
    '/dashboard/**': { appLayout: 'dashboard' },
    // 경로에 레이아웃 비활성화
    '/landing': { appLayout: false },
  },
})

레이아웃에 Props 넘기기 (+4.4)

레이아웃에 props를 넘기는 방법이 몇 가지 있어요. definePageMetalayout 속성을 객체 문법으로 쓰면 페이지에서 바로 props를 전달할 수 있는데, 레이아웃의 defineProps를 기준으로 완전히 타입이 잡혀 에디터에서 자동완성과 타입 검사를 받을 수 있어요.

<!-- app/pages/dashboard.vue -->
<script setup lang="ts">
definePageMeta({
  layout: {
    name: 'panel',
    props: {
      sidebar: true,
      title: 'Dashboard',
    },
  },
})
</script>

setPageLayout으로 레이아웃을 동적으로 바꿀 때도 props를 넘길 수 있어요.

setPageLayout('panel', { sidebar: true, title: 'Dashboard' })

페이지 단위로 레이아웃 재정의

pages를 쓰고 있다면 layout: false로 두고 페이지 안에서 <NuxtLayout> 컴포넌트를 직접 쓰는 방식으로 완전히 제어할 수 있어요. 단, 페이지 안에서 <NuxtLayout>을 쓸 땐 그게 루트 엘리먼트가 되지 않게 해야 해요(그렇지 않으면 레이아웃/페이지 트랜지션을 꺼야 해요).

<!-- app/pages/index.vue -->
<script setup lang="ts">
definePageMeta({
  layout: false,
})
</script>

<template>
  <div>
    <NuxtLayout name="custom">
      <template #header>
        Some header template content.
      </template>

      The rest of the page
    </NuxtLayout>
  </div>
</template>

더 알아보기

  • <NuxtLayout>, setPageLayout, definePageMeta 세부 옵션은 API 문서를 참고하세요
  • 레이아웃 전환 트랜지션은 Views·Transitions 문서에서 다뤄요