(6) Vuex와 Store – 간단 실습

on

스크린샷 2018-05-12 오전 12.13.04

스크린샷 2018-05-12 오전 10.58.53

이것은 내가 요즘 만들고 있는 사이트의 첫 페이지의 모습이다. 이름은 My Gallery이고, 사이트의 시그니처인 고흐의 ‘아를의 침실’ 아래에 그리드 형식으로 15*2개의 세계적인 화가들의 그림이 랜덤하게 렌더링되며 각각의 그림이나 화가 이름을 클릭하면 그림을 더 크게 볼 수 있고, 화가에 대한 더 자세한 정보도 알 수 있는 구조이다. 척 보면 엄청 간단한데 사실 이만큼 하느라 애먹었다. ㅠㅠ 아직도 손 볼 구석이 많지만 일단은 완성이다.

나의 목표는 분홍색 툴 바에 적힌 My Gallery 글자가 페이지에 맞춰 변화하게 만드는 것이다. 첫 페이지에서는 My Gallery가 그대로 보이고, 서치 페이지로 넘어가면 Search로, 작품 페이지로 넘어가면 작품의 이름으로 바뀌었으면 좋겠다. 이것을 구현하기 위해서 공부해야 할 것은 vuex와 state management 그리고 store이다.

분홍색 툴 바는 MyGallery 프로젝트의 default.vue 파일에 다음과 같이 표현되어 있다. title이라고 적혀 있는 부분을 읽어보면 된다.

스크린샷 2018-05-12 오전 9.46.39

 

 

Vuex란 무엇인가? 이에 대해서는 Vue 공식 사이트에 자세한 설명이 나와 있다.

“Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. …”
(https://vuex.vuejs.org/en/intro.html#)

Vuex는 Vue에서 사용하는 라이브러리로, 애플리케이션의 모든 컴포넌트가 가지는 상태State를 미리 정해진 규칙 아래에서 변경할 수 있도록 스토어를 제공한다고 한다.

그렇다면 상태State란 뭘까? 다음은 같은 페이지의 상태 관리에 대한 설명이다.

new Vue({
  // state
  data () {
    return { count: 0 }
  }, 
  // view
  template: `
    
{{ count }}
`
, // actions methods: { increment () { this.count++ } } })

“It is a self-contained app with the following parts:

  • The state, which is the source of truth that drives our app;
  • The view, which is just a declarative mapping of the state;
  • The actions, which are the possible ways the state could change in reaction to user inputs from the view. …”

flow(https://vuex.vuejs.org/en/intro.html#)

즉, State는 애플리케이션에 사용되는 실제적인 데이터를 담고 있어 애플리케이션의 소스 역할을 하고, View는 상태를 화면에 어떻게 뿌리는지에 대한 데이터를, Action은 사용자의 행동에 따라 State를 어떻게 변화시킬 수 있는지에 대한 데이터를 담고 있다고 말할 수 있다. 그림에서 알 수 있듯 이 예제에서는 Action과 View, State가 간단한 one-way data flow를 이루고 있다.

그러나 모든 애플리케이션들이 다 이렇게 one-way data flow로 이루어져 있는 것은 아니다. View에 수많은 컴포넌트들이 있고, 그 컴포넌트들은 또 각자가 필요로 하는 여러 가지 상태를 이용하기 때문에 애플리케이션은 훨씬 더 복잡한 구조를 가지며 다양한 문제들이 발생한다. 다음에 자세한 설명이 있다.

“However, the simplicity quickly breaks down when we have multiple components that share common state:

  • Multiple views may depend on the same piece of state.
  • Actions from different views may need to mutate the same piece of state.

For problem one, passing props can be tedious for deeply nested components, and simply doesn’t work for sibling components. For problem two, we often find ourselves resorting to solutions such as reaching for direct parent/child instance references or trying to mutate and synchronize multiple copies of the state via events. Both of these patterns are brittle and quickly lead to unmaintainable code. …”
(https://vuex.vuejs.org/en/intro.html#)

문제는 한 화면에 존재하는 여러 개의 컴포넌트가 한 State에 의존적일 때 발생한다. State가 변했을 때 그에 의존적인 여러 개의 컴포넌트에 변화를 알려주기 위해서 복잡한 트리 구조를 파라미터 형식으로 줄줄이 타고 들어가는 건 아주 지루하고 비효율적인 방식이고, 그런 방식으로는 한 번에 두 개 이상의 컴포넌트를 변화시킬 수도 없다. instance reference를 사용하거나 이벤트를 발생시켜 여러 개의 State 복사본들을 동기화하는 방법도 안정적이지 못하기는 마찬가지라는 내용이다. 다음 문단에서 Store를 사용하는 해결책에 대한 내용을 읽어볼 수 있다.

“So why don’t we extract the shared state out of the components, and manage it in a global singleton? With this, our component tree becomes a big “view”, and any component can access the state or trigger actions, no matter where they are in the tree!

In addition, by defining and separating the concepts involved in state management and enforcing certain rules, we also give our code more structure and maintainability. …”
(https://vuex.vuejs.org/en/intro.html#)

결론적으로 모든 shared state, 즉 두 개 이상의 컴포넌트가 의존성을 갖는 State들을 모두 글로벌 스코프에서 사용할 수 있게 하여, 컴포넌트가 View 트리의 어느 가지에 있건 상관없이 State를 변화시키고 State의 변화를 읽을 수 있도록 하는 방법이 가장 이상적이다.

다음은 Store에 대한 설명이다.

“At the center of every Vuex application is the store. A “store” is basically a container that holds your application state. There are two things that make a Vuex store different from a plain global object:

  1. Vuex stores are reactive. When Vue components retrieve state from it, they will reactively and efficiently update if the store’s state changes.
  2. You cannot directly mutate the store’s state. The only way to change a store’s state is by explicitly committing mutations. This ensures every state change leaves a track-able record, and enables tooling that helps us better understand our applications. …”
    (https://vuex.vuejs.org/en/getting-started.html)

Store가 일반적인 글로벌 스코프 객체와 다른 점은 1) 반응형이라는 점, 2) 직접 State를 변화시킬 수 없고 반드시 mutation을 실행해야 한다는 점이다.

 

 

다음으로 실습을 해보았다. 실습이라고 해도 정말정말 간단한 예제이다! 어찌저찌 성공하기 전에 4시간 정도 뻘짓을 했는데, 어떻게 성공했는지 모르겠다ㅋㅋ. 시간을 투자한 의미가 있는건지 없는건지

실습은 Nuxt.js Korea 공식 사이트 Vuex 가이드와 소스코드를 참조하였다.
(https://ko.nuxtjs.org/guide/vuex-store/,
https://ko.nuxtjs.org/examples/vuex-store/)

스크린샷 2018-05-12 오후 1.56.21

실습 내용은 화면 중앙에 덩그러니 떠 있는 count의 옆의 숫자를 변경하는 것이다. 이전에 Vue를 처음 배울 때 이벤트 핸들링에서 v-on:click을 사용한 비슷한 예제를 다룬 적이 있는데(https://kr.vuejs.org/v2/guide/events.html) 지금은 Store을 활용하고 있기 때문에 그때와 겉보기만 비슷하지 실은 완전 다른 내용이다. count는 다음과 같이 노란색으로 하이라이트된 라인에 구현되어 있다.

스크린샷 2018-05-12 오후 1.24.08.png

 

스토어를 만들기 위해 프로젝트의 store 폴더에 따로 js파일을 만들었다. createStore 라는 객체는 Store를 리턴하는데, counter라는 state와 increment라는 mutation을 갖는다. 앞서 store와 글로벌 객체와의 다른 점을 설명하며 언급했듯 state를 변경하기 위해서는 반드시 mutation에 있는 함수를 써야만 한다. 나는 숫자를 늘리는 게 목표이므로 state.counter를 1씩 증가시키는 함수를 만들었다.

스크린샷 2018-05-12 오후 1.31.59.png

 

다음으로 vuex에서 mapState를 import한다. increment에 필요한 연산을 하기 위해 computed property에서 mapState 헬퍼를 사용하고, methods의 increment에서 store의 mutation을 불러올 수 있도록 함수를 구현한다.

스크린샷 2018-05-12 오후 1.36.13

스크린샷 2018-05-12 오후 1.38.44.png

 

끝! 이렇게 하면 화면의 count 부분을 클릭할 때마다 숫자가 1씩 증가한다.

스크린샷 2018-05-12 오후 1.58.04.png

하는 동안은 어려웠지만 막상 하고나니 너무너무 간단해 보인다. 포스트 머릿말에 툴바의 제목을 페이지별로 바꾸고 싶다고 적었는데 조금 더 공부하면 금방 할 수 있을 것 같다.
세상에 나보다 미리 공부한 사람들이 많아서 내가 조금만 찾으면 유용한 자료들을 많이 찾을 수 있는 게 매우 좋다. 내가 지금 쓰는 내용들도 나중에 누군가에게 도움이 될 수 있을까? 되도록이면 꾸준히 블로그를 쓰도록 노력해야겠다.

 

참조

Vuex 시작하기 1 – Vuex 와 State (https://joshua1988.github.io/web-development/vuejs/vuex-start/)
넉스트 공식 홈페이지 (https://ko.nuxtjs.org/)
Vuex 공식 홈페이지 (https://vuex.vuejs.org/)

One Comment Add yours

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s