[Vue-DRF] Vuex로 로그인 상태 관리하기

JinUk Lee·2022년 12월 20일
0

vuex는 vue 상태(state) 관리를 위한 공식 라이브러리이다.

상태 관리라는 것이 조금 생소할 수 있는데, 간단하게 요약하자면 간이DB라고 생각한다.

Django를 사용하다가 프론트엔드로 넘어왔을때 가장 불편한 것은 DB의 존재였다.

예를 들어서 Django에서는 {article.user.userpk} 이런 식으로 DB를 활용한 호출이 가능했는데 vue에서는 백엔드 API에서 보내준 정보만 사용 가능한 것이 너무 불편했다.

DB가 없기 때문에 다른 페이지 간의 데이터를 공유할 수도 없는데, 이를 가능하게 해주는 것이 vuex 이다.

즉, 페이지간의 상태(state)라는 데이터를 공유하는 db를 만드는 라이브러리라고 할 수 있다.

앞선 포스팅에서 토큰을 헤더에 담긴 했으나 사용자가 로그인을 했는지 안했는지를 판단하는 것이 어려웠는데

이것을 vuex를 활용하여 간이DB에 로그인 여부, 로그인한 유저의 정보를 담아서 여러 페이지의 코드에서 활용할 것이다.

Vuex 시작하기

https://vuex.vuejs.org/

vue와 마찬가지로 vuex 또한 공식 문서가 상당히 잘되어 있어서 시간이 충분하다면 공식문서로 배우는 것이 가장 좋다고 생각한다.

밑에는 내가 속성으로 배운 내용이다.

1. 설치하기

npm install vuex --force

참고로 npm 패키지를 설치할때 옵션으로 --force--legacy-peer-deps 가 있는데 종속성 관련 옵션이며 더 자세한 것은 구글링을.. ( force 를 해보고 안되면 legacy 를 사용하는 것 같다.)

2. 구조 파악하기

vuex 의 구조도 버전에 따라 정말 다양하게 생겼는데, 내가 사용한 구조는 다음과 같다.

보통 store 폴더에서 상태를 관리하는데 이 store 폴더에 index.js 를 만들어서 사용할 모듈을 호출하고 각각의 모듈은 modules 폴더에 코드를 작성하여 index.js에서 호출하여 사용하는 식이다.

로그인 로직을 작성한 코드는 다음과 같다.

// store/modules/loginStore.js
import router from '@/router'
import axios from 'axios'
const loginStore = {
  state: {
    userInfo: null,
    isLogin: false,
  },
  mutations: { // 로그인 상태를 변경해주는 코드
    loginSuccess: function (state, payload) {
      state.userInfo = payload
      state.isLogin = true
    },
    logoutTest: function (state) {
      state.userInfo = null
      state.isLogin = false
      localStorage.removeItem('access_token')
      localStorage.removeItem('refresh_token')
      localStorage.removeItem('vuex')
    },
  },
  actions: {
    login (dispatch, loginObj) {
      axios
        .post('Login API URL', loginObj) // 로그인 URL로 ID, PW를 보냄
        .then((res) => {
          const token = res.data.access_token
          localStorage.setItem('access_token', token) // 토큰을 저장함
          const refretoken = res.data.refresh_token
          localStorage.setItem('refresh_token', refretoken) // 토큰을 저장함
          axios.defaults.headers.common.Authorization = `Bearer ${token}`
          this.dispatch('getMemberInfo') // 유저 정보를 가져오는 actions 호출
        })
    },
    logouttest_act ({ commit }) { // 로그아웃 actions
      commit('logoutTest')
      window.location.href="URL" // redirect할 URL 입력
    },
    getMemberInfo ({ commit }) { // 토큰으로 유저 정보를 받아오는 코드
      const token = localStorage.getItem('access_token') // 저장된 access 토큰을 가져옴
      const config = {
        headers: {
          Authorization: 'Bearer ' + token
        }
      }
      axios
        .get('User API URL', config) // 가져온 토큰을 헤더에 Authorization 로 담아서 요청을 보냄
        .then((response) => {
          const userInfo = {
            pk: response.data.pk,
            email: response.data.email,
          } // 유저 정보를 받아옴
          commit('loginSuccess', userInfo) // mutations 호출
        })
        .catch((err) => {
          alert('이메일과 비밀번호를 확인하세요.')
        })
    }
  }
}

export default loginStore

코드를 이해하기 위해선 state, mutations, actions부터 알아야 한다.

state

state는 상태를 정의하는 구간이다.

나는 로그인 상태 관리를 위해 userInfo, isLogin 라는 상태를 선언해주었다.

각각 로그인한 유저 정보를 담은 변수, 로그인 여부를 담은 변수이다.

mutations

mutations은 상태를 '변경' 하는 구간이다.

복잡한 로직이 있는 구간이 아니고 오직 상태를 '변경'해준다.

해당 코드에서는 로그인에 성공했을때 userInfo 에 유저 정보를 담고 isLogin = True 로 변경해주는 loginSuccess 와 로그아웃시 모든 상태를 초기화하고 로컬스토리지를 초기화한다.

actions

actions은 상태를 변경하기 위한 동작을 하는 구간이다.

직접적으로 상태를 변경하기보다는 어떠한 로직을 수행하고, 결과에 따른 mutations를 호출하는 역할이다.

앞선 포스팅에서 login 후 모든 과정을 methods에 작성했는데 이 과정을 actions으로 옮기고 해당 페이지에서는 actions를 호출하는 방식이다.

state, mutations, actions이 무엇인지 알면 이제 이 그림이 조금은 이해가 될 것이다.

3. 최종 코드

vuex를 반영하여 로그인 코드를 정리하자면 다음과 같다.

Login.Vue

## Login.Vue
<template>
  <div>
    <h1 class="form-title">로그인</h1>
    <div class="input-wrap">
        <label for="email">이메일</label>
        <input type="email" id="email" v-model="email" class="input-text"/>
    </div>
    <div class="input-wrap">
        <label for="password">패스워드</label>
        <input type="password" id="password" v-model="password" class="input-text"/>
    </div>
    <div style="max-width: 350px; margin: 0 auto;">
        <button @click="loginSubmit()" class="form-btn my-shadow">로그인</button>
    </div>
  </div>
</template>
  
<script>
import axios from 'axios'
export default {
  data () {
    return {
      email: null,
      password: null
      }
  },
  mounted() {
  },
  methods: {
    loginSubmit () {
      const saveData = {}
      saveData.email = this.email
      saveData.password = this.password
      this.$store.dispatch('login', saveData)
    },
}
}
</script>

store/modules/loginStore.js

// store/modules/loginStore.js
import router from '@/router'
import axios from 'axios'
const loginStore = {
  state: {
    userInfo: null,
    isLogin: false,
  },
  mutations: { // 로그인 상태를 변경해주는 코드
    loginSuccess: function (state, payload) {
      state.userInfo = payload
      state.isLogin = true
    },
    logoutTest: function (state) {
      state.userInfo = null
      state.isLogin = false
      localStorage.removeItem('access_token')
      localStorage.removeItem('refresh_token')
      localStorage.removeItem('vuex')
    },
  },
  actions: {
    login (dispatch, loginObj) {
      axios
        .post('Login API URL', loginObj) // 로그인 URL로 ID, PW를 보냄
        .then((res) => {
          const token = res.data.access_token
          localStorage.setItem('access_token', token) // 토큰을 저장함
          const refretoken = res.data.refresh_token
          localStorage.setItem('refresh_token', refretoken) // 토큰을 저장함
          axios.defaults.headers.common.Authorization = `Bearer ${token}`
          this.dispatch('getMemberInfo') // 유저 정보를 가져오는 actions 호출
        })
    },
    logouttest_act ({ commit }) { // 로그아웃 actions
      commit('logoutTest')
      window.location.href="URL" // redirect할 URL 입력
    },
    getMemberInfo ({ commit }) { // 토큰으로 유저 정보를 받아오는 코드
      const token = localStorage.getItem('access_token') // 저장된 access 토큰을 가져옴
      const config = {
        headers: {
          Authorization: 'Bearer ' + token
        }
      }
      axios
        .get('User API URL', config) // 가져온 토큰을 헤더에 Authorization 로 담아서 요청을 보냄
        .then((response) => {
          const userInfo = {
            pk: response.data.pk,
            email: response.data.email,
          } // 유저 정보를 받아옴
          commit('loginSuccess', userInfo) // mutations 호출
        })
        .catch((err) => {
          alert('이메일과 비밀번호를 확인하세요.')
        })
    }
  }
}

export default loginStore

store/index.js

import { createStore } from 'vuex'
import loginStore from './modules/loginStore'
export default createStore({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    loginStore
  },
})

이렇게 로그인 상태관리에 성공하면 loginStore.state.loginStore.userInfo 이런 식으로 상태를 호출하여 vue의 템플릿에서 변수로 활용하여 사용 가능하다.

그런데 문제는 새로고침 시 로그인이 풀려버린다.

이 문제는 다음 포스팅에서 다뤄보겠다.

참고

https://carrotweb.tistory.com/120

profile
개발자 지망생

0개의 댓글