FE/Vue.js

[Vue] Pinia 설치 / Store 사용 (loading, axios)

셰욘 2024. 12. 23. 11:38
728x90

Pinia

상태 관리 라이브러리

https://pinia.vuejs.kr/

 

Pinia | The intuitive store for Vue.js

Intuitive, type safe, light and flexible Store for Vue

pinia.vuejs.org

 

 

여러 컴포넌트에서 동일한 값을 가져다 써야 할 때 필요한 곳에서 import를 해서 쓴다.

 

여러 곳에서 많이 쓴다면 상태를 쓰는 컴포넌트마다 import를 해줘야 하기 때문에

보통 main.js에서 import 한 후 사용한다.

 

Pinia 설치

npm install pinia

 

 

Pinia 사용

import { createPinia } from "pinia";

const pinia = createPinia();

 

main.js

import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import { createPinia } from "pinia";

const pinia = createPinia();
const app = createApp(App);

app.use(pinia);
app.mount("#app");

 

 


Store

변수를 저장해놓는 곳

 

state

  • 변수 설정

getters

  • 변수의 값을 가져오는 곳

actions 

  • 변수의 값을 바꾸거나 실행시키고 싶은 함수를 등록하는 곳
import { defineStore } from "pinia";

export const useMessageStore = defineStore("message", {
  state: () => ({ message: "", count: 0 }),
  getters: {
    getMessage: (state) => state.message,
    getCount: (state) => state.count + 1,
  },
  actions: {
    setMessage(input) {
      this.message = this.message + input;
    },
  },
});

 

 


Store 사용

Parents.vue

<script setup>
import { ref } from "vue";
import Child from "./Child.vue";
import { useMessageStore } from "../stores/useMessageStore";

const message = ref('');
const messageStore = useMessageStore();

</script>

<template>
  <h1>부모 컴포넌트</h1>
  <input type="text" v-model="message">
  <button @click="messageStore.setMessage(message)">버튼</button>
  <Child></Child>
</template>

<style scope></style>

 

 

Child.vue

<script setup>
import { useMessageStore } from "../stores/useMessageStore";

const messageStore = useMessageStore();

</script>

<template>
  <h3>자식 컴포넌트</h3>
  <p>자식에서 스토어 사용: {{ messageStore.getMessage }}</p>
</template>

<style scope></style>

 

 

 


Store로 로딩 상태 추가

 

useLoadingStore.js

import { defineStore } from "pinia";

export const useLoadingStore = defineStore("loading", {
  state: () => ({ isLoading: false }),

  actions: {
    startLoading() {
      this.isLoading = true;
    },
    stopLoading() {
      this.isLoading = false;
    },
  },
});

 

 

App.vue

<script setup>
import Parents from "./components/Parents.vue";
import { useLoadingStore } from "./stores/useLoadingStore";

const loadingStore = useLoadingStore();

const loadData = async () => {
  loadingStore.startLoading();
  await new Promise((resolve) => setTimeout(resolve, 3000));
  loadingStore.stopLoading();
};
</script>

<template>
  <div v-if="loadingStore.isLoading">로딩중</div>
  <button @click="loadData">로딩 시작</button>
  <div v-if="!loadingStore.isLoading">로딩 끝</div>
</template>

<style scoped></style>

 

 

 

 


Store로 상품 목록 정보 가져오기

  1. 상품 목록 정보를 저장하고 있는 store 하나 생성
  2. Main에서 store의 상품 정보를 가져와서
  3. Product에 props로 전달

 

useProductStore.js

import { defineStore } from "pinia";

export const useProductStore = defineStore("products", {
  state: () => ({
    products: [
      {
        name: "상품01",
        price: 14000,
      },
      {
        name: "상품02",
        price: 9000,
      },
      {
        name: "상품03",
        price: 21000,
      },
    ],
  }),

  getters: {
    getProducts: (state) => state.products,
  },
});

 

Main.vue

<script setup>
import Product from "./components/Product.vue";
import { useProductStore } from "./stores/useProductStore";

const productStore = useProductStore();
</script>

<template>
  <Product :products="productStore.getProducts"></Product>
</template>

<style scope></style>

 

 

Product.vue

<script setup>
const props = defineProps({
  products: {
    type: Object,
    required: true,
  },
});
</script>

<template>
  <div v-for="product in products">
    <p>{{ product.name }}</p>
    <p>{{ product.price }}</p>
  </div>
</template>

<style scope></style>

 

 


Store에서 axios로  데이터 가져오기

로딩 상태를 추가하여 axios를 사용해서 데이터를 가져오기

 

 

데이터 URL

[
{"name": "상품01", "price": 1000, "seller": "판매자01"},
{"name": "상품02", "price": 2000, "seller": "판매자02"},
{"name": "상품03", "price": 3000, "seller": "판매자03"},
{"name": "상품04", "price": 4000, "seller": "판매자04"}
]

 

 

useLoadingStore.js

import { defineStore } from "pinia";
 
export const useLoadingStore = defineStore("loading", {
  state: () => ({ isLoading: false }),
 
  actions: {
    startLoading() {
      this.isLoading = true;
    },
    stopLoading() {
      this.isLoading = false;
    },
  },
});

 

 

useProductStore.js

import axios from "axios";
import { defineStore } from "pinia";
 
export const useProductStore = defineStore("product", {
  state: () => ({
    products: [],
  }),
 
  actions: {
    async fetchProducts() {
      const response = await axios.get(
        "https://c228aa9c-c71d-4ea9-ab6c-f8fffbbfe51a.mock.pstmn.io/product/list"
      );
      this.products = response.data;
    },
  },
});

 

 

Main.vue

 

onMounted

  • vue에서 컴포넌트가 화면에서 사용될 때 실행되는 함수
  • 생명주기 훅
<script setup>
import { onMounted } from "vue";
import { useProductStore } from "./stores/useProductStore";
import { useLoadingStore } from "./stores/useLoadingStore";
 
const loadingStore = useLoadingStore();
const productStore = useProductStore();
 
// vue에서 컴포넌트가 화면에서 사용될 때 실행되는 함수
// 생명주기 훅
onMounted(async () => {
  loadingStore.startLoading();
  await productStore.fetchProducts();
  loadingStore.stopLoading();
});
</script>
 
<template>
  <div>
    <!-- 만약에 로딩 중이면 로딩을 출력 -->
    <p v-if="loadingStore.isLoading">Loading...</p>
    <!-- 그렇지 않으면 다음 내용이 보이게 -->
    <ul v-if="!loadingStore.isLoading">
      <li v-for="product in productStore.products" :key="product.id">
        {{ product.name }} - {{ product.price }}
      </li>
    </ul>
  </div>
</template>
 
<style scope></style>

 

 


vite.config에서 프록시 경로 설정

vite.config.js 파일에서 프록시할 경로를 설정해주기

 

// vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      "/api": {
        // 프록시할 경로
        target: "요청 보낼 서버", // 대상 서버
        changeOrigin: true, // 대상 서버의 호스트 헤더 변경 여부
        rewrite: (path) => path.replace(/^\/api/, ""), // '/api' 부분 제거
      },
    },
  },
});

 

 

이렇게 하면 axios 요청 경로에 /api를 넣으면 자동으로 대상 서버로 이동한다.

 

 

728x90