Web

[핵심만 골라 배우는 Vue.js by 수코딩] 10. 피니아(Pinia), storeToRefs

반응형
Pinia(피니아) Vue.js의 공식 상태 관리 라이브러리(vue3)
Pinia는 전역 상태(state)를 관리하는 도구로, Vue 컴포넌트 간 데이터를 공유할 수 있게 해줍니다.
* 라우팅으로 상태관리가 어려워 피니아 사용

1. 피니어가 공유되면 초기화가 안되니  변수를 다르게 하거나, 매개변수를 분리
2. 파일은 js or 타입스크립트 가능
  명명규칙  1. use + 파일명 + Store   ex) useCounterStore 
2. 고유값은 파일명과 같게 ex) defineStore( ' counter ', 
 
  구조분해할당JS문법 const store = {
  count: 5,
  name: 'Vue'
}
const { count, name } = store    
동일 : const count = store.count,  const name   = store. name 
 
  storeToRefs import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
watch(() => store.count, (newVal) => {
  console.log('store.count changed:', newVal)
})
const { count } = store  // > 구조가 분해됨. 최초이후 반응성 없음
watch(() => count, (newVal) => {
  console.log('count changed:', newVal)  // ❌ 작동안함
})

import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
watch(() => store.count, (newVal) => {
  console.log('store.count changed:', newVal)
})
const { count } = storeToRefs(store) //>구조분해 반응성 유지
watch(() => count, (newVal) => {
  console.log('count changed:', newVal)  // ✅ 이제 count는 ref
})

state, getters를 ref로 안전하게 추출

**actions 는 반응형이 아님
const { increment } = store 
  Option Store vs Setup Store : 
  Option Store Vue2 : state, getters, actions 객체로 나눔

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

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

-----------------------------------------------------
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
const { count, name, doubleCount } = storeToRefs(store) // ✅ 반응형 유지
const { increment } = store                             // ✅ 메서드는 그냥
  Setup Store Vue3 : ref, computed, function을 setup()처럼 사용

// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const name = ref('피니아')
  const doubleCount = computed(() => count.value * 2)

  function increment() { // this 사용안함
    count.value++
  }

  return { count, name, doubleCount, increment }
})
--------------------------------------------
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
// ✅ 모든 속성이 ref나 computed 이므로, 구조분해 바로 사용해도 반응형 유지
const { count, doubleCount, increment } = store
기본구현 main.js import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
app.use(createPinia())
app.mount('#app')
npm install pinia

npm create vue@latest 사용시 여부 물어봄
stores/counter.js

src/
├── main.js
├── App.vue
├── stores/
│   └── counter.js
import { defineStore } from 'pinia'

// 스토어 정의
export const useCounterStore = defineStore('counter', {// 이름 파일동일
  state: () => ({  // data options 랑 같음
    count: 0,
    name: '피니아'
  }),
  getters: { // computed options 랑 같음, status 에 매개변수전달
    doubleCount: (state) => state.count * 2
  },
  actions: { //methods 같음, status this.로전달, 반응형아님
    increment() {
      this.count++
    },
    async incrementLater() {
      await new Promise(resolve => setTimeout(resolve, 1000))
      this.increment()
    }
  }
})
 
App.vue <template>
  <div style="padding: 2rem">
    <h1> Pinia 샘플</h1>

    <p>이름: {{ counter.name }}</p>
    <p>카운트: {{ counter.count }}</p>
    <p>2배 카운트: {{ counter.doubleCount }}</p>

    <button @click="counter.increment">+1</button>
    <button @click="counter.incrementLater">1초 후 +1</button>
  </div>
</template>

<script setup>
import { useCounterStore } from './stores/counter'

// 스토어 인스턴스 불러오기
const counter = useCounterStore()
</script>