[번역] Vue 3를 위한 필수 라이브러리 VueUse

박종훈·2022년 3월 9일
15
post-thumbnail

원문: VueUse as must-have library for Vue 3

VueUse가 익숙하지 않은 분들은, 한 번 사용해 보는 것을 추천합니다. 마치 모든 js 프로젝트에 lodash 라이브러리가 포함됐던 것처럼, 이 라이브러리는 Vue 3 프로젝트에 사실상 표준이 될 수도 있기 때문입니다.

어떤 분들은 이미 이 라이브러리가 제공하는 광범위한 기능을 확인하셨을 겁니다. 또 어떤 분들은 Vue 2 프로젝트에서 이미 사용을 해보셨을 수도 있지만, 구버전에서는 모든 새로운 기능을 지원하진 않습니다. 이 라이브러리는 마우스 좌표를 얻는 것과 같은 간단한 유틸리티 뿐만 아니라, 파이어베이스, Axios, 쿠키, QR, 로컬 스토리지, 브라우저, RxJS, 애니메이션, 지리 위치, 표준 Vue 훅을 위한 익스텐션, 미디어 플레이어 등처럼 다양한 기능과의 복잡한 연계 또한 지원합니다. 이 라이브러리의 주목받는 후원자 중에는 Vue의 창시자인 Evan You가 있으며, 이는 좋은 징조로 보입니다. 해당 라이브러리는 정기적인 업데이트와 버그 픽스를 진행하고, 커뮤니티 또한 자라고 있습니다. 라이브러리가 성공하기 위해 갖춰야 하는 것은 모두 갖춘 것이죠.

이 글에서 저는 오직 4개의 기능만 언급하겠지만, 당연히, 다른 기능들도 주목할만합니다.

onClickOutside - 엘리먼트 바깥을 클릭할 때

라이브러리 설치는 혼자서 하실 수 있을 테니, 곧바로 흥미로운 피처에 관해서 얘기합시다. 준비운동으로, 엘리먼트의 바깥에 대한 클릭을 처리하는 간단한 훅, onClickOutside,을 생각해 봅시다. 이 기능을 제공하는 수많은 패키지가 있고, 거의 모든 사람이 그 함수를 직접 작성했을 것입니다. 이 기능은 주로 v-clickOutside와 같은 커스텀 Vue 디렉티브로 작성되고 원하는 엘리번트에 적용됩니다. 하지만 훅의 사용법은 조금 다릅니다.
,저는 todo 앱의ToDoItem.vue` 컴포넌트에 이 훅을 사용해보았습니다.

<template>
  <li ref="todoitem">
    <input type="checkbox" />
    <span v-if="!editable" @click="editable = !editable">
      {{ todo.text ? todo.text : "Click to edit Todo" }}
    </span>
    <input
      v-else
      type="text"
      :value="todo.text"
      @keyup.enter="editable = !editable"
    />
  </li>
</template>
<script lang="ts">
  import { defineComponent, PropType, ref } from "vue"
  import Todo from "@/models/ToDoModel"
  import { onClickOutside } from "@vueuse/core"
  export default defineComponent({
    name: "TodoItem",
    props: {
      todo: {
        type: Object as PropType<ToDo>,
        requied: true
      }
    },
    setup() {
      const todoItem = ref(null)
      const editable = ref(false)
      onClickOutside(todoItem, () => {
        editable.value = false
      })
      return { todoItem, editable }
    }
  })
</script>

필요하지 않은 부분은 제거했지만, 컴포넌트가 여전히 크네요. setup 훅 안의 코드에 집중해서 보겠습니다. 먼저 template의 원하는 엘리먼트와 연결할 비어있는 todoItem 링크를 만들었고, onClickOutside 훅의 첫 번째 인자로 전달했습니다. 그리고 두 번째 인자로 우리가 필요로 하는 액션을 콜백으로서 전달했습니다. span 태그를 클릭하면, input 태그로 대체되고, ref="todoItem" 속성을 가진 li 태그의 바깥을 클릭하면, 다시 input 태그가 span 태그로 대체됩니다.

useStoragecreateGlobalState - 반응형 로컬 스토리지

다음으로 살펴볼 함수는 useStorage입니다. 이 함수는 Window.localStorage에 데이터를 저장합니다. 이 함수는 글로벌 스토리지를 생성하는 createGlobalState와 함께 사용하면 편리합니다. 이제 데이터는 자동으로 저장, 업데이트, 삭제되고 페이지가 리로드된 후에도 사라지지 않을 것입니다. 아래는 이 함수들을 사용한 예시입니다:

// @/store/index.ts
import { createGlobalState, useStorage } from '@vueuse/core'
import Note from '@/models/NoteModel'
// state
export const useGlobalNotes = createGlobalState(
  () => useStorage('my-notes' [] as Note[]),
)
// actions
const notes = useGlobalNotes() // 로컬 사용을 위해
export const addNote = function (note) {
  notes.value.push(note)
}
export const deleteGlobalNote = function (noteId: number) {
  notes.value = notes.value.filter(note => note.id != noteId)
}

useStorage 함수는 첫 번째 인자로 localStorage에 데이터를 저장할 키를 받고, 두 번째 인자로 초기 값을 받습니다.

createGlobalState는 컴포넌트에 상태를 전달하는 래퍼(wrapper) 함수를 생성합니다. 이 함수를 호출함으로써 (예시의 경우 useGlobalNotes()를 의미합니다), Vue 컴포넌트나 예시의 파일에서처럼 반응형 노트 리스트를 얻을 수 있습니다. 노트 배열은 프록시 객체로서, 일반적인 배열처럼 사용될 수 있고, 리스트 자체는 notes.value에 저장되어 있습니다. 컴포넌트의 마크업 템플릿에서 .value를 더할 필요가 없습니다.

비교를 위해서 라이브러리 저자의 useStorage 예시를 보는 것도 도움이 됩니다. 차이는 setup에서는 반응형 스토리지에 직접 접근하는 것이 아니라 value 프로퍼티를 이용해 접근하지만, HTML 템플릿에서는 일반 변수처럼 사용하는 것입니다.

useRefHistory - 변경 히스토리

useRefHistory는 데이터의 변경 히스토리를 기록해서 취소/재적용 기능을 제공하는 훅입니다. 저는 노트 편집 페이지에서 Undo와 Redo 버튼을 만들기 위해 이 훅을 사용했습니다. 먼저, 저는 ref를 사용해 반응형 변수를 생성했습니다. 코드를 더 자세히 살펴 보시죠:

<!-- Note.vue -->
<template>
  <!-- ... -->
  <div>
    <button type="button" @click="undo" :disabled="!canUndo">Undo</button>
    <button type="button" @click="redo" :disabled="!canRedo">Redo</button>
  </div>
  <!-- ... -->
</template>
<script lang="ts">
  import { defineComponent } from "vue"
  import { useRefHistory } from "@vueuse/core"
  import ToDo from '@/models/ToDoModel'
  export default defineComponent({
    setup() {
      const note = ref({
              title: "",
              todos: [] as ToDo[]
            })
      const {
          undo,
          redo,
          canUndo,
          canRedo,
          clear
          } = useRefHistory(note, { deep: true })
      }
      const updateTitle = (title: string) => {
          note.value.title = title
      }
      const addNewTodo = () => {
          note.value.todos.push({} as ToDo)
      }
      const onRemoveTodo = (index: number) => {
          note.value.todos.splice(index, 1)
      }
      return {
          note,
          addNewTodo,
          onRemoveTodo,
          updateTitle,
          undo,
          redo,
          canUndo,
          canRedo,
          clear
      }
    }
  })
</script>

ref를 이용해 반응형 변수를 만들어 useRefHistory 훅에 전달하고, 중첩된 객체를 위한 deep: true를 훅 인자에 표시했습니다. 구조 분해 할당을 활용해 useRefHistory로부터 historyundo, redo, canUndo, canRedo, clear를 얻었습니다. canUndocanRedo 프로퍼티가 버튼의 disabled 속성에 할당됐습니다. clear는 레코드 편집을 마무리한 후에 히스토리를 비우는 데 필요합니다. useManualRefHistory 훅은 거의 비슷한 역할을 하지만, 히스토리 저장이 commit() 커맨드가 호출될 때에만 일어납니다.

결론

Vue 3 개발을 위한 VueUse의 방대한 기능 중에서 오직 4개 함수만 다루었습니다. 더 깊이 학습하기 위해서는, 이 멋진 라이브러리의 사이트를 방문할 것을 추천합니다. 문서는 아직 개선이 필요할 수 있지만, 라이브러리와 함께 문서 또한 지속적으로 업데이트되고 있습니다.

이 라이브러리를 테스트한 예시의 전체 코드는 여기서 볼 수 있습니다.

profile
유쾌한 동행과, 함께하는 성장을 사랑하는 Arch 리눅스 유저입니다.

2개의 댓글

comment-user-thumbnail
2022년 3월 17일

잘 보고 갑니다

답글 달기
comment-user-thumbnail
2022년 4월 4일

잘 보고 갑니다!

답글 달기