iBetter Books
수정

Ch 03. Composable 직접 만들기

useFetchuseRoute처럼 use로 시작하는 함수들을 Composable이라고 부릅니다. Vue 3 Composition API 함수들을 조합해서 상태와 로직을 하나로 묶은 재사용 단위입니다. 컴포넌트가 "어떻게 보여줄지"를 담당한다면, Composable은 "어떻게 동작할지"를 담당합니다.

Composable이란 무엇인가

Composable은 composables/ 폴더에 위치한 일반 TypeScript 함수입니다. 특별한 클래스나 데코레이터가 필요 없습니다. ref, computed, watch 같은 Composition API를 내부에서 자유롭게 사용할 수 있고, 결과를 반환하면 됩니다.

Vue 3의 Composition API 함수(ref 등)와 Composable의 차이는 추상화 수준에 있습니다. ref는 단순히 반응형 값을 만들지만, Composable은 특정 목적에 맞는 상태와 로직을 함께 묶어서 제공합니다.

useCounter 만들기

카운터 로직을 컴포넌트에서 분리해 Composable로 만들어 보겠습니다.

// composables/useCounter.tsexport function useCounter(initial = 0) {  const count = ref(initial)  const increment = () => count.value++  const decrement = () => count.value--  const reset = () => { count.value = initial }  return { count, increment, decrement, reset }}

컴포넌트에서는 이렇게 사용합니다.

<script setup lang="ts">
const { count, increment, decrement, reset } = useCounter(10)
</script>

<template>
  <p>{{ count }}</p>
  <button @click="increment">+1</button>
  <button @click="decrement">-1</button>
  <button @click="reset">초기화</button>
</template>

같은 Composable을 여러 컴포넌트에서 호출하면 각각 독립적인 인스턴스가 생성됩니다. A 컴포넌트와 B 컴포넌트가 모두 useCounter()를 호출해도 count 값은 서로 공유되지 않습니다.

useFetch를 래핑한 Composable

API 호출 로직을 Composable로 감싸면 페이지 컴포넌트를 훨씬 간결하게 유지할 수 있습니다.

// composables/usePosts.tsexport function usePosts() {  const { data: posts, pending, error } = useFetch('/api/posts')  return { posts, pending, error }}

이제 페이지에서는 API 주소나 로딩 로직을 직접 작성하지 않아도 됩니다.

<script setup lang="ts">
const { posts, pending, error } = usePosts()
</script>

SSR 안전 주의사항

Nuxt는 서버에서도 코드를 실행합니다. 브라우저 전용 API인 windowdocument는 서버에서 존재하지 않으므로 Composable 안에서 바로 사용하면 오류가 발생합니다. 반드시 onMounted 안에서 사용해야 합니다.

// composables/useWindowSize.tsexport function useWindowSize() {  const width = ref(0)  const height = ref(0)  onMounted(() => {    // onMounted는 클라이언트에서만 실행됩니다.    width.value = window.innerWidth    height.value = window.innerHeight  })  return { width, height }}

또는 Nuxt가 제공하는 import.meta.client로 클라이언트 환경 여부를 확인할 수도 있습니다.