본문 바로가기
Front-End (web)/Vue.js Typescript

[Vue.js/Typescript] 3. Vuex - State, Getter

by 햄과함께 2019. 1. 26.
320x100

데이터가 특정 컴포넌트에 종속되지 않고 다른 컴포넌트에서도 사용되는 데이터라면 vuex를 사용하여 공통 저장소에 저장하는게 좋다.

(부모, 자식 컴포넌트간 데이터 교환이라면 props, events로 해도 되긴한다.)




Vuex 사용하기


npm i --save vuex
cs

vuex를 사용하려면 먼저 의존성에 vuex를 추가한다.


// src/store/index.ts
 
import Vue from "vue";
import Vuex from "vuex";
 
Vue.use(Vuex);
 
export const store = new Vuex.Store({
    state:{
        // data
    }
});
cs

그 후 src/store 폴더를 만들고 index.ts 파일을 만듭니다.

그리고 위와 같이 작성한다.

state에 공통 데이터를 적어주면 된다.


// src/index.ts
 
import Vue from "vue";
import {store} from './store'; // store 임포트
 
let v = new Vue({
    el: "#app",
    template: `
    <div>
    </div>`,
    store // 추가
});
cs


그리고 Vue를 생성하는 곳에 위와 같이 방금 생성한 store를 임포트하고 new Vue에 추가해준다.

만약 만든 store 이름이 다르면(storeFolder 라 하자) 


import {storeFolder} from './storeFolder';
 
// ...
 
let v = new Vue({
    // ...
    store : storeFolder // 추가
cs


위와 같이 store : <생성한 store 이름> 으로 추가한다.

store 이름이 같은 경우 생략할 수 있기 때문에 웬만하면 store라고 네이밍 짓기를 추천한다.




Module로 분리하기


vuex를 위와 같이만 사용한다면 모든 데이터가 src/store/index.ts 파일안에 존재하기 때문에 큰 프로젝트에서는 보기가 불편하다.

따라서 modules로 vuex 데이터를 분리해보자.



src/store 밑에 modules라는 이름으로 폴더를 추가한다.

그리고 새로운 ts 파일을 하나 만든다. (product.ts)


// src/store/modules/product.ts
 
export const namespaced = true;
 
export interface ProductInfo {
    productNo: number,
    name: string
}
 
interface State {
    pageName: string,
    product: ProductInfo
}
 
export const state: State = {
    pageName: '상품 페이지',
    product: {
        productNo: 0,
        name'상품 이름'
    }
}
cs


그리고 위와 같이 작성한다.

타입스크립트에서는 타입 지정 해주는게 중요하기 때문에 데이터의 타입을 인터페이스로 만들었다.(ProductInfo, State)

그리고 State의 구현체인 state를 만든다. state가 실제로 사용할 데이터이다.


// src/store/index.ts
 
import * as product from './modules/product'//import
 
// ...
 
export const store = new Vuex.Store({
    // ...
    modules: {
        product // module 등록
    }
}
cs


그리고 store/index.ts에 위와 같이 방금 생성한 product 데이터를 가져와서 모듈에 등록해준다.




실제로 돌려서 확인해보면 state에 product이름으로 Object가 하나 생기고 이 밑에 product.ts 에서 정의한 데이터들이 들어간 것을 확인할 수 있다.




State


이제 product.ts의 state 를 실제 컴포넌트에서 사용해보자.


// src/components/Product/index.ts
 
import { Vue, Component, Prop } from "vue-property-decorator";
import { mapState } from 'vuex'
 
@Component({
    template: require('./Product.html'),
    computed: mapState('product'// product module
        ['pageName''product'] // state
    )
})
export default class Product extends Vue {
}
cs


Product라는 컴포넌트를 하나 만들고 위와 같이 작성한다.

mapState 를 이용해서 product 모듈의 pageName, product 데이터를 가지고 온다.


// src/components/Product/Product.html
 
<div>
    Product Component
    <h1>pageName : {{pageName}}</h1>
    <h2>Product : {{product}}</h2>
    <h3>product's No : {{product.productNo}}</h3>
    <h3>product's Name : {{product.name}}</h3>
</div>
cs


Product 컴포넌트에서 사용할 템플릿은 위와 같이 작성한다.

product 모듈의 데이터를 화면에 뿌려보는 것이다.



결과는 위와 같다.

데이터가 잘 나온다. :)


// src/components/Product/index.ts

// product모듈에서 선언한 ProductInfo 데이터 타입 임포트
import { ProductInfo } from "../../store/modules/product";
 
@Component({
    template: require('./Product.html'),
    computed: mapState('product'
        ['pageName''product']
    )
})
export default class Product extends Vue {
 
    // 데이터 선언
    pageName!: string;
    product!: ProductInfo;
 
    private mounted() {
        console.log(this.pageName);
        console.log(this.product);
    }
}
cs


ts 파일에서 데이터를 쓰고 싶으면 가져온 데이터와 이름을 같게 만들어서 데이터 선언을 해준다.

ts에서는 데이터에 타입을 중요하게 생각하므로 ProductInfo 타입을 product 모듈에서 가져와서 import 해준다. (이를 위해 ProductInfo 인터페이스는 export 해주었다.)




이를 mount 될 때 콘솔에 찍어보면 위와 같이 데이터가 잘 나온다.


// src/components/Product/index.ts
import { ProductInfo } from "../../store/modules/product";
import {State} from 'vuex-class'// add
 
@Component({
    template: require('./Product.html'),
    // computed: mapState('product', ['pageName', 'product'])
})
export default class Product extends Vue {
 
    @State
    constants!: object;
 
    @State(state => state.product.pageName)
    pageName!: string;
    
    @State(state => state.product.product)
    product!: ProductInfo;
 
    private mounted() {
        console.log(this.pageName);
        console.log(this.product);
        console.log(this.constants);
    }
}
cs

vuex-class의 @State를 이용해서도 state를 가져올 수 있다.

사용법은 위와 같다.

@State(state => state.product.product) 은 product 모듈의 product state를 가져온다는 의미이다.

(참고 : vuex-class github)


@State('product',{namespace: 'product'})
product!: ProductInfo;
cs

위 코드를 이런 식으로도 사용할 수 있다.



Getter


다른데에서도 사용되는 데이터 관련 함수들은 getter로 만들어두면 편리하다.


// src/store/modules/products.ts
 
import {GetterTree} from 'vuex' // GetterTree import
 
// ...
 
export interface ProductInfo {
    productNo: number,
    name: string,
    registerYmdt: Date // 추가
}
 
export const state: State = {
    pageName: '상품 페이지',
    product: {
        productNo: 0,
        name'0번 상품',
        registerYmdt: new Date() // 추가
    }
}
 
// Getters
export const getters: GetterTree<State, any> = {
    
    // registerYmdt 중 year만 가져오는 getters 함수
    productRegisterYear: (state, getters) => () => {
        return state.product.registerYmdt.getFullYear()
    }
}
cs

ProductInfo 인터페이스에 registerYmdt를 추가해보자.

그리고 registerYmdt 중 연도만 가져오는 productRegisterYear 라는 게터 함수를 만든다.


// src/components/Product/index.ts
 
import {State, Getter} from 'vuex-class';
 
// ...
export default class Product extends Vue {
 
    // ...
 
    @Getter('productRegisterYear', {namespace: 'product'})
    productRegisterYear!: () => string;
 
    registerYear: string = '';
 
    private mounted() {
        // getter
        this.registerYear = this.productRegisterYear();
    }
}
cs

getter를 가져와서 사용하려면 vuex-class의 Getter 어노테이션을 이용해 State와 비슷하게 가져올 수 있다.

product 모듈에서 productRegisterYear 게터를 가져왔다.

그리고 registerYear 변수를 하나 만들고 mounted 될 때 게터로 가져온 연도를 해당 변수에 저장했다.


<!-- src/components/Product/Product.html -->
 
<div>
    <!-- ... -->
    <h3>product's registerYear : {{registerYear}}</h3<!-- 추가 -->
</div>
cs

템플릿에 registerYear 변수를 화면에 출력하는 코드를 추가했다.


연도인 2019만 가지고와서 출력된다.




참고 깃허브 : add Vuex State, Getter

320x100

댓글