components

components/ 디렉토리는 모든 Vue 컴포넌트를 저장하는 곳이다.

Nuxt 는 이 디렉터리의 모든 컴포넌트(사용 중인 모듈에 의해 등록된 구성 요소와 함께)를 자동으로 가져온다.

 

 

디렉토리 구조

| components/
--| AppHeader.vue
--| AppFooter.vue
<!--
app.vue
-->

<template>
  <div>
    <AppHeader />
    <NuxtPage />
    <AppFooter />
  </div>
</template>

 

 

 

컴포넌트 이름

다음과 같이 중첩된 디렉터리에 컴포넌트가 있는 경우.

디렉토리 구조

| components/
--| base/
----| foo/
------| Button.vue

컴포넌트의 이름은 자체 경로 디렉터리와 파일 이름을 기반으로 하며 중복 세그먼트는 제거된다. 따라서 컴포넌트의 이름은 다음과 같다.

<BaseFooButton />
명확하게 하기 위해 컴포넌트의 파일 이름이 해당 이름과 일치하는 것이 좋다. 따라서 위의 예에서는 Button.vue 의 이름을 BaseFooButton.vue 로 바꿀 수 있다.

 

경로가 아닌 이름만을 기준으로 구성 요소를 자동으로 가져오려면 구성 객체의 확장 형식을 사용하여 pathPrefix 옵션을 false 로 설정해야한다.

// nuxt.config.ts

export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
+     pathPrefix: false,
    },
  ],
});

위 예는 Nuxt 2 에서 사용된 것과 동일한 전략을 사용하여 컴포넌트를 등록한다. 예를 들어 ~/components/Some/MyComponent.vue은 <MyComponent>로 사용할 수 있지만 <SomeMyComponent> 는 사용할 수 없다.

 

동적 컴포넌트

Vue 의 <component :is="someComputedComponent"> 같은 문법을 사용하려면 Vue 에서 제공하는 resolveComponent 헬퍼 함수를 사용하거나, #component 패키지에 포함된 component 의 is 속성으로 전달된 컴포넌트를 직접 임포트한다.

<!--
pages/index.vue
-->

<script setup lang="ts">
import { SomeComponent } from '#components'

const MyButton = resolveComponent('MyButton')
</script>

<template>
  <component :is="clickable ? MyButton : 'div'" />
  <component :is="SomeComponent" />
</template>
동적 구성요소를 처리하기 위해 resolveComponent 를 사용하는 경우 컴포넌트 이름 외에는 아무것도 삽입하면 안된다. 구성요소 이름은 변수가 아닌 문자열이어야 한다.

 

권장되지는 않지만 모든 컴포넌트를 전역으로 등록하면 모든 구성 요소에 대한 비동기 청크가 생성되어 애플리케이션 전체에서 사용할 수 있다.

  export default defineNuxtConfig({
    components: {
+     global: true,
+     dirs: ['~/components']
    },
  })

또한 일부 컴포넌트를 ~/components/global 디렉토리에 배치하여 전역적으로 등록할 수도 있다.

global 옵션은 컴포넌트 디렉토리별로도 설정할 수도 있다.

 

동적 임포트

컴포넌트를 동적으로 가져오려면(컴포넌트 지연 로딩이라고도 함) 컴포넌트 이름에 Lazy 접두어를 추가하기만 하면 됩니다. 이는 컴포넌트가 항상 필요하지 않은 경우 특히 유용하다.

 

Lazy 접두사를 사용하면 적절한 순간까지 컴포넌트간의 코드 로드를 지연할 수 있으며 이는 자바스크립트 번들 크기를 최적화하는 데 도움이 될 수 있다.

<!--
pages/index.vue
-->

<script setup>
const show = ref(false)
</script>

<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
  </div>
</template>

 

다이렉트 임포트

Nuxt의 자동 가져오기 기능을 우회하고 싶거나 우회해야 하는 경우 #components 에서 명시적으로 구성요소를 가져올 수도 있다.

<!--
pages/index.vue
-->

<script setup lang="ts">
import { NuxtLink, LazyMountainsList } from '#components'

const show = ref(false)
</script>

<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
    <NuxtLink to="/">Home</NuxtLink>
  </div>
</template>

 

사용자 정의 디렉토리

기본적으로 ~/components 디렉토리만 검사다. 다른 디렉터리를 추가하거나 이 디렉터리의 하위 폴더 내에서 구성 요소를 검색하는 방법을 변경하려는 경우 구성에 컴포넌트 디렉터리를 추가할 수 있다.

// nuxt.config.ts

export default defineNuxtConfig({
  components: [
    // ~/calendar-module/components/event/Update.vue => <EventUpdate />
    { path: '~/calendar-module/components' },

    // ~/user-module/components/account/UserDeleteDialog.vue => <UserDeleteDialog />
    { path: '~/user-module/components', pathPrefix: false },

    // ~/components/special-components/Btn.vue => <SpecialBtn />
    { path: '~/components/special-components', prefix: 'Special' },

    // It's important that this comes last if you have overrides you wish to apply
    // to sub-directories of `~/components`.
    //
    // ~/components/Btn.vue => <Btn />
    // ~/components/base/Btn.vue => <BaseBtn />
    '~/components'
  ]
})

 

npm 패키지

npm 패키지의 컴포넌트를 자동 임포트하려면 로컬 모듈(module 디렉토리) 에서  addComponent 를 사용하여 npm 컴포넌트를 등록할 수 있다.

~/modules/register-components

import { addComponent, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup() {
    // import { MyComponent as MyAutoImportedComponent } from 'my-npm-package'
    addComponent({
      name: 'MyAutoImportedComponent',
      export: 'MyComponent',
      filePath: 'my-npm-package',
    })
  },
})
<!--
app.vue
-->

<template>
  <div>
    <!--  the component uses the name we specified and is auto-imported  -->
    <MyAutoImportedComponent />
  </div>
</template>

 

구성 요소 확장

기본적으로 nuxt.config.ts 파일 확장자 키에 지정된 확장자를 가진 모든 파일은 컴포넌트로 처리됩니다. 컴포넌트로 등록해야 하는 파일 확장자를 제한해야 할때,  components 디렉토리 선언의 형식과 함께 해당 익스텐션 키 를 사용할 수 있다.

export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
+     extensions: ['.vue'],
    }
  ]
})

 

클라이언트 구성요소

구성 요소가 클라이언트 측에서만 렌더링되도록 되어 있는 경우 컴포넌에 .client 접미사를 추가할 수 있다.

 

디렉토리 구조

| components/
--| Comments.client.vue
<!--
pages/example.vue
-->

<template>
  <div>
    <!-- this component will only be rendered on client side -->
    <Comments />
  </div>
</template>
이 기능은 Nuxt 자동 임포트 및 #components 임포트 에서만 작동합니다. 실제 경로에서 이러한 컴포넌트를 명시적으로 가져와도 클라이언트 전용 컴포넌로 변환되지 않는다.

.client 컴포넌트는 마운트된 후에만 렌더링된다. onMounted()을 사용하여 렌더링된 템플릿에 액세스하려면 후크의 콜백
onMounted() 에 await nextTick() 를 추가하면 된다. 

 

서버 컴포넌트

서버 컴포넌트를 사용하면 클라이언트 측 앱 내에서 개별 컴포넌트를 서버 렌더링할 수 있다. 정적 사이트를 생성하는 경우에도 Nuxt 내에서 서버 컴포넌트를 사용할 수 있다. 이를 통해 동적 컴포넌트, 서버 렌더링 HTML, 심지어 정적인 마크업 덩어리까지 혼합된 복잡한 사이트를 구축할 수 있다.

 

서버 컴포넌트는 단독으로 사용하거나 클라이언트 컴포넌트와 함께 사용할 수 있다.

 

독립형 서버 구성요소

독립 실행형 서버 컴포넌트는 항상 아일랜드 컴포넌트라고도 하는 서버에서 렌더링된다.

 

컴포넌트 속성이 업데이트되면 렌더링된 HTML을 내부에서 업데이트하는 네트워크 요청이 발생한다.

 

서버 컴포넌는 현재 실험적이며 이를 사용하려면 n uxt.config의 '아일랜트 컴포넌트' 를 활성화해야한다.

// nuxt.config.ts

export default defineNuxtConfig({
  experimental: {
    componentIslands: true
  }
})

이제 .server 접미사를 사용하여 서버 전용 컴포넌트를 등록하고 애플리케이션의 어느 곳에서나 자동으로 사용할 수 있다.

 

디렉토리 구조

| components/
--| HighlightedMarkdown.server.vue
pages/example.vue

<template>
  <div>
    <!--
      this will automatically be rendered on the server, meaning your markdown parsing + highlighting
      libraries are not included in your client bundle.
     -->
    <HighlightedMarkdown markdown="# Headline" />
  </div>
</template>

서버 전용 컴포넌트는 내부적으로 <NuxtIsland> 를 사용합니다. 즉, lazy 속성 #fallback 슬롯 이 모두 컴포넌트에 전달된다.

 

서버 컴포넌트 컨텍스트

서버 전용 또는 아일랜드 컴포넌트를 렌더링할 때 <NuxtIsland>는 NuxtIslandResponse와 함께 반환되는 fetch 요청을 만든다. (서버에서 렌더링되는 경우 내부 요청이고, 클라이언트측 탐색에서 렌더링되는 경우 네트워크 탭에서 볼 수 있는 요청이다.)

 

이는 다음을 의미한다.

  • 새로운 Vue 앱이 서버 측에 생성되어 NuxtIslandRepose를 생성한다.
  • 컴포넌트를 렌더링하는 동안 새로운 '아일랜드 컨텍스트' 가  생성된다.
  • 앱의 나머지 부분에서 '아일랜드 컨텍스트'에 액세스할 수 없다.  아일랜드 컴포넌트에서는 앱의 나머지 부분의 컨텍스트에 액세스할 수 없다. 즉, 서버 구성요소 또는 아일랜드는 앱의 나머지 부분과 격리된다.
  • 플러그인은 env: { islands: false } 설정(객체 구문 플러그인에서 수행 가능)하지 않는 한 아일랜드를 렌더링할 때 다시 실행된다.


아일랜드 컴포넌트 내에서는 nuxtApp.ssrContext.islandContext 를 통해 아일랜드 컨텍스트에 액세스할 수 있다. 아일랜드 컴포넌트는 여전히 실험적이라고 표시되어 있지만 이 컨텍스트의 형식은 변경될 수 있다.

슬롯은 대화형일 수 있으며 <div> 내 display: contents; 로 래핑된다.

 

클라이언트 구성요소와 페어링

아래의 경우 .server + .client 구성 요소는 두 개의 '반쪽' 이다. 구성 요소의 구성 요소이며 서버 측과 클라이언트 측에서 구성 요소를 별도로 구현하기 위한 고급 사용 사례이다.

 

디렉토리 구조

| components/
--| Comments.client.vue
--| Comments.server.vue
<!--
pages/example.vue
-->

<template>
  <div>
    <!-- this component will render Comments.server on the server then Comments.client once mounted in the browser -->
    <Comments />
  </div>
</template>

 

<ClientOnly> 컴포넌트

Nuxt 는 클라이언트 측에서만 의도적으로 컴포넌트를 렌더링하기 위한 <ClientOnly> 컴포넌트를 제공한다.

<!--
pages/example.vue
-->

<template>
  <div>
    <Sidebar />
    <ClientOnly>
      <!-- this component will only be rendered on client-side -->
      <Comments />
    </ClientOnly>
  </div>
</template>

슬롯을 <ClientOnly> 클라이언트측에 마운트될 때까지 폴백으로 사용하자.

<template>
  <div>
    <Sidebar />
    <!-- This renders the "span" element on the server side -->
    <ClientOnly fallbackTag="span">
      <!-- this component will only be rendered on client side -->
      <Comments />
      <template #fallback>
        <!-- this will be rendered on server side -->
        <p>Loading comments...</p>
      </template>
    </ClientOnly>
  </div>
</template>

 

<DevOnly> 컴포넌트

Nuxt는 개발 중에만 컴포넌트를 렌더링할 수 있는 <DevOnly> 컴포넌트를 제공한다.

 

콘텐츠는 프로덕션 빌드 및 트리 셰이크에 포함되지 않는다.

<!--
pages/example.vue
-->

<template>
  <div>
    <Sidebar />
    <DevOnly>
      <!-- this component will only be rendered during development -->
      <LazyDebugBar />

      <!-- if you ever require to have a replacement during production -->
      <!-- be sure to test these using `nuxt preview` -->
      <template #fallback>
        <div><!-- empty div for flex.justify-between --></div>
      </template>
    </DevOnly>
  </div>
</template>

 

<NuxtClientFallback> 컴포넌트

Nuxt는 하위 요소 중 하나라도 SSR에서 오류를 발생시키는 경우 클라이언트에 콘텐츠를 렌더링하는 <NuxtClientFallback> 컴포넌트를 제공한다. fallbackTag를 지정하여 서버에서 렌더링에 실패한 경우 특정 태그를 렌더링하도록 할 수 있다.

<!--
pages/example.vue
-->

<template>
  <div>
    <Sidebar />
    <!-- this component will be rendered on client-side -->
    <NuxtClientFallback fallback-tag="span">
      <Comments />
      <BrokeInSSR />
    </NuxtClientFallback>
  </div>
</template>

 

라이브러리 개발자

자동 트리 쉐이킹 및 구성 요소 등록을 통해 Vue 구성 요소 라이브러리를 만드는 것은 매우 쉽다.

 

components:dirs 후크를 사용하여 Nuxt 모듈에서 사용자 구성을 요구하지 않고 디렉토리 목록을 확장할 수 있다.

 

다음과 같은 디렉터리 구조를 생각할 수 있다.

| node_modules/
---| awesome-ui/
------| components/
---------| Alert.vue
---------| Button.vue
------| nuxt.js
| pages/
---| index.vue
| nuxt.config.js

그런 다음 awesome-ui/nuxt.js에서 components:dirs 후크를 사용할 수 있다.

import { defineNuxtModule, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  hooks: {
    'components:dirs': (dirs) => {
      const { resolve } = createResolver(import.meta.url)
      // Add ./components dir to the list
      dirs.push({
        path: resolve('./components'),
        prefix: 'awesome'
      })
    }
  }
})

이제 프로젝트에서 UI 라이브러리를 nuxt.config 파일을 이용해 Nuxt 모듈로 가져올 수 있다.

// nuxt.config.ts

export default defineNuxtConfig({
  modules: ['awesome-ui/nuxt']
})

pages/index.vue 에서 모듈 구성 요소(awesome- 접두사가 붙음)를 직접 사용한다.

<template>
  <div>
    My <AwesomeButton>UI button</AwesomeButton>!
    <awesome-alert>Here's an alert!</awesome-alert>
  </div>
</template>

사용되는 경우에만 컴포넌트를 자동으로 가져오고 node_modules/awesome-ui/components/에서 컴포넌트를 업데이트할 때 HMR도 지원한다.

 

자동 임포트 - 사용 예

<!--
app.vue
-->

<script setup>
const message = ref('Nuxt')

function hello () {
  sayHello(message.value)
}
</script>

<template>
  <NuxtExampleLayout dir="features/auto-imports">
    <h1>Demo with auto imports</h1>
    <form class="flex gap-2" @submit.prevent="hello">
      <CustomInput v-model="message" />
      <UButton type="submit">Hello</UButton>
    </form>
  </NuxtExampleLayout>
</template>

 

'Nuxt 공식문서 번역 > Directories' 카테고리의 다른 글

content  (0) 2023.12.17
composables  (1) 2023.12.17
assets  (0) 2023.12.17
.output  (0) 2023.12.17
.nuxt  (0) 2023.12.17
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유