Jin의 개발 블로그

Vue의 Pinia 고급 기능 소개 본문

Vue

Vue의 Pinia 고급 기능 소개

Youngjin Kwak 2023. 9. 30. 09:50

Introduction

Pinia 는 Vue 3에서 Vuex를 밀어내고 가장 인기 있는 Statement manage 패키지로 올라섰습니다. 또한 한국어로 잘 번역되어 있기 때문에 한국에서 많이 사용하고 있습니다. 이 게시물은 Pinia를 조금 더 잘 사용하는 방법에 대해 설명할 것입니다.

Setup Store

`Setup store`는 `option store` 와 달리 `<script setup />` 처럼 사용 할 수 있게 합니다.

option store와 비교

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

  return { count, name, doubleCount, increment }
})

그러나 저는 option store만을 사용하고 있습니다. 대부분의 예제가 `option store` 로 쓰여져 있기 때문입니다.

storeToRefs

`storeToRefs` 함수 는 store 내에 존재하는 state와 getters 데이터를 Destructing하여 .Vue 파일 내에서변경할 수 있도록 도와줍니다.

const { count, doubleCount } = storeToRefs(useCounterStore())
count.value = 100

ref, reactivity API로 되어 있기 때문에, .value로 쉽게 변경이 가능합니다.

 

Action은 storeToRefs()로 Destructing 하지않아도 됩니다.

Subscription

State 혹은 Action이 변경되었을 때 다른일을 하고 싶으면 subscribe를 사용 할 수 있습니다. Watch보다 더 나은 Performence를 제공합니다.

State

counterStore.$subscribe((mutation, state) => {
  // import { MutationType } from 'pinia'
  mutation.type // 'direct' | 'patch object' | 'patch function'
  // Store id
  mutation.storeId
  // mutation.type === 'patch object'일때만 사용 가능함
  mutation.payload // store.$patch()로 들어옴

  console.log('state is changed', state)
})

store의 `$subscribe`는 state의 데이터가 변경되었을 때 작동합니다.

 

https://pinia.vuejs.org/core-concepts/actions.html#Subscribing-to-actions

 

Pinia | The intuitive store for Vue.js

Intuitive, type safe, light and flexible Store for Vue

pinia.vuejs.org

Action

const counterStore = uesCounter()
const unsubscribe = counterStore.$onAction(
  ({
    name, // Action 명
    store, // Store 인스턴스
    args, // action arguement 배열
    after, // Action의 return 혹은 resolve 된 후 작동
    onError, // 에러가 발생했거나, rejected 된 후 작동
  }) => {
    // 테스트용 시작 시간
    const startTime = Date.now()
    // 테스트용 메시지
    console.log(`Start "${name}" with params [${args.join(', ')}].`)

    // this will trigger if the action succeeds and after it has fully run.
    // it waits for any returned promised
    after((result) => {
      console.log(
        `Finished "${name}" after ${
          Date.now() - startTime
        }ms.\nResult: ${result}.`
      )
    })

    // this will trigger if the action throws or returns a promise that rejects
    onError((error) => {
      console.error(
        `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
      )
    })
  }
)

// 수동으로 구독 해제
unsubscribe()

예제 코드는 공식문서의 코드를 조금 변형했습니다.

store의 `$onAction`는 action이 호출 되었을 때 작동합니다.

 

https://pinia.vuejs.org/core-concepts/actions.html#Subscribing-to-actions

 

Pinia | The intuitive store for Vue.js

Intuitive, type safe, light and flexible Store for Vue

pinia.vuejs.org

Middleware

정확히는 공식문서에서 말하는 Middleware는 따로 없습니다. 하지만 Middleware처럼 사용 할 수 있는 방법은 존재합니다.

pinia.use(({ store }) => {
  store.$subscribe(() => {
    // react to store changes
  })
  store.$onAction((store, name, args) => {
    // react to store actions
  })
})

pinia의 use 함수내에서 subscriptin과 onAction 을 이용하여 middleware 같이 작동 시킬 수 있습니다.

`$subscribe()` 메소드는 store가 변경되었을 때 작동하며, `$onAction()` 메소드는 store의 action이 호출 되었을 때 작동합니다.

Persist

영구적으로 보관 하는 방법은 2가지가 있습니다. 직접 구현 방법과 패키지를 사용하는 방법입니다. 

Localstorage

간단한 방법으로 Localstorage에 데이터를 저장하고 가지고오는 방법입니다.

export const useCounterStore = defineStore('counter', {
  state: () => ({ 
  	count: localStorage.getItem('pinia/counter/count'), 
  	name: useLocalStorage('pinia/counter/name', 'bob').
  }),
  getters: {
    doubleCount: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
      localStorage.setItem('counter/count', this.count)
    },
  },
})

저장된 데이터를 삭제하기 위해서는 개발자 도구 (F12) -> Application -> Local Storage 에서 데이터들을 삭제하시면 됩니다.

추가적으로, VueUse 패키지의 `useLocalStroage()` 를 사용 할 수 도 있습니다. 예제는 state의 name을 확인해주세요.

 

Subscribtion과 같이 사용하기

counterStore.$subscribe((mutation, state) => {
  localStorage.setItem('pinia/counter/count', state.counter)
})

위에서 설명한 `$subscribe`와 같이 사용하면 state 자동 저장 기능을 만들 수 있습니다.

Package

패키지는 위와 비슷한 방법으로 저장하고 있으나, 더 쉬운 방법으로 저장할 수 도와줍니다.

https://prazdevs.github.io/pinia-plugin-persistedstate/

 

Home | pinia-plugin-persistedstate

pinia-plugin-persistedstate Configurable persistence and rehydration of Pinia stores.

prazdevs.github.io

우선 패키지를 설치해주세요.

npm i pinia-plugin-persistedstate
yarn add pinia-plugin-persistedstate
pnpm i pinia-plugin-persistedstate

 

pinia에 plugin을 등록해줘야합니다

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

Persist 지정하기

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0, name: 'Eduardo' }),
  getters: {
    doubleCount: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
  persist: true
})

간단하게 `persist: true`를 추가하면 store내 모든 정보가 local storage에 저장됩니다.

 

localstorage는 clientside사용이 됩니다. Nuxt에서 다른 storage를 사용하고 싶을 경우

  persist: {
    storage: persistedState.sessionStorage,
  },

위와 같은 방법으로 변경하면 됩니다.

Devtool

Chrome Vue devtool 확장프로그램을 이용하여 모든 store를 디버깅 할 수 있습니다.

https://devtools.vuejs.org/guide/installation.html#chrome

 

Installation | Vue Devtools

Installation Previous version If you want to install the previous version of the devtools (v5), see here. Chrome Install the extension on the Chrome Web Store: Install on Chrome Beta To install the beta version of the devtools, remove or disable any existi

devtools.vuejs.org

References

https://pinia.vuejs.org/

 

Pinia | The intuitive store for Vue.js

Intuitive, type safe, light and flexible Store for Vue

pinia.vuejs.org