PromleeBlog
sitemap
aboutMe

posting thumbnail
Vue 커스텀 디렉티브와 컴포저블 - Vue 심화 외전 1편
Vue Custom Directives and Composables - Vue Advanced Part 1

📅

🚀

들어가기 전에 🔗

이번 외전 1편에서는 코드의
재사용성
을 극대화하는 두 가지 강력한 패턴, 바로
커스텀 디렉티브
컴포저블(Composable)
에 대해 배워보겠습니다.
이 두 가지를 잘 활용하면 반복적인 코드를 크게 줄이고, 더욱 깔끔하고 유지보수하기 좋은 애플리케이션을 만들 수 있습니다.

🚀

커스텀 디렉티브 🔗

우리는 이미 v-if, v-model 같은 Vue의 기본 디렉티브에 익숙합니다.
디렉티브는 v- 접두사가 붙은 특별한 속성으로, HTML 요소에 직접 추가하여 선언적인 방식으로 DOM을 조작하는 역할을 합니다.

그런데 만약 Vue가 기본으로 제공하지 않는, 우리만의 DOM 조작이 필요하다면 어떻게 해야 할까요?
예를 들어, 페이지가 로드되자마자 특정 입력창에 자동으로 포커스를 주고 싶을 때가 있습니다.
이럴 때 사용하는 것이 바로
커스텀 디렉티브
입니다.

v-focus라는 이름의 커스텀 디렉티브를 직접 만들어 보겠습니다.
<script setup lang="ts">
import { onMounted, ref } from 'vue'
 
// 'vFocus'라는 이름으로 커스텀 디렉티브를 정의합니다.
// Vue는 카멜 케이스(vFocus)를 케밥 케이스(v-focus)로 자동 변환해 줍니다.
const vFocus = {
  // 디렉티브가 바인딩된 요소가 DOM에 삽입되었을 때 호출됩니다.
  mounted: (el: HTMLElement) => {
    el.focus()
  }
}
</script>
 
<template>
  <!-- 이제 어떤 입력창이든 v-focus만 추가하면 자동으로 포커스됩니다. -->
  <input v-focus placeholder="페이지 로드 시 포커스됩니다." />
</template>
위 코드에서 vFocus라는 객체를 만들고, mounted라는 훅을 사용했습니다.
이 훅은 디렉티브가 적용된 <input> 요소가 실제 화면에 그려진 직후에 호출됩니다.
그리고 el이라는 매개변수로 해당 요소를 직접 받아 el.focus()를 실행하는 아주 간단한 원리입니다.

이처럼 커스텀 디렉티브는 Vue의 선언적인 템플릿 문법을 벗어나, 특정 요소의 DOM에 직접 접근하여 무언가 조작해야 할 때 매우 유용합니다.

🚀

컴포저블 🔗

애플리케이션을 만들다 보면 여러 컴포넌트에서 완전히 동일한 로직을 반복해서 사용해야 할 때가 있습니다.
예를 들어, '마우스의 현재 위치 좌표 추적하기', '창의 크기 변화 감지하기', 'API 데이터 가져오기'와 같은 기능들입니다.

이런 반복적인 로직을 각 컴포넌트에 복사-붙여넣기 하는 것은 매우 비효율적입니다.
Vue 3의 Composition API는 이러한 문제를 해결하기 위한 우아한 해법으로
컴포저블(Composable)
이라는 패턴을 제시합니다.

컴포저블은 간단히 말해,
Vue의 반응성 API를 활용하여 상태 저장 로직을 캡슐화하고 재사용하는 함수
입니다.
관례적으로 함수 이름은 use로 시작합니다.

마우스의 위치를 추적하는 useMouse 컴포저블을 직접 만들어 보겠습니다.
먼저, src/composables라는 폴더를 새로 만들고, 그 안에 useMouse.ts 파일을 생성합니다.
src/composables/useMouse.ts
import { ref, onMounted, onUnmounted } from 'vue'
 
// 컴포저블 함수는 특별한 것이 아니라, 그냥 함수입니다.
export function useMouse() {
  // 컴포저블 내부에서 상태를 관리합니다.
  const x = ref(0)
  const y = ref(0)
 
  // 컴포저블 내부에서 생명주기 훅을 사용하여 이벤트를 관리할 수 있습니다.
  function update(event: MouseEvent) {
    x.value = event.pageX
    y.value = event.pageY
  }
 
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))
 
  // 관리하던 상태를 객체로 반환하여 외부 컴포넌트에서 사용할 수 있도록 합니다.
  return { x, y }
}

컴포저블 사용하기 🔗

이제 이렇게 만든 useMouse 컴포저블을 어떤 컴포넌트에서든 아주 쉽게 가져와 사용할 수 있습니다.
src/components/MouseTracker.vue
<script setup lang="ts">
// 우리가 만든 컴포저블을 임포트합니다.
import { useMouse } from '@/composables/useMouse'
 
// 함수를 호출하여 마우스 위치 상태를 가져옵니다.
const { x, y } = useMouse()
</script>
 
<template>
  <div>
    마우스 위치: {{ x }}, {{ y }}
  </div>
</template>
복잡한 이벤트 리스너 추가/제거 로직은 모두 useMouse 함수 안에 숨겨져 있습니다.
컴포넌트는 오직 xy라는 상태를 가져와 사용하기만 하면 됩니다.

useMouse 함수는 이제
마우스 좌표 추적
기능이 필요한 어떤 컴포넌트에서든 똑같이 재사용할 수 있습니다.
이것이 바로 컴포저블의 강력함입니다.
컴포저블
컴포저블

🚀

결론 🔗

오늘은 Vue 애플리케이션의 코드 재사용성을 한 단계 끌어올리는 두 가지 중요한 방법을 배웠습니다.
이제 여러분의 코드에서 반복되는 부분이 보인다면, '이걸 컴포저블로 만들 수 있지 않을까?'라고 먼저 생각해 보세요.
다음 외전 2편에서는 컴포넌트를 더욱 유연하게 만들어주는 SlotTeleport에 대해 알아보겠습니다.

참고 🔗