Vuex란?
Vue.js용 상태 관리 라이브러리
Vuex는 Vue.js 애플리케이션의 상태(데이터)를 관리하기 위한 중앙 저장소 역할을 합니다.
Flux 아키텍처 패턴을 따르며 상태는 저장소라는 단일 정보 소스에 저장되고 컴포넌트는 actions 및 mutations을 통해 상태를 업데이트하고 접근할 수 있습니다.
✅ Vuex를 사용해야 하는 이유
컴포넌트 기반 프레임워크에서는 작은 단위로 쪼개진 여러 개의 컴포넌트로 화면을 구성합니다.
Vue를 기준으로 컴포넌트끼리 데이터를 주고 받기 위해서는 부모 컴포넌트가 자식 컴포넌트로 데이터를 넘겨주어 처리합니다.
하지만 중간에 거쳐야할 컴포넌트가 많아지거나 같은 레벨 간에 데이터의 이동이 필요할 경우 데이터 흐름이 복잡해집니다.
이를 더 효율적으로 해결하기 위해서 데이터를 중앙 집중식으로 한 곳에 모아서 관리하는 상태 관리를 이용합니다.
✅ Vuex 사용해보기
Vue.js를 사용하여 쇼핑몰의 장바구니 기능을 구현하면서 Vuex에 대해 알아봅시다.
장바구니의 항목 및 총 가격과 같은 장바구니 데이터는 제품 목록, 장바구니 보기 및 결제 페이지와 같은 여러 컴포넌트에서 접근하고 업데이트해야 합니다.
Vuex를 사용하면 장바구니 상태를 유지하는 쇼핑몰을 만들 수 있으며 컴포넌트는 Vuex의 store에서 장바구니 데이터에 접근할 수 있습니다.
✏️ 설치하기
$ npm install vuex # Vue3
$ npm install vuex@3 # Vue2
Vue2에서는 Vuex3를 사용해야 하고 Vue3부터는 Vuex4를 지원합니다.
✏️ main.js 작성
// main.js
import Vue from "vue";
import App from "./App.vue";
// store.js를 불러오는 코드
import { store } from "./store";
new Vue({
// 뷰 인스턴스의 store 속성에 연결
store: store,
render: h => h(App)
}).$mount("#app");
✏️ store.js 작성
src 폴더 안에 store.js라는 파일을 생성하고 아래와 같이 작성합니다.
// Define the store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
cartItems: [],
totalPrice: 0
},
mutations: {
addToCart(state, item) {
state.cartItems.push(item)
state.totalPrice += item.price
},
removeFromCart(state, item) {
const index = state.cartItems.indexOf(item)
if (index > -1) {
state.cartItems.splice(index, 1)
state.totalPrice -= item.price
}
}
},
actions: {
addToCart({ commit }, item) {
commit('addToCart', item)
},
removeFromCart({ commit }, item) {
commit('removeFromCart', item)
}
}
})
export default store
store는 vuex의 상태를 모두 담고 있는 변수입니다.
store 안에는 state, getters, mutations, actions를 정의할 수 있습니다.
위의 예시에서는 cartItems 및 totalPrice를 state로 사용하여 Vuex store를 정의합니다.
또한 장바구니 상태를 업데이트하기 위한 mutations와 actions를 정의합니다.
✏️ state
- state는 Vue.js 애플리케이션에서 관리해야 하는 데이터를 의미합니다.
- Vuex의 State는 중앙 집중식 저장소에 저장되며 컴포넌트는 mutations 및 actions를 통해 상태에 접근하고 업데이트할 수 있습니다.
✏️ getters
- getters는 계산된 방식으로 state에서 데이터를 검색하는데 사용됩니다.
- Vue.js 컴포넌트의 computed 속성과 유사합니다.
- getters를 사용하면 state에서 새 값을 파생하고 계산할 수 있으며 state가 변경될 때만 캐시되고 업데이트됩니다.
- getters는 state와 관련된 복잡한 논리 또는 계산된 속성을 캡슐화하는데 유용합니다.
✏️ mutations
- mutations는 Vuex에서 state를 수정하는 역할을 합니다.
- 동기식이며 state를 직접 수정할 수 있습니다.
- mutations는 actions에 대한 응답으로 state를 업데이트하는데 사용되며
컴포넌트에서 직접 호출하면 안됩니다.- 컴포넌트에서 mutations 직접 호출(commit)하기는 왜 안될까?
- mutations를 호출하는 것은 actions에서만 된다고 했다. 이는 단방향 데이터의 흐름을 깨기 때문이다. 하지만 논리적인 흐름에 오류가 없다면 직접 commit을 통해 호출해도 된다고 한다.
- mutations는 애플리케이션에서 예측 가능하고 추적 가능한 상태 변경 흐름을 유지하는데 도움이 됩니다.
✏️ actions
- actions는 비동기 작업을 처리하고 mutations를 commit하여 state를 업데이트합니다.
- mutations와 유사하지만 비동기적이며 여러 mutations를 포함할 수 있습니다.
- actions는 API 요청 또는 여러 mutations의 dispatching과 같은 것을 처리할 때 사용됩니다.
- 또한 복잡한 비즈니스 논리를 캡슐화하는데에도 사용할 수 있습니다.
- actions는 컴포넌트에서 호출됩니다.
✅ Component 작성
cart.vue라는 컴포넌트를 작성해서 Vuex를 사용해봅시다.
// Example component using the cart store
<template>
<div>
<div v-for="item in cartItems" :key="item.id">{{ item.name }} - {{ item.price }}</div>
<div>Total Price: {{ totalPrice }}</div>
<button @click="addToCart(product)">Add to Cart</button>
<button @click="removeFromCart(product)">Remove from Cart</button>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState(['cartItems', 'totalPrice']),
},
methods: {
...mapActions(['addToCart', 'removeFromCart']),
addToCart(item) {
this.addToCart(item)
},
removeFromCart(item) {
this.removeFromCart(item)
}
}
}
</script>
컴포넌트에서 Vuex의 mapState 및 mapActions 헬퍼를 사용하여 store에 접근할 수 있습니다.
컴포넌트는 장바구니 항목과 장바구니의 총 가격을 표시하고 addToCart 및 removeFromCart 작업을 호출하여 해당 버튼을 클릭할 때 장바구니 상태를 업데이트합니다.
✅ Vuex 흐름
큰 흐름을 보면 이렇게 됩니다.
✏️ 간단하게 알아보기
비동기 처리일 경우 Dispatch를 통해 Actions를 호출하고
Mutations에 정의된 함수를 Actions에서 Commit하여 실행합니다.
단순 동기 처리일 경우 Mutations에 정의된 함수를 Commit을 통해 실행하면 됩니다.
component에서 Mutations를 직접 commit하면 Vuex가 제공하는 단방향 데이터 흐름과 관심사 분리가 깨지기 때문에 코드 구조가 불명확하고 관리가 어려울 수 있습니다.
이제 코드를 분석해보자.
- mapState는 Store에 저장된 State를 불러옵니다.
- mapActions는 Store에 저장된 Actions를 불러옵니다.
우선 템플릿에서 쓰여진 addToCart 메소드는 컴포넌트 내에서 사용하는 메소드입니다.
빨간색으로 그어진 선을 따라가면 methods에 정의된 addToCart 메소드를 확인할 수 있습니다.
이 addToCart 메소드는 this.addToCart라는 함수로 연결되는데 이 때 addToCart 함수는 store의 actions에 정의된 함수입니다.
컴포넌트에서 Store에 저장된 State를 사용하기 위해서 mapState라는 헬퍼를 사용합니다.
이는 원래 길게 사용되던 this.$store.state.cartItems를 단축시킨 것 입니다.
🚨 원래 코드
<template>
<div>
<div v-for="item in this.$store.state.cartItems" :key="item.id">{{ item.name }} - {{ item.price }}</div>
<!-- ... -->
</div>
</template>
✅ 헬퍼 이용 코드
<template>
<div>
<div v-for="item in cartItems" :key="item.id">{{ item.name }} - {{ item.price }}</div>
<div>Total Price: {{ totalPrice }}</div>
<!-- ... -->
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['cartItems', 'totalPrice']),
},
// ...
}
</script>
Store에 저장된 Actions를 사용하기 위해 mapActions라는 헬퍼를 사용합니다.
🚨 원래 코드
methods: {
addToCart(item) {
this.$store.dispatch('addToCart', item);
}
},
✅ 헬퍼 이용 코드
<template>
<div>
<!-- ... -->
<button @click="addToCart(product)">Add to Cart</button>
<button @click="removeFromCart(product)">Remove from Cart</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions(['addToCart', 'removeFromCart']),
addToCart(item) {
this.addToCart(item)
},
removeFromCart(item) {
this.removeFromCart(item)
}
}
}
</script>
이 외에도 mapGetters, mapMutations가 있으니 필요에 맞게 import한 뒤 사용하면 됩니다.
'이론 (Front-end) > Vue' 카테고리의 다른 글
[Vue] ref vs. reactive (0) | 2023.03.10 |
---|---|
[Vue] 뷰 템플릿 (0) | 2023.02.07 |
[Vue] 뷰 HTTP 통신 (0) | 2023.02.07 |
[Vue] 뷰 라우터 (0) | 2023.02.07 |
[Vue] 뷰 컴포넌트 통신 (0) | 2023.02.07 |