모던웹(NEMV) 혼자 제작 하기 3기 - 49 뷰저장소(vuex) 이정도만 쓰자

2 분 소요

토큰 유무에 따라 로그인/아웃 상태를 전체 페이지에서 판단할 수 있는 뷰저장소에 대해 알아봅니다.

복잡해보이는 뷰저장소 기능 중 딱 한 기능만 배우고 그것만 써보겠습니다.

토큰 존재 유무로 페이지 바꿔보기

우측 상단 메뉴에 로그인은 토큰이 있을 때는 안보여야겠죠?

<v-list-tile v-if="localStorage.getItem('token')" @click="$router.push('/sign')">
  <v-list-tile-title>로그인</v-list-tile-title>
</v-list-tile>
<v-list-tile v-else @click="signOut">
  <v-list-tile-title>로그아웃</v-list-tile-title>
</v-list-tile>

이렇게 하면 될 것 같은데 안됩니다.

로컬스토리지는 뷰의 것이 아니기 때문에 에러가 납니다.

전역변수를 써도 마찬가지입니다.

sign.vue와 App.vue등 서로 다른 콤포넌트가 자원을 공유해서 변수를 사용하고 그 변수가 변경될 때 일반적인 바인딩 효과가 일어나지 않는 것입니다.

eg)sign.vue에서 변경한 변수를 App.vue DOM이 알아차리지 못하는 것 같습니다.

뷰저장소(vuex)

뷰 저장소에 대해 간단히 알아봅니다.

참고: https://vuex.vuejs.org/kr/

5가지가 있는데 쉽게 풀어보면

  • 상태: 전역 변수
  • Getter: 변수 조합용
  • 변이: 변수 변경
  • 액션: 비동기 변수 변경
  • 모듈: 여러개 사용할 때

더 깊은 뜻이 있지만 간단하게 풀어 본 것입니다.

이중 상태와 변이만 이용해서 전역변수를 사용해보겠습니다.

저장소 정의 하기

뷰컴3(vue-cli)로 프로젝트를 만들 때 vuex를 선택했기 때문에 fe/src/store.js가 골격이 만들어져 있습니다.

fe/src/store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    token: localStorage.getItem('token')
  },
  mutations: {
    getToken (state) {
      state.token = localStorage.getItem('token')
    },
    delToken (state) {
      localStorage.removeItem('token')
      state.token = null
    }
  },
  actions: {

  }
})
  • state에 전역으로 사용할 변수를 선언합니다. 초기값을 로컬스토리지값을 넣습니다.(새로고침해도 토큰값을 유지하기 위함)
  • mutations에 전역변수가 변이할 때 할 일들을 정의 해둡니다.

저장소 사용하기

읽을때

<template>
  <div>{{$store.state.token}}</div>
</template>
<script>
export default {
  mounted () {
    console.log(this.$store.state.token)
  }
}
</script>

쓸 때

export default {
  mounted () {
    this.$store.commit('getToken')
    this.$store.commit('delToken')
    // this.$store.commit('getToken', localStorage.getItem('token'))
  }
}

함수 명과 값을 넘겨 줄 수 있습니다.

위에 주석처리 해놓은 것처럼 값을 넘겨 사용할 수 있지만,

관리적인 측면에서 getToken은 공용으로 사용하기 때문에 매번 저렇게 쓰는 것 보다 변이 안에 넣는게 좋겠죠?

화면에 반영하기

메인 화면

fe/src/App.vue

<template>
  <v-app>
    <v-navigation-drawer
      persistent
      v-model="drawer"
      enable-resize-watcher
      fixed
      app
    >
      <v-list>
        <v-list-tile
          value="true"
          v-for="(item, i) in items"
          :key="i"
          :to="item.to"
        >
          <v-list-tile-action>
            <v-icon v-html="item.icon"></v-icon>
          </v-list-tile-action>
          <v-list-tile-content>
            <v-list-tile-title v-text="item.title"></v-list-tile-title>
          </v-list-tile-content>
        </v-list-tile>
      </v-list>
    </v-navigation-drawer>
    <v-toolbar
      app
    >
      <v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon>
      <v-toolbar-title v-text="title"></v-toolbar-title>
      <v-spacer></v-spacer>
      <v-toolbar-items>
        <v-menu bottom left>
          <v-btn icon slot="activator">
            <v-icon>more_vert</v-icon>
          </v-btn>
          <v-list>
            <v-list-tile v-if="!$store.state.token" @click="$router.push('sign')">
              <v-list-tile-title>로그인</v-list-tile-title>
            </v-list-tile>
            <v-list-tile v-else @click="signOut">
              <v-list-tile-title>로그아웃</v-list-tile-title>
            </v-list-tile>
          </v-list>
        </v-menu>
      </v-toolbar-items>
    </v-toolbar>
    <v-content>
      <router-view/>
    </v-content>
    <v-footer fixed app>
      <span>&copy; 2017 {{$store.state.token}}</span>
    </v-footer>
  </v-app>
</template>

<script>

export default {
  name: 'App',
  data () {
    return {
      drawer: null,
      items: [
        {
          icon: 'home',
          title: '홈aaa',
          to: {
            path: '/'
          }
        },
        {
          icon: 'face',
          title: '사용자관리',
          to: {
            path: '/user'
          }
        },
        {
          icon: 'face',
          title: 'header',
          to: {
            path: '/header'
          }
        }
      ],
      title: this.$apiRootPath
    }
  },
  mounted () {
  },
  methods: {
    signOut () {
      // localStorage.removeItem('token')
      this.$store.commit('delToken')
      this.$router.push('/')
    }
  }
}
</script>
  • v-if=”!$store.state.token” 으로 토큰이 없을때는 로그인 있을때는 로그아웃이 처리되었습니다.
  • 로그아웃에 delToken 변이를 호출해서 토큰 값을 지우는 것이 확인되었습니다.

로그인 화면

fe/src/views/sign.vue

signIn () {
  axios.post(`${this.$apiRootPath}sign/in`, this.form)
    .then(r => {
      if (!r.data.success) return console.error(r.data.msg)
      localStorage.setItem('token', r.data.token)
      this.$store.commit('getToken')
      this.$router.push('/')
      // location.href = '/header'
    })
    .catch(e => console.error(e.message))
}
  • 토큰을 받고 로컬스토리지에 써놓은 다음에 변이 getToken을 이용해 state.token을 갱신했습니다.

alt apply view

마치며

뮤테이션과 액션에 공용으로 사용할 함수들을 등록해 놓고 여러 콤포넌트에서 사용하면 복잡한 프로젝트에서 빛이 날 것 같습니다.

제 강좌를 보고 입사한 새내기 박군에게 뷰저장소를 어제 처음 배워봤는데요..

이제는 제가 제자네요~

제가 엄청 잘해서 이 강좌를 하고 있는 것이 아닙니다.

자료 생성 및 복습 겸 하고 있는 것이 제일 큰 이유죠.

그리고 하수도 강좌가 가능하고 하수가 만든 강좌가 조금 더 이해가 쉽지 않을까 하는 생각이 있는 것이죠..

영상

댓글남기기