Vue.js에서 인증
게시 됨: 2022-03-10인증은 사용자 데이터를 저장하는 애플리케이션에 매우 필요한 기능입니다. 권한이 없는 사용자가 다른 사용자의 데이터인 개인 데이터에 액세스할 수 없도록 하여 사용자의 신원을 확인하는 프로세스입니다. 이로 인해 인증된 사용자만 액세스할 수 있는 제한된 경로가 있습니다. 이러한 인증된 사용자는 로그인 세부 정보(예: 사용자 이름/이메일 및 암호)를 사용하고 응용 프로그램의 보호된 리소스에 액세스하는 데 사용할 토큰을 할당하여 확인됩니다.
이 문서에서는 다음에 대해 배울 것입니다.
- Axios를 사용한 Vuex 구성
- 경로 정의
- 사용자 처리
- 만료된 토큰 처리
종속성
인증에 도움이 되는 다음 종속성으로 작업할 것입니다.
- 악시오스
API에서 데이터 전송 및 검색 - 뷰엑스
API에서 가져온 데이터 저장용 - 뷰 라우터
항로의 탐색 및 보호를 위해
우리는 이러한 도구를 사용하여 함께 작업하여 앱에 강력한 인증 기능을 제공할 수 있는 방법을 알아볼 것입니다.
백엔드 API
우리는 이 API를 사용할 간단한 블로그 사이트를 구축할 것입니다. 문서를 확인하여 끝점과 요청을 보내는 방법을 확인할 수 있습니다.
문서에서 잠금으로 연결된 엔드포인트가 거의 없음을 알 수 있습니다. 이는 승인된 사용자만 해당 끝점에 요청을 보낼 수 있음을 표시하는 방법입니다. 무제한 엔드포인트는 /register
및 /login
엔드포인트입니다. 인증되지 않은 사용자가 제한된 끝점에 액세스하려고 하면 상태 코드 401
오류가 반환되어야 합니다.
사용자 로그인에 성공하면 일부 데이터와 함께 액세스 토큰이 Vue 앱에서 수신되며 쿠키 설정에 사용되며 향후 요청에 사용할 요청 헤더에 첨부됩니다. 백엔드는 제한된 엔드포인트에 대한 요청이 있을 때마다 요청 헤더를 확인합니다. 액세스 토큰을 로컬 저장소에 저장하려고 하지 마십시오.
비계 프로젝트
Vue CLI를 사용하여 아래 명령을 실행하여 애플리케이션을 생성합니다.
vue create auth-project
새 폴더로 이동합니다.
cd auth-project
vue-router를 추가하고 vuex 및 axios에 더 많은 종속성을 설치합니다.
vue add router npm install vuex axios
이제 프로젝트를 실행하면 브라우저에 아래와 같은 내용이 표시됩니다.
npm run serve
1. Axios를 사용한 Vuex 구성
Axios는 브라우저에서 API로 요청을 보내는 데 사용되는 JavaScript 라이브러리입니다. Vuex 문서에 따르면;
“Vuex는 Vue.js 애플리케이션을 위한 상태 관리 패턴 + 라이브러리 입니다. 상태가 예측 가능한 방식으로만 변경될 수 있도록 하는 규칙과 함께 애플리케이션의 모든 구성 요소에 대한 중앙 집중식 저장소 역할을 합니다.”
그게 무슨 뜻이야? Vuex는 모든 구성 요소에서 사용할 수 있는 데이터를 저장 하고 이러한 데이터를 변경할 수 있는 방법을 제공하는 Vue 응용 프로그램에서 사용되는 저장소입니다. Vuex에서 Axios를 사용하여 요청을 보내고 상태(데이터)를 변경합니다. Axios는 Vuex actions
에서 GET
및 POST
를 전송하는 데 사용되며, 받은 응답은 mutations
에 정보를 전송하고 스토어 데이터를 업데이트하는 데 사용됩니다.
새로 고침 후 Vuex 재설정을 처리하기 위해 페이지 다시 로드 사이에 Vuex 데이터를 저장하는 라이브러리 vuex-persistedstate
를 사용합니다.
npm install --save vuex-persistedstate
이제 Vuex 저장소를 구성하기 위해 src
에 새 폴더 store
를 생성해 보겠습니다. store
폴더에서 새 폴더를 만듭니다. modules
및 파일 index.js
. 폴더가 자동으로 생성되지 않는 경우에만 이 작업을 수행하면 됩니다.
import Vuex from 'vuex'; import Vue from 'vue'; import createPersistedState from "vuex-persistedstate"; import auth from './modules/auth'; // Load Vuex Vue.use(Vuex); // Create store export default new Vuex.Store({ modules: { auth }, plugins: [createPersistedState()] });
여기에서는 Vuex
를 사용하고 modules
폴더에서 스토어로 인증 module
을 가져옵니다.
모듈
모듈은 다음을 포함하여 유사한 작업을 함께 처리하는 스토어의 다른 부분입니다.
- 상태
- 행위
- 돌연변이
- 게터
계속 진행하기 전에 main.js
파일을 편집해 보겠습니다.
import Vue from 'vue' import App from './App.vue' import router from './router'; import store from './store'; import axios from 'axios'; axios.defaults.withCredentials = true axios.defaults.baseURL = 'https://gabbyblog.herokuapp.com/'; Vue.config.productionTip = false new Vue({ store, router, render: h => h(App) }).$mount('#app')
./store
폴더와 Axios 패키지에서 store
객체를 가져왔습니다.
앞서 언급했듯이 액세스 토큰 쿠키 및 API에서 가져온 기타 필요한 데이터는 향후 요청을 위해 요청 헤더에 설정해야 합니다. 요청을 할 때 Axios를 사용할 것이기 때문에 이것을 사용하도록 Axios를 구성해야 합니다. 위의 스니펫에서 axios.defaults.withCredentials = true
를 사용하여 이를 수행합니다. 기본적으로 Axios는 쿠키를 전달하지 않기 때문에 이 작업이 필요합니다.
aaxios.defaults.withCredentials = true
는 다음과 같은 자격 증명이 있는 모든 요청을 보내도록 Axios에 지시합니다. 인증 헤더, TLS 클라이언트 인증서 또는 쿠키(이 경우와 같이).
API
에 대한 Axios 요청에 대해 axios.defaults.baseURL
을 설정했습니다. 이렇게 하면 Axios를 통해 전송할 때마다 이 기본 URL을 사용합니다. 이를 통해 매번 전체 URL을 언급하지 않고 /register
및 /login
과 같은 끝점만 작업에 추가할 수 있습니다.
이제 store
의 modules
폴더 안에 auth.js
라는 파일을 만듭니다.
//store/modules/auth.js import axios from 'axios'; const state = { }; const getters = { }; const actions = { }; const mutations = { }; export default { state, getters, actions, mutations };
state
state
dict에서 데이터와 기본값을 정의할 것입니다.
const state = { user: null, posts: null, };
우리는 초기 값이 null
인 user
와 posts
을 포함하는 객체인 state
의 기본값을 설정하고 있습니다.
행위
작업은 상태를 변경하기 위해 돌연변이를 commit
하는 데 사용되거나 다른 작업을 호출하는 등 dispatch
하는 데 사용할 수 있는 함수입니다. 그것은 다른 컴포넌트나 뷰에서 호출될 수 있고 우리 상태의 돌연변이를 커밋할 수 있습니다.
액션 등록
Register
작업은 데이터 형식을 취하고 데이터를 /register
끝점으로 보내고 응답을 변수 response
에 할당합니다. 다음으로, login
작업에 양식 username
과 password
를 전달합니다. 이렇게 하면 사용자가 가입한 후 로그인하여 /posts
페이지로 리디렉션됩니다.
async Register({dispatch}, form) { await axios.post('register', form) let UserForm = new FormData() UserForm.append('username', form.username) UserForm.append('password', form.password) await dispatch('LogIn', UserForm) },
로그인 작업
여기에서 주요 인증이 발생합니다. 사용자가 사용자 이름과 암호를 입력하면 FormData 객체인 User
에게 전달되고, LogIn
함수는 User
객체를 가져와 /login
끝점에 POST
요청을 만들어 사용자 로그인을 수행합니다.
Login
함수는 마지막으로 username
을 setUser
변형에 커밋합니다.
async LogIn({commit}, User) { await axios.post('login', User) await commit('setUser', User.get('username')) },
게시물 작업 만들기
CreatePost
작업은 post
을 가져와 /post
엔드포인트로 보낸 다음 GetPosts
작업을 전달하는 함수입니다. 이를 통해 사용자는 생성 후 게시물을 볼 수 있습니다.
async CreatePost({dispatch}, post) { await axios.post('post', post) await dispatch('GetPosts') },
게시물 작업 가져오기
GetPosts
작업은 API의 게시물을 가져오기 위해 /posts
엔드포인트에 GET
요청을 보내고 setPosts
변형을 커밋합니다.
async GetPosts({ commit }){ let response = await axios.get('posts') commit('setPosts', response.data) },
로그아웃 작업
async LogOut({commit}){ let user = null commit('logout', user) }
LogOut
작업은 브라우저 캐시에서 user
를 제거합니다. 이는 logout
을 커밋하여 수행합니다.
돌연변이
const mutations = { setUser(state, username){ state.user = username }, setPosts(state, posts){ state.posts = posts }, LogOut(state){ state.user = null state.posts = null }, };
각 돌연변이는 Logout
을 제외하고 커밋하는 작업의 state
와 값을 가져옵니다. 얻은 값은 LogOut
에서 특정 부분 또는 전부를 변경하거나 모든 변수를 다시 null로 설정하는 데 사용됩니다.
게터
Getter는 상태를 가져오는 기능입니다. 현재 상태를 가져오기 위해 여러 구성 요소에서 사용할 수 있습니다. isAuthenticatated
함수는 state.user
가 정의되어 있는지 또는 null인지 확인하고 각각 true
또는 false
를 반환합니다. StatePosts
및 StateUser
는 각각 state.posts
및 state.user
값을 반환합니다.
const getters = { isAuthenticated: state => !!state.user, StatePosts: state => state.posts, StateUser: state => state.user, };
이제 전체 auth.js
파일이 GitHub의 내 코드와 유사해야 합니다.
구성 요소 설정
1. NavBar.vue
및 App.vue
구성 요소
src/components
폴더에서 HelloWorld.vue
와 NavBar.vue
라는 새 파일을 삭제합니다.
이것은 탐색 모음의 구성 요소이며 여기에서 라우팅된 구성 요소의 다른 페이지에 대한 링크입니다. 각 라우터 링크는 앱의 경로/페이지를 가리킵니다.
v-if="isLoggedIn"
은 사용자가 로그인한 경우 Logout
링크를 표시하고 Register
및 Login
경로를 숨기는 조건입니다. 로그인한 사용자만 액세스할 수 있는 logout
방법이 있습니다. 이 방법은 Logout
링크를 클릭할 때 호출됩니다. LogOut
작업을 전달한 다음 사용자를 로그인 페이지로 안내합니다.
<template> <div> <router-link to="/">Home</router-link> | <router-link to="/posts">Posts</router-link> | <span v-if="isLoggedIn"> <a @click="logout">Logout</a> </span> <span v-else> <router-link to="/register">Register</router-link> | <router-link to="/login">Login</router-link> </span> </div> </template> <script> export default { name: 'NavBar', computed : { isLoggedIn : function(){ return this.$store.getters.isAuthenticated} }, methods: { async logout (){ await this.$store.dispatch('LogOut') this.$router.push('/login') } }, } </script> <style> #nav { padding: 30px; } #nav a { font-weight: bold; color: #2c3e50; } a:hover { cursor: pointer; } #nav a.router-link-exact-active { color: #42b983; } </style>
이제 App.vue
구성 요소를 다음과 같이 편집합니다.
<template> <div> <NavBar /> <router-view/> </div> </template> <script> // @ is an alias to /src import NavBar from '@/components/NavBar.vue' export default { components: { NavBar } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } </style>
여기에서 위에서 생성하고 <router-view />
앞에 템플릿 섹션에 배치한 NavBar 구성 요소를 가져왔습니다.
2. 뷰 컴포넌트
보기 구성 요소는 경로 아래에 정의되고 탐색 모음에서 액세스할 수 있는 앱의 다른 페이지입니다. 시작하려면 views
폴더로 이동하여 About.vue
구성 요소를 삭제하고 다음 구성 요소를 추가합니다.
-
Home.vue
-
Register.vue
-
Login.vue
-
Posts.vue
Home.vue
Home.vue
를 다음과 같이 다시 작성하십시오.
<template> <div class="home"> <p>Heyyyyyy welcome to our blog, check out our posts</p> </div> </template> <script> export default { name: 'Home', components: { } } </script>
이렇게 하면 사용자가 홈페이지를 방문할 때 환영 텍스트가 표시됩니다.
Register.vue
이것은 사용자가 응용 프로그램에 가입할 수 있기를 바라는 페이지입니다. 사용자가 양식을 작성하면 해당 정보가 API로 전송되고 데이터베이스에 추가된 다음 로그인됩니다.
API를 보면 /register
엔드포인트에는 사용자의 username
, full_name
및 password
가 필요합니다. 이제 이러한 정보를 얻기 위한 페이지와 양식을 만들어 보겠습니다.
<template> <div class="register"> <div> <form @submit.prevent="submit"> <div> <label for="username">Username:</label> <input type="text" name="username" v-model="form.username"> </div> <div> <label for="full_name">Full Name:</label> <input type="text" name="full_name" v-model="form.full_name"> </div> <div> <label for="password">Password:</label> <input type="password" name="password" v-model="form.password"> </div> <button type="submit"> Submit</button> </form> </div> <p v-if="showError">Username already exists</p> </div> </template>
Register
구성 요소에서 양식 데이터를 수신할 Register
작업을 호출해야 합니다.
<script> import { mapActions } from "vuex"; export default { name: "Register", components: {}, data() { return { form: { username: "", full_name: "", password: "", }, showError: false }; }, methods: { ...mapActions(["Register"]), async submit() { try { await this.Register(this.form); this.$router.push("/posts"); this.showError = false } catch (error) { this.showError = true } }, }, }; </script>
mapActions
에서 mapActions를 가져오는 것으로 시작합니다. 이 작업은 스토어에서 구성 요소로 작업을 가져오는 것입니다. 이를 통해 구성 요소에서 작업을 호출할 수 있습니다.
data()
에는 이 구성 요소에서 사용할 로컬 상태 값이 포함되어 있으며 초기 값이 빈 문자열로 설정된 username
, full_name
및 password
를 포함하는 form
개체가 있습니다. 또한 오류를 표시하거나 표시하지 않는 데 사용되는 부울인 showError
가 있습니다.
methods
에서 Mapactions
를 사용하여 Register
작업을 구성 요소로 가져오므로 Register
작업을 this.Register
로 호출할 수 있습니다.
this.Register
를 사용하여 액세스할 수 있는 Register
작업을 호출하고 this.form
을 보내는 제출 메서드가 있습니다. error
가 발생하지 않으면 this.$router
를 사용하여 사용자를 로그인 페이지로 보냅니다. 그렇지 않으면 showError
를 true로 설정합니다.
그런 다음 몇 가지 스타일을 포함할 수 있습니다.
<style scoped> * { box-sizing: border-box; } label { padding: 12px 12px 12px 0; display: inline-block; } button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px; } button[type=submit]:hover { background-color: #45a049; } input { margin: 5px; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px; } #error { color: red; } </style>
Login.vue
로그인 페이지는 등록된 사용자가 API에 의해 인증을 받고 사이트에 로그인하기 위해 username
과 password
를 입력하는 곳입니다.
<template> <div class="login"> <div> <form @submit.prevent="submit"> <div> <label for="username">Username:</label> <input type="text" name="username" v-model="form.username" /> </div> <div> <label for="password">Password:</label> <input type="password" name="password" v-model="form.password" /> </div> <button type="submit">Submit</button> </form> <p v-if="showError">Username or Password is incorrect</p> </div> </div> </template>
이제 요청을 보내는 작업에 양식 데이터를 Posts
다음 보안 페이지로 푸시해야 합니다.
<script> import { mapActions } from "vuex"; export default { name: "Login", components: {}, data() { return { form: { username: "", password: "", }, showError: false }; }, methods: { ...mapActions(["LogIn"]), async submit() { const User = new FormData(); User.append("username", this.form.username); User.append("password", this.form.password); try { await this.LogIn(User); this.$router.push("/posts"); this.showError = false } catch (error) { this.showError = true } }, }, }; </script>
Mapactions
를 가져와서 submit
기능에서 사용할 구성 요소로 LogIn
작업을 가져오는 데 사용합니다.
Login
작업 후 사용자는 /posts
페이지로 리디렉션됩니다. 오류가 발생하면 오류가 포착되고 ShowError
가 true로 설정됩니다.
이제 몇 가지 스타일링:
<style scoped> * { box-sizing: border-box; } label { padding: 12px 12px 12px 0; display: inline-block; } button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px; } button[type=submit]:hover { background-color: #45a049; } input { margin: 5px; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px; } #error { color: red; } </style>
Posts.vue
당사 게시물 페이지는 인증된 사용자만 사용할 수 있는 보안 페이지입니다. 이 페이지에서 API 데이터베이스의 게시물에 액세스할 수 있습니다. 이를 통해 사용자는 게시물에 액세스할 수 있고 API에 대한 게시물을 작성할 수도 있습니다.
<template> <div class="posts"> <div v-if="User"> <p>Hi {{User}}</p> </div> <div> <form @submit.prevent="submit"> <div> <label for="title">Title:</label> <input type="text" name="title" v-model="form.title"> </div> <div> <textarea name="write_up" v-model="form.write_up" placeholder="Write up..."></textarea> </div> <button type="submit"> Submit</button> </form> </div> <div class="posts" v-if="Posts"> <ul> <li v-for="post in Posts" :key="post.id"> <div> <p>{{post.title}}</p> <p>{{post.write_up}}</p> <p>Written By: {{post.author.username}}</p> </div> </li> </ul> </div> <div v-else> Oh no!!! We have no posts </div> </div> </template>
위의 코드에는 사용자가 새 게시물을 작성할 수 있는 양식이 있습니다. 양식을 제출하면 게시물이 API로 전송되어야 합니다. 곧 이를 수행하는 메서드를 추가할 것입니다. 또한 API에서 가져온 게시물을 표시하는 섹션이 있습니다(사용자에게 게시물이 있는 경우). 사용자에게 게시물이 없으면 단순히 게시물이 없다는 메시지를 표시합니다.
StateUser
및 StatePosts
getter는 매핑됩니다. 즉, Posts.vue
를 사용하여 mapGetters
로 가져온 다음 템플릿에서 호출할 수 있습니다.
<script> import { mapGetters, mapActions } from "vuex"; export default { name: 'Posts', components: { }, data() { return { form: { title: '', write_up: '', } }; }, created: function () { // a function to call getposts action this.GetPosts() }, computed: { ...mapGetters({Posts: "StatePosts", User: "StateUser"}), }, methods: { ...mapActions(["CreatePost", "GetPosts"]), async submit() { try { await this.CreatePost(this.form); } catch (error) { throw "Sorry you can't make a post now!" } }, } }; </script>
title
과 write_up
을 키로 갖고 값이 빈 문자열로 설정된 객체인 form
에 대한 초기 상태가 있습니다. 이 값은 사용자가 구성 요소의 템플릿 섹션에 있는 양식에 입력하는 모든 것으로 변경됩니다.
사용자가 게시물을 제출하면 양식 개체를 수신하는 this.CreatePost
를 호출합니다.
created
수명 주기에서 볼 수 있듯이 구성 요소가 생성될 때 게시물을 가져오는 this.GetPosts
가 있습니다.
약간의 스타일링,
<style scoped> * { box-sizing: border-box; } label { padding: 12px 12px 12px 0; display: inline-block; } button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px; margin: 10px; } button[type=submit]:hover { background-color: #45a049; } input { width:60%; margin: 15px; border: 0; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px; } textarea { width:75%; resize: vertical; padding:15px; border-radius:15px; border:0; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); height:150px; margin: 15px; } ul { list-style: none; } #post-div { border: 3px solid #000; width: 500px; margin: auto; margin-bottom: 5px;; } </style>
2. 경로 정의
router/index.js
파일에서 보기를 가져오고 각각에 대한 경로를 정의합니다.
import Vue from 'vue' import VueRouter from 'vue-router' import store from '../store'; import Home from '../views/Home.vue' import Register from '../views/Register' import Login from '../views/Login' import Posts from '../views/Posts' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/register', name: "Register", component: Register, meta: { guest: true }, }, { path: '/login', name: "Login", component: Login, meta: { guest: true }, }, { path: '/posts', name: Posts, component: Posts, meta: {requiresAuth: true}, } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
3. 사용자 처리
- 승인되지 않은 사용자
게시물 경로를 정의할 때 사용자가 인증되어야 함을 나타내기 위해meta
키를 추가한 것을 발견했다면 이제router.BeforeEach
탐색 가드가 경로에meta: {requiresAuth: true}
키가 있는지 확인해야 합니다. 경로에meta
키가 있으면 저장소에서 토큰을 확인합니다. 존재하는 경우login
경로로 리디렉션합니다.
const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) router.beforeEach((to, from, next) => { if(to.matched.some(record => record.meta.requiresAuth)) { if (store.getters.isAuthenticated) { next() return } next('/login') } else { next() } }) export default router
- 승인된 사용자
/register
및/login
경로에 대한meta
도 있습니다.meta: {guest: true}
는 로그인한 사용자가guest
메타로 경로에 액세스하는 것을 중지합니다.
router.beforeEach((to, from, next) => { if (to.matched.some((record) => record.meta.guest)) { if (store.getters.isAuthenticated) { next("/posts"); return; } next(); } else { next(); } });
결국 파일은 다음과 같아야 합니다.
import Vue from "vue"; import VueRouter from "vue-router"; import store from "../store"; import Home from "../views/Home.vue"; import Register from "../views/Register"; import Login from "../views/Login"; import Posts from "../views/Posts"; Vue.use(VueRouter); const routes = [ { path: "/", name: "Home", component: Home, }, { path: "/register", name: "Register", component: Register, meta: { guest: true }, }, { path: "/login", name: "Login", component: Login, meta: { guest: true }, }, { path: "/posts", name: "Posts", component: Posts, meta: { requiresAuth: true }, }, ]; const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes, }); router.beforeEach((to, from, next) => { if (to.matched.some((record) => record.meta.requiresAuth)) { if (store.getters.isAuthenticated) { next(); return; } next("/login"); } else { next(); } }); router.beforeEach((to, from, next) => { if (to.matched.some((record) => record.meta.guest)) { if (store.getters.isAuthenticated) { next("/posts"); return; } next(); } else { next(); } }); export default router;
4. 만료된 토큰 처리(금지된 요청)
API는 30분 후에 토큰이 만료되도록 설정되어 있습니다. 이제 30분 후에 posts
페이지에 액세스하려고 하면 401
오류가 발생합니다. 즉, 다시 로그인해야 합니다. 401
오류가 발생하면 login
페이지로 다시 리디렉션됩니다.
main.js
파일에서 Axios 기본 URL 선언 뒤에 아래 스니펫을 추가하세요.
axios.interceptors.response.use(undefined, function (error) { if (error) { const originalRequest = error.config; if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; store.dispatch('LogOut') return router.push('/login') } } })
이렇게 하면 코드가 GitHub의 예제와 동일한 상태가 됩니다.
결론
끝까지 따라왔다면 이제 완벽하게 작동하고 안전한 프런트 엔드 애플리케이션을 구축할 수 있을 것입니다. 이제 Vuex와 Axios를 통합하는 방법, 다시 로드한 후 데이터를 저장하는 방법에 대해 자세히 배웠습니다.
코드는 GitHub에서 사용할 수 있습니다 →
호스팅 사이트:
https://nifty-hopper-1e9895.netlify.app/
API:
https://gabbyblog.herokuapp.com
API 문서:
https://gabbyblog.herokuapp.com/docs
자원
- "Axios로 쿠키 처리", Aditya Srivastava, Medium
- "Vue에서 인증 탐색 가드 만들기", Laurie Barth, Ten Mile Square 블로그
- "Vuex 시작하기", 공식 가이드
- "Vuex 및 Vue 라우터를 사용한 Vue.js JWT 인증", BezKoder