본문 바로가기
vue

[vue] vuex pathify를 사용한 메뉴바 구성하기(vuex pathify 사용법)

by devjh 2021. 10. 27.
반응형

vuex-pathify란

vue를 사용하다보면 컴포넌트간 데이터 교환을 해야하는 경우가 많습니다.

부모자식간의 통신의 경우 props emit을 활용하면 되지만

손자가 고조할아버지나 삼촌 사촌 등과 통신하는경우 props를 사용할 수는 없습니다.(vue 라이프사이클 이슈 포함: 부모 mount 이전에 자식들은 모두 create와 mount가 진행되어버립니다)

 

그럴때 사용하는것이 vuex입니다.

모든 데이터 통신을 한 곳에서(중앙 집중식) 관리할 수 있습니다.

 

(캡틴판교님의 vuex)

 

Vuex 시작하기 1 - Vuex와 State

Vue 중급으로 레벨업 하기. 상태 관리란 무엇인가? Vuex를 이용한 상태 관리. state 소개

joshua1988.github.io

그러나 vuex의 사용법을 숙지하고나면 

또 다른문제점에 직면하게 됩니다.

 

getter mutation action을 통해 state를 제어하다보면 

배보다 배꼽이 크다는 생각이 듭니다.

 

그럴 때  pathify를 사용하면 조금더 간편하게 데이터를 관리할 수 있습니다.

 

Vuex Pathify

Ridiculously simple + scalable Vuex setup + wiring

davestewart.github.io

vuex, pathify를 활용한 메뉴 바 구성예제입니다.

(https://github.com/jaeho310/vue-study)

 

1. 프로젝트 생성

$ vue create vue-study

vue2를 선택해서 프로젝트를 생성합니다.

 

2. 의존성 추가

$ yarn add vuetify vue-router vuex vuex-pathify vuex-router-sync

UI 컴포넌트를 가져오기 위한 vuetify

중앙 집중식 데이터 관리를 위한 vuex와 vuex를 편하게 쓰기위한 vuex-pathify

메뉴를 만들꺼니 vue-router, vuex-router-sync

 

 

3. vuetify적용

$ vue add vuetify

vuetify를 프로젝트에 적용해줍니다.

 

4. 디렉토리 생성

src 밑에

layouts
store
views
router
디렉토리를 생성 해줍니다.

 

5. plugins/vuex-pathify.js 파일 생성

import pathify from 'vuex-pathify'

// options
pathify.options.mapping = 'simple'
pathify.options.strict = true

export default pathify

 

공식문서에

In your store's entry point, setup your store, and activate the plugin:

하라고 나와있습니다.

 

플러그인 만들어줍니다.

 

 

6. main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import vuetify from './plugins/vuetify'
import store from './store'
import { sync } from 'vuex-router-sync'

Vue.config.productionTip = false

sync(store, router)

new Vue({
  router,
  vuetify,
  store,
  render: h => h(App),
}).$mount('#app')

main.js입니다.

/store과 /router의 index.js는 아래에서 만들겠습니다.

 

 

7. App.vue

<template>
  <v-app>
    <layout-index />
  </v-app>  
</template>

<script>
export default {
  name: 'App',
  components: {
    LayoutIndex: () => import(
        '@/layouts/LayoutIndex'
    ),
  },
};
</script>


컴포넌트는 layoutindex로부터 시작합니다.

 

 

8. layouts/LayoutIndex.vue

<template>
  <div>
    <core-header />
    <core-view />
  </div>
</template>
<script>

export default {
  name: "LayoutIndex",
  components: {
    CoreHeader: () => import(
      '@/layouts/Header'
    ),
    CoreView: () => import(
      '@/layouts/View'
    )
  },
}
</script>
<style>

</style>

 

header와 view로 나눠줍니다.

 

 

9. layouts/Header.vue

<template>
  <div>
    <v-app-bar
      color="blue lighten-2"
      dense
      dark
    >
      <v-menu
        right
        bottom
      >
        <template v-slot:activator="{ on, attrs }">
          <v-btn
            icon
            v-bind="attrs"
            v-on="on"
          >
            <v-icon>mdi-dots-vertical</v-icon>
          </v-btn>
        </template>

        <v-list>
          <v-list-item
            v-for="(item,idx) in items"
            :key="idx"
            :to="item.to"
          >
            <v-list-item-icon>
                <v-icon v-if="item.icon">{{ item.icon }}</v-icon>
            </v-list-item-icon>
            <v-list-item-title class="mr-15"> {{ item.title }}</v-list-item-title>
          </v-list-item>
        </v-list>
      </v-menu>
      <v-toolbar-title>Vue sample</v-toolbar-title>
    </v-app-bar>
  </div>
</template>

<script>
import { get } from 'vuex-pathify'
export default {
  computed: {
    items: get('app/items'),
  },
}
</script>

<style>
</style>

 

헤더의 메뉴는 drawer와 appbar로 이루어져있습니다.

vuex-pathify의 get을 이용합니다.
computed에서 items에 get('app/items')을 해주면 편리하게 vuex를 사용할 수 있습니다.

기존 vuex라면 state에 접근, 변경하기위해 
getter를 사용하지만 pathify를 사용하면 get이라는 메서드만 사용하시면 됩니다.

 

10. layouts/View.vue

<template>
  <router-view />
</template>
<script>
export default {

}
</script>
<style>

</style>

 

LayoutIndex에서 사용한 View.vue파일 입니다.

router view를 넣어줍니다

 

11. router/index.js

import Router from 'vue-router'
import Home from '@/views/Home'
import Test from '@/views/Test'
import Vue from 'vue'

Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: "Home",
      component: Home
    }
    ,{
      path: '/test',
      name: 'Test',
      component: Test
    }
  ]
})

router입니다.

/에는 Home.vue를

/test에는 Test.vue를

<router-view /> 에 연결시켜줍니다. 

 

12 store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import pathify from '@/plugins/vuex-pathify'

import * as modules from './modules'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules,
  plugins: [
    pathify.plugin,
  ],
})

export default store

vuex를 사용하기 위한 store 디렉토리 입니다.

plugin에 pathify의 플러그인을 추가해줍니다.

modules에 index.js를 import하여 사용합니다.

 

13. store/modules/index.js

export { default as app } from './app'

store 디렉토리 아래 modules 디렉토리를 생성해서
모듈을 쪼개 vuex를 사용합니다.

 

14. store/modules/app.js

import { make } from 'vuex-pathify'

const state = {
  items: [
    {
      title: "home",
      to: '/',
      icon: 'mdi-home'
    },
    {
      title: 'test',
      to : '/test',
      icon: 'mdi-ghost'
    },
  ]
}

const mutations = make.mutations(state)

const actions = {
  ...make.actions(state),
  init: async ({ dispatch }) => {
  },
}

const getters = {

}

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

해당방법으로 모듈을 나눠준 후

pathify를 사용하면 get("app/item")으로 state에 편하게 접근 가능합니다.

 

15. Home.vue

<template>
  <v-container tag="section">
    <h1>Home</h1>
  </v-container>
</template>

<script>
export default {
  name: "Home"
}
</script>

view-router에서 home 메뉴를 클릭하면 보여줄 vue 파일입니다.

 

16. Test.vue

<template>
    <v-card
      class="mx-auto mt-15"
      width="400"
    >
      <v-card-title>
        <span class="text-h5">메뉴 추가</span>
      </v-card-title>
      <v-card-text>
        <v-container>
          <v-row>
            <v-text-field
              v-model="inputValue"
              label="추가할 메뉴명을 입력해주세요"
            ></v-text-field>
          </v-row>
        </v-container>
      </v-card-text>

      <v-card-actions>
        <v-spacer></v-spacer>
        <v-btn
          color="blue darken-1"
          text
          @click="save"
        >
          저장
        </v-btn>
      </v-card-actions>
    </v-card>
</template>

<script>
import { sync } from 'vuex-pathify'
export default {
  name: "Test",
  data() {
    return {
      inputValue: '',
    }
  },
  methods: {
    save() {
      if (!this.inputValue) {
        alert('메뉴명을 입력해주세요')
        return
      }
      this.items.push({
          title: this.inputValue,
          to : '/',
          icon: 'mdi-account'
      })
      alert('메뉴가 추가되었습니다')
      this.inputValue = ''

    }
  },
  computed: {
    items: sync('app/items'),
  }
}
</script>


이번에는 store에 접근할때 sync를 사용합니다.

 

pathify의 sync는 양방향 바인딩이 되어있어 손쉽게 상태를 변경할 수 있습니다.(예제에서는 sync대신 set을 사용하여도 무방합니다)

 

get과의 차이는 get은 단순히 불러오는 용도지만 sync는 mutation을 포함하고있어서 값을 변경하기만 해도 알아서 commit 됩니다. v-model 같은 곳에 sync된 데이터를 넣어놓고 외부 컴포넌트에서 axios이후 해당 store에 set 해주면 데이터가 동기화됩니다.

 

17. 마치며..

pathify 예제가 부족한것 같아 vuex와 pathify를 통한 예제를 만들어봤습니다.

하지만 vuex를 사용하여 만든 메뉴바에는 치명적인 단점이 있습니다.

페이지를 새로고침하면 vuex에 있는 데이터는 모두 사라집니다.

vuex는 데이터 통신을 한곳에서 하는 용도이지 영속성을 책임지는 곳이 아닙니다.

 

새로고침시 데이터가 날아가는걸 막아주려면 api서버를 만들어 db에 넣어주거나,

vuex-persistedstate를 다운받고 createPersistedState와 paths 설정하여

브라우저 스토리지(웹 스토리지(localStorage, sessionStorage 등)에 저장하도록 해야 합니다. 

반응형

댓글