학습 노트 조회
Today I Learned의 핵심 기능인 학습 노트 조회 기능이다. 로그인 시 발급된 인증(토큰) 정보를 이용하여 학습 노트 데이터 조회를 할 수 있다.
로그인 토큰 값 확인
로그인 시 발급 받는 토큰을 이용하여 학습 노트 데이터를 조회 할 수 있다.
// src/components/LoginForm.vue
methods: {
async submitForm() {
try {
// 비즈니스 로직
const userData = {
username: this.username,
password: this.password,
};
const { data } = await loginUser(userData);
// 토큰 확인
console.log(data.token);
this.$store.commit('setUsername', 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 = '';
},
},
스토어를 이용한 HTTP 헤더에 토큰 저장 및 활용
스토어를 이용하여 로그인 시 발급 받은 토큰을 HTTP Request Header
에 저장하여 인증 시 사용할 수 있도록 구현한다.
// src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
username: '',
// 토큰 저장할 변수
token: '',
},
getters: {
isLogin(state) {
return state.username !== '';
},
},
mutations: {
setUsername(state, username) {
state.username = username;
},
clearUsername(state) {
state.username = '';
},
// 토큰 저장할 함수
setToken(state, token) {
state.token = token;
},
},
});
// src/components/LoginForm.vue
methods: {
async submitForm() {
try {
// 비즈니스 로직
const userData = {
username: this.username,
password: this.password,
};
const { data } = await loginUser(userData);
console.log(data.token);
// 스토어를 이용하여 setToken 호출
this.$store.commit('setToken', data.token);
this.$store.commit('setUsername', 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/api/index.js
import axios from 'axios';
import store from '@/store/index';
const instance = axios.create({
baseURL: process.env.VUE_APP_API_URL,
// 헤더 설정
headers: {
// 스토어에 저장된 토큰 사용
Authorization: store.state.token,
},
});
토큰을 이용한 API 요청 시 의도치 않은 동작 확인
위처럼 스토어를 이용하여 저장된 토큰을 사용하여 API 요청을 하였다. 코드 상으로 보았을 때 정상 동작이 예상되었지만 결과를 보면 HTTP Request Header
내 Authorization
의 값이 비어있었다.
일단 현재 로직은 Axios를 create
하면서 Authorization
에 스토어에 저장된 토큰을 사용하도록 구성하고 create
된 Axios 인스턴스를 사용하여 로그인API를 호출한다. 자세히 보자면 토큰은 로그인이 된 후 API결과에 담겨있는데 그 전에 Axios 인스턴스에 토큰을 저장하면 빈 토큰을 저장하는 것이다.
그렇기 때문에 Axios Interceptor를 이용하여 특정 시점에 토큰 저장 처리를 해주어야 한다.
Axios Interceptor 사용
로그인 후 발급되는 토큰의 저장 시점을 제어하기 위해 Axios Interceptor를 사용한다.
Interceptor 모듈화
인터셉터 처리 코드를 효율적으로 관리하기 위해 모듈화를 진행한다.
// src/api/common/interceptors.js
export function setInterceptors(instance) {
// Add a request interceptor
instance.interceptors.request.use(
function(config) {
// Do something before request is sent
return config;
},
function(error) {
// Do something with request error
return Promise.reject(error);
},
);
// Add a response interceptor
instance.interceptors.response.use(
function(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
},
);
return instance;
}
Interceptor 연결
Axios 인스턴스에 Interceptor를 연결하여 시점 제어를 구현한다.
// src/api/index.js
import axios from 'axios';
import { setInterceptors } from './common/interceptors';
function createInstance() {
const instance = axios.create({
baseURL: process.env.VUE_APP_API_URL,
});
// Interceptor 사용
return setInterceptors(instance);
}
const instance = createInstance();
function registerUser(userData) {
return instance.post('signup', userData);
}
function loginUser(userData) {
return instance.post('login', userData);
}
export { registerUser, loginUser };
Axios Interceptor 사용하여 HTTP 헤더 설정
인터셉터를 사용하여 Request 요청 시 스토어에 저장되어 있는 토큰을 인터셉터 내 config headers
에 저장하여 이후 API 요청 마다 해당 토큰을 사용하여 인증처리하도록 구현한다.
// src/api/common/interceptors.js
import store from '@/store/index';
export function setInterceptors(instance) {
// Add a request interceptor
instance.interceptors.request.use(
function(config) {
// Do something before request is sent
// config.headers 에 스토어에 저장되어 있는 토큰 저장
config.headers.Authorization = store.state.token;
return config;
},
function(error) {
// Do something with request error
return Promise.reject(error);
},
);
// Add a response interceptor
instance.interceptors.response.use(
function(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
},
);
return instance;
}
학습 노트 조회 API 구현
학습 노트 데이터를 조회하기 위한 API 함수를 구현한다.
// src/api/index.js
import axios from 'axios';
import { setInterceptors } from './common/interceptors';
// axios 초기화 함수
function createInstance() {
const instance = axios.create({
baseURL: process.env.VUE_APP_API_URL,
});
return setInterceptors(instance);
}
const instance = createInstance();
// 학습 노트 데이터 조회 API
function fetchPosts() {
return instance.get('posts');
}
export { fetchPosts };
학습 노트 데이터 표시
Swagger API 문서를 통해 생성한 학습 노트 데이터를 바인딩하여 화면에 표시한다.
<!-- src/views/MainPage.vue -->
<template>
<div>
<div class="main list-container contents">
<h1 class="page-header">Today I Learned</h1>
<!-- 학습 노트 데이터 표시 -->
<ul>
<li v-for="postItem in postItems" :key="postItem._id">
<div class="post-title">
{{ postItem.title }}
</div>
<div class="post-contents">
{{ postItem.contents }}
</div>
<div class="post-time">
{{ postItem.createdAt }}
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
// 조회 API 함수 임포트
import { fetchPosts } from '@/api/index';
export default {
data() {
return {
postItems: [],
};
},
methods: {
async fetchData() {
const { data } = await fetchPosts();
this.postItems = data.posts;
},
},
// 라이프사이클 훅을 이용해 인스턴스가 작성된 후 호출
created() {
this.fetchData();
},
};
</script>
<style></style>
학습 노트 목록 아이템 컴포넌트화
학습 노트 데이터를 표시하는 부분을 컴포넌트화 하여 코드를 간결하게 할 수 있고 props
속성을 사용하여 구현할 수 있다. 추가적으로 데이터 로딩상태를 표시하는 스피너 컴포넌트를 등록하여 구현할 수 있다.
<!-- src/views/MainPage.vue -->
<template>
<div>
<div class="main list-container contents">
<h1 class="page-header">Today I Learned</h1>
<!-- Loading Spinner -->
<LoadingSpinner v-if="isLoading"></LoadingSpinner>
<ul v-else>
<!-- props 속성 사용 -->
<PostListItem
v-for="postItem in postItems"
:key="postItem._id"
:postItem="postItem"
></PostListItem>
</ul>
</div>
</div>
</template>
<script>
// 리스트 컴포넌트
import PostListItem from '@/components/posts/PostListItem.vue';
// 로딩바 컴포넌트
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
import { fetchPosts } from '@/api/index';
export default {
components: {
PostListItem,
LoadingSpinner,
},
data() {
return {
postItems: [],
// 로딩중 여부
isLoading: false,
};
},
methods: {
async fetchData() {
this.isLoading = true;
const { data } = await fetchPosts();
this.postItems = data.posts;
this.isLoading = false;
},
},
created() {
this.fetchData();
},
};
</script>
<!-- src/components/posts/PostListItem.vue -->
<template>
<li>
<div class="post-title">
{{ postItem.title }}
</div>
<div class="post-contents">
{{ postItem.contents }}
</div>
<div class="post-time">
{{ postItem.createdAt }}
</div>
</li>
</template>
<script>
export default {
// props 속성 사용
props: {
postItem: {
type: Object,
required: true,
},
},
};
</script>
'Dev > Vue.js' 카테고리의 다른 글
[Vue.js] Today I Learned - 학습 노트 생성 (0) | 2020.10.28 |
---|---|
[Vue.js] Today I Learned - 브라우저 저장소 (0) | 2020.10.28 |
[Vue.js] Today I Learned - 로그인 (0) | 2020.10.27 |
[Vue.js] Today I Learned - 공통 환경 설정 (0) | 2020.10.26 |
[Vue.js] Today I Learned - 회원 가입 (0) | 2020.10.26 |
댓글