Vue 3.0/07.pinia

[vue3-pinia] 01. Pinia 개요

  • -

이번 포스트에서는 vue에서 사용되는 pinia에 대해 살펴보자.

 

Pinia 개요

 

스토어?

스토어특정 컴포넌트에 바인딩되지 않은 상태 또는 처리해야 할 일의 로직을 가지는 것이다. 즉 모두가 사용하는 전역 상태를 호스팅 한다. 따라서 스토어에 저장된 정보는 모든 페이지나 컴포넌트에서 공유될 수 있는 것이다.

이렇게 이야기 하면 많은 경우 스토어를 만능 저장소로 생각해서 모든 데이터를 빨아들이는 블랙홀로 생각할 수 있는데 당연히 지양해야 할 태도다. 우리가 프로그래밍 하다 보면 전역 변수의 사용이 많지 않은 것과 같은 이유이다.

스토어에 저장할만한 정보여러 페이지에서 보여지는 사용자의 로그인 정보나 다단계를 거쳐서 구성되는 복잡한 데이터 등이다. 상태 관리를 한다고 해서 Pinia를 데이터베이스 처럼 사용해서는 곤란하다. pinia는 마치 backend의 세션 정도로 생각하면 좋겠다. 게시판을 작성한다고 했을 때 세션에 게시물 정보를 저장하지는 않는다.

다만 여러 페이지에서 공유해야 할 사용자 정보나 검색 조건을 유지해야 할 때 검색 조건, 페이지 번호 정도가  pinia에 관리해볼 만한 내용이다. 또한 단순히 부모 자식 간의 props/emits를 store에서 관리할 것인지도 고민할 꺼리다. props와 emits는 직관적으로 파악이 가능하지만 store는 분석이 필요하다. 또한 depth가 깊어지면 provide/inject도 훌륭한 도구이다.

3가지 경우(props/emits, provide/inject, store)를 칼같이 나눠서 언제 누구를 써야한다라고 정의하기는 너무 어렵다. pinia에서도 왜 pinia를 써야하는지를 언급하면서 비지니스 로직적인 차이를 언급하지는 않고 부가적인 장점(devtool 연동등)을 이야기 할 정도다. 상황에 맞춰서 판단하자.

다만 관리의 측면에서는 확실히 Pinia의 사용이 권장된다.

https://pinia.vuejs.kr/introduction.html#why-should-i-use-pinia

 

Vue 3의 공식 Store | Vue.js를 위한 직관적인 스토어

직관적이고 타입 안전한 가벼운 그리고 유연한 Vue 스토어

pinia.vuejs.kr

 

 

Pinia란?

pinia(piːnjʌ)는 pineapple의 스페인어 표현으로 vue에서 스토어 관리를 위해 사용되는 라이브러리이다.

https://pinia.vuejs.kr

기존의 Vue에서 상태 관리를 위해서는 Vuex라는 녀석이 사용됐었는데 3.0으로 업데이트 하면서 Pinia로 대체 되었다. 여전히 Vuex를 사용할 수는 있지만 Pinia가 훨씬 깔끔하고 쉽다. ㅎ

 

설치 및 초기화 설정

vite를 이용해 프로젝트 생성 시 pinia를 사용하기 위해서는 Add Pinia for state management 항목에서 Yes를 선택하면 된다.

위와 같이 프로젝트를 생성하면 main.js에 createPinia()를 통해 pinia 객체(루트 저장소)를 만들어 앱에 전달한다.

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia()) // pinia instance(루트 저장소)생성 및 앱에 전달
app.use(router)

app.mount('#app')

 

추가로  src/stores/counter.js를 샘플로 넣어준다. (친절! 한데 나중에는 귀찮 ㅠㅠ)

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})

 

기존 프로젝트에 pinia를 추가로 설치하기 위해서는 다른 라이브러리 처럼 npm을 이용할 수 있다.

npm install pinia

물론 이 과정 후에는 main.js에 추가하는 작업 등은 따로 작업해주어야 한다.

 

기본 스토어 분석

 

defineStore

샘플로 넣어준 counter.js 파일을 분석해보자.

우선 pinia가 제공하는 defineStore 라는 함수를 이용해서 store를 정의하게 된다. 

defineStore(store_id, option 객체 | setup function)

 

defineStore의 첫 번째 파라미터는 store_id라고 하는데 여러 개의 store를 구별할 수 있게 unique 해야 한다.  그리고 defineStore의 반환 값을 저장할 변수는 통상 "use" + store_id+ "Store"의 형태를 권장한다.

따라서 다음은 counter와 관련된 store를 정의하고 useCounterStore에 저장 후 export 시키는 내용이다.

export const useCounterStore = defineStore('counter', . . .)

 

setup function | Option객체 

pinia 저장소는 크게 다음의 3가지로 구성된다.

  • state: 관리하려는 대상(데이터)
  • getters: state의 값을 filtering 하거나 readonly로 조회하기 위한 함수로 computed로 작성
  • actions: business logic에 의해 state를 변경하기 위한 함수

이 3가지를 구성하는 것이 defineStore의 두 번째 파라미터인데 option 객체 또는 setup function을 사용할 수 있다. 어치피 동작은 동일하기 때문에 선호하는 vue 작성 스타일에 따라 작성해주면 된다. 아래 그림의 좌측이 setup, 우측이 options 객체 형태이다.

우리는 setup으로 간다!

options 방식은 state, getters, actions 가 명확히 구분되어있다. 이에 대응하는 setup 함수의 요소를 살펴보면 state는 ref 또는 reactive를 통한 반응형 객체, getters는 computed, actions는 일반 함수가 된다. (options가 명확해보이지만 하다보면 setup이 편하다는..)

 

참고로 vuex에서는 mutations(변경작업)와 actions(비동기작업)로 분리되었었지만 pinia에서는 둘이 합쳐졌다.

 

사용해보기

store를 컴포넌트에서 사용할 때는 저장소 js를 import 한 후 defineStore로 작성한 함수 객체를 이용한다.

import { useCounterStore } from "@/stores/counter.js"

const counterStore = useCounterStore()                       // proxy 객체
console.log(`[CounterView.vue] counterStore:`, counterStore)

// destructuring을 하련느 경우 storeToRefs 활용
import { storeToRefs } from "pinia";
// ref, computed 처럼 XXRefImpl 타입의 객체들(단순 destrecturing은 반응성이 소멸됨)
const { count, doubleCount } = storeToRefs(counterStore)
// 그 외의 객체들(storeToRefs에서는 undefined 처리됨)
const { increment } = counterStore;

store에서 return 된 객체는 reactive로 감싸서 전달된다. 따라서 값에 접근할 때는 초기 타입(ref, reactive 등)에 상관 없이 .value를 사용하지 않고 접근한다.

만약 매번 변수를 통해서 접근하는 것이 부담스럽다면 destructuring 해서 사용할 수 있는데 그냥 destructuring 하면 XXRefImpl 반응성을 잃어버리기 때문에 주의가 필요하다.  XXRefImpl을 반응성을 유지한 채 destructuring 처리할 때에는 pinia에서 제공하는 storeToRefs 함수를 이용하면 된다. storeToRefs는 vue의 toRefs와 유사한데 비 반응성 데이터나 일반 함수는 애초에 제외하는 등 차이가 있다. (훨씬 깔끔하게 벗겨진다고 해야하나..)

개인적으로 굳이 destructuring 해야할 필요가 있나 싶다. 반응성을 위해서 다시 변환해야 하고, 여러개의 store를 사용한다면 이름 충돌을 대비하여 namespace를 지정해야할텐데.. 

 

script 영역에 store를 가져왔다면 화면에서는 이제까지의 사용법과 동일하다.

<template>
  <div>
    <h1>pinia count</h1>
    <ul>
      <li>ref:{{ counterStore.count }}, {{ count }}</li>
      <li>computed:{{ counterStore.doubleCount }}, {{ doubleCount }}</li>
    </ul>
    <button @click="counterStore.increment">++</button>
    <button @click="increment">++</button>
  </div>
</template>

'Vue 3.0 > 07.pinia' 카테고리의 다른 글

[vue3-pinia] 04. pinia-plugin-persistedstate  (2) 2023.10.15
[vue3-pinia] 03. 기타  (0) 2023.10.15
[vue3-pinia] 02. todos 관리  (0) 2023.10.15
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.