가수면

[Vue] 전역 상태 관리 본문

Vue

[Vue] 전역 상태 관리

니비앙 2024. 5. 16. 23:46

Vuex

타입스크립트 적용

1. vuex 타입 에러 문제

공식 문서에 따르면 특별한 TypeScript 구성이 필요하지 않다고 하는데 모듈 선언에 대한 오류가 발생할 수 있다.

import { createStore } from "vuex";

모듈 'vuex'에 대한 선언 파일을 찾을 수 없습니다. 'c:/Users/yhhnn/Desktop/dev/frontend/Private/Vue/weather/node_modules/vuex/dist/vuex.mjs'에는 암시적으로 'any' 형식이 포함됩니다.
There are types at 'c:/Users/yhhnn/Desktop/dev/frontend/Private/Vue/weather/node_modules/vuex/types/index.d.ts', but this result could not be resolved when respecting package.json "exports". The 'vuex' library may need to update its package.json or typings.

이 경우 그냥 모듈의 타입 선언을 우회하는 편법을 사용해 해결할 수 있다.

// src\@types\모듈명\index.d.ts

declare module "모듈명";

2. $store로 사용 시 타입 에러 문제

{{ $store?.state?.count }}

'CreateComponentPublicInstance<Readonly<ExtractPropTypes<{}>>, { Navbar: DefineComponent<{}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ... 5 more ..., {}>; MainComp: DefineComponent<...>; weatherData: Ref<...>; onSearchCity: (inputText: string) => void; }, ... 17 more ..., {}>' 형식에 '$store' 속성이 없습니다.

이 경우 전역 선언을 통해 해결할 수 있다.

// src\store\vuex.d.ts

import { Store } from "vuex";

declare module "@vue/runtime-core" {
  interface State {
    count: number;
  }

  interface ComponentCustomProperties {
    $store: Store<State>;
  }
}

3. useStore를 사용할 때 타입 에러 문제

import { useStore } from "vuex/types/index.js";

[plugin:vite:import-analysis] Failed to resolve import "vuex/types/index.js" from "src/App.vue". Does the file exist?

useStore를 스토어에 직접 선언하고 export해 해결할 수 있다.

// src\store\store.ts

import { InjectionKey } from "vue";
import { createStore, useStore as baseUseStore } from "vuex";
import { Store } from "vuex/types/index.js";

export interface State {
  count: number;
}

export const store = createStore({
  state: {
    count: 0,
  },
  mutations: {
    addCount(state: State, payload: number) {
      state.count += 1 + payload;
    },
  },
});

export const key: InjectionKey<Store<State>> = Symbol();

export function useStore() {
  return baseUseStore(key);
}
// src\main.ts

import { store, key } from "./store/store";

createApp(App).use(store, key)

기본 사용법

$store 사용

$store?.commit(mutations 함수명, payload)

  <p>count : {{ $store?.state?.count }}</p>
  <button @click="$store?.commit('addCount', 10)">증가</button>

useStore 사용

<script setup lang="ts">
import { useStore } from "./store/store";

const store = useStore();
</script>

...

  <p>count : {{ store?.state?.count }}</p>
  <button @click="store?.commit('addCount', 10)">증가</button>

actions

비동기적인 작업을 할 때는 actions를 이용해 mutations, state를 변경

 

Pinia

store

스토어 내에서 상태 및 actions 사용 시 this

비동기 actions 함수에는 async/await 사용

import { defineStore } from "pinia";

export const useStore = defineStore("weather", {
  state: () => ({
    weatherData: {
      icon: "icon",
      temp: 0,
      text: "text",
      location: "location",
      city: "Seoul",
    },
  }),
  actions: {
    updateWeather(payload: any) {
      this.weatherData.icon = payload.weather[0].icon;
      this.weatherData.temp = payload.main.temp;
      this.weatherData.text = payload.weather[0].description;
      this.weatherData.location = payload.sys.country;
      this.weatherData.city = payload.name;
    },
    onSearchCity(payload: string) {
      this.weatherData.city = payload;
    },
    async getWeather() {
      const API_URL = `https://api.openweathermap.org/data/2.5/weather?q=${this.weatherData.city}&appid=050d4b026cee0fc9716845267b7b107c`;
      await fetch(API_URL)
        .then((res) => res.json())
        .then((data) => {
          this.updateWeather(data);
        })
        .catch(() => alert("날씨를 불러오는 데 실패했습니다."));
    },
  },
});

사용

상태값 사용 - storeToRefs와 스토어에서 export한 defineStore 사용

useStore에서 직접 사용해도 되지만, storeToRefs를 이용해 상태값을 사용하는 것이 직접 사용하는 것보다 Vue의 반응형 시스템과 잘 통합되어 있기 때문에 안전하다.

또한, storeToRefs로 전역 상태값을 사용하는 것이 props로 전달하는 방식에 있어 더 적합하다.

<template>
  <div class="weather-info">
    <div class="icon"><img :src="`https://openweathermap.org/img/wn/${weatherData.icon}@2x.png`" alt="날씨 아이콘" /></div>
    <div class="temp">{{ (weatherData.temp - 273.15).toFixed(1) }}&deg;</div>
    <div class="text">{{ weatherData.text }}</div>
    <div class="location">{{ weatherData.city }} ,{{ weatherData.location }}</div>
  </div>
</template>

<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useStore } from "../store/store";

const store = useStore();
const { weatherData } = storeToRefs(store);
</script>

함수 사용

defineStore에서 바로 사용

<script setup lang="ts">
import Navbar from "./components/Navbar.vue";
import MainComp from "./components/MainComp.vue";
import { onMounted } from "vue";
import { useStore } from "./store/store";

const store = useStore();

onMounted(() => {
  store.getWeather();
});
</script>

'Vue' 카테고리의 다른 글

Vuetify 컴포넌트 자식 요소의 스타일 커스텀하는 방법  (0) 2024.08.26
[Vue] 심화  (0) 2024.05.25
[Vue] Router  (0) 2024.05.16
[Vue] Composition API 방식  (0) 2024.05.13
[Vue] 기본  (0) 2024.05.13
Comments