[Vue.js] Vuex 시작 (2) - Getters와 Mutations

dl_edge·2021년 9월 30일
5

Vue.js

목록 보기
3/5

지난 글에 이어 Vuex의 기본인 Getters와 Mutations를 배워보자!

Getters 란?

중앙 데이터 관리식 구조(Vuex)에서 발생하는 문제점 중 하나는 각 컴포넌트에서 Vuex의 데이터를 접근할 때 중복된 코드를 반복 호출하게 되는 것이다.

예를 들어, 아래와 같은 코드가 있다.

// App.vue
computed: {
  doubleCounter() {
    return this.$store.state.counter * 2;
  }
},

// Child.vue
computed: {
  doubleCounter() {
    return this.$store.state.counter * 2;
  }
},

App.vue와 Child.vue 등 여러 컴포넌트에서 같은 로직을 중복 사용하고 있다. 이 때, Vuex의 데이터(state) 변경을 각 컴포넌트에서 수행하는 게 아니라, Vuex에서 수행하도록 하고, 각 컴포넌트에서 수행 로직을 호출하면, 가독성도 올라가고 성능면에서도 이점이 생긴다.


// store.js (Vuex)
getters: {
  doubleCounter: function (state) {
    return state.counter * 2;
  }
},

// App.vue
computed: {
  doubleCounter() {
    return this.$store.getters.doubleCounter;
  }
},

// Child.vue
computed: {
  doubleCounter() {
    return this.$store.getters.doubleCounter;
  }
},


Getters 등록을 위한 코드 정리

에 이어서 getters를 추가해보자.

  • 지난번 코드에서 정리해야 하는 부분은 다음과 같다.

    <!-- App.vue -->
    <div id="app">
      Parent counter : {{ this.$store.state.counter }}
      <!-- ... -->
    </div>

    Vue 공식사이트에서 언급된 것처럼 Template의 표현식은 최대한 간소화해야 한다.

    따라서,

    <!-- App.vue -->
    <div id="app">
      Parent counter : {{ parentCounter }}
      <!-- ... -->
    </div>
    
    <!-- Child.vue -->
    <div>
      Child counter : {{ childCounter }}
      <!-- ... -->
    </div>
    // App.vue
    computed: {
      parentCounter() {
        return this.$store.state.counter;
      }
    },
    
    // Child.vue
    computed: {
      childCounter() {
        return this.$store.state.counter;
      }
    },

    computed 속성을 활용함으로써 Template 코드가 더 간결해지고, 가독성이 좋아졌다.

    *computed 속성이란?

    computed 속성의 대상으로 정한 data 속성이 변했을 때 이를 감지하고 자동으로 다시 연산해준다.

    • computed 속성은 인자를 받지 않는다.
    • HTTP 통신과 같이 컴퓨팅 리소스가 많이 필요한 로직을 정의하지 않는다.

Getters 등록

  • getters를 Vuex에 추가한다.
// store.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        counter: 0
    },
    // getCounter 라는 getters를 추가
    getters: {
        getCounter: function(state) {
            return state.counter;
        }
    }
});

Getters 사용

등록된 getters를 각 컴포넌트에서 사용하려면 this.$store를 이용하여 getters에 접근한다.

// App.vue
computed: {
  parentCounter() {
    this.$store.getters.getCounter;
  }
},

// Child.vue
computed: {
  childCounter() {
    this.$store.getters.getCounter;
  }
},

mapGetters

Vuex에 내장된 helper함수, mapGetters로 이미 위에서 한번 가독성이 올라간 코드를 더 직관적이게 작성할 수 있다.

<!-- App.vue -->
<div id="app">
  Parent counter : {{ parentCounter }}
  <!-- ... -->
</div>
// App.vue
import { mapGetters } from 'vuex'

// ...
computed: mapGetters({
  parentCounter : 'getCounter' // getCounter 는 Vuex 의 getters 에 선언된 속성 이름
}),

또는, 아래와 같이 간단하게 선언할 수도 있다.

<!-- App.vue -->
<div id="app">
  Parent counter : {{ getCounter }}
  <!-- ... -->
</div>
// App.vue
import { mapGetters } from 'vuex'

computed: mapGetters([
  'getCounter'
]),

Mutaions 란?

Mutations 란, Vuex의 데이터 즉 state의 값을 변경하는 로직들을 의미한다.

Getters와의 차이점

  1. 인자를 받아 Vuex에 넘겨줄 수 있고
  2. computed가 아닌 methods에 등록한다.

또한, Actions 와의 차이점

  1. Mutations 는 동기적 로직을 정의
  2. Actions 는 비동기적 로직을 정의

Mutations 의 성격상 안에 정의한 로직들이 "순차적"으로 일어나야 각 컴포넌트의 반영 여부를 제대로 추적할 수가 있기 때문이다.


지금까지 counter를 변경할때 아래와 같이 직접 state에 접근하여 변경하였다.

return this.$store.state.counter++;
return this.$store.state.counter;

하지만, 이 방식은 안티패턴으로써 Vue의 상태관리 패턴에 맞지 않는 방식이다.

안티패턴인 이유는 여러 개의 컴포넌트에서 같은 state 값을 동시에 제어하게 되면, state 값이 어느 컴포넌트에서 호출해서 변경된건지 추적하기 어렵기 때문이다.

하지만, 상태변화를 명시적으로 수행함으로써 테스팅, 디버깅, Vue의 Reactive 성질 준수의 혜택을 얻는다.


아래와 같이 commit을 이용하여 state를 변경한다.

:pushpin:Tip!
  • Mutations 가 낯설다면 기억하기 쉽게 Setters 로 이해할 수 있다.

Mutations 등록

getters와 마찬가지로 Vuex 에 mutations 속성을 추가한다.

// store.js
export const store = new Vuex.Store({
  // ...
  mutations: {
    addCounter: function (state, payload) {
      return state.counter++;
    }
  }
});

Mutations 사용

App.vue의 기존 코드는 addCounter에서 state의 counter 값을 바로 접근하여 1을 더하는 코드였다.

state를 컴포넌트에서 직접 접근하여 증가시키는 부분을 mutations 를 이용하는 코드로 바꾸자.

<!-- App.vue(parent) -->
!<template>
  <div id="app">
    Parent counter : {{ parentCounter }} <br>
    <button @click="addCounter">+</button>
    <button @click="subCounter">-</button>
   <child></child> 
  </div>
</template>

<script>
import Child from "./Child.vue";
import { mapGetters } from 'vuex';

export default {
  components: {
    child : Child
  },

  computed: mapGetters({
    parentCounter : 'getCounter'
  }),

  methods: {
    addCounter(){
      // this.$store.state.counter++;
      // commit을 이용하여 mutations 이벤트를 호출해야한다.
      this.$store.commit('addCounter'); 
    }
  }
};
</script>

Mutations 에 인자 값 넘기기

각 컴포넌트에서 Vuex 의 state를 조작하는데 필요한 특정 값들을 넘기고 싶을 때commit() 에 두번째 인자를 추가한다.

this.$store.commit('addCounter', 10);
this.$store.commit('addCounter', {
  value: 10,
  arr: ["a", "b", "c"]
});

이를 Vuex 에서 아래와 같이 받을 수 있다.

mutations: {
  // payload 가 { value : 10 } 일 경우
  addCounter: function (state, payload) {
    state.counter = payload.value;
  }
}

mapMutations

mapGetters와 마찬가지로, Vuex 에 내장된 mapMutations 를 이용하여 코드 가독성을 높일 수 있다.

// App.vue
import { mapMutations } from 'vuex'

methods: {
  // Vuex 의 Mutations 메서드 명과 App.vue 메서드 명이 동일할 때 [] 사용
  ...mapMutations([
    'addCounter'
  ]),
  // Vuex 의 Mutations 메서드 명과 App.vue 메서드 명을 다르게 매칭할 때 {} 사용
  ...mapMutations({
    addCounter: 'addCounter' // 앞 addCounter 는 해당 컴포넌트의 메서드를, 뒤 addCounter 는 Vuex 의 Mutations 를 의미
  })
}




📔 REFERENCE: [캡틴판교] https://joshua1988.github.io/web-development/vuejs/vuex-getters-mutations/

profile
TIL (Today I Learned!) 전공 까먹기 싫어서 정리하는 중🎁

0개의 댓글