Vue.js의 컴포넌트와 컴포넌트간 통신 방법을 알고 있다는 전제하에 시작한다.
Vue.js의 상태 관리를 위한 패턴이자 라이브러리.
다른 상태 관리 패턴이나 라이브러리와 비교했을 때 뷰의 반응성 체계를 효율적으로 활용하여 화면을 업데이트 한다는 차이점이 있다.
상태 관리란, 여러 컴포넌트 간의 데이터 전달과 이벤트 통신을 한 곳에서 관리하는 패턴을 의미한다.
상태 관리는 중대형 규모의 웹 애플리케이션에서 컴포넌트 간에 데이터를 더 효율적으로 전달할 수 있다.
일반적으로 앱의 규모가 커지면서 생기는 문제점들은 아래와 같다.
이러한 문제점을 해결하기 위해 모든 데이터 통신을 한 곳에서 중앙 집중식으로 관리하는 것이 상태 관리다.
[상태 관리 구성요소]
new Vue({
// state = > 공유할 데이터
data() {
return {
counter: 0
};
},
// view
template: `
<div>{{ counter }}</div>
`,
// actions
methods: {
increment() {
this.counter++;
}
}
});
위 구성요소는 아래와 같은 단방향 흐름으로 동작한다.
뷰엑스를 알아보기 위해 버튼으로 숫자를 늘리고 줄일 수 있는 카운터 앱을 만들어보자. Vue CLI로 프로젝트를 생성한 다음 아래와 같이 Parent, Child 컴포넌트를 제작한다.
<!-- App.vue(parent) -->
<template>
<div id="app">
Parent counter : {{ counter }} <br>
<button @click="addCounter">+</button>
<button @click="subCounter">-</button>
<!-- Child 컴포넌트를 등록하고 counter 데이터 속성을 props로 전달한다. -->
<child v-bind:num="counter"></child>
</div>
</template>
<script>
// chilc 컴포넌트 import
import Child from "./Child.vue";
export default {
components: {
// child 컴포넌트를 App.vue의 하위 컴포넌트로 등록
child : Child
},
// data 등록
data(){
return {
counter: 0
};
},
methods: {
// 이벤트 추가
addCounter(){
this.counter++;
},
subCounter(){
this.counter--;
}
}
};
</script>
<template>
<div>
<hr>
Child counter : {{ num }} <br>
<button>+</button>
<button>-</button>
</div>
</template>
<script>
export default {
// 상위 컴포넌트에서 내려준 counter 속성을 num으로 받음
props: ["num"]
};
</script>
하위 컴포넌트인 Child.vue는 App.vue에게서 props 속성으로 counter를 받는다.
그래서, 결국은 App.vue의 +를 누르거나 -를 눌러도 child.vue의 counter도 같은 값을 가지게 된다.
다르게 말해, 같은 데이터 속성을 2개의 컴포넌트에서 접근하여 같은 값을 표현하고 있다는 것이다. 이 구조는 뷰의 props 속성을 이용한 기본적인 컴포넌트 통신 방법이다.
이 비효율적인 컴포넌트 간 데이터 전달 방식을 Vuex로 해결할 수 있다!:mask:
npm install vuex
.js
파일 새로 생성. 이름은 관례에 따라 store.js
로 지정// store.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export const store = new Vuex.Store({
// ...
});
main.js
파일로 가서 store.js
를 불러와 등록한다.import Vue from "vue";
import App from "./App.vue";
import router from "./router";
// store.js를 불러오는 코드
import { store } from "./store.js";
Vue.config.productionTip = false;
new Vue({
// 뷰 인스턴스의 store 속성에 연결
store: store,
router,
render: h => h(App)
}).$mount("#app");
state를 vuex에 추가한다.
// store.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export const store = new Vuex.Store({
// counter 라는 state 속성을 추가
state: {
counter: 0
}
});
state에 정의된 counter
속성은 Parent 컴포넌트에서 사용하던 data 속성 counter
와 동일한 역할을 합니다. 이미 앞 상태 관리 패턴에서 설명했듯이, state는 컴포넌트 간에 공유할 data 속성을 의미한다.
vuex의 state에 등록한 counter 속성은 컴포넌트 템플릿 코드에서 $store.state.counter
로 접근할 수 있다.
<!-- App.vue(parent) -->
!<template>
<div id="app">
Parent counter : {{ $store.state.counter }} <br>
<button @click="addCounter">+</button>
<button @click="subCounter">-</button>
<!-- 기존코드 -->
<!-- Child 컴포넌트를 등록하고 counter 데이터 속성을 props로 전달한다. -->
<!-- <child v-bind:num="counter"></child> -->
<child></child>
</div>
</template>
<script>
// chilc 컴포넌트 import
import Child from "./Child.vue";
export default {
components: {
// child 컴포넌트를 App.vue의 하위 컴포넌트로 등록
child : Child
},
/* 기존코드
// data 등록
data(){
return {
counter: 0
};
}, */
methods: {
addCounter(){
//this.counter++;
this.$store.state.counter++;
},
subCounter(){
//this.counter--;
this.$store.state.counter--;
}
}
};
</script>
위 코드는 기존 App.vue 코드와 2가지가 다르다.
counter
값 제거counter
를 전달하지 않음 (props 사용 x)결국 Parent 컴포넌트에서 관리하던 counter
데이터를 Vuex의 state에 넘겨줬다.
Child 컴포넌트에서 접근하던 Parent 컴포넌트의 data 속성이 Vuex로 갔기 때문에 이제 Child에서는 Vuex의 state를 바라보면 된다.
이제 Parent와 Child 모두 state에 접근할 수 있게 된 것. 이외에 모든 컴포넌트는 state에 접근을 할 수 있다.
Vuex를 사용하고 나서도, Parent 컴포넌트의 + 버튼을 눌렀을 때 동일하게 동작하는 것을 볼 수 있다.
동일하게 Child 컴포넌트에도 뷰엑스 적용해보자.
!<template>
<div>
<hr>
Child counter : {{ $store.state.counter }} <br>
<button>+</button>
<button>-</button>
</div>
</template>
<script>
export default {
// 상위 컴포넌트에서 내려준 counter 속성을 num으로 받음
/* 기존 코드
props: ["num"]
*/
};
</script>
Parent 컴포넌트에서 props 속성으로 counter
를 전달받던 방식에서 Vuex의 state인 counter를 바로 접근하는 방식으로 변경했다.
📕 REFERENCE: [캡틴판교] https://joshua1988.github.io/web-development/vuejs/vuex-start/