diff --git a/src/App.jsx b/src/App.jsx index c5fbd3885cfd72d427bb4e9023b1fd80585a8896..e631707b3f801cf62427f2824fe8e220e081f86b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,10 +2,12 @@ import React from "react"; import { BrowserRouter as Router } from "react-router-dom"; import Layout from "./layout"; import AppRoutes from "./AppRoutes"; +import TokenExpirationChecker from './components/TokenExpirationChecker'; function App() { return ( <Router> + <TokenExpirationChecker /> <Layout> <AppRoutes /> </Layout> diff --git a/src/api/axios.js b/src/api/axios.js index f30c01fccefcf275092bf1d80fb326694353b097..bc48919784d7d401cd4881cd2df0f511890aba18 100644 --- a/src/api/axios.js +++ b/src/api/axios.js @@ -10,7 +10,19 @@ const api = axios.create({ api.interceptors.request.use(config => { const token = sessionStorage.getItem('accessToken'); + const expiresAt = sessionStorage.getItem('tokenExpiresAt'); + if (token) { + // 토큰 만료 체크 + if (expiresAt && Date.now() >= parseInt(expiresAt)) { + sessionStorage.removeItem('accessToken'); + sessionStorage.removeItem('tokenExpiresAt'); + if (!window.location.pathname.includes('/login')) { + alert('로그인이 만료되었습니다. 다시 로그인해주세요.'); + window.location.href = '/login'; + } + return Promise.reject('Token expired'); + } config.headers.Authorization = `Bearer ${token}`; } return config; @@ -19,9 +31,15 @@ api.interceptors.request.use(config => { api.interceptors.response.use( response => response, error => { + // 401 에러 처리 if (error.response?.data.statusCode === 401) { sessionStorage.removeItem('accessToken'); - window.location.href = '/login'; + sessionStorage.removeItem('tokenExpiresAt'); + + if (!window.location.pathname.includes('/login')) { + alert('로그인이 필요합니다. 로그인 페이지로 이동합니다.'); + window.location.href = '/login'; + } } return Promise.reject(error); } diff --git a/src/pages/LoginPage/LoginPage.jsx b/src/pages/LoginPage/LoginPage.jsx index 78b43a6fa9e1381630b9e518db5298464e21ac53..cdbde4fad7c715ace369766a49e5aeb6a184e6bc 100644 --- a/src/pages/LoginPage/LoginPage.jsx +++ b/src/pages/LoginPage/LoginPage.jsx @@ -15,6 +15,7 @@ const LoginPage = () => { const data = await axiosLogin({ email, password }); if (data && data.token) { sessionStorage.setItem('accessToken', data.token); + sessionStorage.setItem('tokenExpiresAt', data.expiresAt.toString()); setMessage(`로그인 성공!`); window.location.href = '/'; } else { diff --git a/src/pages/LoginPage/components/SignupModal.jsx b/src/pages/LoginPage/components/SignupModal.jsx index b910070d29d5a2c356d676ecd1ea61be99469191..0370ea0f46cfc529d13b353674c37b5103dc7990 100644 --- a/src/pages/LoginPage/components/SignupModal.jsx +++ b/src/pages/LoginPage/components/SignupModal.jsx @@ -158,11 +158,13 @@ const SignupModal = ({ onClose }) => { Object.values(formData).some(value => !value)) { return; } - + try { const result = await axiosSignup(formData); if (result) { sessionStorage.setItem('accessToken', result.token); + // 만료 시간도 저장 + sessionStorage.setItem('tokenExpiresAt', result.expiresAt.toString()); window.location.href = '/'; } } catch (error) {