Vuex

VueJS를 사용하면서 상태를 관리하는 대표적인 방법은 Vuex를 사용하는 방법이 있습니다.(Vue에서 공식적으로 추천하는 상태관리 라이브러리 이기도 합니다.) 이번 포스트에서는 Vuex를 Typescript 적인 방법으로 사용하는 방법에 대해서 작성하도록 하겠습니다.

Vuex에 대한 사전 지식은 공식사이트에 자세히 설명이 되어 있습니다. (한글로 말이죠..)
그러나 이곳에 나와있는 방법으로는 Typescript와는 무엇인가 입맛에 잘 맞지 않습니다.

우리는 vuex를 클래스 형태로 만들어서 사용하기로 합시다.

vue-cli를 통해 프로젝트를 생성 하면서 vuex를 선택하게 되면 src/store.ts 라는 파일에 store 가 정의가 됩니다.

우리는 이 store.ts 파일을 지우고, stores 폴더를 만들어 store를 각각 namesapce 별로 모듈화 하여 분리하고, 각각의 스토어 모듈은 클래스 형태로 정의해서 쓰도록 합시다.

이를 위해 우리는 vuex-module-decoratorsvuex-class 라는 라이브러리를 사용합니다.

터미널에서 yarn add 명령어를 통해 라이브러리를 추가해 줍니다.

1
2
yarn add vuex-module-decorators
yarn add vuex-class

src 폴더 하단에 stores 폴더를 만들고, index.ts 파일을 새로 생성합니다.

그리고 기존 store.ts파일을 import 하고 있던 main.ts 파일을 열어 아래와 같이 바꿉니다.

1
2
3
4
5
6
7
8
9
10
11
12
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './stores'; //이부분.'./store' -> './stores'

Vue.config.productionTip = false;

new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');

그리고 /src/stores/index.ts 파일을 열어 아래와 같이 내용을 넣습니다.

1
2
3
4
5
6
7
8
9
10
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
modules: {

},
});

기본적인 틀은 셋팅이 끝났습니다. 우리는 이제 원하는대로 클래스 스타일 스토어 모듈을 작성하고 modules 밑에 넣어주기만 하면 됩니다.

Store 모듈 생성

/src/stores/CountStore.ts 파일을 만들고 아래 내용을 넣습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators';

@Module({namespaced: true})
export default class CountStore extends VuexModule {

// states
public count: number = 0;

// getters
get doubledCount() {
return this.count * 2;
}

// mutations
@Mutation
public increment(delta: number) {
console.log(`increment mutation: ${delta}`);
this.count += delta;
}

// actions
@Action({ commit: 'increment' })
public incr(delta: number) {
console.log(`increment action: ${delta}`);
return delta;
}

}

state, getter, mutation, action 들은 위와 같은 형식으로 적용하여 사용합니다. 컴포넌트에서 incr 액션함수를 호출하면, 함수가 return 하는 delta라는 값으로 increment mutation을 commit 하게 됩니다. 설명이 약간 어설픈데요, 결국 위에 선언한 incr 이라는 이름의 Action 은 아래 코드와 동일합니다. (namespace를 사용하지 않으려면 @Module 데코레이터의 내용을 빼고 작성합니다.)

1
2
3
4
5
actions: {
incr: (context: any, delta: number) => {
context.commit('increment', delta);
},
}

/src/stores/index.ts 파일을 열어 방금 만든 CountStore 모듈을 추가하는 구문을 넣어줍니다.

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue';
import Vuex from 'vuex';
import CountStore from './CountStore';

Vue.use(Vuex);

export default new Vuex.Store({
modules: {
CountStore,
},
});

이제 우리는 CountStore 라는 StoreModule을 완성했습니다.
이제는 우리가 원하는 컴포넌트에서 CountStore를 연결하여 사용할 차례입니다.

Home.vue 파일을 열어 아래와 같이 바꿔줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
<div class="home">
<p>{{count}} * 2 = {{doubledCount}}</p>
<button @click="incr(1)">incr</button>
</div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';
import { namespace, State, Action, Getter } from 'vuex-class';

const CountStoreModule = namespace('CountStore');

@Component
export default class Home extends Vue {

@CountStoreModule.State('count')
private count!: number;

@CountStoreModule.Getter('doubledCount')
private doubledCount!: number;

@CountStoreModule.Action('incr')
private incr!: (delta: number) => void;

}
</script>

CountStoreModule을 namespace를 통해 연결하고, state와 getter를 통해 상태값을 가져와 화면에 바인딩 합니다. 그리고 버튼을 incr 액션 생성함수와 연결 해줍니다.

잘 작동하는 것을 확인할 수 있습니다.

이 외에 async를 활용한 @MutationAction도 있습니다. 이에 대한 상세한 설명은
vuex-module-decorators github 페이지를 참조하시기 바랍니다.

부족한 글 읽어주셔서 감사합니다.

Comment and share

shockshot@naver.com

author.bio


author.job