[vue] vuex

Yeong·2023년 6월 2일
post-thumbnail

지금까지는 바로 api를 받아 컴포넌트로 뿌려주었지만, 이렇게 되면 안 좋은 점이 매번 api를 생성해주고 import로 index.js를 불러와 해당 함수를 등록해주어야한다.

코드가 복잡해짐은 물론 한 줄이라도 코드가 더 늘어나니 이를 줄일 수 있으면 좋을 것 같다.
-> 그래서 사용하게 된 것이 Vuex 라이브러리 !!

" 상태 관리 도구 = vuex "
상태라는 것은 여러 컴포넌트 간에 공유되는 데이터

  • 모든 것들을 하나의 공간에서 관리해주는 것이 필요한데, Vue에서는 전역에 공유되는 상태 관리를 위해 vuex를 이용한다.
  • 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소를 제공하는 라이브러리다.

vuex는 vue.js 애플리케이션 상태 관리 패턴 + 라이브러리라고 공식 홈페이지에서 설명하고 있다.

vuex는 state, mutations, actions 등 여러가지 속성들이 있고, 이를 활용해 데이터를 조작하기 때문에 다룰 데이터가 많아지고 설정할 것이 많아지면 애플리케이션의 구조를 확인할 수 있는 main.js가 복잡해진다.

이럴 때에는 따로 store 폴더를 만들어 index.js를 생성하고 이를 main.js에 등록하는 것이 코드를 분리하고 더 깔끔하게 다룰 수 있도록 도와준다.

1. Main.js에 store 등록

import Vue from 'vue'
import App from './App.vue'
import {store} from './store/index.js';
import {router} from './router/index';

Vue.config.productionTip = false

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

2. store > index.js 생성하기

import Vue from 'vue';
import Vuex from 'vuex';
import {fetchNewsList} from "../api/index.js";

Vue.use(Vuex);

export const store = new Vuex.Store({
    state:{
        news:[]
    },
    mutations:{
     //state로 데이터를 넘기기 때문에 첫 인자는 무조건 state로 해야한다.
     
        SET_NEWS(state, news){
            state.news = news;
        }
    },
    actions: {
        FETCH_NEWS(context){
            fetchNewsList()
                .then(response=>{
                console.log(response);
                context.commit('SET_NEWS',response.data);
                //state.news=response.data;
                })
                .catch(error => {
                    console.log(error);
                });
        }
    }
    // getters,
    // mutations,
    // actions
})

구조상 actions에서 state로 바로 데이터를 바인딩 할 수 없다.

actions에서는 mutations을 거쳐 state로 가기 때문에
mutations에서 이를 담아주는 함수를 실행해야함.
actions에서는 mutations에 접근할 수 있게 context가 제공된다.
context.commit으로 해당 mutations에 접근할 수 있다.
(context는 actions에서 mutation에 접근할 수있도록 제공하는 것)

Vue가 Vuex를 사용할 수 있게 Vue.use(Vuex)를 선언해준다.
이후 나오는 모든 속성들은 외부로 export되어 컴포넌트에서 사용되기 때문에 위와 같이 관리한다.

export const store = new Vuex.Store({
	state : {},
    mutations : {},
    actions : {},
    ...
});

actions에서는 데이터 api를 받아오는 역할,
mutations에서는 데이터를 다루는 함수 등을 작성,
state에서는 해당 데이터를 저장해 사용할 수 있도록 만드는 것

💡 store의 action

actions : {
      FETCH_NEWS(context) {
        fetchNewsList()
        .then(response => {
            context.commit('SET_NEWS', response.data);
        })
        .catch(error => {
            console.log(error);
        });
      }
  }
  • FETCH_NEWS를 통해 routes에 정의해놓은 fetchNewsList()를 실행한다. 해당 함수는 axios를 통해 정해진 url에서 데이터를 받아오고
    🔎 api > index.js 에서 해당 함수를 import 해와야 한다.

  • 그 데이터가 성공적으로 들어오면(.then) context.commit을 통해 mutations로 해당 데이터를 넘겨준다는 뜻이다.

  • 여기서 context는 actions에서 mutations에 접근할 수 있도록 제공하는 것으로 아래처럼 사용해 데이터 전달이 가능하다.

    context.commit('전송할 mutations 함수의 이름', 전송할 데이터);

다시 코드를 보면 SET_NEWS라는 mutations으로 데이터가 넘어가게 된다. 그럼 해당 mutations를 보자

💡 store의 mutations

mutations: {
  //state로 데이터를 넘기기 때문에 첫 인자는 무조건 state로 해야한다.
    SET_NEWS(state, news) {
        state.news = news;
    }
  }

mutations에서는 이후 state로 데이터가 옮겨가기 때문에 첫 번째 인자로 꼭 state를 넣어야한다.

이전에 vm을 생성해서 데이터를 넣어야 전달이 되던 것 처럼 state 인자를 통해 state.변수이름 = 데이터;를 사용해야한다.

💡 데이터를 받아올 뷰 컴포넌트로 이동

<template>
  <div>
      <div v-for="user in this.$store.state.news">{{ user.title }}</div>
  </div>
</template>

<script>
export default {
  created() {
   this.$store.dispatch('FETCH_NEWS');
  }
}
</script>

<style>

</style>

위와같이 데이터를 받아올 수 있다. store를 통해 정의한 state 안의 데이터들은 어느 컴포넌트에서든지 this.$store.state.데이터이름을 통해 호출할 수 있기 때문에 그만큼 관리가 편해진다.

created도 store/index.js에서 데이터를 부르는 actions를 선언하는 것 만으로 원하는 데이터를 호출할 수 있다.

created() {
    this.$store.dispatch('FETCH_NEWS');
  }

중앙 집중식 저장소라고 생각하면 편하고, 로직이 굴러가는 과정을 잘 아는 것이 중요하다!!


💡 map 헬퍼 함수를 이용한 vuex

해당 컴포넌트에서 mapState 를 이용해서 데이터값을 가져와보자.

<template>
  <div>
    <div v-for="(ask, i) in ask" :key="i">
      {{ask.id}}
    </div>
  </div>
</template>
<script>
import {mapState} from "vuex";

export default {
  computed:{
    ...mapState({
      ask : state => state.ask
    }),
  },
  created() {
    this.$store.dispatch('FETCH_ASKS');
  }
}
</script>

mapState를 통해 ask를 담고 template 에서 ask의 배열길이만큼 출력했다.
ask : state => state.ask 에서 앞 ask는 작명하면 되고 뒤의 state.ask는 스토어 안 mutation의 값이다.

이 방법도 복잡하다는 생각이 들 때 사용하는 것이 있다. "Getters"!!!

( store > index.js )

export const store = new Vuex.Store({
    state:{
        ask:[],
    },
    getters:{
        fetchedAsk(state){
            return state.ask;
        }
    },
    mutations:{
        SET_ASK(state, ask){
            state.ask = ask;
        }
    },
    actions: {
        FETCH_ASKS({commit}){
            fetchAskList()
            .then(({data})=>{
               // console.log(response);
                commit('SET_ASK', data);
            })
                .catch(error=>{
                    console.log(error);
                })
        }

    }
( view > AskView.vue )

<template>
  <div>
    <div v-for="(ask, i) in fetchedAsk" :key="i">
      {{ask.id}}
    </div>
  </div>
</template>
<script>
import {mapGetters, mapState} from "vuex";

export default {
  name: 'AskView',
  computed: {
    ...mapGetters([
        'fetchedAsk'
    ])
  },
  created() {
    this.$store.dispatch('FETCH_ASKS');
  }
}
</script>
<style></style>

💡 스토어 속성 모듈화

그리고 스토어 속성을 모듈화 할 수도 있다. 예를들어 store 폴더 안에 mutation.js 나 action.js 이렇게 모듈화할 수 있는 것이다.

index.js 안에 있는 mutation의 내용들을 뜯어와서 새로 만든 mutaion.js 파일에 붙여넣기!

export default {
    SET_NEWS(state, news){
        state.news = news;
    },
    SET_JOBS(state, jobs){
        state.jobs = jobs;
    },
    SET_ASK(state, ask){
        state.ask = ask;
    }
}

그리고 원래의 index.js 에서는

import mutation from "@/store/mutation";
 
export const store = new Vuex.Store({
    state:{
        news:[],
        jobs:[],
        ask:[],
    },
    getters:{
        fetchedAsk(state){
            return state.ask;
        }
    },
    mutaion,
    //mutations: mutation
    //앞뒤가 같기때문에 mutation하나만 적어도 된다.
    ,
    actions: {
        FETCH_NEWS(context){
            fetchNewsList()
                .then(response=>{
                console.log(response);
                context.commit('SET_NEWS',response.data);
                //state.news=response.data;
                })
                .catch(error => {
                    console.log(error);
                });
        },
        FETCH_JOBS({commit}){
            fetchJobsList()
                .then(({data})=>{
                    commit('SET_JOBS', data);
                })
                .catch(error=>{
                    console.log(error);
                })
        },
        FETCH_ASKS({commit}){
            fetchAskList()
            .then(({data})=>{
               // console.log(response);
                commit('SET_ASK', data);
            })
                .catch(error=>{
                    console.log(error);
                })
        }

    }
    // getters,
    // mutations,
    // actions
})

mutaion : mutation (앞뒤가 같으니까 mutaion만 적어도 됨) 처럼 import 하면 된다.

0개의 댓글