본문 바로가기
Dev/Vue.js

[Vue.js] Today I Learned - 브라우저 저장소

by dev_jsk 2020. 10. 28.
728x90
반응형

브라우저 저장소

HTTP 쿠키와 웹 스토리지 등으로 구성되어 있으며 데이터를 서버가 아닌 클라이언트에 저장하는 것이다.

새로고침 시 문제

애플리케이션 로그인 후 새로고침 시 학습 노트 데이터가 조회되지 않는 문제가 있다. 이러한 문제가 발생하는 이유는 학습 노트 데이터 조회 API 호출 시 로그인 시 발급받은 인증 정보를 바탕으로 조회가 가능하기 때문에 새로고침 시 해당 인증정보가 비워지기 때문이다.

따라서 로그인시 발급받은 인증 정보를 브라우저 쿠키에 저장함으로서 새로고침시에도 문제없이 데이터 조회가 가능하도록 구현할 수 있다.

 

쿠키 저장 모듈

데이터를 쿠키에 저장하는 기능을 가진 모듈을 구현한다.

// src/utils/cookies.js
function saveAuthToCookie(value) {
  document.cookie = `til_auth=${value}`;
}

function saveUserToCookie(value) {
  document.cookie = `til_user=${value}`;
}

export {
  saveAuthToCookie,
  saveUserToCookie,
};

쿠키에 인증정보 저장

브라우저 저장소 내 쿠키에 로그인 시 발급된 인증정보를 저장하는 기능을 구현한다.

// src/components/LoginForm.vue
import { saveAuthToCookie, saveUserToCookie } from '@/utils/cookies';

export default {
  data() {
    return {
      // form values
      username: '',
      password: '',
      // log
      logMessage: '',
    };
  },
  methods: {
    async submitForm() {
      try {
        const userData = {
          username: this.username,
          password: this.password,
        };
        const { data } = await loginUser(userData);
        this.$store.commit('setToken', data.token);
        this.$store.commit('setUsername', data.user.username);
        // 브라우저 쿠키에 토큰 저장
        saveAuthToCookie(data.token);
        // 브라우저 쿠키에 사용자명 저장
        saveUserToCookie(data.user.username);
        this.$router.push('/main');
      } catch (error) {
        console.log(error.response.data);
        this.logMessage = error.response.data;
      } finally {
        this.initForm();
      }
    },
    initForm() {
      this.username = '';
      this.password = '';
    },
  },
};

// src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
// cookies util 사용
import { getAuthFromCookie, getUserFromCookie } from '@/utils/cookies';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    // 쿠키에 정보가 있으면 그 정보를 사용하고
    // 없으면 빈값처리
    username: getUserFromCookie() || '',
    token: getAuthFromCookie() || '',
  },
  getters: {
    isLogin(state) {
      return state.username !== '';
    },
  },
  mutations: {
    setUsername(state, username) {
      state.username = username;
    },
    clearUsername(state) {
      state.username = '';
    },
    setToken(state, token) {
      state.token = token;
    },
  },
});

actions 속성을 이용한 쿠키에 인증정보 저장

컴포넌트(LoginForm.vue)의 비지니스 로직 노출을 줄이기 위해 Vuex의 actions 속성을 사용하여 인증정보 저장 기능을 구현할 수 있다.

// src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import {
  saveAuthToCookie,
  saveUserToCookie,
  getAuthFromCookie,
  getUserFromCookie,
} from '@/utils/cookies';
import { loginUser } from '@/api/index';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    username: getUserFromCookie() || '',
    token: getAuthFromCookie() || '',
  },
  getters: {
    isLogin(state) {
      return state.username !== '';
    },
  },
  mutations: {
    setUsername(state, username) {
      state.username = username;
    },
    clearUsername(state) {
      state.username = '';
    },
    setToken(state, token) {
      state.token = token;
    },
  },
  // actions 속성 사용
  actions: {
    // 비동기 처리
    async LOGIN({ commit }, userData) {
      const { data } = await loginUser(userData);
      commit('setToken', data.token);
      commit('setUsername', data.user.username);
      // 브라우저 쿠키에 토큰 저장
      saveAuthToCookie(data.token);
      // 브라우저 쿠키에 사용자명 저장
      saveUserToCookie(data.user.username);

      return data;
    },
  },
});

// src/components/LoginForm.vue
export default {
  data() {
    return {
      username: '',
      password: '',
      logMessage: '',
    };
  },
  methods: {
    async submitForm() {
      try {
        const userData = {
          username: this.username,
          password: this.password,
        };
        // 비동기 처리가 끝나고 메인으로 이동해야 하기 때문에
        // await 붙여야 한다.
        // await를 제외하고 진행하면 받은 토큰을 스토어에 저장하기 전에
        // 메인에 진입하여 조회를 요청하였기 때문에 오류 발생
        await this.$store.dispatch('LOGIN', userData);
        this.$router.push('/main');
      } catch (error) {
        console.log(error.response.data);
        this.logMessage = error.response.data;
      } finally {
        this.initForm();
      }
    },
    initForm() {
      this.username = '';
      this.password = '';
    },
  },
};
728x90
반응형

댓글