항상 반복하는 작업.. 정리해놓겠다.
npm i -S http-proxy-middleware
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:8000',
changeOrigin: true,
pathRewrite: {
'^/api': '', // URL ^/server -> 공백 변경
},
})
);
};
/api를 엔드포인트로 놓는다. 알아서 setupProxy.js에서 설정한 서버 url로 변경된다. npm i -S axios
import axios from 'axios';
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
export default axios;
서버와 통신하는 (/api) 용도의 api들을 호출할 때 사용할 axios 객체를 생성한다.
import moment from 'moment';
import axios from './config';
import { refresh, refreshErrorHandle } from './refresh.ts';
import auth from './accountAPI';
const Api = axios.create({
timeout: 10000,
params: {},
});
Api.interceptors.request.use(refresh, refreshErrorHandle); // 요청 보내기 전 토큰 유효성 검사
Api.interceptors.response.use(
// 응답이 401에러인경우 refresh 후 다시 요청 수행해보기
(res) => res,
async (err) => {
const {
config,
response: { status },
} = err;
/** refresh 요청자체의 에러나 401 에러가 아닌경우는 refresh를 해야할 필요가 없으므로 에러를 그대로 reject */
if (String(config.url).includes('/accounts/dj-rest-auth/token/refresh/') || status !== 401 || config.sent) {
return Promise.reject(err);
}
/** refresh 요청이 끝나고 재요청을 보냈는데도 에러가 발생한 경우 재귀적으로 loop가 발생할 수 있기 때문에 이를 방지하기 위한 주석 2번 부분처럼 config.sent를 true로 설정*/
config.sent = true;
// refresh 요청
// TODO: refresh 중복요청 문제 해결 필요 (참고:https://gusrb3164.github.io/web/2022/08/07/refresh-with-axios-for-client/)
const { data } = await auth.refresh();
const token = data.access;
localStorage.setItem('access_token', token);
localStorage.setItem('access_expiration', moment().add(30, 'minute').format('yyyy-MM-DD HH:mm:ss'));
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return axios(config);
}
);
export default Api;
import isExpired from '@utils/isExpired';
import auth from './accountAPI';
const goToLogin = () => {
alert('로그인이 필요합니다.');
window.location.replace('/login');
};
const refresh = async (config) => {
const expireAt = localStorage.getItem('access_expiration');
let token = localStorage.getItem('access_token');
if (!expireAt) {
console.log(`accessToken doesn't exist`);
goToLogin();
return config;
}
if (isExpired('access_expiration')) {
console.log(`accessToken expired`);
goToLogin();
return config;
}
if (config.headers) {
config.headers.Authorization = `Bearer ${String(token)}`;
}
return config;
};
const refreshErrorHandle = (err) => {
console.log('refreshErrorHandle', err);
if (err !== null) {
auth.logout().catch((err) => {
console.error(err);
});
goToLogin();
}
};
export { refresh, refreshErrorHandle };
// 로그인
auth
.login({ username, password })
.then(res => {
console.log('login sucess', res);
localStorage.setItem('access_token', res.data.access);
localStorage.setItem(
'access_expiration',
moment().add(30, 'minute').format('yyyy-MM-DD HH:mm:ss'),
);
window.location.href = '/';
})
.catch(err => {
console.log(err);
if (err.response.status == 400) {
alert('유효하지 않은 회원정보입니다.');
}
});
import axios from './config';
import Api from './api';
export default {
idCheck(data) {
return axios.post('/api/accounts/id-availability-check', data);
},
signUp(data) {
return axios.post('/api/accounts/dj-rest-auth/registration', data);
},
login(data) {
return axios.post('/api/accounts/dj-rest-auth/login/', data);
},
logout(data) {
return Api.post('/api/accounts/dj-rest-auth/logout/', data);
},
getUser() {
return Api.get('/api/accounts/dj-rest-auth/user/');
},
refresh() {
return axios.post(`/api/accounts/dj-rest-auth/token/refresh/`);
},
};