ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Vue.js] Vuex - (1) Vuex 기본
    Front-End(Web)/Vue 2021. 6. 10. 01:28
    반응형

    드디어, Vuex 라고 하는 Vue.js의 상태관리 라이브러리를 학습하는 단계에 돌입했다!

    다행인 점은, Context나 Redux를 사용해 보았기에 Vuex를 처음 접하는데 위화감은 덜 할 것 같다.

    심지어, Redux의 Flux 패턴과 어느 정도 유사하면서도, 좀 더 간소화된 절차를 가진다는 것으로 선임 개발자님께 대략적으로 들었다.

     

    Redux와는 또 다른, Vuex만의 특징과 장점은 어느 것이 있을지 한 번 학습하면서 정리해보겠다! (분량이 길 시 포스팅 분리하겠다.)

     

    * 이 글은 캡틴판교님의 Vue.js 강의 및 블로그를 기반으로 학습한 내용을 정리하고 있습니다.


    💚 Vuex 란?

    공식문서에서는, Vue.js 애플리케이션의 복잡한 컴포넌트들을 관리하는 상태관리 패턴 + 라이브러리로 정의한다.

    컴포넌트들에 대한 중앙 집중식 저장소 역할을 하며, 예측 가능한 방식으로 상태를 변경할 수 있다.

     

    React의 Redux 등과 같은 Flux 패턴을 차용하여 데이터를 관리하며, Vue devtools 와도 통합되어 디버깅이 가능하다.

     

    - 배경

    출처 : https://vuex.vuejs.org/kr/

    특정 앱의 기능은 다음 요소들로 이루어진다. 이 세 가지 요소가 기본적으로 순환되는 것이 "단방향 데이터 흐름" 이다.

    • 데이터이자 소스인 상태
    • 표시하는 선언적 매핑인 뷰
    • 반응적으로 상태를 바꾸는 액션

    하지만, 이 상태가 공통요소이고 여러 컴포넌트가 공유할 경우 단순함이 저하되며, 아래와 같은 문제에 봉착한다.

    • 여러 뷰는 같은 상태에 의존한다. => 뎁스가 깊은 자식 컴포넌트는 많은 props가 소요되며, 형제 컴포넌트 적용이 번거로움
    • 서로 다른 뷰의 작업은 동일한 상태에 적용되야 한다. => 부모로 끌어올리던가, 이벤트 버스로 복사본을 전파하는 추가소요가 생김

     

    이러한 문제점들로 인한 위험성과 유지보수의 불리함을 개선하기 위해, 공통상태를 전역 싱글톤을 관리하는 Vuex가 등장한 것이다.

     

    - Vuex 컨셉 및 구조

    위에서 언급했듯이, Vue는 Flux 패턴 기반의 단방향 데이터 컨트롤이다. 다만, React와 용어 및 원리가 일부 다르다.

     

    * MVC는 Model 기반의 양방향 데이터 컨트롤, Flux 패턴은 Store 기반의 단방향 데이터 컨트롤이다. 자세한 내용은 첨부 참조바란다!

    ( 참조링크 : https://beomy.tistory.com/44 )

    전반적인 과정은, 컴포넌트에서의 디스패칭을 시작으로 Actions(비동기), 이를 통한 Mutations(동기) 호출이 State를 갱신한다.


    💚 Vuex 사용법

    - 설치

    // CDN
    <script src="/path/to/vue.js"></script>
    <script src="/path/to/vuex.js"></script>
    
    // NPM or Yarn
    npm install vuex --save
    yarn add vuex

     

    - 초기설정

    Redux와 유사하게, store > store.js 경로를 만들어서 여기에 환경을 구축한다.

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export const store = new Vuex.Store({
      state: {
        // ...
      },
      mutations: {
        // ...
      },
    });

    use()는 Vue를 사용하는 모든 영역에 글로벌하게 기능을 추가하는 문법이다. Vuex와 같은 기능을 "플러그인" 이라고 명명한다.

    이러한 플러그인들에는 메서드($method)가 지원되며, 이를 전역에서 사용 가능하다. (예시 : Vuex 의 this.$store 등 ..)

     

    이제, main.js 혹은 필요한 Vue 컴포넌트의 인스턴스 store 필드를 설정해주기만 하면 된다.

    // main.js
    new Vue({
      el: "#app",
      store,
      render: h => h(App),
    });

     

    1) Store(저장소)

    Store는 기본적으로 앱의 전역상태를 보유하는 컨테이너이다. Vuex Store는 일반 전역개체와 크게 2가지 다른 점이 존재한다.

    1. Store는 반응형이다. Vue 컴포넌트는 상태를 검색할 때, Store의 상태가 변경되면 효율적으로 대응한다.
    2. Store 상태를 직접 변경할 수 없다. 명시적인 "commit" 요청을 통한 갱신만 가능하다. (안전성, 기록추적 등의 장점)

     

    - 기술요소

    2) State(상태)

    여러 컴포넌트 간에 공유되는 데이터(상태)이다. Vuex는 단일 객체가 모든 애플리케이션의 상태를 포함하고 있다.

     

    // Vue
    data: {
      msg: 'hello',
    };
    // Vuex
    state: {
      msg: 'hello',
    };
    
    
    // Vue
    <p>{{ msg }}</p>
    // Vuex
    <p>{{ this.$store.state.msg }}</p>

     

    3) getters

    state 값을 접근하는 속성이자 computed() 처럼 상태값의 연산에 관한 속성이다. Store 상태값에 기인한 계산이 있을때 유용하다.

    // store.js
    state: {
      num: 10,
    },
    getters: {
      getNumber(state) {
        return state.num
      },
      doubleNumber(state) {
        return state.num * 2
      }
    }
    
    <p>{{ this.$store.getters.getNumber }}</p>
    <p>{{ this.$store.getters.doubleNumber }}</p>

     

    4) mutations(변이)

    state 값을 변경하는 유일한 방법이자 메서드이다. mutation 메서드는 첫 번째 인자가 state, 두 번째 인자부터는 arguments 다.

    commit() 키워드로 작동시키며, commit의 첫 번째 인자로 "mutations 메서드명", 두 번째 인자부터는 arguments를 입력한다.

    // store.js
    state: { num: 10 },
    mutations: {
      printNums(state) {
        return state.num
      },
      sumNums(state, value) {
        return state.num + value
      }
    },
    
    // App.vue
    this.$store.commit('printNums');		// 10
    this.$store.commit('sumNums', 20);		// 30

     

    React와 비교하면, mutations의 메서드명은 action, 메서드들은 reducer와 유사하다.

    그렇기에, 메서드명도 action처럼 상수화하여, Vuex의 동작들을 types 파일에 집약하면서도 linter 효율성을 극대화시킬 수 있다.

    // mutation-types.js
    export const SOME_MUTATION = 'SOME_MUTATION'
    
    // store.js
    import Vuex from 'vuex'
    import { SOME_MUTATION } from './mutation-types'
    
    const store = new Vuex.Store({
      state: { ... },
      mutations: {
        // ES2015에서 계산 된 프로퍼티 이름 기능을 사용하여
        // 상수를 함수 이름으로 사용할 수 있습니다
        [SOME_MUTATION] (state) {
          // 변이 상태
        }
      }
    })

     

    * payload 를 가진 commit

    payload에 객체 형태로 많은 데이터를 첨부한다. (payload 외에 다른 변수명을 사용해도 되나 일반적으로 통용되는 변수명임)

    // store.js
    state: { num: 10 },
    mutations: {
      modify(st, payload) {
        console.log(payload.str);
        return st.num += payload.num
      }
    }
    
    // App.vue
    this.$store.commit('modify', {
      str: 'passed from App.vue',
      num: 20,
    });

     

    * 객체 스타일 commit

    좀 더 개선된 방법으로, commit의 첫 번째 인자인 mutations 메서드명을 객체의 type 필드로 대입할 수 있다.

    // 개선 전
    this.$store.commit('modify', {
      str: 'passed from App.vue',
      num: 20,
    });
    
    // 개선 후
    this.$store.commit({
      type: 'modify',		// 메서드명
      str: 'passed from App.vue',
      num: 20,
    });

     

    5) actions

    비동기 처리로직을 선언하는 메서드(mutations). 데이터 요청, Promise 등 비동기 처리는 actions에 선언한다.

     

    actions의 메서드들은 상태를 갱신하는게 아니라 mutations의 상태관련 로직들을 비동기로 실행한다.

    그래서 context를 인자로 받으며, 이를 통해 mutations 메서들들에 접근해서 commit 할 수 있다.

     

    dispatch() 키워드로 동작시키며, commit(mutations)과 마찬가지로 actions의 메서드명을 첫 번째 인자로 입력한다.

    // store.js
    state: {
      num: 10,
    },
    mutations: {
      doubleNum(state) {
        state.num * 2;
      }
    },
    actions: {
      delayDoubleNum(context) {
        context.commit('doubleNum');
      }
    }
    
    
    // App.vue
    this.$store.dispatch('delayDoubleNum');

     

    * actions 비동기 코드 예제

    // 1) Timer
    mutations: {
      addCounter(state) {
        state.count += 1;
      },
    },
    actions: {
      delayedCount(context) {
        setTimeout(() => context.commit('addCounter'), 2000);
      },
    };
    // 2) Server Request(axios)
    mutations: {
      setData(state, data) {
        state.list = data;
      },
    },
    actions: {
      fetchData(context) {
        return axios.get('https://domain.com/products)
          			.then(res => context.commit('setData, res);
      },
    };

    Vuex를 처음 접하면서, Redux와는 또 사뭇 다른 부분이 정말 많았다.

    우선, 비동기 로직을 actions로 별도로 분리하는 것이, 서드파티를 연결해야하는 Redux에 비해 좀 더 정형적이어서 맘에 들었다.

    또한, state, getters, mutations(동기로직), actions(비동기로직) 이 각각 관리되는 점이 셋팅하는데 있어 번거로움은 존재하나, 유지보수에는 확실히 이점이 있을 거라고 생각됬다.

     

    Vuex도 React-redux의 useSelector, useDispatch 훅들과 같이 store를 좀 더 용이하게 사용하는 내장함수가 존재한다고 한다.

    이를, Helper 함수라고 명명한다. 이 부분에 대해서는 다음 포스팅에서 정리해보겠다!

     

    [출처]

    - Vuex 공식문서(번역본) : https://vuex.vuejs.org/kr/

    - Vuex 캡틴판교 님의 블로그  : https://joshua1988.github.io/web-development/vuejs/vuex-start/

    반응형

    'Front-End(Web) > Vue' 카테고리의 다른 글

    [Vue.js] Vuex - (3) Store 모듈화  (0) 2021.06.13
    [Vue.js] Vuex - (2) Helper 함수  (0) 2021.06.11
    [Vue.js] Template 문법  (0) 2021.06.03
    [Vue.js] Vue Router  (0) 2021.06.02
    [Vue.js] 컴포넌트(Component), Props & Event  (0) 2021.05.26
Designed by Tistory.