Pinia

Lee kyu min·2024년 4월 12일

Vue

목록 보기
10/13

Pinia

  • Vue3 버전과 함께 출시 된 상태 관리 라이브러리
  • Vue3의 Composition API를 기반
  • Nuxt기준 pinia 설치
  • Nuxt는 서버측 렌더링 관련 많은 작업을 처리하여 Nuxt와 함께 Pinia를 사용하는 것이 더 쉽다
yarn add pinia @pinia/nuxt
# or with npm
npm install pinia @pinia/nuxt

1. 장점

1) TypeScript와 완전 호환
2) 전역 상태, 개별 Component의 로컬 상태 모두 관리 가능
3) reactive 함수를 사용하여 상태 관리, 상태 변화를 실시간으로 추적, 반응형 처리, 성능 최적화

2. Store

  • 구성요소에 바인딩되지 않은 상태 및 비즈니스 논리를 보유하는 엔티티
  • 전역상태를 호스팅
  • state, getter, actions 개념이 존재

1) 매장 생성

  • pinia에서 상점을 생성
  • defineStore()의 반환 값에 이름은 자유
  • 상점 이름을 사용하는데는 use를 사용하는걸 권장 ex) useUserStore, useCartStore, useProductStore
  • 첫번째 인수는 애플리케이션 전체에 걸친 상점 고유 id(id는 빠질 수 없다)
  • 두번째 인수로는 고유 값(Setup 함수 또는 Options 개체)가 올 수 있다.
import { defineStore } from 'pinia'

export const useAlertsStore = defineStore('alerts', {
  // 옵션
})

2) 매장 사용

  • 저장소는 구성 요소 내에서 호출될 때 까지 생성되지 않기에 저장소 정의( setup() )
<script setup>
import { useCounterStore } from '@/stores/counter'

// access the `store` variable anywhere in the component ✨
const store = useCounterStore()
</script>

3) Option Store

* Vue Option API와 유사하게 actions 속성을 사용하여 옵션 객체 전달 가능

interface IUser {
  email: string
  name: string
}
// auth 스토어
export const useAuthStore = defineStore('auth', {
  state: () => {
    return {
      user: {
        email: '',
        name: ''
      } as IUser
    }
  },
  getters: {
    getEmail: (state): string => state.user.email
  },
  actions: {
    setUser(data: IUser) {
      this.user = data
    }
  }
})

if (import.meta.hot) {  //HMR(개발중 애플리케이션 수정 시 페이지 새로 고치지 않고 변경사항을 빠르게 반영)
  import.meta.hot.accept(acceptHMRUpdate(useAuthStore, import.meta.hot))
}
  • 구현
<template>
  <div>
    <v-btn @click="handleLogin">로그인</v-btn>
    <div>{{ getEmail }}</div>
  </div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useAuthStore } from '~/stores/auth'

// 스토어 생성 
const authStore = useAuthStore()
// 반응형 객체로 변환
const { getEmail } = storeToRefs(authStore)

const handleLogin = () => {
  const user = {
    email: 'test@naver.com',
    name: 'kim'
  }
  authStore.setUser(user)
}
</script>
<style scoped></style>
  • storeToRefs : Pinia의 유틸리티함수, reactive한 Vue ref 객체로 변환하는 데 사용

4) Setup Stores

  • Vue Composition API 설정함수와 유사하게 반응형 속성과 메서드를 정의하는 함수를 전달하고 노출하는 속성과 메서드가 포함된 객체 반환 가능
  • ref() -> state
  • computed() -> getter
  • function() -> actions
  • 예시
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 }
})

5) 반응성 유지한 채 저장소에서 속성 추출( storeToRefs()

ex)

  • name, doubleCount는 반응형 참조, 플러그인에 의해 추가된 속성에 대한 참조도 추출
  • 작업이나 비반응성(비참조/반응성) 속성은 건너뜀
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'

const store = useCounterStore()

const { name, doubleCount } = storeToRefs(store)	

const { increment } = store
</script>

6) 자동 가져오기(Nuxt)

  • 폴더 내에 정의된 모든 Store를 자동으로 가져옴(중첩된 상점 제외) 다음 옵션 을 설정하여 이 동작을 사용자 정의가능
  • nuxt.config.ts
export default defineNuxtConfig({
  //다른옵션
  modules: ['@pinia/nuxt'],
  pinia: {
    storesDirs: ['./stores/**', './custom-folder/stores/**'],
  },
})

3. 기본예

1) stores 폴더 생성 후 js파일 생성

  • stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 }
  },

  actions: {
    increment() {
      this.count++
    },
  },
})

2). 구성요소에서 사용

  • 사용할 vue파일
<script setup>
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()
</script>

4. State

1). 상태 정의( 초기 상태 반환 함수 )

ex)

import { defineStore } from 'pinia'

export const useStore = defineStore('storeId', {
  // arrow function recommended for full type inference
  state: () => {
    return {
      // all these properties will have their type inferred automatically
      count: 0,
      name: 'Eduardo',
      isAdmin: true,
      items: [],
      hasChanged: true,
    }
  },
})

2) 타입스크립트 호환 상태 만들기

export const useUserStore = defineStore('user', {
  state: () => {
    return {
      // for initially empty lists
      userList: [] as UserInfo[],
      // for data that is not yet loaded
      user: null as UserInfo | null,
    }
  },
})

interface UserInfo {
  name: string
  age: number
}

3) 상태 변경(Accessing the state)

  • 인스턴스를 통해 상태를 직접 읽고 쓰기 가능
  • 초기상태가 미리 정의되지 않으면 사용불가
const store = useStore()

store.count++

4) 상태 리셋

  • Option Stores는 $reset() 메서드를 호출하여 상태를 초기값으로 재설정 가능
const store = useStore()

store.$reset()
  • Setup Stores는 자신의 방법 설정
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)

  function $reset() {
    count.value = 0
  }

  return { count, $reset }
})

5. 플러그인

  • 스토어에 추가할 속성을 선택적으로 반환하는 함수

1) context

export function myPiniaPlugin(context) {
  context.pinia // `createPinia()`로 생성된 피니아
  context.app // `createApp()`으로 생성된 현재 앱(Vue 3만 해당)
  context.store // 플러그인이 확장되는 스토어
  context.options // `defineStore()`에 전달된 스토어를 정의하는 옵션 객체
  // ...
}
  • pinia.use()를 사용하여 pinia에 전달
    (plugin은 pinia가 전달된 후에만 적용)
pinia.use(myPiniaPlugin)
  • 예제(객체를 반환하여 모든 스토어에 정적속성 추가)
import { createPinia } from 'pinia'

// 이 플러그인이 설치된 후 생성된 모든 저장소에 `secret`이라는 속성을 추가합니다.
// 이것은 다른 파일에 있을 수 있습니다.
function SecretPiniaPlugin() {
  return { secret: '임금님 귀는 당나귀 귀!' }
}

const pinia = createPinia()
// 플러그인을 피니아에 제공
pinia.use(SecretPiniaPlugin)

// 다른 파일에서
const store = useStore()
store.secret // '임금님 귀는 당나귀 귀!'
  • Nuxt와 함게 pinia사용하는 경우 먼저 Nuxt 플러그인 생성
// plugins/myPiniaPlugin.ts
import { PiniaPluginContext } from 'pinia'

function MyPiniaPlugin({ store }: PiniaPluginContext) {
  store.$subscribe((mutation) => {
    // react to store changes
    console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}.`)
  })

  // Note this has to be typed if you are using TS
  return { creationTime: new Date() }
}

export default defineNuxtPlugin(({ $pinia }) => {
  $pinia.use(MyPiniaPlugin)
})

0개의 댓글