VueUse는 Vue Composition API 기반의 유틸리티 composable 모음입니다. "바퀴를 재발명하지 말자"를 실천하는 최고의 라이브러리입니다.

면접에서 "자주 사용하는 Vue 라이브러리가 있나요?"라고 물으면, VueUse를 언급하고 구체적인 composable 활용 사례를 설명하면 좋습니다.


설치

BASH
npm install @vueuse/core

자주 쓰는 composable — 브라우저

VUE
<script setup lang="ts">
import {
  useLocalStorage,
  useSessionStorage,
  useDark,
  useToggle,
  useClipboard,
  useTitle,
  useMediaQuery
} from '@vueuse/core'

// 로컬 스토리지 — 자동 동기화
const settings = useLocalStorage('app-settings', {
  theme: 'light',
  language: 'ko'
})

// 다크 모드
const isDark = useDark()
const toggleDark = useToggle(isDark)

// 클립보드
const { copy, copied } = useClipboard()

// 페이지 제목
const title = useTitle('초기 제목')
title.value = '새 제목'  // 자동으로 document.title 변경

// 미디어 쿼리
const isMobile = useMediaQuery('(max-width: 768px)')
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
</script>

<template>
  <button @click="toggleDark()">
    {{ isDark ? '라이트' : '다크' }} 모드
  </button>

  <button @click="copy('복사할 텍스트')">
    {{ copied ? '복사됨!' : '복사' }}
  </button>

  <p>모바일: {{ isMobile }}</p>
</template>

자주 쓰는 composable — 센서

VUE
<script setup lang="ts">
import {
  useMouse,
  useWindowSize,
  useScroll,
  useIntersectionObserver,
  useResizeObserver,
  useElementVisibility
} from '@vueuse/core'
import { ref } from 'vue'

// 마우스 위치
const { x, y } = useMouse()

// 윈도우 크기
const { width, height } = useWindowSize()

// 스크롤 위치
const { y: scrollY, isScrolling } = useScroll(window)

// Intersection Observer
const targetRef = ref<HTMLElement>()
const isVisible = useElementVisibility(targetRef)

// Resize Observer
const boxRef = ref<HTMLElement>()
const boxSize = ref({ width: 0, height: 0 })
useResizeObserver(boxRef, (entries) => {
  const entry = entries[0]
  boxSize.value = {
    width: entry.contentRect.width,
    height: entry.contentRect.height
  }
})
</script>

자주 쓰는 composable — 유틸리티

VUE
<script setup lang="ts">
import {
  useDebounceFn,
  useThrottleFn,
  useAsyncState,
  useTimeoutFn,
  useIntervalFn,
  watchDebounced,
  whenever,
  computedAsync
} from '@vueuse/core'
import { ref } from 'vue'

// 디바운스된 함수
const debouncedSearch = useDebounceFn((query: string) => {
  console.log('검색:', query)
}, 300)

// 쓰로틀된 함수
const throttledScroll = useThrottleFn(() => {
  console.log('스크롤 처리')
}, 100)

// 비동기 상태 관리
const { state: users, isLoading, isReady, execute } = useAsyncState(
  () => fetch('/api/users').then(r => r.json()),
  [],  // 초기값
  { immediate: true }
)

// 디바운스된 watch
const searchQuery = ref('')
watchDebounced(searchQuery, (val) => {
  console.log('검색:', val)
}, { debounce: 300 })

// 조건이 참이 될 때 실행
const data = ref<any>(null)
whenever(data, (val) => {
  console.log('데이터 로드됨:', val)
})

// 비동기 computed
const asyncUsers = computedAsync(async () => {
  const res = await fetch('/api/users')
  return res.json()
}, [])
</script>

자주 쓰는 composable — 네트워크

VUE
<script setup lang="ts">
import {
  useOnline,
  useFetch as useVueFetch,
  useEventSource,
  useWebSocket
} from '@vueuse/core'

// 온라인 상태
const isOnline = useOnline()

// Fetch API 래퍼
const { data, isFetching, error, execute } = useVueFetch('/api/users', {
  refetch: true,         // URL 변경 시 자동 재요청
  afterFetch: (ctx) => { // 후처리
    ctx.data = ctx.data.map(/* ... */)
    return ctx
  }
}).json()

// WebSocket
const { data: wsData, send, close, status } = useWebSocket('ws://localhost:3000')
</script>

<template>
  <div :class="isOnline ? 'online' : 'offline'">
    {{ isOnline ? '온라인' : '오프라인' }}
  </div>
</template>

Tree-shaking

VueUse는 완벽하게 tree-shakable합니다. 사용한 함수만 번들에 포함됩니다.

TYPESCRIPT
// 이 import는 useMouse 함수만 번들에 포함
import { useMouse } from '@vueuse/core'

// 전체 라이브러리가 포함되지 않음!

면접 팁

  • VueUse를 알고 있으면 생태계에 대한 이해도 를 보여줄 수 있습니다
  • "직접 만들 수도 있지만 검증된 라이브러리를 쓰는 것이 유지보수에 유리하다"는 실용적 관점을 보여주세요
  • useDebounceFn, useLocalStorage, useIntersectionObserver 같은 구체적 사례를 들면 실무 경험을 어필할 수 있습니다

요약

VueUse는 200개 이상의 검증된 composable을 제공하는 Vue 유틸리티 라이브러리입니다. 브라우저 API(localStorage, 클립보드), 센서(마우스, 스크롤, IntersectionObserver), 유틸리티(디바운스, 비동기 상태) 등 자주 필요한 기능을 바로 사용할 수 있습니다. Tree-shaking을 지원하여 번들 크기에 부담이 없습니다.

댓글 로딩 중...