Learning
레슨 5 / 9·20분

라이프사이클과 Composables

컴포넌트 라이프사이클

Vue 컴포넌트는 생성 → 마운트 → 업데이트 → 언마운트 순서의 라이프사이클을 거칩니다. Composition API에서는 onMounted, onUpdated, onUnmounted 등의 훅으로 각 단계에 로직을 삽입합니다.

vue
<script setup lang="ts">
import {
  ref, onMounted, onUpdated, onUnmounted, onBeforeUnmount
} from 'vue';

const data = ref(null);
const timer = ref<number | null>(null);

// 마운트 후 — DOM에 접근 가능, API 호출 등
onMounted(() => {
  console.log('컴포넌트가 DOM에 마운트됨');
  fetchData();

  timer.value = window.setInterval(() => {
    console.log('폴링 중...');
  }, 5000);
});

// 업데이트 후
onUpdated(() => {
  console.log('반응형 데이터 변경으로 DOM 업데이트됨');
});

// 언마운트 전 — 정리 작업
onBeforeUnmount(() => {
  if (timer.value) {
    clearInterval(timer.value);
  }
  console.log('타이머 정리 완료');
});

async function fetchData() {
  const res = await fetch('/api/data');
  data.value = await res.json();
}
</script>

Composables (커스텀 합성 함수)

Composable은 Composition API를 활용해 재사용 가능한 상태 로직을 추출하는 함수입니다. React의 커스텀 Hook과 유사한 패턴으로, use 접두사를 붙여 이름짓는 것이 관례입니다.

typescript
// composables/useFetch.ts
import { ref, watchEffect } from 'vue';

export function useFetch<T>(url: string) {
  const data = ref<T | null>(null);
  const error = ref<string | null>(null);
  const loading = ref(true);

  async function execute() {
    loading.value = true;
    error.value = null;
    try {
      const res = await fetch(url);
      if (!res.ok) throw new Error("HTTP " + res.status);
      data.value = await res.json();
    } catch (e: any) {
      error.value = e.message;
    } finally {
      loading.value = false;
    }
  }

  execute();

  return { data, error, loading, refetch: execute };
}
vue
<!-- 컴포넌트에서 composable 사용 -->
<script setup lang="ts">
import { useFetch } from '@/composables/useFetch';

type User = { id: number; name: string; email: string };
const { data: users, loading, error } = useFetch<User[]>('/api/users');
</script>

<template>
  <div v-if="loading">로딩 중...</div>
  <div v-else-if="error">에러: {{ error }}</div>
  <ul v-else>
    <li v-for="user in users" :key="user.id">
      {{ user.name }} ({{ user.email }})
    </li>
  </ul>
</template>
typescript
// composables/useLocalStorage.ts
import { ref, watch } from 'vue';

export function useLocalStorage<T>(key: string, defaultValue: T) {
  const stored = localStorage.getItem(key);
  const data = ref<T>(stored ? JSON.parse(stored) : defaultValue);

  watch(data, (newVal) => {
    localStorage.setItem(key, JSON.stringify(newVal));
  }, { deep: true });

  return data;
}

// 사용
const settings = useLocalStorage('settings', {
  theme: 'light',
  fontSize: 14,
});
💡

Composable 안에서 onMounted, watch 등 라이프사이클 훅과 반응형 API를 자유롭게 사용할 수 있습니다. 각 컴포넌트에서 호출될 때마다 독립적인 상태가 생성되므로 상태 충돌 걱정이 없습니다.