본문 바로가기

Vue의 개발을 편리하게 도와 주는 공식 툴 중에 Vuex가 있습니다. Vuex의 주요 기능은 개발하는 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소 역활 및 관리 입니다.
만약 이게 없다면 컴포넌트간 데이터를 주고받기 위해서 부모는 자식에서 props의 방법으로, 자식은 부모에게 Emit event의 방법으로 데이터를 주고 받아야 합니다. 또한 형제 컴포넌트간 데이터를 주고 받으려면 너무 복잡해져서 EventBus를 활용해야 그나마 사용을 할 수 있게 됩니다. 간단한 프로젝트인 경우에야 사용이 가능하겠지만 대형 프로젝트인 경우에는 이러한 방법으로는 도저히 감당이 되지 않고 개발 후 운영을 한다고 해도 거의 맨붕이 올 것입니다.
이러한 문제를 해결해 주는 것이 Vuex라고 보시면 됩니다. 데이터store라는 파일을 통해 관리하고 프로젝트 전체에 걸쳐 활용할 수 있게 해 주는 방법입니다.

기본적인 설명은 해당 사이트에서 확인하시면 됩니다. 한글로 되어 있어서 기본적인 것을 익히는데는 어려움이 없습니다.

이번 포스트에는 NUXT를 활용하여 토이 프로젝트를 진행하면서 알게된 것을 스팟형태로 정리해 보려고 합니다.
참고로 NUXT에도 Vuex를 활용하고 있습니다.

State, Mutations, Actions, Getters의 특징

Vuex의 핵심구성은 State, Mutations, Actions, Getters로 구성되어 있습니다. 각각 기본적인 특징과 다른점을 먼저 알기 쉽게 정리해 보겠습니다.

State

  • state는 쉽게 말하면 프로젝트에서 공통으로 사용할 변수를 정의 합니다.
  • 프로젝트 내의 모든 곳에서 참조 및 사용이 가능합니다.
  • state를 통해 각 컴포넌트에서 동일한 값을 사용할 수 있습니다.
export const state = () => ({
  account: null, 
  isAdmin: null, 
  item: null
});

Mutations

  • Mutations의 주요 목적은 state를 변경시키는 역활을 합니다. (Mutations을 통해서만 state를 변경해야 함)
  • 비동기 처리가 아니라 동기처리를 합니다. 위의 함수가 실행되고 종료된 후 그 다음 아래의 함수가 실행됩니다.
  • commit('함수명', '전달인자')으로 실행 시킬 수 있습니다.
  • mutations 내에 함수 형태로 작성합니다.
export const mutations = {
  currentUser(state,  account) {
    state.account = account;  // state의 account변수에 넘겨 받은 account값을 입력함
  }
};

Actions

  • Actions의 주요 목적은 Mutations를 실행시키는 역활을 합니다. Mutations이 실행되면 state도 변경이 되겠지요.
  • 동기 처리가 아니라 비동기처리를 합니다. 순서에 상관없이 먼저 종료된 함수의 피드백을 받아 후속 처리를 하게 됩니다.
  • dispatch('함수명', '전달인자')으로 실행 시킬 수 있습니다. ex) dispatch('함수명', '전달인자', {root:true})
  • actions 내에 함수 형태로 작성합니다.
  • 비동기 처리이기 때문에 콜백함수로 주로 작성합니다.
일반 형태로 실행
dispatch('setAccount', account );
export const actions = {
  setAccount({ commit, dispatch }, account) {
    commit('currentUser', account);
    dispatch('setIsAdmin', account.uid);
  }
}
Components에서 then()으로 콜백함수 실행
dispatch('setAccount', account ).then(() => {   });
export const actions = {
  setAccount({ commit }, account) {

    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('currentUser', account);
        resolve()
      }, 1000)
    })

  }
}

Getters

  • 각 Components의 계산된 속성(computed)의 공통 사용 정의라고 보시면 됩니다.
  • 여러 Components에서 동일한 computed가 사용 될 경우 Getters에 정의하여 공통으로 쉽게 사용할 수 있습니다.
  • 하위 모듈의 getters를 불러오기 위해서는 특이하게 this.$store.getters["경로명/함수명"]; 을 사용해야 합니다.
export const getters = {
  isAuthenticated(state) { // 현재 로그인 상태인지 확인 (true/false)
    return !!state.user;
  },

  getAccount(state) {      // 회원정보 불러오기
    return state.account;
  },
};

Vuex의 데이터 처리 상관 관계

기본적인 내용이긴 한데, Vuex에서 사용하는 클래스는 보통 state, mutation, action, getters로 구성되어 있고 아래의 구조로 구현됩니다.
아래의 형태는 기본형은 아니고 모듈별로 구현하기 위한 구조입니다. 모듈별이라 함은 store/account.js, store/member.js, store/item.js, store/category.js 등등.. 파일을 모듈 별로 작성하여 개발하는 것을 말합니다. 이것은 나중에 컴파일 과정에서 하나로 합쳐져서 사용됩니다.

예> store/index.js
import { AUTH, DB, STORAGE, GoogleProvider } from '@/services/fireinit.js';
export const strict = false;

/*
 ** STATES
 */
export const state = () => (   );

/*
 ** MUTATIONS(동기 처리)
 */
export const mutations = {   };

/*
 ** ACTIONS (비동기처리)
 */
export const actions = {   };

/*
 ** GETTERS
 */
export const getters = {   };

Vuex의 특징은 아래의 순서로 데이터를 단방향으로 관리한다는 것입니다.

각각 컴포넌트 (dispatch)--> actions (commit)--> mutations (state)--> state -->모든 컴포넌트에서 활용

이것은 메뉴얼에서 나오는 기본 내용입니다.
하지만 역방향인 mutations에서 actions을 실행시킬 수 있는지, 없는지, 모듈간의 실행은 어떻게 하는지 등은 처음 vuex를 접해본 사람에게는 많은 고민이 필요할 겁니다.

rootState는 actions과 getters에서만 사용가능

vuex를 모듈형으로 개발 할 떄 기본적으로 state 변수 값은 동일 모듈에 있는 state만 참조하게 됩니다.
만약 내가 최상위에 있는 state 변수나 다른 모듈의 변수 값을 활용하기 위해서는 state가 아니라 rootState를 활용해야 합니다.
하지만 이 rootState는 actions과 Getters의 인자로만 사용될 수 있습니다.
만약 Mutations에서 사용하기를 원한다면 Actions에서 받아서 Mutations쪽으로 commit해서 활용해야 합니다.

Mutations과 Actions의 사용가능 인자

Mutations과 Actions 내에 있는 함수에서 인자의 사용은 사용 약간의 규칙이 있습니다.

Mutations내 함수 인자

Mutitions내에 있는 함수의 인자는 state와 payload입니다.
기본 인자state만 사용할 수 있고 commit으로 넘어온 전달 인자는 payload만 있습니다. payload는 여러개를 묶은 객체 형태로 전달 될 수 있습니다.
또한 바로 중괄호로 묶어서 개체 형태로도 전달 받을 수 있습니다.

mutationA(state, payload) { }
mutationA(state, {data1, data2}) { }

Actions내 함수 인자

Actions은 비동기 처리를 합니다. 일단 실행 시키고 회신을 기다렸다가 먼저 회신 온 것 부터 처리를 합니다.
actions에서는 { rootState, state, dispatch, commit }의 기본 인자를 받을 수 있습니다.
기본 인자는 중괄호로 묶어서 전달 받습니다. 또한 payload는 Mutations와 마찮가지로 객체형태로 받을 수 있습니다.

actionsA({ rootState, state, dispatch, commit }, payload) { }

Components에서 각 store 모듈에 접근하는 방법

Components에서 store에 있는 state, Mutations, Actions, Getters에 접근하는 방법은 아래와 같습니다.

state에 접근하는 방법

state에 접근하기 위해서는 Commponent의 computed 내에 작성을 해야 합니다.

  1. 기본 접근방법 : this.$store.state.items
  2. mapState 활용방법
    ```javascript
    computed: {
    …mapState({
    items: state => state.items,
    }),
#### Mutations에 접근하는 방법   
Mutations을 실행하기 위해서는 Commponent의 **methods** 영역 내에 작성을 해야 합니다.   

1. 기본 접근방법 : `this.$store.commit('경로명/함수명')`   
1. mapMutations 활용방법    

javascript
methods: { // methods영역에서 호출해야 함
…mapMutations({
add: 'item/increment' // this.add()를 this.$store.commit('item/increment')에 매핑합니다.
})
}

#### Actions에 접근하는 방법   
Actions을 실행하기 위해서는 Commponent의 **methods** 영역 내에 작성을 해야 합니다.   

1. 기본 접근방법 : `this.$store.dispatch('경로명/함수명')`   
1. mapActions 활용방법    

javascript
methods: { // methods영역에서 호출해야 함
…mapActions({
add: 'item/increment' // this.add()를 this.$store.dispatch('item/increment')에 매핑합니다.
})
}

#### Getters에 접근하는 방법   
Getters을 실행하기 위해서는 Commponent의 **computed** 영역 내에 작성을 해야 합니다.   

1. 기본 접근방법 : `this.$store.getters["경로명/함수명"];`, `this.$store.getters.doneTodosCount`,  `this.$store.getters.getTodoById(2)`   
1. mapGetters 활용방법    

javascript
computed: {
…mapGetters({
doneCount: 'item/doneTodosCount'
})
}
```

모듈로 구성된 vuex에서 상위의 모듈에 있는 dispatch, commit를 실행시키는 방법

모듈로 구성할 경우 하위 모듈에서 형제 또는 부모 모듈의 state에 접근하기 위해서는 rootState를 사용하면 됩니다.
그러면 형제 또는 부모 모듈의 Mutations나 Actions를 실행시킬 경우는 어떻게 해야 하느냐!!!
세번쨰 인자에 { root: true }를 지정해 주면 됩니다

dispatch("path1/actionA", payload, { root: true });

이제 최상위 경로인 root에서 부터 하위로 경로를 찾아 들어갈 수있습니다.
commit도 위와 같이 처리가 가능합니다.

commit("path1/actionA", payload, { root: true });

어쩌다보니여기까지

고급지게 만들어 저렴하게 배포는 공작소

12
  • 프로필사진 eggplantiny 2020.01.30 11:04
    좋은정보 감사합니다
  • 감사합니다
  • 프로필사진 안녕 2020.02.05 09:58
    우와 신기하다
  • 감사합니다 ^^
  • 프로필사진 ㅁㄴㅇㄹ 2020.02.11 19:51
    감사합니다
  • 프로필사진 방문자1 2020.02.12 09:28
    우아
    !
  • 프로필사진 나그네 2020.02.16 16:36
    좋은 내용 잘 봤습니다. 감사합니다.
    저도 이번에 처음으로 vue를 공부 하는 중인데 컴포넌트가 3단계만 중첩이 되어도 emit이나 eventBus로는 너무 복잡해져서 도저히 감당이 안되더라구요. 혹시 store를 이용할 경우 store가 모든 데이터를 항상 가지고 있어야 해서 혹시 성능상에 이슈는 없는지 궁금합니다. 공부 후 데이터가 1만개 정도인 DB를 이용해서 프로그램을 개발 할 예정이라서요. 감사합니다.
  • store에 익숙하면 이벤트버스는 거들떠도 보지 않게되더라구요.
    그리고 여러 컴포넌트에 공유 되는 변수만 store로 잡으면 됩니다. 변수를 모두 잡을필요는 없구요
  • 프로필사진 나그네 2020.02.16 16:47
    헉!! 빠른 답변 감사합니다. 좋은 하루 되세요 ^^
  • 프로필사진 익명개발자 2020.02.20 16:18
    안녕하세요 개발중에 질문남깁니다. computed 내에서 a라는 변수로 getters를 이용해 값을 가져왔는데, 이후 methods내에 dispatch를 통해 위에 getters로 가져온 값이 바뀌었습니다. 그런데 보여지는 a는 store내 바뀐 값을 렌더링 하지 않더군요. 혹시 이런 경험있으시면 조언 부탁드리겠습니다.
  • 프로필사진 j 2020.03.04 02:03
    컴포넌트에 있는 데이터를 스토에에 담으려면 어떻게 해야하나요??
  • dispatch 명령어로 스토어의 Actions 쪽으로 보내고 Actions 에서 commit으로 mutations 을 실행시켜 state를 변경하는 식으로 처리하는게 일반적인 방법입니다