토큰 갱신 기능 추가
All checks were successful
Build And Test / build-and-push (push) Successful in 1m4s

This commit is contained in:
2025-05-20 00:33:12 +09:00
parent f5c261dfa8
commit 372670c3f3
6 changed files with 67 additions and 34 deletions

View 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);
}
);
};

View File

@ -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;

View File

@ -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;

View File

@ -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
View 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;
};

View File

@ -1 +1 @@
0.0.10-r1 0.0.11