# ๋ทฐ์์ค ํ์ ์ ์ ๋ฐฉ๋ฒ
WARNING
โ ๏ธ ์ฃผ์! ์ด ๊ธ์ Vuex์ ํ์ ์คํฌ๋ฆฝํธ์ ๊ธฐ๋ณธ ๊ฐ๋ ์ ๋ชจ๋ ์์๋ ๋ถ๋ค์ด ์ฝ์ผ์ค ์ ์๋ ๊ธ์ ๋๋ค. ์ธํ๋ฐ Vue.js ํ์ต ๋ก๋๋งต (opens new window)์ ๋ชจ๋ ์๊ฐํ์ ๋ถ๋ค๊ป ์ ํฉํฉ๋๋ค ๐
Vue.extend() ๋ฐฉ์์ ์ด์ฉํ์ฌ ๋ทฐ์์ค๋ฅผ ํ์ดํํ๋ ค๋ฉด ๋ทฐ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด๋ถ์ ์ผ๋ก ์ ๊ณตํ๋ ํ์
์ ์ฝ๊ฐ ๋ณํํด ์ฃผ์ด์ผ ํฉ๋๋ค. ์ฝ๋ ์์ฑ ๋ฐฉ์์ ์์๋ณด๊ธฐ ์ํด ํ ํฐ์ ์ค์ ํ๋ ๋ทฐ์์ค ์ฝ๋๋ฅผ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค.
# Vuex ๊ธฐ๋ณธ ์ฝ๋
๋จผ์  store/index.ts์ ์๋์ ๊ฐ์ด ์ ์ํฉ๋๋ค.
// store/index.ts
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = {
  state: {
    token: ''
  }
};
export default new Vuex.Store(store);
# state ์ ์
์ ๊ธฐ๋ณธ ์ฝ๋์์ state๋ฅผ ๋ณ๋์ ํ์ผ๋ก ๋ถ๋ฆฌํฉ๋๋ค. store/state.ts์ ๋๊ฒ ์ต๋๋ค.
// store/state.ts
export const state = {
  token: '',
}
export type RootState = typeof state;
state๋ฅผ ์ ์ํ ๋ค์ ํด๋น ๊ฐ์ฒด ๊ตฌ์กฐ์ ํ์
์ RootState์ ํ์
 ๋ณ์๋ก ์ ์ธํฉ๋๋ค.
# mutations ์ ์
๋ฎคํ
์ด์
 ์ฝ๋๋ store/mutations.ts ํ์ผ์ ๋ณ๋๋ก ์์ฑํฉ๋๋ค.
// store/mutations.ts
import { RootState } from "./state";
// ๋ฎคํ
์ด์
 ํ์
export enum MutationTypes {
  SET_TOKEN = "SET_TOKEN",
}
// ๋ฎคํ
์ด์
 ์์ฑ ํจ์
export const mutations = {
  [MutationTypes.SET_TOKEN](state: RootState, token: string) {
    state.token = token;
  },
};
export type Mutations = typeof mutations;
์ถํ ๋ฎคํ
์ด์
 ์์ฑ ํจ์์ ํ์
 ์ถ๋ก ์ ์ํด ๋ฎคํ
์ด์
 ํจ์์ ์ด๋ฆ์ ๋ชจ๋ enum ๊ฐ์ผ๋ก ์ ์ธํ๊ณ  ํด๋น ๊ฐ์ ํจ์์ ์ด๋ฆ์ผ๋ก ์ ์ํด ์ค๋๋ค. ์์์ ์ ์ํ state์ ํ์
์ธ RootState๋ฅผ ๋ค๊ณ  ์์ ๋ฎคํ
์ด์
 ์์ฑ ํจ์์ ์ฒซ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ ํ์
์ผ๋ก ์ฐ๊ฒฐํด ์คฌ์ต๋๋ค.
# ๋ทฐ ์ปดํฌ๋ํธ์์ ํ์ฉํ ์ ์๋๋ก ๋ทฐ์์ค ์ปค์คํ  ํ์ ์ ์
๊ธ ์๋์ ์ธ๊ธํ ๊ฒ์ฒ๋ผ ๋ทฐ์์ค์ ๋ด๋ถ ํ์
 ๋ฐฉ์์ผ๋ก๋ ์์์ ์ ์ํ state์ mutations๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์ถ๋ก ๋์ง ์์ต๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด store/types.ts์ ์๋์ ๊ฐ์ด ์์ฑํฉ๋๋ค.
// store/types.ts
import { CommitOptions, Store } from "vuex";
import { Mutations } from "./mutations";
import { RootState } from "./state";
type MyMutations = {
  commit<K extends keyof Mutations, P extends Parameters<Mutations[K]>[1]>(
    key: K,
    payload?: P,
    options?: CommitOptions
  ): ReturnType<Mutations[K]>;
};
export type MyStore = Omit<
  Store<RootState>,
  "commit"
> &
  MyMutations
์ ์ฝ๋๋ ๋ทฐ์์ค ๋ด๋ถ์ ์ผ๋ก ์ ์๋ ํ์
์ ์ฐ๋ฆฌ๊ฐ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฉํ๊ธฐ ์ํด ์์ฑํ state, mutations ํ์
 ์ฝ๋๋ฅผ ์ถ๊ฐํ ์ฝ๋์
๋๋ค. ์๋ก ์ ์๋ MyStore ํ์
์ ์ด์  ํ๋ก์ ํธ์์ ์ธ์ํ  ์ ์๊ฒ๋ง ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
TIP
๋ทฐ์์ค ๋ด๋ถ ํ์
์ด ๊ถ๊ธํ์  ๋ถ๋ค์ Store ํ์
์ ์ซ์์ ๋ค์ด๊ฐ๋ณด์ธ์ ๐
# ํ๋ก์ ํธ ํ์ ์ ์ ํ์ฅํ๊ธฐ
์ด์  ์์์ ์ ์ํ MyStore ํ์
์ ์๋์ ๊ฐ์ด ์ปดํฌ๋ํธ ์ต์
 ์์ฑ์์ ์ถ๋ก ๋  ์ ์๊ฒ ํด๋ณด๊ฒ ์ต๋๋ค.

๋ทฐ + ํ์
์คํฌ๋ฆฝํธ ํ๋ก์ ํธ ๋ฃจํธ ๋ ๋ฒจ์ src/types/project.d.ts ํ์ผ์ ์์ฑํ๊ณ  ์๋ ๋ด์ฉ์ ์์ฑํฉ๋๋ค.
// src/types/project.d.ts
import Vue from "vue";
import { MyStore } from "../store/types";
declare module "vue/types/vue" {
  interface Vue {
    $store: MyStore;
  }
}
declare module "vue/types/options" {
  interface ComponentOptions<V extends Vue> {
    store?: MyStore;
  }
}
๋ค์์ผ๋ก ํ๋ก์ ํธ์ ํ์ ์คํฌ๋ฆฝํธ ์ค์  ํ์ผ์ ์๋ ์ต์ ์ ์ถ๊ฐํฉ๋๋ค.
// ...
"include": [
  "src/**/*.ts",
  "src/**/*.tsx",
  "src/**/*.vue",
  "tests/**/*.ts",
  "tests/**/*.tsx",
  "src/types/**.d.ts",
],
"exclude": [
  // ...
]
๊ทธ๋ฆฌ๊ณ  node_modules/vuex/types/vue.d.ts ํ์ผ์ ์ญ์ ํฉ๋๋ค. ์ด์  ์ฌ์ฉ์ค์ธ ๊ฐ๋ฐ ํด์ด๋ ์ฝ๋ ํธ์ง๊ธฐ๋ฅผ ์ข
๋ฃํ๊ณ  ๋ค์ ์คํํ์ฌ ์ถ๋ก ์ด ์ ๋๋์ง ํ์ธํฉ๋๋ค.

TIP
Vue 2์์๋ node_modules ๋ฐ์ ํ์ ์ ์ธ ํ์ผ์ ์ง์์ค์ผ ํ์ง๋ง, Vue 3์์๋ ๋ด๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฑด๋ค์ง ์๊ณ ๋ ํ์ฅํ ์ ์๊ฒ ๋ค์๊ณผ ๊ฐ์ ์ธํฐํ์ด์ค๊ฐ ์ ๊ณต๋ฉ๋๋ค ๐ Vuex 4 ๋ฆด๋ฆฌ์ฆ ๋ ธํธ (opens new window)
# actions ์ ์
actions ํจ์๋ ์๋์ ๊ฐ์ด ์ ์ํ  ์ ์์ต๋๋ค.
// store/actions.ts
import { ActionContext } from "vuex";
import { Mutations } from "./mutations";
import { RootState } from "./state";
export enum ActionTypes {
  FETCH_NEWS = "FETCH_NEWS"
}
interface News {
  title: string;
  id: number;
}
type MyActionContext = {
  commit<K extends keyof Mutations>(
    key: K,
    payload?: Parameters<Mutations[K]>[1]
  ): ReturnType<Mutations[K]>;
} & Omit<ActionContext<RootState, RootState>, "commit">;
export const actions = {
  async [ActionTypes.FETCH_NEWS](context: MyActionContext, payload?: number) {
    const res = await fetch("https://jsonplaceholder.typicode.com/users/1");
    const user: News[] = await res.json();
    return user;
  }
};
export type Actions = typeof actions;
์คํ ์ด ์ปค์คํ  ํ์ ์ด ์ ์๋ ํ์ผ์ ์๋ ๋ด์ฉ์ ์ถ๊ฐํฉ๋๋ค.
// store/types.ts
import { CommitOptions, DispatchOptions, Store } from "vuex";
import { Actions } from "./actions";
import { Mutations } from "./mutations";
import { RootState } from "./state";
type MyMutations = {
  commit<K extends keyof Mutations, P extends Parameters<Mutations[K]>[1]>(
    key: K,
    payload?: P,
    options?: CommitOptions
  ): ReturnType<Mutations[K]>;
};
type MyActions = {
  dispatch<K extends keyof Actions>(
    key: K,
    payload?: Parameters<Actions[K]>[1],
    options?: DispatchOptions
  ): ReturnType<Actions[K]>;
};
export type MyStore = Omit<
  Store<RootState>,
  "commit" | "dispatch"
> &
  MyMutations &
  MyActions;
# getters ์ ์
getters ์์ฑ ํจ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ํฉ๋๋ค.
// store/getters.ts
import { RootState } from "./state";
export const getters = {
  getToken(state: RootState) {
    return state.token + "!";
  }
};
export type Getters = typeof getters;
์คํ ์ด ์ปค์คํ  ํ์ผ์ ์๋์ ๊ฐ์ด ์ถ๊ฐํฉ๋๋ค.
import { Action, CommitOptions, DispatchOptions, Store } from "vuex";
import { Actions } from "./actions";
import { Getters } from "./getters";
import { Mutations } from "./mutations";
import { RootState } from "./state";
type MyMutations = {
  commit<K extends keyof Mutations, P extends Parameters<Mutations[K]>[1]>(
    key: K,
    payload?: P,
    options?: CommitOptions
  ): ReturnType<Mutations[K]>;
};
type MyActions = {
  dispatch<K extends keyof Actions>(
    key: K,
    payload?: Parameters<Actions[K]>[1],
    options?: DispatchOptions
  ): ReturnType<Actions[K]>;
};
type MyGetters = {
  getters: {
    [K in keyof Getters]: ReturnType<Getters[K]>;
  };
};
export type MyStore = Omit<
  Store<RootState>,
  "getters" | "commit" | "dispatch"
> &
  MyMutations &
  MyActions &
  MyGetters;
โ Vue Property Decorator ref โ