💬
목차
< 뒤로가기
인쇄

컴포넌트 LifeCycle & Hook

Nuxt 3은 Vue 3의 Composition API를 기반으로 하며, 이에 따라 Vue 3의 컴포넌트 라이프사이클을 그대로 상속합니다. 이 라이프사이클 훅들은 컴포넌트의 생성부터 파괴까지 다양한 시점에 코드를 실행할 수 있게 해줍니다.

다음은 Vue 3 / Nuxt 3 컴포넌트 라이프사이클의 주요 훅들입니다:

  1. setup(): 컴포넌트가 초기화되는 단계입니다. 이 훅은 beforeCreatecreated 훅 사이에서 실행되며, props, context, reactive state 등을 설정합니다.

  2. onBeforeMount(): 컴포넌트가 DOM에 마운트되기 바로 전에 호출됩니다.

  3. onMounted(): 컴포넌트가 DOM에 마운트된 후에 호출됩니다. 이 훅은 DOM이나 자식 컴포넌트에 접근해야 할 때 유용합니다.

  4. onBeforeUpdate(): 반응형 데이터가 변경되어 업데이트 사이클이 시작되기 전에 호출됩니다.

  5. onUpdated(): 컴포넌트와 그 자식들이 업데이트된 후에 호출됩니다.

  6. onBeforeUnmount(): 컴포넌트가 언마운트되기 바로 전에 호출됩니다. 이 훅은 이벤트 리스너나 타이머를 제거하는 데 사용할 수 있습니다.

  7. onUnmounted(): 컴포넌트가 언마운트된 후에 호출됩니다. 이 훅은 컴포넌트가 더 이상 활성화되지 않을 때 필요한 정리 작업을 수행하는 데 사용됩니다.

또한, onErrorCaptured()onRenderTracked(), onRenderTriggered() 같은 고급 훅들도 제공되며, 이는 에러 처리나 디버깅에 유용합니다.

여기에 setup() 함수 안에서 라이프사이클 훅을 사용하는 간단한 예시가 있습니다:

<template>
  <div>{{ count }}</div>
</template>

<script setup>
import { ref, onMounted, onUpdated, onBeforeUnmount } from 'vue'

const count = ref(0)

// 마운트될 때 실행할 작업
onMounted(() => {
  console.log('컴포넌트가 마운트되었습니다.')
  setInterval(() => {
    count.value++
  }, 1000)
})

// 데이터가 업데이트될 때 실행할 작업
onUpdated(() => {
  console.log('컴포넌트가 업데이트되었습니다. 현재 count는:', count.value)
})

// 언마운트되기 전 실행할 작업
onBeforeUnmount(() => {
  console.log('컴포넌트가 언마운트됩니다.')
})
</script>

Nuxt 3에서는 이러한 훅들을 사용하여 SSR을 구현할 때 서버 사이드와 클라이언트 사이드 모두에서 실행되는 코드를 관리하거나, 클라이언트 사이드에서만 실행되어야 하는 동작을 정의할 수 있습니다. 예를 들어, onMounted는 클라이언트 사이드에서 컴포넌트가 마운트될 때만 실행되기 때문에, 서버 사이드 렌더링에서는 호출되지 않습니다.

Composition API 설명

Nuxt 3은 Vue 3 기반으로 구축되었으며, Vue 3의 Composition API를 완벽하게 지원합니다. Composition API는 데이터와 메서드를 조직하는 새로운 방식을 제공하여, 재사용 가능하고 관리하기 쉬운 코드를 작성할 수 있게 해줍니다. 이 API는 기존의 Options API보다 더 유연하고 구성이 용이한 방식으로 컴포넌트의 로직을 구성할 수 있게 해줍니다.

Composition API 주요 특징

  1. Reactive State: refreactive 함수를 사용하여 반응형 데이터 상태를 만들 수 있습니다. ref는 기본형 데이터를 반응형으로 만들고, reactive는 객체 데이터를 반응형으로 만듭니다.

  2. Computed Properties: computed 함수를 사용하여 반응형으로 계산된 속성을 만들 수 있습니다.

  3. Watchers: watchwatchEffect 함수로 반응형 데이터의 변화를 관찰하고 그에 반응하는 로직을 실행할 수 있습니다.

  4. Lifecycle Hooks: Composition API에서는 onMounted, onUpdated, onUnmounted 등의 라이프사이클 훅을 사용할 수 있습니다.

  5. Provide / Inject: 컴포넌트 트리에서 깊은 곳에 있는 컴포넌트에 데이터를 전달하기 위해 provideinject 함수를 사용할 수 있습니다.

  6. Custom Composition Functions: 로직을 추상화하여 재사용 가능한 함수로 만들 수 있으며, 이를 다른 컴포넌트에서 재사용할 수 있습니다.

Nuxt 3에서의 사용 예

Nuxt 3 컴포넌트 내에서 Composition API를 사용하는 기본적인 예시는 다음과 같습니다:

<template>
  <div>{{ count }}</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const count = ref(0)

onMounted(() => {
  console.log('The component has been mounted.')
})
</script>

위 코드에서 <script setup>은 Composition API를 사용하겠다는 선언이며, ref를 사용하여 반응형 데이터 count를 만들고, onMounted 훅을 사용하여 컴포넌트가 마운트될 때 콘솔에 메시지를 출력합니다.

Nuxt 3에서는 이와 같은 Composition API를 사용하여 서버 측과 클라이언트 측 로직을 모두 통합할 수 있습니다. 예를 들어, 서버 측에서 데이터를 가져와 클라이언트 측에서 반응형으로 만들고, 이를 템플릿에서 직접 사용할 수 있습니다. 또한 Nuxt 3는 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)을 위한 특별한 기능도 함께 제공합니다.

반응형 데이터

Nuxt 3에서 ref, reactive, 그리고 readonly는 Vue 3의 Composition API를 활용한 반응형 데이터 관리를 위한 핵심 함수들입니다. 각각의 함수는 특정한 사용 사례와 특징을 가지고 있습니다.

1. ref()

ref() 함수는 어떤 타입의 값이든 반응형 참조로 만들 수 있습니다. 주로 기본 타입(예: 문자열, 숫자, 불리언)을 반응형 데이터로 만들 때 사용됩니다. ref.value 프로퍼티를 통해 해당 값을 감싸고, 그 값을 통해 반응성을 관리합니다.

import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    return { count, increment };
  }
}

위 예제에서 count는 0으로 초기화된 반응형 참조입니다. 이 값을 변경하면 Nuxt/Vue는 관련된 DOM을 자동으로 업데이트합니다.

2. reactive()

reactive() 함수는 객체 또는 배열과 같은 복합 타입의 값을 반응형 객체로 만듭니다. 이 함수를 사용할 때는 내부 속성들이 반응형으로 변환되고, 이 객체의 변경사항이 뷰에 자동으로 반영됩니다.

import { reactive } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0
    });

    function increment() {
      state.count++;
    }

    return { state, increment };
  }
}

state 객체는 reactive를 통해 생성된 반응형 객체입니다. state.count의 값을 변경하면 이에 응답하여 자동으로 업데이트됩니다.

3. readonly()

readonly() 함수는 reactiveref를 통해 만들어진 반응형 객체를 수정할 수 없는 읽기 전용 상태로 만듭니다. 이는 데이터를 노출시키되, 외부에서 데이터를 변경하지 못하게 할 때 유용합니다. 이 읽기 전용 객체는 원본 데이터가 변경될 때 업데이트됩니다.

import { reactive, readonly } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0
    });

    const stateAsReadonly = readonly(state);

    return { state, stateAsReadonly };
  }
}

위 예제에서 stateAsReadonlystate의 읽기 전용 버전입니다. stateAsReadonly.count를 직접 수정하려고 하면 경고가 표시되며, 변경이 적용되지 않습니다.

이 함수들을 사용함으로써, Nuxt 3와 Vue 3 애플리케이션에서 다양한 상태 관리 패턴을 유연하게 적용할 수 있습니다. 데이터 흐름을 명확하게 하고, 예측 가능하며 유지보수하기 쉬운 코드베이스를 구성하는 데 큰 도움이 됩니다.

Prop / Emit

Nuxt 3에서도 Vue 3의 컴포넌트 모델을 그대로 사용하며, 컴포넌트 간의 데이터와 이벤트 전달에 propsemit을 활용합니다. 이 두 메커니즘은 부모-자식 컴포넌트 간의 커뮤니케이션을 위한 기본적인 방법입니다.

Props

props는 부모 컴포넌트로부터 자식 컴포넌트로 데이터를 전달할 때 사용합니다. 자식 컴포넌트는 props를 정의하여 이 값을 받을 수 있습니다.

<!-- ParentComponent.vue -->
<template>
  <ChildComponent :user="userData" />
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const userData = ref({ name: 'Alice', age: 30 });
</script>
<!-- ChildComponent.vue -->
<template>
  <div>{{ user.name }} - {{ user.age }}</div>
</template>

<script setup>
import { defineProps } from 'vue';

const props = defineProps({
  user: Object
});
</script>

위의 예에서, ParentComponentuserData라는 반응형 데이터를 가지고 있고, 이를 ChildComponentuser prop으로 전달합니다.

Emit

emit은 자식 컴포넌트가 부모 컴포넌트로 이벤트를 전송할 때 사용합니다. 이를 위해 자식 컴포넌트는 defineEmits를 사용하여 이벤트를 정의할 수 있습니다.

<!-- ChildComponent.vue -->
<template>
  <button @click="updateUser">Update User</button>
</template>

<script setup>
import { defineEmits } from 'vue';

const emit = defineEmits(['user-updated']);

function updateUser() {
  // ... 사용자 업데이트 로직
  emit('user-updated', { name: 'Bob', age: 25 });
}
</script>
<!-- ParentComponent.vue -->
<template>
  <ChildComponent :user="userData" @user-updated="handleUserUpdated" />
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const userData = ref({ name: 'Alice', age: 30 });

function handleUserUpdated(updatedUser) {
  userData.value = updatedUser;
}
</script>

ChildComponent에서 버튼 클릭 이벤트가 발생하면 updateUser 함수가 호출되고, emit을 통해 user-updated 이벤트가 부모 컴포넌트로 전달됩니다. ParentComponent에서는 handleUserUpdated 함수를 통해 이 이벤트를 처리하고, userData를 업데이트합니다.

이렇게 propsemit을 사용하여 컴포넌트 간의 데이터 흐름과 이벤트 처리를 쉽게 관리할 수 있습니다. Nuxt 3는 이러한 Vue의 패턴을 그대로 따르므로, Vue에 익숙한 개발자라면 Nuxt 3에서도 빠르게 개발을 진행할 수 있습니다.

레이아웃

Nuxt 3은 페이지의 레이아웃을 정의하는 강력하고 유연한 시스템을 제공합니다. 레이아웃은 웹 애플리케이션의 전체적인 페이지 구조를 정의하는 데 사용되며, 여러 페이지에 걸쳐 공통되는 디자인 요소들을 쉽게 관리할 수 있게 해줍니다. 예를 들어, 헤더, 사이드바, 푸터 등의 UI 컴포넌트가 모든 페이지에 공통적으로 필요하다면 레이아웃을 통해 이를 구현할 수 있습니다.

기본 레이아웃

Nuxt 3 프로젝트를 생성하면, layouts 디렉토리가 기본적으로 생성됩니다. 이 디렉토리 안에는 애플리케이션의 기본 레이아웃을 정의하는 default.vue 파일이 있습니다.

<!-- layouts/default.vue -->
<template>
  <div>
    <Header />
    <Nuxt />
    <Footer />
  </div>
</template>

<script setup>
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
</script>

<Nuxt /> 컴포넌트는 실제 페이지 컴포넌트가 렌더링되는 자리 표시자 역할을 합니다. 즉, 각 페이지의 내용은 <Nuxt /> 태그의 위치에 표시됩니다.

사용자 정의 레이아웃

사용자는 layouts 디렉토리 안에 새로운 레이아웃 파일을 추가함으로써, 다양한 레이아웃을 정의할 수 있습니다. 예를 들어, blog.vue 레이아웃을 만들고 싶다면 다음과 같이 할 수 있습니다.

<!-- layouts/blog.vue -->
<template>
  <div>
    <BlogHeader />
    <Nuxt />
    <BlogFooter />
  </div>
</template>

<script setup>
import BlogHeader from '@/components/BlogHeader.vue'
import BlogFooter from '@/components/BlogFooter.vue'
</script>

이렇게 정의된 레이아웃은 페이지 컴포넌트에서 layout 속성을 사용해 적용할 수 있습니다.

<!-- pages/posts.vue -->
<template>
  <!-- 페이지 컴포넌트의 내용 -->
</template>

<script setup>
export default {
  layout: 'blog'
}
</script>

레이아웃 변경

Nuxt 3에서는 프로그래매틱하게 레이아웃을 변경할 수도 있습니다. 이를 위해 useLayout 함수를 사용할 수 있습니다.

const { setLayout } = useLayout()
setLayout('blog') // 레이아웃을 'blog'로 변경합니다.

에러 레이아웃

에러 핸들링을 위해 Nuxt 3는 layouts/error.vue 파일을 사용하여 에러 레이아웃을 정의할 수 있습니다. 이 레이아웃은 앱에서 어떠한 에러가 발생했을 때 표시됩니다.

<!-- layouts/error.vue -->
<template>
  <div>
    <h1>Error occurred!</h1>
    <p>{{ error.message }}</p>
  </div>
</template>

<script setup>
import { useError } from 'nuxt/app'

const error = useError()
</script>

Nuxt 3 레이아웃 시스템은 매우 유연하고, 다양한 페이지와 앱 상태에 따라 쉽게 레이아웃을 조정하거나 변경할 수 있게 해줍니다. 이는 더 일관되고 효율적인 사용자 경험을 제공하며, 개발자는 코드 중복

을 최소화하면서도 프로젝트의 유지보수성을 높일 수 있습니다.

중첩 라우팅

Nuxt 3에서 중첩 라우팅(Nested Routes)은 디렉토리 구조와 파일 이름을 사용하여 매우 직관적으로 설정할 수 있습니다. 중첩된 라우트를 만들기 위해, pages 디렉토리 내에 경로 구조에 맞게 폴더와 파일을 생성하면 Nuxt는 이 구조를 기반으로 자동으로 라우트를 생성합니다.

예를 들어, 다음과 같은 파일 구조가 있다고 가정해 봅시다:

pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue

이 구조는 다음과 같은 라우트를 생성합니다:

  • /users: users.vue에 의해 처리됩니다.
  • /users/index: users/index.vue에 의해 처리됩니다. (일반적으로 /users와 동일)
  • /users/:id: users/_id.vue에 의해 처리됩니다. :id 부분은 동적입니다.

여기서 users/index.vue/users 경로에 대한 ‘기본’ 페이지를 제공하고, users/_id.vue/users와 같은 경로 아래에 있는 동적인 ID를 가진 하위 페이지를 나타냅니다. _ 접두사는 해당 파일이나 폴더가 동적 파라미터를 받는다는 것을 Nuxt에 알려줍니다.

중첩된 뷰 구현하기

Nuxt는 자동으로 router-view를 삽입하여 해당 위치에 맞는 컴포넌트를 렌더링합니다. 하지만 때로는 부모 라우트가 자식 라우트를 직접 제어해야 할 필요가 있습니다. 이를 위해 <NuxtChild> 컴포넌트를 사용합니다.

users.vue 파일을 다음과 같이 작성할 수 있습니다:

<template>
  <div>
    <h1>Users</h1>
    <NuxtChild />
  </div>
</template>

이 구조에서 /users 를 방문하면 Users 제목이 표시되고, /users/123을 방문하면 동일한 Users 제목 아래에 users/_id.vue 컴포넌트의 내용이 NuxtChild 위치에 렌더링됩니다.

동적 중첩 라우트

중첩된 라우트도 동적일 수 있습니다. 예를 들어, 사용자에 대한 프로필 페이지와 사용자가 작성한 게시글 목록 페이지를 중첩된 라우트로 설정하려면 다음과 같은 구조를 만들 수 있습니다:

pages/
--| users/
-----| _id/
--------| index.vue
--------| posts.vue

이 구조는 다음과 같은 라우트를 생성합니다:

  • /users/:id: users/_id/index.vue에 의해 처리됩니다.
  • /users/:id/posts: users/_id/posts.vue에 의해 처리됩니다.

이렇게 설정하면, 각 사용자 ID에 따라 프로필 페이지와 게시글 목록 페이지를 중첩된 라우트로 표현할 수 있습니다. :id 부분은 사용자 ID로 대체됩니다.

Nuxt 3는 이처럼 파일과 폴더 이름을 사용하여 강력하고 유연한 라우팅 시스템을 제공하며, 개발자는 복잡한 라우팅 설정 없이도 애플리케이션의 라우트를 쉽게 관리할 수 있습니다.

이전 프로젝트 디렉토리 구성 가이드
다음 Nuxt 3rd Party 소개