diff --git a/src/apis/domains/users/queries.ts b/src/apis/domains/users/queries.ts index 18c842a8..d21ffea9 100644 --- a/src/apis/domains/users/queries.ts +++ b/src/apis/domains/users/queries.ts @@ -23,10 +23,10 @@ export const usePostKakaoLogin = () => { const userData = response; if (userData) { - const { accessToken, nickname, role } = userData; + const { accessToken, nickname, refreshToken, role } = userData; if (accessToken && nickname) { - setUserData({ nickname, accessToken, role }); + setUserData({ nickname, accessToken, refreshToken, role }); } else { console.error("accessToken or nickname is undefined"); } diff --git a/src/routes/AuthRequierd.tsx b/src/routes/AuthRequierd.tsx new file mode 100644 index 00000000..60eb1c00 --- /dev/null +++ b/src/routes/AuthRequierd.tsx @@ -0,0 +1,77 @@ +import { get, instance } from "@apis/index"; +import { AxiosResponse } from "axios"; +import React, { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import useModal from "src/hooks/useModal"; + +interface AuthRequiredProps { + children: React.ReactNode; +} + +const AuthRequired = ({ children }: AuthRequiredProps) => { + const navigate = useNavigate(); + + const { openAlert } = useModal(); + + useEffect(() => { + const user = localStorage.getItem("user"); + + if (!user || !JSON.parse(user)?.refreshToken) { + localStorage.clear(); + openAlert({ title: "다시 로그인 해주세요." }); + navigate("/auth"); + return; + } + + const interceptor = instance.interceptors.response.use( + (response) => response, + async (error) => { + const originalConfig = error.config; + const status = error.response?.status; + const msg = error.response?.data?.message; + + if (status === 401) { + try { + const refreshToken = JSON.parse(user)?.refreshToken; + + const response: AxiosResponse<{ data: { accessToken: string } }> = await get( + "/users/refresh-token", + { + headers: { Authorization_Refresh: refreshToken }, + } + ); + + const newAccessToken = response.data?.data?.accessToken; + + if (newAccessToken) { + localStorage.setItem("accessToken", `Bearer ${newAccessToken}`); + originalConfig.headers["Authorization"] = `Bearer ${newAccessToken}`; + return instance(originalConfig); // 기존 요청 재시도 + } + throw new Error("Failed to refresh access token"); + } catch (refreshError) { + console.error("Token refresh failed:", refreshError); + localStorage.clear(); + openAlert({ title: "장시간 미활동으로 인해 \n자동으로 로그아웃 되었습니다." }); + navigate("/auth"); + window.location.reload(); + } + } else if (status === 500) { + openAlert({ + title: "서버에 문제가 발생했습니다. 잠시 후 다시 시도해주세요.", + }); + } + + return Promise.reject(error); + } + ); + + return () => { + instance.interceptors.response.eject(interceptor); + }; + }, [navigate, openAlert]); + + return <>{children}; +}; + +export default AuthRequired; diff --git a/src/routes/Router.tsx b/src/routes/Router.tsx index 44115650..538f5328 100644 --- a/src/routes/Router.tsx +++ b/src/routes/Router.tsx @@ -1,32 +1,35 @@ -import Layout from "@components/layout/Layout"; +import Admin from "@admin/pages/admin/Admin"; +import AdminNotFound from "@admin/pages/adminNotFound/AdminNotFound"; import AdminLayout from "@components/layout/AdminLayout"; +import Layout from "@components/layout/Layout"; import Intro from "@pages/intro/Intro"; import KakaoAuth from "@pages/kakaoAuth/KakaoAuth"; import Main from "@pages/main/Main"; import NotFound from "@pages/notFound/NotFound"; -import AdminNotFound from "@admin/pages/adminNotFound/AdminNotFound"; import OnBoarding from "@pages/onBoarding/OnBoarding"; import { GIG_ROUTES, LOOKUP_ROUTES, MANAGE_ROUTES, REGISTER_ROUTES, TEST_ROUTES } from "@routes"; +import DesktopGlobalStyle from "@styles/desktop"; import { createBrowserRouter } from "react-router-dom"; import ADMIN_ROUTES from "./AdminRoutes"; -import Admin from "@admin/pages/admin/Admin"; -import DesktopGlobalStyle from "@styles/desktop"; -import TokenRefresher from "src/hooks/useTokenRefresher"; +import AuthRequired from "./AuthRequierd"; const router = createBrowserRouter([ { path: "/main", element: ( - <> - +
- + ), }, { path: "/intro", element: }, { path: "/", - element: , + element: ( + + + + ), children: [ { path: "", element: }, ...GIG_ROUTES, diff --git a/src/stores/user.ts b/src/stores/user.ts index 46a37d1b..bbdfc810 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -4,5 +4,6 @@ import { atomWithStorage } from "jotai/utils"; export const userAtom = atomWithStorage("user", { nickname: "", accessToken: "", + refreshToken: "", role: "", }); diff --git a/src/typings/userType.ts b/src/typings/userType.ts index ea90a66e..9dcac8a3 100644 --- a/src/typings/userType.ts +++ b/src/typings/userType.ts @@ -1,5 +1,6 @@ export interface UserProps { nickname: string; accessToken: string; + refreshToken: string; role: string; }