9 Vuex (v4.x)
source: categories/study/vue-project/vue-project_9-00.md
9 Vuex (v4.x)
- 애플리케이션이 복잡해지고 컴포넌트 수가 많아지면, 컴포넌트 간의 데이터 전달 및 관리가 점점 어려워집니다.
- Vuex는 Vue에서 모든 컴포넌트가 접근 가능한 중앙 집중식 저장소를 두고 데이터 관리 및 상태 관리를 할 수 있도록 해주는 상태 관리 패턴 + 라이브러리입니다.
- 이번 챕터에서는 Vuex의 설치 및 사용방법에 대해서 알아봅니다.
9.1 Vuex란?
- 복잡한 애플리케이션에서 컴포넌트 수가 많아지면 컴포넌트 간의 데이터 전달이 어려워집니다.
- Vuex는 Vue.js 애플리케이션을 위한 상태관리 패턴 + 라이브러리입니다.
- 모든 컴포넌트에 대한 중앙집중식 저장소 역할을 하며 예측 가능한 방식으로 상태를 변경할 수 있습니다.
- Vuex를 이용하지 않는다면 컴포넌트 간에 데이터를 주고받는 것은 방법은 있지만, 대규모 프로젝트가 될수록 복잡해집니다.
- 데이터를 store에 저장하고, 프로젝트 전체에서 사용할 수 있도록 해주는 것이 Vuex입니다.
9.2 Vuex 설치
- 터미널에서 다음 명령어를 통해 설치합니다.
npm i vuex@next
9.3 시작하기
- 모든 Vuex 애플리케이션의 중심에는 store가 있습니다.
- 저장소(store)는 애플리케이션 상태를 저장하고 있는 컨테이너입니다.
- Vuex 저장소가 일반 전역 개체와 두 가지 다른 점이 있습니다.
- Vuex store는 반응형(reactivity)입니다.
- Vue 컴포넌트는 저장소의 상태(state)를 검색할 때 저장소의 상태에 정의된 변수 값의 변경 여부를 바로 알 수 있습니다.
- 저장소의 상태를 직접 변경할 수 없습니다.
- 저장소의 상태를 변경하는 유일한 방법은 명시적인 커밋을 이용한 변이입니다. (mutations)
- 이렇게 하면 모든 상태에 대한 추적이 가능한 기록이 남을 수 있으며 툴을 사용하여 앱을 더 잘 이해할 수 있습니다.
- Vuex store는 반응형(reactivity)입니다.
- 아주 단순한 형태의 store를 하나 만들어 보겠습니다.
import { createStore } from 'vuex';
const store = createStore({
state() {
return {
count: 0,
}
},
mutations: {
increment (state) {
state.count++;
}
}
})
export default store;
- Vue 컴포넌트에서는
this.$store
로 접근이 가능합니다.
<template>
<p>Count: {{ count }}</p>
<button type="button" @click="increment">Increment</button>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
}
},
methods: {
increment() {
this.$store.commit('increment');
}
}
}
</script>
- 여기서 저장소의
state
에 바로 접근해서 변경하는 것이 아니라,commit
을 통해서만 변경을 할 수 있습니다.
9.4 state
state
는 프로젝트 전체에서 공통으로 사용할 변수를 정의하는 곳입니다.state
에 변수를 정의함으로써, 모든 컴포넌트에서 사용 가능합니다.state
관리를 통해 모든 컴포넌트에서 동일한 값을 사용할 수 있습니다.
state
에 정의된 변수는 Vue 컴포넌트에서는computed
속성을 이용해서 그 변경사항을 항상 추적할 수 있습니다.
<script>
export default {
computed: {
count() {
return this.$store.state.count;
}
}
}
</script>
9.5 getters
- 우리가 쇼핑몰을 이용할 때를 생각해 보겠습니다.
- 쇼핑몰 웹사이트에서 장바구니에 다음 제품의 수가 항상 화면 상단의 장바구니 아이콘에 표기되는 것을 보았을 것입니다.
- 어던 화면에서든지 제품을 장바구니에 추가하면 바로 장바구니 아이콘의 제품 수가 증가하는 것을 확인할 수 있습니다.
- 장바구니에 담긴 제품 데이터를 저장소의 state에 변수로 관리하고 있다면 장바구니에 담긴 제품 수, 특정 카테고리 제품 리스트 등을 getters를 정의하여 쉽게 가져올 수 있습니다.
import { createStore } from 'vuex';
const store = createStore({
state() {
return {
count: 0,
cart: [
{ product_id: 1, product_name: '아이폰 거치대', category: 'A' },
]
}
},
getters: {
cartCount: (state) => {
return state.cart.length;
}
},
mutations: {
increment (state) {
state.count++;
}
}
})
export default store;
- Vue 컴포넌트에서 다음과 같이 저장소의
getters
에 정의된 값에 접근할 수 있습니다.
<script>
export default {
computed: {
cartCount() {
return this.$store.getters.cartCount;
}
}
}
</script>
9.6 Mutations
- Vuex는 state에 정의된 변수를 직접 변경하는 것을 허용하지 않습니다.
- 반드시 mutations를 이용해서 변경을 해야 합니다.
- 즉, mutations는 state를 변경시키는 역할을 합니다.
- mutations는 비동기(Async) 처리가 아니라 동기(Sync) 처리를 통해 state에 정의된 변수의 변경사항을 추적할 수 있게 해줍니다.
- 다음과 같이 mutations에 정의된 함수를 commit을 통해서 호출하는 것으로 저장소의 state에 정의된 변수의 값을 변경할 수 있습니다.
<script>
export default {
methods: {
increment() {
return this.$store.commit('increment');
}
}
}
</script>
9.7 Actions
- actions는 mutations와 매우 유사한 역할을 합니다.
- actions를 통해 mutations에 정의된 함수를 실행시킬 수 있습니다.
- mutations가 있는데 굳이 actions를 통해서 mutations를 실행하는지 의문이 생길 수 있습니다.
- actions에 정의된 함수 안에서는 여러 개의 mutations를 실행시킬 수 있을 뿐만 아니라, mutations와 달리 비동기 작업이 가능합니다.
- 즉, actions에 등록된 함수는 비동기 처리 후 mutations를 커밋할 수 있어서 저장소(store)에서 비동기 처리 로직을 관리할 수 있게 해줍니다.
import { createStore } from 'vuex';
const store = createStore({
state() {
return {
count: 0,
cart: [
{ product_id: 1, product_name: '아이폰 거치대', category: 'A' },
]
}
},
getters: {
cartCount: (state) => {
return state.cart.length;
}
},
mutations: {
increment (state) {
state.count++;
}
},
actions: {
increment (context) {
// 비동기 처리 로직 수행 가능
context.commit('increment');
}
}
})
export default store;
9.8 Vuex 실무 예제
- vuex에 대해 실무에서 가장 많이 사용되는 예는 필자의 경우:
- 사용자가 로그인을 하면 사용자 정보를 vuex의 store에 저장해서 사용합니다.
- 모든 컴포넌트에서는 사용자가 로그인 했는지 정보를 알 필요가 있습니다.
- 사용자 계정(account) 정보와 같이 프로젝트 전체에 걸쳐서 변경사항을 관리해야 하는 데이터를 처리할 때 매우 유용합니다.
import { createStore } from 'vuex';
import persistedState from 'vuex-persistedState';
const store = createStore({
state() {
return {
user: {}
}
},
mutations: {
user(state, data) {
state.user = data;
}
},
plugins: {
persistedState({
paths: ['user'],
})
}
})
export default store;