프로그래밍/Vue.js + Electron

타입스크립트로 Vuex를 완벽(?)하게 사용하기

『하날엔☆。 2021. 4. 23. 17:00

이번에 달새 작업을 하면서 소스 코드의 데이터 전체를 Vuex로 전환 하는 작업을 하게 됐습니다.
왜 이런 짓을 하게 되었는가 하면....
Vuex에서 메모리 이슈를 크게 겪고 아 메모리 안터지게 할거야~ 했다가
바인딩 안 되는 지옥도를 맛 보고 결국 Vuex로 돌아오게 됐습니다.
님들은 제발 처음부터 Vuex 쓰세요
이번에도 너무 고생을 했기에 글로 한번 정리합니다.

Vuex에서 타입스크립트를 완벽(?)하게 사용하기

타입스크립트를 사용 하는 이유가 무엇이겠습니까
강타입! 자동완성! any가 없는 타입 추론!
Vuex를 도입하게 되고 겪은 문제가 타입 추론이 되지 않는다는 문제였습니다.

사용 라이브러리

  • Vuex
    • vuex-module-decorators

store/index.ts

//vuex를 모듈화해서 사용합니다. 다른 모듈을 import
import { ISwitterState } from './modules/SwitterStore';
import Vuex from 'vuex';
import Vue from 'vue';
Vue.use(Vuex);//Vue.use(Vuex)는 이 코드에만 사용 합니다.

//단순한 루트 State입니다.
export interface IRootState {
  //modules에서 가져온 SwitterStore의 State 인터페이스
  switter: ISwitterState;
}
//설정이 완료 된 Vuex 객체를 최종 export합니다.
export default new Vuex.Store<IRootState>({ getters: {} });

store/modules/SwitterStore.ts

import * as I from '@/Interfaces';//이건 제 개인적인 인터페이스 네임스페이스입니다
import { Module, VuexModule, Mutation, Action, getModule } from 'vuex-module-decorators';//라이브러리에서 사용 할 애들
import * as A from '@/store/Interface';//
import store from '@/store';//

//이 모듈의 State 인터페이스
export interface ISwitterState {
  switter: I.Switter;
}

//dynaic: Vuex가 동적으로 모듈을 로딩 하는 값
//store: root(store/index.ts)에서 export한 Vuex 루트 객체
//name: 뭔가 있어보임. ※실제로 어떻게 사용하는지 찾지 못 함
@Module({ dynamic: true, store, name: 'switter' })
class SwitterStore extends VuexModule {
  //State객체입니다. 사용하실 객체를 전부 넣어주시면 됩니다.
  switter!: I.Switter;
  /*
  예: user!: User;
  예2: count: number;
  */

  //컴포넌트에서 사용하게 될 computed입니다.
  get selectID() {
    let id = this.switter?.selectUser.user_id;
    id = id ? id : '';
    return id;
  }

  //※Muation과 Action의 이름은 서로 달라야 합니다.

  //일반적인 Mutation입니다 실제 State에 값을 할당 하는 로직을 구성 합니다.
  //명시적으로 private선언을 해서 IDE에서 잘못 호출 하지 않도록 합니다.
  @Mutation
  private setKey(setkey: A.SetKey) {
    this.tempUser = new I.DalsaeUser();
    this.tempUser.oauth_token = setkey.publicKey;
    this.tempUser.oauth_token_secret = setkey.secretKey;
  }
  //외부에서 호출 하게 될 Action 별 내용은 없습니다.
  @Action
  public SetKey(setKey: A.SetKey) {
    this.context.commit('setKey', setKey);//Mutation을 호출 합니다.
  }
}
//최종적으로 getModule에 Store클래스를 넘겨 모듈을 생성 후 export 합니다
//moduleSwitter는 외부에서 접근 할 이름입니다.
export const moduleSwitter = getModule(SwitterStore);

어딘가의 ts파일

import { moduleSwitter } from '@/store/modules/SwitterStore';
async Test1(){
 moduleSwitter.SetKey({
      publicKey: result.data.oauth_token,
      secretKey: result.data.oauth_token_secret
    });
}
async Test2(){
 moduleSwitter.SetKey({
  store.dispatch('SetKey', {
    publicKey: result.data.oauth_token,
    secretKey: result.data.oauth_token_secret
  }); 
}

Test1은 타입스크립트에 맞춘 형식이고

Test2는 고전적인 string dispatch입니다
하지만 우리는 타입스크립트를 사용하고 싶습니다...
자동 완성 좋아

Test1의 경우 IDE에서

Test1의 경우 정말 타입이 다 나오니 간단하게 파라메터를 넘길 수 있겠군요!

Test2의 경우 IDE에서

Test2의 경우 type: string으로 설정 되며 payload는 any형이 됩니다.

Vue디버거 툴에서 확인 되는 Vuex 업데이트

짜잔! vuex에 정상 반영이 되었습니다.

외부에서 state에 접근 하고 싶을 경우

Root Store부터 접근할 경우 이렇게 사용합니다.
모듈만 import 했을 경우

import store from '@/store';
import { moduleSwitter } from '@/store/modules/SwitterStore';
store.state.switter.switter.listUser//root Store부터 접근 시
moduleSwitter.switter.listUser//module store만 가져와 접근 시

왜 이렇게 동작 하나요? ※이 내용은 정확하지 않을 수 있습니다.

Node.js에서 import(js의 경우 require)를 할 경우
동기로 모듈을 읽어와 메모리에 캐시를 하게됩니다.
모듈에서 읽어와 캐시 작업을 하는 것은 export한 객체입니다.
캐시의 범위(스코프?)는 package를 로드하는 범위입니다.
같은 스코프 안에서는 import한 모듈이 공유 됩니다.

main.ts

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

main파일에서 Vuex의 store를 import하게 됩니다.
import를 하며 store파일을 읽어 해당 파일의 코드를 진행 합니다.

store/index.ts

import { ISwitterState } from './modules/SwitterStore';

index에서 import를 하며 해당 모듈이 node.js의 메모리에 올라가게 됩니다.
같은 패키지 내에서는 싱글톤처럼...? 메모리를 공유 하게 됩니다.
캐시 처리 되는 부분은 ts파일의 export된 모든 항목입니다.

store/modules/SwitterStore.ts

export const moduleSwitter = getModule(SwitterStore);

index에서 import를 하며 export된 항목을 쭉 훑습니다.
훑으면서 마지막에 moduleSwitter가 생성되어 메모리에 올라가게 되고

무언가.ts

import { moduleSwitter } from '@/store/modules/SwitterStore';

main, index, SwitterStore 파일을 거쳐오며 모듈이 메모리에 올라가게 되고
외부에서 모듈의 객체(State, Action)에 접근이 가능해지는 겁니다.

다시 말씀 드리지만 동작 관련 부분은 저도 정확히 알고 있는 내용은 아닙니다.

컴포넌트에서 접근, 사용하기

해당 내용은 제가 구현 한 뒤에 다시 수정하겠습니다.