본문 바로가기
개발/게시판 만들기

[Vue] Vue.js 게시판 만들기 14 - Vue Router 네비게이션 가드

by onethejay 2022. 6. 28.
728x90

지난 포스팅에서 JWT를 이용한 로그인을 구현하고 화면에서 로그인 성공 후 발급받은 토큰을 확인했습니다.
이번 포스팅에서는 게시판을 이용하려면 로그인이 필요하도록 화면을 수정해보겠습니다.

Vue Router 네비게이션 가드

Vue의 router/index.js에 포함되어있는 컴포넌트로 이동하려고 할때, Vue Router의 네비게이션 가드를 통해 특정 권한을 가지고 있는지 혹은 데이터를 가지고 있는지 확인하여 이동을 제어할 수 있습니다.
(https://v3.router.vuejs.org/kr/guide/advanced/navigation-guards.html)

네비게이션 가드의 종류는 3가지가 있습니다.

  1. 전역 가드
  2. 라우트 별 가드
  3. 컴포넌트 내부 가드

이번 포스팅에서 다뤄볼 가드는 라우트 별 가드입니다. 여러개의 라우트 중 게시판과 관련된 부분에만 적용하는 방식으로 진행하겠습니다.

컴포넌트 beforeEnter

컴포넌트 진입 전을 제어할 수 있는 beforeEnter 가드를 추가하고 권한을 확인하는 함수를 적용하도록 하겠습니다.

먼저 router/index.js에 requireAuth 함수를 생성합니다. 이 함수는 localStorage에 user_token이 저장되어 있는지 확인합니다.
생성한 함수를 게시판과 관련된 컴포넌트의 beforeEnter 가드에 추가합니다.

import {createRouter, createWebHistory} from 'vue-router'
import Home from '@/views/Home.vue'
import List from "@/views/board/List"
import Detail from "@/views/board/Detail"
import Write from "@/views/board/Write"
import Login from "@/views/common/Login";
import {store} from "@/vuex/store";

const requireAuth = () => (from, to, next) => {
  const token = localStorage.getItem('user_token')
  if (token) {
    store.state.isLogin = true
    return next()
  } // isLogin === true면 페이지 이동
  next('/login') // isLogin === false면 다시 로그인 화면으로 이동
}

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },
  {
    path: '/board/list',
    name: 'List',
    component: List,
    beforeEnter: requireAuth()
  },
  {
    path: '/board/detail',
    name: 'Detail',
    component: Detail,
    beforeEnter: requireAuth()
  },
  {
    path: '/board/write',
    name: 'Write',
    component: Write,
    beforeEnter: requireAuth()
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

백엔드와 프론트엔드 서버를 모두 실행하고 확인해봅니다.

게시글 목록을 가져오는데 실패하며 403 에러 코드를 리턴하고 있습니다.
이전 시간에 백엔드 서버에 세팅한 TokenRequestFilter 클래스에서 로그인을 제외한 다른 요청시에는 토큰을 확인하고 토큰이 없으면 403으로 응답하도록 작업했기 때문입니다.
그러므로 화면에서 서버로 요청하는 모든 데이터에는 토큰이 포함되어야 합니다.

해당 작업을 진행하기 위해 vue-frontboard 프로젝트에 utils 폴더를 생성하고 axios.js파일을 추가합니다.
전역에서 사용하는 axios의 request와 response를 커스터마이징 하는 소스를 구현합니다.

import axios from 'axios';

axios.interceptors.request.use(function (config) {
  const token = localStorage.getItem('user_token');
  config.headers.Authorization = "Bearer " + token;
  return config;
});

axios.interceptors.response.use(function (config) {
  return config
});

export default axios;

또한 vue-frontboard의 main.js에 추가되어있는 axios는 utils/axios를 사용하도록 수정해야 합니다.

import './assets/w3.css'
import './assets/common.css'

import {createApp} from 'vue'
import App from './App.vue'
import axios from './utils/axios'   //여기가 변경되었음
import router from './router'
import {store} from './vuex/store'

const app = createApp(App)
app.config.globalProperties.$axios = axios  //전역변수로 설정 컴포넌트에서 this.$axios.js 호출할 수 있음
app.config.globalProperties.$serverUrl = '//localhost:8081' //api server
app.config.globalProperties.$store = store
app
  .use(router)
  .use(store)
  .mount('#app')

수정이 완료되었다면 다시 호출하여 게시글 목록을 확인합니다.

로그인 상태 화면에 표시하기

우리가 사용하는 많은 웹사이트들은 로그인 되어있지 않으면 로그인 메뉴가 보이고, 로그인에 성공하였으면 로그아웃 및 내 정보 등이 표시됩니다.
현재 로그인 되어있다면 로그아웃 메뉴를 표시하고, 로그인 되어있지 않으면 로그인 메뉴를 출력하도록 화면을 수정하겠습니다.

먼저 layout/Header.vue 파일을 열고 template과 script의 내용을 수정합니다.

<template>
  <header>
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/board/list">게시판</router-link> |
      <router-link to="/login" v-if="!this.$store.state.isLogin">로그인</router-link>
      <a v-if="this.$store.state.isLogin" @click="fnLogout">로그아웃</a>
    </div>
  </header>
  <hr/>
</template>

<script>
export default {
  methods: {
    fnLogout() {
      localStorage.removeItem("user_token")
      localStorage.removeItem("user_role")
      location.reload()
    }
  }
}
</script>

현재 로그인 상태일 경우 로그인이 로그아웃으로 변경됩니다.

로그아웃을 클릭하면 다시 로그인 메뉴가 표시됩니다.

정리

이번 포스팅에서는 Vue Router의 네비게이션 가드를 적용하여 로그인되어있지 않을 경우 게시판 화면으로 접근이 불가능하도록 수정하였습니다.
또한, 로그인 상태에 따라 로그인 혹은 로그아웃 메뉴가 표시되도록 v-if를 적용하였습니다.

다음 포스팅에서는 API 요청, 응답시 로딩 이미지를 표시하는 LoadingBar를 구현하도록 하겠습니다.

728x90

댓글