Vue keep login state using token (with axios)

TL;DR

https://github.com/SilNex/vue-practice/commit/9f385a7571cf8d13504ac8f9fbc06b04059c9f06

https://github.com/SilNex/vue-practice/tree/master/vue-axios-login
Vue 강좌를 보며,
Vue-route, axios, Vuex를 이용해 만든 간단한 Login 페이지 이다.

다만 위에 프로젝트에서 한가지 문제가 있는데, 바로 /login 페이제서 로그인 후 tokenlocalstorage에 저장하여서 beforeEnter에 넣어 주었으나, 해당 부분이 실행되지 않고 로그인이 필요하는 메시지와 함께 /login으로 이동하게된다.
하지만 /login 페이지에선 잠시후 로그인이 완료되었다는 알림창이 표시된다.

즉, Login인 상태가 컴포넌트에서 컴포넌트론 유지가 되는데,
새로고침이나 혹은 /mypage에 직접적으로 접근시 state를 유지하지 못한다.(혹은 axios를 통해 정보를 가져오지 못한다.)

문제가 되는 동작 흐름도는 다음과 같다.
1. localstorage에 토큰이 저장되어 있지않으면?
/mypage 접근시 로그인이 필요하다는 창과 함께 로그인을 요청한다.
2. localstorage에 토큰이 저장되어 있지않으면?
/mypage 접근시 로그인이 필요하다는 창과 함께 로그인페이지로 이동한다.
하지만, 잠시후 로그인이 완료되었다는 메시지가 뜬다.
(axios로 token을 이용해 user정보를 가져왔다는 뜻)

Use promise

위의 증상을 격고 여러가지 테스트를 해본결과
onlyAuthUser이 함수안에서 dispatch로 axios를 통해 user 정보를 가져오려고 할 때 axios의 결과 처리를 기다리지 않고 이후 다음 프로세스로 진행하게 되면서 문제가 발생하는 것으로 발켜졌다.

이러한 증상에 대한 해결방법으로 javascript에 promise를 사용하여 해결할 수 있다.

먼저 dispatch에 getMemberInfo부분에

    getMemberInfo({ commit }) {
      return new Promise((resolve, reject) => {
        let token = localStorage.getItem("access_token")
        let userInfo = {}
        let config = {
          headers: {
            "access-token": token
          }
        }
        axios
          .get("https://reqres.in/api/users/2", config)
          .then(res => {
            userInfo = {
              id: res.data.data.id,
              first_name: res.data.data.first_name,
              last_name: res.data.data.last_name,
              avatar: res.data.data.avatar
            }
            commit("loginSuccess", userInfo)
            resolve()
          })
          .catch(err => {
            alert("이메일과 비밀번호를 확인해주세요")
            console.log(err)
            reject()
          })
      })
    }

위와 같이 axios를 받아오는 부분을 promise에 콜백 함수로 넣어 주고,
처리가 성공되는 부분에 resolve(), 실패하는 부분에 reject()이렇게 2가지를 추가한 뒤,

위의 dispatch 로직이 완전히 실행된 후 실행할 내용을 다음과 같이 then 구분에 넣어주면 된다.

const onlyAuthUser = (to, from, next) => {
  store.dispatch("getMemberInfo").then(() => {
    if (store.state.isLogin === false) {
      alert("로그인이 필요한 화면입니다.")
      next({ name: "login" })
    } else {
      next()
    }
  })
}
카테고리Vue

글의 문제가 있다면 댓글을 달아 주세요.

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.