IT/언어

[Vue3] 이제는 Vuex나 Pinia를 사용하지 않아도 되지 않을까? (Composition API)

개발자 두더지 2023. 5. 6. 20:39
728x90

일본의 한 블로그 글을 번역할 포스트입니다. 오역 및 의역,. 직역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.

 

 Vue의 상태 관리로 Vuex나 Pinia를 사용합시다라는 말이 많지만 외부의 라이브러리를 사용하지 않고 Vue3 자체의 기능으로 충분하지 않은가에 대한 이야기이다.

 

 

상태 관리 라이브러리를 사용하지 않고 어떻게 스토어(상태과 액션)을 정의하는가?


 Vue3에서 도입된 Composition API를 사용하면 된다. 예를 들어 다음과 같다.

// ~/composables/todo.js

import { reactive, computed, readonly } from 'vue';


export function useTodo() {
  const items = reactive([]);

  const firstItem = computed(() => items[0] ?? null);

  function add(todo) {
    items.push(todo);
  }

  return { items: readonly(items), firstItem, add };
}

 Composition API이라는 Vue 자체의 기능만으로 간단히 상태와 액션을 정의할 수 있다. reactive, ref, computed, watch, readonly등의 함수를 구사하여 상태 관리 라이브러리에서 할 수 있는 모든 것을 할 수 있다. Composition APi의 쪽이 보다 유연하고 심플하다고 생각된다.

 

 

어떻게 스토어를 컴포넌트로 통해 글로벌하게 공유할 수 있게 하는가?


 Vue자체의 기능인 provide와 inject를 사용하면 된다. provide함수와 inject함수는 Vue의 의존 관계 주입 구조로 부모의 컴포넌트에 provide('key', value)로 정의한 값을 자식 컴포넌트에서 const value = inject('key')로 하여 꺼낼 수 있게 된다.

 그러므로 어플리케이션 전체에서 공유하는 스토어는 탑 App 컴포넌트내에서 생성하여 provide로 하면, 그 스토어를 사용하고 싶은 각 컴포넌트는 inject로 사용할 수 있게 된다.

// ~/App.vue

<template>
<router-view />
</template>

<script>
import { provide } from 'vue';
import { useTodo } from '~/composables/todo.js';


export default {
  setup() {
    const todoStore = useTodo();
    provide('todo', todoStore);
  },
};
</script>
// ~/pages/list.vue

<template>
<ul>
  <li v-for="item of todos">{{ item }}</li>
</ul>
</template>

<script>
import { inject } from 'vue';


export default {
  setup() {
    const todo = inject('todo');
    return { todos: todo.items };
  },
};
</script>

 이 구조를 사용하면 글로벌에 공유할 뿐만 아니라, 특정의 컴포넌트 트리안에서만 공유하는 스토어도 만들 수 있다. 대규모이자 신규 어플리케이션에서 어떤 기능군에서만 스토어를 공유하도록 할 수 있지도 모른다. Vuex나 Pinia에서는 이러한 것을 할 수 없다.

 

 

어떤 스토어안에서 다른 스토어를 참조하고 싶을 때는 어떻게 할까?


 스토어를 생성할 때에 참조하고 싶은 스토어를 인수로 전달한다. 예를 들어 아래의 useTodoSearch함수로 한다.

// ~/pages/list.vue

<template>
<input v-model="searchQuery" />
<ul>
  <li v-for="item of todos">{{ item }}</li>
</ul>
</template>

<script>
import { inject } from 'vue';
import { useTodoSearch } from '~/composables/todo-search.js';


export default {
  setup() {
    const todo = inject('todo');
    const todoSearch = useTodoSearch(todo);
    return { searchQuery: todoSearch.query, todos: todoSearch.matched };
  },
};
</script>

 useTodoSearch의 구현은 아래와 같다.

// ~/composables/todo-search.js

import { ref, computed } from 'vue';


export function useTodoSearch(todoStore) {
  const query = ref('');

  function matches(q, todo) {
    // todo가 검색 쿼리 q에 매치하는지 아닌지를 반환하는 함수
  }

  const matched = computed(() => todoStore.items.filter(
    item => matches(query.value, item)
  ));

  return { query, matched };
}

 이로 인해 스토어간의 의존관계가 명확히 한다는 장점이 있다. Vuex나 Pinia에서는 스토어는 글로벌로 어더이세든 참조할 수 있으므로 스토어간의 의존성이 불명확하다. Vuex나 Pinia의 스토어는 본질적으로 기피해야 할 글로벌 변수이며, 모든 곳에서 상태가 변경될 수 있고, 모든 곳에서 그 상태에 의존한 처리를 쓸 수 있게 된다.

 

 

상태관리 라이브러리의 장점


 Vuex나 Pinia등 개별 상태 라이브러리를 사용하는 장점은 좋은 디버그 툴이 있기 때문이다. 브라이저의 확장기능을 넣으면 각 스토어의 현재 상태를 한꺼번에 확인하거나 과거의 상태로 거슬러 올라가기도 한다. 그러나 이번 포스트에서 설명한 Vue3만을 사용하는 방법으로는 이러한 방법으로 불가능하다.


참고자료

https://qiita.com/silane1001/items/f5f61f51fd785e031eb1

 

728x90