vue와 vue-router, vuex 함께 사용하기


Vue의 컴패니언 라이브러리 (vue-router, vuex)

이 글은 vuex 와 vue-router를 적용하는 방법에 대해서 알아봅니다. 매우 간략한 내용만 다루고 있으므로 더 자세한 사용 방법에 대한 내용은 vuex 한국어 문서vue-router 한국어 문서를 읽어보세요.

vue-cli

vue-cli는 기본적으로 vue만 포함하고 있습니다. vuejs 템플릿 github 조직은 vue-cli의 템플릿을 간결하게 유지하고 싶기 때문에 vuex 또는 vue-router를 포함하려 하지 않고 있습니다. 이와 관련한 논의는 이 이슈이 이슈를 읽어보세요.

하지만 매번 vuex와 vue-router를 추가하는 것은 매우 까다로운 일이기 때문에 템플릿을 만들었습니다. 이 저장소를 살펴보세요. 간단한 사용법은 아래에 있습니다. 수정하려면 fork 하여 사용하시면 됩니다. 현재 standard와 airbnb 스타일을 모두 지원하고 있습니다.

$ npm install -g vue-cli
$ vue init changjoo-park/webpack my-project
$ cd my-project
$ npm install
$ npm run dev

자신만의 템플릿을 만드는 것은 사용자 정의 템플릿 작성해보기를 읽어보세요

vue-router 와 vuex

프로젝트 구조

위 템플릿으로 만든 my-project를 살펴보겠습니다.

폴더 구조

vue-cli로 만든 my-project 앱의 src 디렉터리의 구조입니다. 추가된 것은 routes.jsvuex 디렉터리입니다.

vue-router

main.js 를 살펴보겠습니다.

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import Router from 'vue-router'
import App from './App'
import routes from './routes'
// Styles
import './assets/styles/styles.scss'

Vue.use(Router)
const router = new Router({
  mode: 'history',
  scrollBehavior: () => ({ y: 0 }),
  routes
})

/* eslint-disable no-new */
new Vue({
  router,
  ...App
}).$mount('#app')

중요한 부분은 4번, 6번, 10번 ~ 15번, 19번 코드 입니다.

vue-router와 라우트에 대한 내용(routes.js)을 가져오고(4번, 6번), Vue에 vue-router를 사용할 것을 알려줍니다.(10번) 그리고 router 인스턴스를 만든 후(11번 ~ 15번) 이 것을 실제 사용할 Vue에 넣어 인스턴스를 만듭니다.(19번)

11번 ~ 15번 코드의 modescrollBehavior는 공식 문서의 HTML5 히스토리 모드스크롤 동작을 읽어보세요.

main.js 파일에서 살펴보지 않은 routes.js 파일을 살펴보겠습니다.

import HomePage from 'components/HomePage'
import CounterPage from 'components/CounterPage'

export default [
  {
    path: '/',
    name: 'home-page',
    component: HomePage
  },
  {
    path: '/counter',
    name: 'counter-page',
    component: CounterPage
  },
  {
    path: '*',
    redirect: '/'
  }
]

앞에서 설명하지 않았지만 changjoo-park/webpack 템플릿에는 HomePageCounterPage 컴포넌트가 포함되어 있습니다.

pathname 그리고 component를 지정해주면 해당 path에 맞는 컴포넌트를 화면에 그려줍니다. 지정하지 않은 path를 걸러내기 위해 맨 아래 라우트를 추가하였습니다. 자세한 내용은 공식 vue-router 가이드의 시작하기를 보시면 됩니다.

App.vue 단일 파일 컴포넌트의 템플릿을 보겠습니다.

<template>
  <div id="app">
    <ul class="navigation">
      <li><router-link v-bind:to="{ name: 'home-page' }">Home</router-link></li>
      <li><router-link v-bind:to="{ name: 'counter-page' }">Counter</router-link></li>
    </ul>
    <router-view></router-view>
  </div>
</template>

App.vue 템플릿에서 보셔야할 부분은 router-linkrouter-view 입니다. router-link는 라우트를 지정해주고, router-view는 지정된 라우트에 맞는 컴포넌트를 화면에 그려줍니다.

새 페이지를 추가하려면

새 페이지를 추가하는 방법은 다음 순서로 하시면 됩니다.

  • 새 페이지를 위한 컴포넌트를 만듭니다.
  • routes.js에 라우트를 정의해 줍니다.
  • 새로 만든 페이지로 이동할 수 있도록 router-link를 만듭니다.

vuex

vuex 는 앱 전체에서 공유할 수 있는 데이터센터 입니다. 부모 컴포넌트와 자식 컴포넌트 사이에 데이터를 주고 받을 때 불편함을 해소하는데 큰 역할을 합니다.

vuex 디렉터리 구조 입니다.

vuex의 자세한 내용은 vuex 시작하기를 읽고 보시면 더 이해가 쉽습니다. vuex의 main.js파일에 해당하는 store.js 부터 살펴보겠습니다.

import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import modules from './modules'

Vue.use(Vuex)

export default new Vuex.Store({
  actions,
  getters,
  modules,
  strict: true
})

vue-router를 추가하는 것과 동일한 흐름으로 이해하시면 됩니다. vuex 구조에서 actions, getters(state), modules(mutations)를 Vuex의 store에 추가하여 vuex인스턴스를 만드는 코드입니다.

vuex 구조

우선 mutation-types.js를 보겠습니다. vuex가 어떤 종류의 mutation을 정의한 파일입니다.

export const DECREMENT_MAIN_COUNTER = 'DECREMENT_MAIN_COUNTER'
export const INCREMENT_MAIN_COUNTER = 'INCREMENT_MAIN_COUNTER'

카운터를 증가시키고 감소시키는 조작만 있으면 됩니다.

이제 실제 조작에 대한 구현을 보겠습니다. counters.js 파일입니다. modules 디렉터리의 index.js는 modules에 있는 module을 export 해주는 유틸 스크립트입니다. 한번 살펴보세요.

import * as types from '../mutation-types'

const state = {
  main: 0
}

const mutations = {
  [types.DECREMENT_MAIN_COUNTER] (state) {
    state.main--
  },
  [types.INCREMENT_MAIN_COUNTER] (state) {
    state.main++
  }
}

export default {
  state,
  mutations
}

state가 있고, mutations가 있습니다. state는 실제 counter의 상태를 보관합니다. 그리고 mutations는 위에서 정의한 조작 요청이 오면 카운터를 증가 / 감소 시킵니다.

mutation을 실제로 발생시키려면 action이 필요합니다. action은 vue 컴포넌트에서 dispatch를 이용해 호출하는 메소드입니다.

import * as types from './mutation-types'

export const decrementMain = ({ commit }) => {
  commit(types.DECREMENT_MAIN_COUNTER)
}

export const incrementMain = ({ commit }) => {
  commit(types.INCREMENT_MAIN_COUNTER)
}

actions는 백엔드 API와 통신하는 역할을 가지지만 여기서는 백엔드가 없는 앱이므로 바로 mutation을 commit을 이용해 요청합니다.

이제 컴포넌트에서 사용할 상태를 호출하기 위한 getters(state) 를 살펴보겠습니다. 파일은 getters.js 입니다.

export const mainCounter = state => state.counters.main

템플릿에 포함된 애플리케이션은 Counter 앱에 대한 상태만 보관하므로 mainCounter라는 getter만 있습니다 .state의 counters의 main을 반환하는 역할을 합니다. 이 곳에 filtermap등을 이용해 원하는 내용만 사용할 수 있습니다.

vuex가 준비되었으므로 counter 앱을 만들어보겠습니다. 가장 먼저, App 컴포넌트에 vuex를 사용한다고 알려줘야 합니다.

App.vuescript 블럭입니다.

<script>
import store from 'src/vuex/store'

export default {
  store,
  name: 'app'
}
</script>

vuex의 store를 가져와 App 컴포넌트에 추가했습니다. 이제 앱의 어디든 this.$store를 이용해 vuex에 접근할 수 있습니다.

CounterPage.vue 파일에 카운터 앱이 있습니다. 따로 컴포넌트로 분리해 보세요. 템플릿을 만들기 쉽게하기 위해 CounterPage.vue에 넣은 것 입니다. 코드를 살펴보겠습니다.

템플릿 코드 입니다.

<div class="counter-number">
  
</div>
<button class="counter-button" v-on:click="incrementCounter">+</button>
<button class="counter-button" v-on:click="decrementCounter">-</button>

스크립트 코드입니다.

computed: {
  counter () {
    return this.$store.getters.mainCounter
  }
},
methods: {
  incrementCounter () {
    this.$store.dispatch('incrementMain')
  },
  decrementCounter () {
    this.$store.dispatch('decrementMain')
  }
}

computedgetters에 지정해 놓은 mainCounter를 가져왔습니다. 그리고 mehotds에는 actions에 추가한 액션들을 호출하는 코드가 있습니다. action을 실행하는 것은 dispatch입니다. 자세한 내용은 Vuex가이드의 액션을 읽어보세요.

새 모듈을 추가하려면

  • mutation-types.js 에 어떤 변이가 필요한지 추가하세요.
  • modules 디렉터리에 사용할 모듈 파일을 만들고, state와 mutations을 추가하세요.
  • actions.js에 mutation을 호출할 메소드를 만드세요.
  • getter.js에 컴포넌트에서 사용할 상태를 추가하세요.
  • 컴포넌트에서 computed에 상태를, methods에 액션을 호출할 dispatch를 추가하세요.

vuex의 가이드에 더 자세한 내용이 있습니다. 꼭 읽어보세요.



위 링크를 통해 후원을 받고 있습니다. 모든 후원금은 Vue.js 한국 커뮤니티 운영에 사용됩니다.