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 저장소가 일반 전역 개체와 두 가지 다른 점이 있습니다.
    1. Vuex store는 반응형(reactivity)입니다.
      • Vue 컴포넌트는 저장소의 상태(state)를 검색할 때 저장소의 상태에 정의된 변수 값의 변경 여부를 바로 알 수 있습니다.
    2. 저장소의 상태를 직접 변경할 수 없습니다.
      • 저장소의 상태를 변경하는 유일한 방법은 명시적인 커밋을 이용한 변이입니다. (mutations)
      • 이렇게 하면 모든 상태에 대한 추적이 가능한 기록이 남을 수 있으며 툴을 사용하여 앱을 더 잘 이해할 수 있습니다.

  • 아주 단순한 형태의 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;


9.9 vuex-persistedState 라이브러리는 뭐지?