[VueJS] Pinia Store 기본 사용법 (ft. storeToRefs)
Store 기본 사용법
공식문서를 통해 store 기본 사용법에 대해 알아보자. (*이 글은 Composition API 위주로 작성되었다.)
사용법을 익히기 전에 설치가 안되어 있다면 아래 링크를 통해 설치와 설정부터 해주고 오도록 하자.
https://mine-it-record.tistory.com/639
스토어 정의하기
import { defineStore } from 'pinia'
// `defineStore()`의 반환 값(함수)을 할당할 변수의 이름은 원하는 대로 지정할 수 있지만,
// 스토어 이름을 사용하고 `use`와 `Store`로 묶는 것이 가장 좋습니다.
// 예: `useUserStore`, `useCartStore`, `useProductStore`
// 첫 번째 인자는 앱 전체에서 스토어의 고유 ID입니다.
export const useMainStore = defineStore('main', {
// 다른 옵션...
})
주석에 다 쓰여있지만, 스토어의 경우 defineStore를 통해 정의하고 첫 번째 인자로는 "고유한 이름" 그리고 두 번째 인자로는 다른 옵션들을 추가할 수 있다.
일반적인 스토어 정의 명명규칙으로는 use를 앞에 사용하고 뒤에 Store를 붙이는 모양이다.
useMainStore 즉, 이 스토어의 고유 이름은 main이므로 첫번째 인자로 main을 넣어준 것이다.
Vue3에서는 Options API 와 Composition API를 골라서 사용할 수 있도록 제공하고 있는데,
Pinia를 Options API처럼 사용하는 방법은 기존의 Vuex와 크게 다를 게 없으므로 이 부분은 본문 하단에 있는 공식 문서를 따로 참고해보도록 하자.
셋업 스토어
Composition API 의 setup과 유사한 방식의 스토어 정의 방법이다.
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const name = ref('Eduardo')
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, name, doubleCount, increment }
})
역시 아무리봐도 Options API 보다는 Composition API가 훨씬 깔끔하다 생각된다.
Pinia에서는 state, getters, actions가 존재하는데, 이 셋업 스토어 내에서 각각 매칭 되는 게 다음과 같다.
- ref() → state
- computed() → getters
- function() → actions
자 이제 정의를 하였으니 직접 사용하러 가보자.
스토어 이용하기 1 - 반응형 x
<template>
<div class="check">
{{ name }} : {{ doubleCount }}
<button @click="increment">increment</button>
</div>
</template>
<script lang="ts">
import { useCounterStore } from "@/store/counter";
import { defineComponent } from "vue";
export default defineComponent({
name: "CheckView",
setup() {
const store = useCounterStore();
const { name, doubleCount, increment } = store;
return {
name,
doubleCount,
increment,
};
},
});
</script>
일단 이런식으로 store를 정의했던 useCounterStore()를 가져와서 사용할 수 있다.
그렇지만 한 가지 문제가 있는데, 구조 분해 할당을 사용할 때의 문제였던 반응형을 유지할 수 없다는 점이다.
실제로 위 코드에서 만들어진 버튼을 통해 increment 함수를 실행하면, store에 정의된 count 값은 올라가는데, 반응성이 사라진 것을 확인할 수 있다.
다음 예제를 통해 반응형을 유지하기 위해 어떤 식으로 해야 하지는 알아보자.
스토어 이용하기 2 - 반응형 o
이번 예제에서는 위와 똑같은 방식으로 사용하면 심심해 보여서 <script setup> 방식으로 구현해보았다.
<template>
<div class="about">
{{ name }} : {{ doubleCount }}
<button @click="store.increment">increment</button>
</div>
</template>
<script setup lang="ts">
import { useCounterStore } from "@/store/counter";
import { storeToRefs } from "pinia";
const store = useCounterStore();
const { name, doubleCount } = storeToRefs(store);
</script>
자 여기서 setup() 과 <script setup>의 차이 말고 뭐가 달라졌을까?
바로 storeToRefs의 사용 유무이다.
storeToRefs를 사용해야만 반응형이 유지가 된다.
storeToRefs
사실 Vue3, Composition API를 사용하다 보면 그렇게 이상할 거 없는 문법이다.
구조 분해 할당을 통해 값을 가져올 때 반응형을 잃지 않도록 도와주는 toRefs가 이미 사용되고 있기 때문이다.
그렇다면 storeToRefs 말고 그냥 toRefs로 쓰면 되지 않을까?
라는 의문을 가질수 있으나, storeToRefs에 대한 설명이 다음과 같이 쓰여있다.
Creates an object of references with all the state, getters, and plugin-added state properties of the store. Similar to toRefs() but specifically designed for Pinia stores so methods and non reactive properties are completely ignored.
(저장소의 모든 상태, getter 및 플러그인 추가 상태 속성을 사용하여 참조 개체를 만듭니다. toRefs()와 유사하지만 Pinia 저장소를 위해 특별히 설계되어 메서드와 비 반응 속성이 완전히 무시됩니다. - 구글 번역)
뭐 그냥 Pinia를 위해 특별히 설계되었다고 생각하면 될 것 같다.
공식문서를 참고하여 정리하였지만, 좀 더 상세한 내용을 보고자 한다면 역시 공식문서를 직접 가서 확인해 보는 걸 추천한다.