2024. 7. 7. 21:08ㆍFE/Vue.js
1. 상태 관리
모든 Vue 컴포넌트 인스턴스는 이미 자체 반응형 상태를 관리한다.
- 상태(State) : 앱 구동에 필요한 기본 데이터
- 뷰(View) : 상태를 선언적으로 매핑하여 시각화
- 기능(Actions) : 뷰에서 사용자 입력에 대해 반응적으로 상태를 변경할 수 있게 정의된 동작
이때 단방향 데이터 흐름을 가진다.
하지만 이런 상태 관리의 단순성이 무너지는 시점이 오는데…
여러 컴포넌트가 상태를 공유할 때 단순성이 무너지기 시작한다.
가. 여러 뷰가 동일한 상태에 종속된 경우
여러 뷰가 하나의 상태의 변화에 따라 복잡하게 상호작용 할 수 있다.
공유하는 상태를 공통 조상 컴포넌트로 끌어올린 다음 이를 props로 전달하면 문제를 해결할 수 있다.
그러나 계층 구조가 깊어질 경우 비효율적이고, 관리가 어려워진다.
또한 Prop 드릴링으로 알려진 또 다른 문제로 이어진다.
나. 서로 다른 뷰의 기능이 동일한 상태를 변경시켜야 하는 경우
템플릿 refs를 통해 직접적인 부모/자식 인스턴스에 도달하거나, emit된 이벤트를 통해 거슬러 올라가면서 상태를 변경시켜야 있다.
당연히 이러면 안된다.
복잡도가 높아지고 유지보수가 어렵다.
다. 반응형 API를 통한 간단한 상태 관리
직관적인 솔루션은 컴포넌트에서 공유 상태를 추출하고 전역 싱글톤에서 관리하는 것이다.
모든 컴포넌트는 트리의 위치에 관계없이 상태에 접근하거나 작업을 트리거할 수 있다.
reactive()
를 사용하여 반응형 객체를 만든 다음 여러 컴포넌트에서 접근한다.
// store.js
import { reactive } from 'vue'
export const store = reactive({
count: 0,
increment() {
this.count++
}
})
<!-- ComponentA.vue -->
<script setup>
import { store } from './store.js'
</script>
<template>
<button @click="store.increment()">
A 컴포넌트에서: {{ store.count }}
</button>
</template>
<!-- ComponentB.vue -->
<script setup>
import { store } from './store.js'
</script>
<template>
<button @click="store.increment()">
B 컴포넌트에서: {{ store.count }}
</button>
</template>
2. Pinia란?
Vue의 공식 상태 관리 라이브러리다.
각 컴포넌트의 공유 생태를 추출하여, 전역에서 참조할 수 있는 저장소에서 관리.
컴포넌트 트리는 하나의 큰 뷰가 되고 모든 컴포넌트는 트리 계층 구조에 관계없이 상태에 접근하거나 기능을 사용할 수 있다.
yarn add pinia
# or with npm
npm install pinia
// main.js
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia' // 추가
import App from './App.vue'
const app = createApp(App)
app.use(createPinia()) // 추가
app.mount('#app')
3. store
가. 왜 씀?
스토어는 컴포넌트 트리에 바인딩되지 않은 상태와 비즈니스 로직을 보유하는 엔티티다.
다시 말해, global state를 호스팅 한다.
항상 존재하고 앱 전체에서 읽고 쓸 수 있다.
props와 emit을 반복하는 것보다 쉽게 상태를 읽고 쓸 수 있다.
반대로 로컬 데이터를 스토어에 저장하지는 말자.
나. store 정의
// 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 }
})
defineStore('counter', () => { … })
: 첫 번째 인자는 앱 전체에서 스토어의 고유 ID다. 스토어와 devtools를 연결하는 데 사용된다.
: 두 번째 인자는 셋업 함수 또는 옵션 객체를 허용한다.return { count, doubleCount, increment }
: 스토어에 등록한 것들은 반환해야 한다.
다. store 사용
<!-- App.vue -->
<script setup>
import { useCounterStore } from '@/stores/counter.js'
const store = useCounterStore();
const onClick = () => {
store.increment()
}
</script>
<template>
<div>
<button @click="onClick">click!</button>
<span> count : {{ store.count }}</span>
<span> double : {{ store.doubleCount }}</span>
</div>
</template>
<style scoped>
</style>
store
는 reative
로 store.value.count
와 같이 .value
를 붙이지 않고 그냥 store.count
로 사용하면 된다.
라. Destructuring from a Store
<!-- App.vue -->
<script setup>
import { useCounterStore } from '@/stores/counter.js'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
const { name, doubleCount } = store // This won't work because it breaks reactivity
const { name, doubleCount } = storeToRefs(store) // `name` and `doubleCount` are reactive refs
const { increment } = store
</script>
const { name, doubleCount } = store
: 이렇게 가져오면 반응형 상태가 아니다.const { name, doubleCount } = storeToRefs(store)
:storeToRefs()
를 사용해야 반응형 상태다.const { increment } = store
: action(= function) 또는 non reactive (non ref/reactive) property는 상관없다.
4. Plugin
pinia에서는 상태 관리를 위한 다양한 plugin을 사용할 수 있다.
그중에서 하나로 pinia-plugin-persistedstate
를 알아보자.
가. pinia-plugin-persistedstate
웹 애플리케이션의 상태(State)를 브라우저의 local storage나 session storage에 영구적으로 저장하고 복원하는 기능을 제공.
페이지를 리로딩해도 store에 저장된 state를 유지할 수 있다.
npm i pinia-plugin-persistedstate
// main.js
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
추가한다.
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(100)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
},{
persist: {
storage: sessionStorage,
// other config
}
}
)
경우에 따라서 저장되는 위치를 localstorage
와 sessionstorage
중 선택할 수 있다.
기본은 localstorage
다.
'FE > Vue.js' 카테고리의 다른 글
[Vue.js] Vue-Router (0) | 2024.07.07 |
---|---|
[Vue.js] Props & Emit (0) | 2024.07.07 |
[Vue.js] Component (0) | 2024.07.07 |
[Vue.js] Watchers (0) | 2024.07.07 |
[Vue.js] 이벤트 핸들링 (0) | 2024.07.07 |