[vue.js] Vuex 정리
vue에서 상태 관리를 하기 위한 라이브러리인 vuex에 대해 정리하고자 한다.
One-Way Data Binding in Vue
- Vue.js에서 부모-자식 컴포넌트 사이에 단방향 바인딩을 형성
- 부모는 props를 통해 자식에게 데이터를 전달하고, 자식은 events(emit)를 통해 부모에게 전달
- props는 아래로, events 위로 전달
- 하위 컴포넌트가 실수로 상위 컴포넌트의 상태를 변경하여 앱의 데이터 흐름을 추론하기 더 어렵게 만드는 것을 방지하기 위함
- 컴포넌트의 개수가 많아지면 컴포넌트 간에 데이터 전달이 어려워짐
만약, 한 컴포넌트에서 데이터를 수정할 때마다 prop, emit을 사용해 전달해야하는데 그 과정이 매우 복잡해질 수 있음- 해결 방법으로 Event Bus와 Vuex 등이 있음
- "단반향 흐름" 컨셉의 간단히 묘사한 그림 (Vue 인스턴스 내 상태 관리)
- state : 컴포넌트 간에 공유할 data
- view : 데이터가 표현될 template
- actions : 사용자의 입력에 따라 반응할 methods
- 이런 단순한 상황은 여러개의 컴포넌트가 같은 state를 공유하는 경우에 빠르게 무너짐
new Vue({
// state
data() {
return {
counter: 0
};
},
// view
template: `
<div>{{ counter }}</div>
<button @click="increment">PLUS</button>
`,
// actions
methods: {
increment() {
this.counter++;
}
}
});
v-model을 사용하여 양방향 바인딩 가능 (two-way binding)
상태 관리 (State Management)
상태 (state) 란?
- 어떤 객체가 가진 데이터 (data)
- 객체 간 데이터를 주고 받으며 application이 구현됨
- 예를 들어, Vue에서는 컴포넌트(객체) 에 data가 상태라 할 수 있음
- 지역적(local) 또는 전역적(global) 상태를 적절히 구분해서 설계해야 함
- 지역적으로 props와 emit으로 컴포넌트 내 데이터를 전달
- 전역적으로 vuex 스토어에 저장하던지, 브라우저의 쿠키(Cookie) 또는 로컬 스토리지(localStorage)에 저장하여 데이터 공유 가능
상태 관리가 필요한 이유
- 규모가 크고 복잡한 application에서 발생할 수 있는 컴포넌트 간 통신 및 데이터 전달 문제를 해결 가능
- 규모가 크고 복잡한 앱일수록
- props, emit 이 거쳐야 할 컴포넌트가 많아짐
- Event Bus를 쓰면 컴포넌트 간 데이터 흐름을 파악하기 어려움
- 이러한 문제점을 해결하기 위해 모든 데이터 통신을 한 곳에서 중앙 집중식으로 관리하는 것이 상태 관리
- 규모가 작은 프로젝트에서는 필요 없을 수 있음
Event Bus 문제점
컴포넌트가 많아지면 어디서 어디로 보냈는지 관리가 되지 않음
(컴포넌트 간 데이터 흐름을 파악하기 어려움)
Vuex 소개
Vuex 란?
- Vue.js에서의 상태 관리 라이브러리
- Vue.js 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소 역할
- 다른 상태 관리 라이브러리인 Flux(facebook), Redux (React)에서 영감을 받음
- Vue.js에서도 Redux를 사용할 수 있지만, Vuex와의 호환이 좋고 더 직관적으로 개발 가능
- Vue.js의 반응성(Reactivity) 체계를 효율적으로 활용하여 화면을 업데이트 가능
Vue 버전에 맞는 Vuex 버전 확인
- Vue 2 & Vuex 3
- Vue 3 & Vuex 4
특징
Single Source of Truth
- 하나의 어플리케이션은 하나의 store만 가진다는 것을 의미
- 하나의 객체가 어플리케이션의 전체 state를 포함하고, 이 객체가 하나의 원본 소스 임
- 단일 상태 트리는 특정 state를 바로 찾을 수 있게 만들고, 현재 앱의 state의 스냅샷을 가져올 수 있어 디버깅을 간편하게 도움
state is Reactive
- 상태가 변경되면 이 상태를 공유하는 다른 곳에서도 상태가 업데이트 됨
state management pattern + library
- Vuex는 상태 관리 패턴이자 라이브러리
- 여러 컴포넌트 간의 데이터 전달과 이벤트 통신을 한곳에서 관리하는 패턴을 Vuex를 통해 구현 가능
- 상태 관리 및 특정 규칙 적용과 관련된 개념을 정의하고 분리함으로써, 코드의 구조와 유지 관리 기능 향상
주의 사항
- 페이지 새로고침시 store에 데이터가 사라짐
이 문제는 브라우저 쿠키 or 스토리지나 다른 라이브러리 등으로 처리 가능- vuex-persistedstate 라이브러리
- 모든 store 값들을 localstorage로 저장할 시 속도 이슈가 발생할 수 있음
- 공통적으로 사용하는 상태가 아닌 경우는 메모리를 낭비할 수 있음
- Vuex 저장소가 일반 전역 개체와 다른 두 가지
- Vuex store는 반응형
Vue 컴포넌트는 상태를 검색할 때 저장소의 상태가 변경되면 효율적으로 대응하고 업데이트 함- 상태를 직접 변경할 수 없음
저장소의 상태를 변경하는 유일한 방법은 명시적인 commit을 이용한 변이
이렇게하면 모든 상태에 대한 추적이 가능한 기록이 남을 수 있으며 툴을 사용하여 앱을 더 잘 이해할 수 있음
Vuex 구조 (Core Concept)
- 관리 포인트는 store 패턴을 사용
state
- Vue의 data와 같음
- 원본 소스의 역할을 하며, View와 직접적으로 연결되어있는 Model
- state는 mutation을 통해서만 변경이 가능
- mutation을 통해 state가 변경이 일어나면 반응적으로 View가 업데이트
mutations
- state를 변경하는 유일한 방법
- 일반적으로 commit을 통해서만 호출 할 수 있음
- Helper 함수로 직접 호출 가능
- 첫 번째 인자는 state, 두 번째 인자는 payload
store.commit({
type: 'increment',
amount: 10
})
/*-----------------------------*/
// store.js
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
- 주로 API를 통해 전달받은 데이터를 mutations에서 가공하여 state를 설정하는데 사용됨
actions
- 비동기 작업이 가능
- 그 외에는 mutation과 비슷
- mutation을 호출하기 위한 commit이 가능
- action에서도 mutation을 통해 state를 변경 가능
- dispatch를 통해 호출할 수 있음
- 첫 번째 인자는 context, 두 번째 인자는 payload
- context는 state, commit, dispatch, rootstate 속성들을 포함
store.dispatch('incrementAsync', {
amount: 10
})
// or
store.dispatch({
type: 'incrementAsync',
amount: 10
})
/*-----------------------------*/
// store.js
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
- 주로 axios를 통한 api 호출과 그 결과에 대해 return을 하거나 mutation으로 commit하는 용도로 사용
getters
- Vue의 computed와 같음 (계산된 속성)
- getter의 결과는 종속성에 따라 캐시 되고 변경된 경우에만 다시 계산
- state에 대해 연산을 하고 그 결과를 view에 바인딩 할 수 있음
- state의 변경 여부에 따라 다시 계산하고 view를 업데이트
modules
- 모듈별(기능 또는 페이지)로 store를 분리하고 관리 가능
- 실무에서는 단 하나의 store를 사용하기 힘듦
- 모듈화에서 추가 내용 참고
Vuex 전체 흐름도
- Vuex 또한 단방향 데이터 흐름을 가짐
- 실행 순서
- Vue Component에서 'dispatch([actions method name])'를 통해 action을 실행
(예를 들어, 버튼 클릭 등 이벤트 발생) - action에서는 외부 Api를 호출하는 등 비동기 로직을 처리
- 비동기 결과를 'commit([mutations method name])'를 통해 mutation을 실행
- mutation에서 state를 변경
- getter를 이용하여 다시 Vue Compoent에 바인딩되어 화면 갱신
- Vue Component에서 'dispatch([actions method name])'를 통해 action을 실행
사용 방법
설치 (NPM)
- vuex install :
npm install vuex
Vue.use()
통해 Vuex 명시적으로 추가
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
<script> 태그 사용 시, 해당 작업 필요 없음
- Promise install :
npm install es6-promise
- Vuex는 Promise 를 필요로 함
- 브라우저에서 Promise를 구현하지 않은 경우에만 설치하기
- Vuex 사용 전에
import 'es6-promise/auto'
를 아무 곳에나 추가하기
예제
- 폴더 구조
├── store
│ └── index.js
├── App.vue
└── main.js
- App.vue
<template>
<div id="app">
<div>
<label>{{ storeGetMsg }}</label>
<input type="number" v-model.number="num"/>
<button @click="onChangedMsg">PLUS</button>
</div>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
num: 1
}
},
computed: {
storeGetMsg () {
return this.$store.getters.getMsg
}
},
methods: {
onChangedMsg () {
this.$store.dispatch('callMutation', { num: this.num })
}
}
}
</script>
- store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
num: 0
},
mutations: {
changeNumber (state, val) {
state.num += val
}
},
actions: {
callMutation ({ state, commit }, { num }) {
commit('changeNumber', num)
}
},
getters: {
getMsg (state) {
return `calculated => ${state.num}`
}
}
})
- main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store/index'
Vue.config.productionTip = false
new Vue({
store: store,
render: h => h(App),
}).$mount('#app')
Vuex Helper 함수
- Vuex 기술 요소들을 컴포넌트에서 더 편하게 쓸 수 있도록 도와주는 API
- "this.$store.(속성명)"을 사용하지 않고 접근 가능하도록 도움
- "this"로 접근 가능
- Spread Operator (...)와 함께 사용 (여러 개의 객체를 하나로 통합)
종류
- state → mapState
- getters → mapGetters
- mutations → mapMutations
- actions → mapActions
예제
<template>
<div>
<!-- <p>{{ $store.state.num }}</p> -->
<p>{{ num }}</p>
<button @click="clickBtn">popup message</button>
<button @click="getPopup">show popup</button>
</div>
</template>
<script>
// App.vue
import { mapState, mapGetters, mapMutations, mapAcations } from 'vuex'
export default {
// this.$store.state.num == mapState(['num'])
// this.$store.getters.countedNum == mapGetters(['countedNum'])
computed() {
...mapState(['num']),
...mapGetters(['countedNum'])
},
methods: {
...mapMutations(['clickBtn', 'addNumber']),
...mapActions({
getPopup: 'showPopup' // 컴포넌트에서 쓸 명칭 : store의 action 명
})
}
}
</script>
Store 모듈화
사용 방법
- Store에 modules 속성에 직접 모듈 추가
- modules 라는 폴더 하위에 각 단위별로 분리한 모듈 파일 import하여 추가 가능
namespace
- 복잡한 앱을 개발할 때 getters & mutations & actions 의 이름을 유일하게 정하지 않으면 충돌 발생할 수 있음
- namespace 를 통해 충돌 방지 가능
- 네임스페이스를 구분하기 위해 types.js 로 각 속성의 이름들을 빼고 store.js 와 각 컴포넌트에 import 하여 사용하는 방법이 있음
- "namespaced: true" 로 설정하여 namespace 추가 가능
해당 모듈에 접근할 때 namespace 붙이기!
예제
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
import coffee from "./modules/groups";
export default new Vuex.Store({
state: () => {},
getters: {},
actions: {},
mutations: {},
modules: {
groups: groups,
guest: {
actions: {
login () { ... } // -> dispatch('login')
}
},
account: {
namespaced: true,
// module assets
state: () => ({ ... }), // module state is already nested and not affected by namespace option
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// nested modules
modules: {
// inherits the namespace from parent module
myPage: {
state: () => ({ ... }),
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// further nest the namespace
posts: {
namespaced: true,
state: () => ({ ... }),
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
정리
- Vuex는 Vue.js에서 사용하는 상태 관리 라이브러리
- 모든 데이터 통신을 한 곳에서 중앙 집중식으로 관리하는 패턴
- 모든 컴포넌트에서 state에 접근하거나 action을 실행할 수 있음
- 단순한 App에서 Vuex를 무작정 사용하는 것 보다 props&emit 또는 Event Bus를 사용하는 것이 좋음
그러나, 중대형 규모의 SPA를 구축하는 경우 Vuex를 통해 Vue컴포넌트 외부의 상태를 보다 잘 처리할 수 있음
참고
https://vuex.vuejs.org
https://vuejs.org/v2/guide/components.html
https://vuejs.org/v2/api/#v-model
https://bbosong-develop.tistory.com/3
https://kdydesign.github.io/2019/05/09/vuex-tutorial
https://dev-jsk.tistory.com/77
Author And Source
이 문제에 관하여([vue.js] Vuex 정리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yjyoo/vue.js-Vuex-정리저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)