Vue에서 상태관리를 해보자! Vuex

Minho Yoo·2022년 10월 19일
1

Vue.js

목록 보기
7/12
post-thumbnail

상태 관리란?

상태 관리란 현대 프론트엔드 프레임워크에서 모두 사용하는 개발 패턴이다.
뷰에서는 뷰엑스(Vuex)라는 상태 관리 패턴을 사용한다.
상태 관리가 필요한 이유는 컴포넌트의 숫자가 많아졌을 때 컴포넌트 간의 데이터 전달이나 관리가 어렵기 때문이다.
데이터 전달을 더 명시적이고 효율적으로 하기 위한 방법이 상태 관리이다.

뷰엑스 소개

뷰엑스란 뷰의 상태 관리 패턴이자 라이브러리이다.
최근 Vue에서 피니아(pinia)도 언급 하였지만 현자는 Vuex를 계속 쓰고 있다.

뷰엑스의 개념을 그림으로 표현했을 때 화면(View) -> 화면에서 이벤트 발생(Actions) -> 데이터 변경(State)의 단방향 데이터 흐름이 특징이다.

뷰엑스 설치

CDN 방식

<script src="https://unpkg.com/vuex"></script>

NPM 방식

npm install vuex

뷰엑스 등록

뷰엑스를 등록하기 위해서는 뷰 라우터와 마찬가지로 뷰 스토어를 하나 생성해야 한다.

// store.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex); // Vue에 Vuex를 장착한다.

export const store = new Vuex.Store({
  // ..
})

뷰 스토어를 하나 생성하고 나서 ES6 import/export 문법으로 main.js 파일의 인스턴스에 주입한다.

// main.js
import Vue from 'vue';
import { store } from './store.js';

new Vue ({
  store,
})

뷰엑스 구조도

뷰엑스의 전체 흐름을 나타낸 그림이다.

데이터 흐름은 Actions -> Mutations -> State 순서임을 알 수 있다.

뷰엑스 기술 요소

뷰엑스의 주요 기술 요소는 다음과 같다.

  • state
  • getters
  • mutations
  • actions

state

상태(state)는 여러 컴포넌트 간에 공유되는 데이터를 의미한다.

state 선언

상태는 아래와 같이 정의한다.

new Vuex.Store({
  state: {
  	message: 'Hello Vue.js',
  }
})

위 코드는 message 라는 상태 값을 정의한 코드이다.

state 접근

message 상태 값을 컴포넌트에서 접근하기 위해서는 아래와 같이 코딩한다.

<div>{{ $store.state.message }}</div>

코드를 실행하면 화면의 div안에 Hello Vue.js가 출력 된다.

getters

getters 속성은 computed 속성과 매칭되는 기술 요소이다.
상태(state) 값이 변경되었을 때 변화에 따른 차이를 자동으로 반영하여 값을 계산해준다.

getters 선언

getters 속성은 다음과 같이 정의한다.

new Vuex.Store({
  state: {
  	message: 'Hello Vue.js',
  },
  getters: {
  	reversMessage() {
      return state.message.split('').reverse().join('');
    }
  }
})

위 코드는 reversMessage 라는 getters 속성을 선언하여 상태 값 message의 문자열 순서를 거꾸로 뒤집는 코드이다.

getters 접근

getters 속성은 컴포넌트에서 아래와 같이 접근한다.

<div>{{ $store.getters.reverseMessage }}</div>

코드를 실행하면 화면에 sj.euV olleH가 출력된다.

mutations

뮤테이션(mutations)은 뷰엑스에서 상태 값을 변경하는 유일한 방법이다.
상태는 항상 뮤테이션으로 변경된다.

mutations 선언

아래 코드는 뮤테이션을 선언하는 코드이다.

new Vuex.Store({
  state: {
  	message: 'Hello Vue.js'
  },
  mutations: {
  	reverseMessage(state) {
      state.message = state.message.split('').reverse().join('');
    }
  }
})

위 코드는 뮤테이션의 reverseMessage() 메서드를 이용하여 message 상태 값을 역순으로 변환하는 코드이다.

mutations 호출

컴포넌트에서 뮤테이션을 호출하려면 commit() API를 사용해야 한다.

new Vue({
  methods: {
  	reverseMsg() {
      this.$store.commit('reverseMessage');
    }
  }
})

위 컴포넌트에서 reverseMsg() 메서드를 호출하면 바로 reverseMessage() 뮤테이션이 호출되면서 상태 값이 변환된다.

actions

액션(actions)은 뮤테이션 중에서 비동기 처리 로직들을 정의하는 속성이다.
동기 처리는 뮤테이션, 비동기 처리는 액션으로 이해하면 된다.

javascript 비동기 처리란?

자바스크립트의 비동기 처리란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미한다.

actions 선언

액션을 정의하는 코드이다.
뮤테이션 코드에서는 컴포넌트 단에서 commit('reverseMessage') API를 호출하여 state 속성에 정의되어 있던 상태 값을 변환했다.

new Vuex.Store({
  state: {
  	message: '',
  },
  mutations: {
  	reverseMessage(state, data) {
      state.meesage = data.split('').reverse().join('');
    },
  },
  actions: {
  	fetchMessage(context) {
      axios.get(url).then(function(response) {
        context.commit('reverseMessage', response.message);
      })
    }
  }
})

이번에는 컴포넌트 단에서 액션을 호출하고 나면 fetchMessage() 라는 액션 메서드가 동작한다.
fetchMessage() 메서드는 HTTP 통신을 처리하기 때문에 비동기 코드가 되고, GET 요청의 응답으로 온값을 뮤테이션의 인자로 넘겨서 역순으로 변환한 다음에 message 상태 값에 담아준다.

actions 호출

액션을 컴포넌트에서 호출하는 방법은 아래와 같다.

new Vue({
  methods: {
  	getMessage() {
      this.$store.dispatch('fetchMessage');
    }
  }
})

getMessage() 메서드를 호출하면 액션의 fetchMessage() 속성 함수가 실행된다.

헬퍼 함수

헬퍼 함수는 뷰엑스 기술 요소들을 컴포넌트에서 더 편하게 쓸 수 있도록 도와주는 API이다.
얼마나 더 편해지는지 일반적인 사용 방식과 헬퍼 함수의 사용 방식을 비교해보자.

일반적인 getters 접근 방법

reverseMessage라는 getters 속성을 컴포넌트에서 접근하려면 아래와 같다.

<div>{{ $store.getters.reverseMessage }}</div>

또는 computed 속성에 reverseMessage()를 만들어 getters를 호출한다.

export default {
  computed: {
  	reverseMessage() {
      return this.$store.getters.reverseMessage;
    }
  }
}

헬퍼 함수의 사용 방식

<div>{{ reverseMessage }}</div>
import { mapGetters } from 'vuex';

export default {
  computed: {
  	...mapGetters(['reverseMessage'])
  }
}

위의 ...는 ES6의 전개연산자(Spread Operator)를 의미한다.
헬퍼 함수를 사용할 때는 무조건 함께 사용하는 것이 좋다.
mapGetters()는 스토어의 getters 속성을 this.$store.getters.... 이런 식으로 접근하지 않고 바로 접근할 수 있게 해준다.

헬퍼 함수의 종류

뷰엑스의 기술 요소 전부 헬퍼 함수를 갖고 있다.
사용법은 아래와 같다.

import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';

export default {
  computed: {
  	...mapState();
    ...mapGetters();
  },
  methods: {
  	...mapMutations(),
    ...mapActions(),
  }
}

mapState와 mapGetters는 computed 속성에 주로 사용하고, mapMutations와 mapActions는 메서드에 주로 연결된다.

스토어 모듈화

애플리케이션에 스토어를 적용해서 사용하다 보면 금방 스토어의 덩어리가 커진다.
그럴때 데이터(상태)의 성격별로 스토어를 모듈화 해주면 훨씬 관리하기가 수월해진다.

스토어 모듈화 코드 형식

new Vuex.Store({
  modules: {
  	모듈 명: 모듈의 내용
  }
})

스토어 모듈화 예시

// store.js
import auth from './modules/auth/index.js';
import product from './moduels/product/index.js';

new Vuex.Store({
  moduels: {
  	auth: auth,
    product // 키와 값이 같다면 하나로 표현할수도 있다.
  }
})

위에서 임포트하는 auth와 product 모듈의 내용은 아래와 같다.

// auth.js, products.js
import state from './state.js';
import getters from './getters.js';
import mutations from './mutations.js';
import actions from './actions.js;

export default {
  namepaced: true,
  state,
  getters,
  mutations,
  actions
}

뷰엑스 모듈화의 핵심은 ES6의 moduels 구문을 잘 이해하는 것이다.
그리고 각 모듈들의 속성명이 유일하도록 namepsace: true를 꼭 추가해줘야 한다.

state vs data

Vue.js 애플리케이션의 규모가 커지면 컴포넌트의 data 속성과 뷰엑스(state 속성) 중 어떤 걸사용해야 하는지 고민하게 된다.
각 속성의 정의와 어떤 상황에서 뷰엑스의 state를 쓰면 좋은지 알아보자.

data 속성

data 속성은 뷰의 반응성(Reactivity)이 주입된 속성이다.
주로 데이터의 값을 화면에 표시학 위해 사용하는 속성이다.

<templat>
  <div>{{ meesage }}</div> <!-- Hello Vue.js -->
</templat>
new Vue({
  data: {
  	message: 'Hello Vue.js'
  }
})

반응성이 주입되어 있기 때문에 데이터의 값이 변하면 화면이 다시 그려진다.

<templat>
  <div>{{ meesage }}</div> <!-- changeMessage()가 실행되면 hi 표시 -->
</templat>
new Vue({
  data: {
  	message: 'Hello Vue.js'
  },
  methods: {
  	changeMessage() {
      this.message = 'hi';
    }
  }
})

state 속성

state 속성도 뷰의 반응성이 주입되어 있는 속성이다.
아래와 같이 스토어에 정의하고 모든 컴포넌트에서 접근할 수 있다.

<div>{{ $store.state.message }}</div>
new Vuex.Store({
  state: {
  	message: 'Hello Vue.js'
  }
})

접근 가능 범위

data 속성은 해당 속성을 선언한 특정 컴포넌트에서만 접근 가능하다.
만약 다른 컴포넌트에서 해당 data 속성을 접근하려면 프롭스 속성을 이용하여 접근해야 한다.
반면에, state 속성은 스토어에 한번 정의하면 모든 컴포넌트에서 접근 할 수 있다.

값 변경 방식

data 속성은 해당 컴포넌트 내에서 자유롭게 변경할 수 있다.
메서드나 라이픗하이클 훅 함수 등 아래와 같이 컴포넌트 내에서 this로 접근하여 값을 변경한다.

<templat>
  <div>{{ count }}</div>
  <button @click="increment">+</button>
</templat>
new Vue({
  data: {
  	count: 0
  },
  methods: {
    increment() {
      this.count += 1;
    }
  }
})

state 속성은 뮤테이션으로만 값을 변경할 수 있다.
뮤테이션을 통해 변경된 값은 해당 state 속성을 사용하고 있는 모든 컴포넌트에 반영되어 최신 상태를 유지한다.

<templat>
  <div>{{ $store.state.count }}</div>
  <button @click="increase">+</button>
</templat>
// count.vue
new Vue({
  methods: {
    increase() {
      this.$store.commit('increment');
    }
  }
})
// store.js
new Vuex.Store({
  state: {
  	count: 0,
  },
  mutations: {
    increment(state) {
      state.count += 1;
    }
  }
})

Vuex를 언제 사용할까?

뷰엑스는 꼭 뷰엑스를 써야만 특정 기능을 구현할 수 있을 때 사용하는 것을 추천한다.
물론 여러 컴포넌트 간에 같은 데이터를 공유할 때는 state 속성을 쓰는 것이 프롭스 속성과 이벤트 에밋 방식으로 전달하는 것보다 더 편해 보이긴하지만 이렇게 모든 데이터가 하나의 공간(Store)에 밀집되는 경우 이후에 해당 공간이 더 복잡해지는 문제점이 생긴다.
또한, 특정 UI 영역에 해당하는 데이터는 최대한 그 UI 영역에 가까이 있게 하는 것이 추후 기능 확장이나 오류 분석에도 용이하다.

뷰엑스를 언제 써야 하는지에 대한 답은 없다.
스스로 컴포넌트의 범위를 구분 짓고 어떤 데이터(상태)를 어디에 선언할지 판단할 수 있을 때 뷰엑스를 더 올바르게 사용할 수 있을 거라고 생각한다.
프롭스로 여러 번 내리고 이벤트로 여러 번 올려서 데이터 상태를 바꾸는 것이 뷰엑스보다 더 편하다면 그렇게 해도 되고 언제 뷰엑스를 사용할지는 자유다.

profile
Always happy coding 😊

0개의 댓글