Pinia
Pinia 적용 배경
기존 프로젝트는 Vue2 위에서 Typescript를 적용하다보니 타입 정의가 매우 복잡했는데요. (참고로 vue3+vuex4 사용 시 더 편해졌다고 해요.)
예를들어 Action 별, Mutation 별, 각 타입 지정을 위해 Enum으로 함수명을 정의해야하고 또 각 타입들을 하나의 스토어로 정의하는 과정도 필요했습니다.
또한 타입 추론 덕분에 자동완성된 내용을 활용하여 접근할 수는 있지만 코드가 점점 복잡해보였습니다. (타입 추론을 활용하기 위해 Vuex Helper 함수들은 사용하지 않았었어요.)
- 스토어의 namespace global 모듈의 aa객체의 bb 속성에 접근
- store.state.global.aa.bb.~
- Enum으로 정의해둔 MutationType을 활용하여 Mutation 호출 (Commit)
- store.commit(GlobalMutationTypes.SET_SIGNED, data);
- Enum으로 정의해둔 ActionType을 활용하여 Action 호출 (Dispatch)
- $store.dispatch(BizActionTypes.FETCH_ITEM_LIST);
Pinia (공식 문서)
- vuex를 대체할 상태 관리 플러그인
- VueConf Toronton 2021 에서 Vue의 창시자 Evan You가 상태 관리 플러그인으로 vuex가 아닌 Pinia를 추천
왜 Pinia를 사용하나요?
- 일단 사용이 쉽습니다. (사용법은 밑에 간단하게 소개해두었습니다.)
- mutations이 없습니다.
- 상태 변경을 위해 Mutations을 정의하고 Commit하는 과정이 필요없습니다.
- 그저 Vue 인스턴스의 state 값을 변경할 때 처럼 read/write 하면 됩니다.
- Typescript를 지원하기 위해 복잡한 래핑을 하지 않아도 됩니다.
- Pinia는 이미 TypeScript 를 통한 유형 추론을 최대한 활용할 수 있게 만들어져있습니다.
- 굳이 타입을 별도로 지정해주지 않아도 타입 추론이 가능해요.
- namespace modules 없음
- devtools 도 지원합니다. (단 최신 버전을 사용해야합니다.)
사용법
스토어 정의
- store/xxx.ts 생성
- 기존 Vuex를 사용할 떄와 같이 State에는 상태 관리할 변수, actions 에는 서버에서 데이터를 받아와 상태 변경 등, state에 뭔가 계산이 필요한 경우 getters 를 정의합니다.
// 예시) app.ts
import { defineStore } from 'pinia';
// defineStore<모듈명, 타입>
export const useAppStore = defineStore('app', {
state: () => ({ drawer: false, lastName: 'test' }),
actions: {
changeDrawer() {
this.drawer = !this.drawer;
},
},
getters: {
getFullName(firstName: String) {
return firstName + lastName;
}
}
});
스토어 사용 - State
-
간단하게 useAppStore()로 store를 가져와 store 내 state에 접근하면 됩니다.
-
mutation이 없기 때문에 state 값을 v-model 에 사용할 때도 기존 vuex를 사용할 때와 같이 setter 를 따로 정의해주지 않아도 됩니다.
-
기존 vuex 코드 (v-model 사용시)

-
Pinia 활용 코드
<input v-model="store.drawer"/>
export default {
setup() {
const store = useAppStore();
return { store }
}
}
- 주의할 점은 구조분해 할당 시 반응성을 잃는다는 점입니다.
// 이렇게 선언할 경우 drawer는 초기값으로 계속 남아있습니다.
const { drawer } = useAppStore();
- appStore 전체 말고 개별로 내보내고 싶은 경우에는 아래와 같은 방법이 있습니다.
// 1. 기존과 같이 computed 활용 (값의 변경도 필요한 경우 setter도 정의 필요)
return {
count: computed(() => store.count)
}
// 2. composition API의 toRefs 활용
const { drawer } = toRefs(useAppStore());
return { drawer }
storeToRefs
Pinia 공식 문서에는 두번째 방법 (composition API의 toRefs 활용) 으로 storeToRefs를 활용하라고 나와있는데요.
현재 Vue2에서 적용이 안됩니다. 😢(#852)
Pinia의 storeToRefs 는 composition API의 toRefs와 비슷하게 동작하는데 단 Method 같은 속성은 무시하고 가져옵니다.
- Composition API - toRefs
- 리액티브 객체를 일반 객체로 변환하여 반환하지만, 반환되는 객체의 각 프로퍼티들이 ref로 원래의 리액티브 객체 프로퍼티로 연결됩니다.
스토어 사용 - Getters
<template>
<div>
fullName: {{ fullName }}
</div>
</template>
export default {
setup() {
const store = useAppStore();
return { fullName: store.getFullName }
}
}
스토어 사용 - Action
<template>
<div>
<v-btn @click="store.changeDrawer">테스트</v-btn>
</div>
</template>
export default {
setup() {
// #1 통째로 내보내기
const store = useAppStore();
return { store }
// #2 특정 액션만 내보내기
// const store = useAppStore();
// return { changeDrawer: store.changeDrawer }
}
}
요약
- 사용법이 매우 간단하다. (= 러닝커브가 낮다.)
- 모듈단위로 작성하기 때문에 모듈이 많아져도 복잡해지지 않는다.
- 신경쓰지 않아도 타입 추론을 알아서 해준다.
마치며
- 상태 변경을 위해 Mutations을 정의하고 Commit하는 과정이 필요없습니다.
- 그저 Vue 인스턴스의 state 값을 변경할 때 처럼 read/write 하면 됩니다.
- Pinia는 이미 TypeScript 를 통한 유형 추론을 최대한 활용할 수 있게 만들어져있습니다.
- 굳이 타입을 별도로 지정해주지 않아도 타입 추론이 가능해요.
// 예시) app.ts
import { defineStore } from 'pinia';
// defineStore<모듈명, 타입>
export const useAppStore = defineStore('app', {
state: () => ({ drawer: false, lastName: 'test' }),
actions: {
changeDrawer() {
this.drawer = !this.drawer;
},
},
getters: {
getFullName(firstName: String) {
return firstName + lastName;
}
}
});
간단하게 useAppStore()로 store를 가져와 store 내 state에 접근하면 됩니다.
mutation이 없기 때문에 state 값을 v-model 에 사용할 때도 기존 vuex를 사용할 때와 같이 setter 를 따로 정의해주지 않아도 됩니다.
기존 vuex 코드 (v-model 사용시)
Pinia 활용 코드
<input v-model="store.drawer"/>
export default {
setup() {
const store = useAppStore();
return { store }
}
}
// 이렇게 선언할 경우 drawer는 초기값으로 계속 남아있습니다.
const { drawer } = useAppStore();
// 1. 기존과 같이 computed 활용 (값의 변경도 필요한 경우 setter도 정의 필요)
return {
count: computed(() => store.count)
}
// 2. composition API의 toRefs 활용
const { drawer } = toRefs(useAppStore());
return { drawer }
storeToRefs
Pinia 공식 문서에는 두번째 방법 (composition API의 toRefs 활용) 으로 storeToRefs를 활용하라고 나와있는데요.
현재 Vue2에서 적용이 안됩니다. 😢(#852)
Pinia의 storeToRefs 는 composition API의 toRefs와 비슷하게 동작하는데 단 Method 같은 속성은 무시하고 가져옵니다.
- Composition API - toRefs
- 리액티브 객체를 일반 객체로 변환하여 반환하지만, 반환되는 객체의 각 프로퍼티들이 ref로 원래의 리액티브 객체 프로퍼티로 연결됩니다.
<template>
<div>
fullName: {{ fullName }}
</div>
</template>
export default {
setup() {
const store = useAppStore();
return { fullName: store.getFullName }
}
}
<template>
<div>
<v-btn @click="store.changeDrawer">테스트</v-btn>
</div>
</template>
export default {
setup() {
// #1 통째로 내보내기
const store = useAppStore();
return { store }
// #2 특정 액션만 내보내기
// const store = useAppStore();
// return { changeDrawer: store.changeDrawer }
}
}
Pinia를 적용하고 기존 Vuex를 사용했을 때 보다 훨씬 사용이 간단해졌다고 느꼈습니다.
현재 Pinia 는 Vue의 공식 플러그인은 아니지만 추후 Pinia와 vuex 두 개의 프로젝트를 하나로 합치거나 쉽게 이동할 수 있도록 지원할 예정이라고 합니다.
새로운 프로젝트가 Vue2 + Typescript 환경일 때 상태 관리가 필요한 경우 Pinia 적용을 고려하셔도 좋을 것 같습니다.
Author And Source
이 문제에 관하여(Pinia), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@gykim/Pinia-g2cvtkaz저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)