This commit is contained in:
41
src/api/attachInterceptors.js
Normal file
41
src/api/attachInterceptors.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { refreshAccessToken } from './tokenUtils';
|
||||||
|
|
||||||
|
export const attachAuthInterceptors = (axiosInstance) => {
|
||||||
|
// 요청 시 access token 자동 첨부
|
||||||
|
axiosInstance.interceptors.request.use(config => {
|
||||||
|
const token = localStorage.getItem('access');
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 응답 시 access 만료 → refresh 재시도
|
||||||
|
axiosInstance.interceptors.response.use(
|
||||||
|
res => res,
|
||||||
|
async err => {
|
||||||
|
const originalRequest = err.config;
|
||||||
|
|
||||||
|
if (
|
||||||
|
err.response?.status === 401 &&
|
||||||
|
!originalRequest._retry &&
|
||||||
|
localStorage.getItem('refresh')
|
||||||
|
) {
|
||||||
|
originalRequest._retry = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newAccess = await refreshAccessToken();
|
||||||
|
originalRequest.headers.Authorization = `Bearer ${newAccess}`;
|
||||||
|
return axiosInstance(originalRequest);
|
||||||
|
} catch (refreshError) {
|
||||||
|
console.error('토큰 갱신 실패', refreshError);
|
||||||
|
localStorage.removeItem('access');
|
||||||
|
localStorage.removeItem('refresh');
|
||||||
|
window.location.href = '/login'; // 또는 useNavigate 사용 가능
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
@ -1,20 +1,12 @@
|
|||||||
// src/api/authApi.js
|
// src/api/authApi.js
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { attachAuthInterceptors } from './attachInterceptors';
|
||||||
|
|
||||||
const authApi = axios.create({
|
const authApi = axios.create({
|
||||||
baseURL: process.env.REACT_APP_API_AUTH,
|
baseURL: process.env.REACT_APP_API_AUTH,
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' }
|
||||||
'Content-Type': 'application/json',
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// JWT 자동 포함 (로그인 이후 /me 호출 등에 사용 가능)
|
attachAuthInterceptors(authApi);
|
||||||
authApi.interceptors.request.use(config => {
|
|
||||||
const token = localStorage.getItem('access');
|
|
||||||
if (token) {
|
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default authApi;
|
export default authApi;
|
||||||
|
@ -1,20 +1,12 @@
|
|||||||
// src/api/blogApi.js
|
// src/api/blogApi.js
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { attachAuthInterceptors } from './attachInterceptors';
|
||||||
|
|
||||||
const blogApi = axios.create({
|
const blogApi = axios.create({
|
||||||
baseURL: process.env.REACT_APP_API_BLOG,
|
baseURL: process.env.REACT_APP_API_BLOG,
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' }
|
||||||
'Content-Type': 'application/json',
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 게시글 작성 시에도 JWT 필요하므로 interceptor 동일하게 구성
|
attachAuthInterceptors(blogApi);
|
||||||
blogApi.interceptors.request.use(config => {
|
|
||||||
const token = localStorage.getItem('access');
|
|
||||||
if (token) {
|
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default blogApi;
|
export default blogApi;
|
||||||
|
@ -1,20 +1,12 @@
|
|||||||
// src/api/todoApi.js
|
// src/api/todoApi.js
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { attachAuthInterceptors } from './attachInterceptors';
|
||||||
|
|
||||||
const todoApi = axios.create({
|
const todoApi = axios.create({
|
||||||
baseURL: process.env.REACT_APP_API_TODO,
|
baseURL: process.env.REACT_APP_API_TODO,
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' }
|
||||||
'Content-Type': 'application/json',
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// JWT 자동 첨부
|
attachAuthInterceptors(todoApi);
|
||||||
todoApi.interceptors.request.use(config => {
|
|
||||||
const token = localStorage.getItem('access');
|
|
||||||
if (token) {
|
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default todoApi;
|
export default todoApi;
|
||||||
|
16
src/api/tokenUtils.js
Normal file
16
src/api/tokenUtils.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export const refreshAccessToken = async () => {
|
||||||
|
const refreshToken = localStorage.getItem('refresh');
|
||||||
|
|
||||||
|
if (!refreshToken) throw new Error('No refresh token');
|
||||||
|
|
||||||
|
const res = await axios.post(
|
||||||
|
`${process.env.REACT_APP_API_AUTH}/api/auth/token/refresh/`,
|
||||||
|
{ refresh: refreshToken }
|
||||||
|
);
|
||||||
|
|
||||||
|
const newAccess = res.data.access;
|
||||||
|
localStorage.setItem('access', newAccess);
|
||||||
|
return newAccess;
|
||||||
|
};
|
Reference in New Issue
Block a user