Mutations, Getters 이외에 Vuex가 제공하는 중요한 개념, 기능이 하나 더 있다.
# mutation의 한계
예를 들어, increment 변형에서 숫자를 즉시 더하지 않고, 2초를 기다린 후 더한다고 해보자.
실제 앱에서는 HTTP 요청을 보내고, 응답을 기다리는 상황이 있을 수 있다.
응답을 받은 후에야 상태를 바꾸는 상황이다.
다른 말로 한다면, 비동기식으로 실행하는 어떤 코드는 즉시 실행을 완료하는 것이 아니라 미래의 어떤 시점에 완료하게 된다.
mutation의 문제는, 언제나 동기식이라는 것.
mutation에는 비동기식 코드가 허용되지 않는다.
따라서 mutation이 실행되면 중단없이 단계별로 실행되면서 상태는 즉시 바뀌어야 한다.
더 나중에 바뀌는 것은 허용되지 않는다.
이렇게 작동하는 이유는 만약 여러 mutation이 실행되면 모든 mutation이 최신 state를 받아야만 하기 때문.
만약 다른 mutation이 커밋되고 실행이 완료되지 않는다면 이 상황은 예상하지 못하기 때문에 프로그램에 오류를 발생한다.
따라서 mutation의 필수 조건은 동기식으로 동작해야 한다는 것.
만약 2초를 기다리려면 어떻게 해아할까?
setTimeout으로 할 수 있다. 2초를 기다리고 함수를 실행시킨다.
그러면 2초 뒤에 상태를 업데이트한다.
mutations: {
increment(state) {
setTimeout(function(){
state.counter = state.counter + 2;
}, 2000)
},
},
그러나 이 코드는 허용되지 않는다. 작동은 하겠지만 좋은 방법이 아니고, 이렇게 해서는 안된다.
# 비동기 코드를 위해서 - Actions
Vuex에는 비동기식 코드를 위한 더 나은 개념이 있다.
→ 바로, Actions
컴포넌트는 Actions를 트리거하고, Actions는 Mutations을 커밋한다.
Actions은 비동기식 코드를 사용할 수 있기 때문에, 컴포넌트와 Muations 사이에 Actions를 넣는 것은 일반적으로 좋은 방식이라고 여겨진다.
# 동기식 코드여도 Actions를 사용하자!
동기식 코드만을 사용한다면 Mutations을 사용해도 문제될 건 없고, 컴포넌트 내부에서 mutations을 직접 commit하는 것도 문제가 되지는 않는다.
그럼에도 Actions을 사이에 놓는 건 좋은 관행으로 여겨지는데,
Mutations에 실수로 비동기식 코드를 넣는 것을 방지해주기 때문.
# Actions 활용방법
Actions은 어떻게 동작할까? 저장소에 어떻게 추가할 수 있을까?
1/ main.js에서 Actions을 추가
- actions도 getters, mutations처럼 객체를 받는다.
- actions은 메서드이다.
- 그리고, mutation에서 사용한 이름을 그대로 사용할 수 있다.
increment라는 mutation이 있고, increment 액션이 있다.
이름을 동일하게 만들 필요는 없지만, 컴포넌트와 mutation 사이에 action을 종종 넣기 때문에 같은 이름을 쓰는게 직관적.
## context 인수
- Actions은 context라는 객체를 인수로 받는다.
context 객체는 아주 흥미롭다. 다른 Vuex 기능과 마찬가지로 Vuex가 이 인수를 자동으로 가져온다.
- context는 호출할 수 있는 commit메서드가 있다.
- mutation을 commit한다. 마치 컴포넌트 내부에서 commit하는 것과 같다.
- commit 함수는 두번 째 인수로 payload를 전달할 수 있고,
다른 방식으로는 객체를 전달하여 두 개의 인수를 사용할 수 있다.
actions: {
increment(context){
context.commit('increment', 10); //10이라는 payload
},
increment(context){
context.commit({ // 이렇게 '객체'를 전달할 수 있다.
type: 'increment',
value: 10,
});
},
},
이제 increment 변형을 커밋하는 action을 만들었다.
흥미로운 점은 액션은 변형과 달리 비동기식 코드를 허용한다는 점.
그래서 액션에 setTimeout을 넣고 context.commit을 감싸면 변형은 여전히 동기식이지만, 액션은 비동기식으로 바뀐다.
이건 Vuex가 허용한다.
2초가 지난 후에야 mutation을 commit한다.
mutations: {
increment(state) {
state.counter = state.counter + 2;
},
increase(state, payload) {
state.counter = state.counter + payload.value;
},
},
actions: {
increment(context){
setTimeout(function() { //이렇게 action은 비동기식 허용!
context.commit('increment');
}, 2000);
},
},
이제 컴포넌트 내부에서 mutation을 커밋하지 않고, action을 사용해보자.
2/ dispatch함수를 이용해 Actions 사용
그렇다면 액션은 어떻게 사용할 수 있을까?
→ 액션을 dispatch할 수 있다.
dispatch구문의 좋은 점은 commit과 비슷하다는 것.
두 개의 인수를 dispatch할 수 있다.
- 첫 번째 인수: 메인 저장소에 입력한 action의 이름으로, 이 경우는 'increment'.
- 두 번째 인수: 원하는 payload
또는 하나의 인수 구문({type: 'increase', value:10})을 사용할 수 있다.
아직 액션에 없는 increase도 dispatch하기위해 increase라는 액션을 생성해서 동작하도록 만들어보자.
새로운 액션 이름인 increase를 입력하고 context.commit('increase')를 추가한다.
이 액션은 dispatch 함수에 추가된 payload를 받는다. 두번째 인수를 추가 인수로 받는다.
그리고 이는 commit한 mutation에 전달된다.
// App.vue
mutations: {
increment(state) {
state.counter = state.counter + 2;
},
increase(state, payload) {
state.counter = state.counter + payload.value;
},
},
actions: {
increment(context){
setTimeout(function() {
context.commit('increment');
}, 2000);
},
increase(context, payload){
context.commit('increase', payload);
},
},
이렇게 payload가 있는 액션을 설정하는 방법과 payload가 변형에 전달되는 방법도 보았다.
물론 mutation을 commit하기 전에 payload를 변경할 수 있다.
increase 액션에서 애플리케이션이 올바르게 작동하기 위해 필요한 모든 것을 할 수 있다.
App.vue 컴포넌트로 돌아와
mutation을 commit한 것처럼 action을 dispatch해보자.
<template>
<base-container title="Vuex" v-if="isAuth">
<!--생략-->
<button @click="addOne">Add 10</button>
</base-container>
</template>
<script>
export default {
//생략..
methods: {
addOne() {
this.$store.dispatch('increase', { value:10 });
},
},
};
</script>
ChangeCounter.vue에서도 increment 액션을 dispatch할 수 있다.
<!--ChangeCounter.vue-->
<template>
<button @click="addOne">Add 2</button>
</template>
<script>
export default {
methods: {
addOne() {
this.$store.dispatch('increment');
},
},
};
</script>
// App.vue
mutations: {
increment(state) {
state.counter = state.counter + 2;
},
increase(state, payload) {
state.counter = state.counter + payload.value;
},
},
actions: {
increment(context){
setTimeout(function() {
context.commit('increment');
}, 2000);
},
increase(context, payload){
context.commit('increase', payload);
},
},
이렇게 모든 것을 수정하고 작동해보면,
'Add 2'버튼을 클릭하면 2초 후에 결과가 바뀐다.
이렇게 action을 사용한 올바른 방식으로 구현하였다.
** 출처: 모든 내용은 Udemy Vue-완벽가이드 강의를 기반으로 작성하였습니다.
'Vue.js' 카테고리의 다른 글
[Udemy Vue 완벽가이드 Section15] 220. 매퍼 헬퍼(Mapper Helper) 사용하기 (0) | 2023.10.25 |
---|---|
[Udemy Vue 완벽가이드 Section15] 219. 액션 "컨텍스트" 이해하기 (0) | 2023.10.25 |
[Udemy Vue 완벽가이드 Section15] 217. 게터(Getters) 소개-데이터를 얻는 더 나은 방법 (1) | 2023.10.25 |
[Udemy Vue 완벽가이드 Section15] 216. payload를 사용하여 변형(Mutation)에 데이터 전달 (0) | 2023.10.24 |
[Udemy Vue 완벽가이드 Section15] 215. 변형(Mutation) 소개 - 데이터를 변경하는 더 나은 방법 (0) | 2023.10.24 |