diff --git a/src/api/auth.ts b/src/api/auth.ts index 34724ac0..0ea7a7a0 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -5,7 +5,9 @@ export const postReissue = async (): Promise => { const { data } = await axios.post( `${import.meta.env.VITE_API_BASE_URL}/api/auth/reissue`, {}, - { withCredentials: true }, + { + withCredentials: true, + }, ); return data; }; diff --git a/src/api/axios/axios.ts b/src/api/axios/axios.ts index 11ad7892..751a8d81 100644 --- a/src/api/axios/axios.ts +++ b/src/api/axios/axios.ts @@ -1,16 +1,24 @@ import axios from "axios"; import { useLocalStorage } from "../../hooks/use-local-storage"; import { LOCAL_STORAGE_KEY } from "../../constants/key"; +import { postLogout, postReissue } from "../auth"; +import toast from "react-hot-toast"; + +const isDev = import.meta.env.DEV; +const BASE_URL = isDev ? "" : import.meta.env.VITE_API_BASE_URL; export const axiosInstance = axios.create({ - baseURL: import.meta.env.VITE_API_BASE_URL, + baseURL: BASE_URL, + headers: { + "Content-Type": "application/json", + }, }); // 요청 인터셉터: 모든 요청 전에 accessToken을 Authorization 헤더에 추가 axiosInstance.interceptors.request.use( (config) => { const { getItem } = useLocalStorage(LOCAL_STORAGE_KEY.accessToken); - let accessToken = getItem(); + const accessToken = getItem(); // accessToken이 존재하면 Authorization 헤더에 Bearer 토큰 형식으로 추가한다 if (accessToken) { @@ -24,3 +32,43 @@ axiosInstance.interceptors.request.use( // 요청 인터셉터가 실패하면, 에러 뿜음 (error) => Promise.reject(error), ); + +// 응답 인터셉터 +axiosInstance.interceptors.response.use( + (response) => response, + async (error) => { + const originalRequest = error.config; + + // 401에러 & 재시도 X + if (error.response?.status === 401 && !originalRequest._retry) { + originalRequest._retry = true; + + try { + const reissueRes = await postReissue(); + + const { setItem } = useLocalStorage(LOCAL_STORAGE_KEY.accessToken); + setItem(reissueRes.result.accessToken); + originalRequest.headers.Authorization = `Bearer ${reissueRes.result.accessToken}`; + if (reissueRes.result.needAgreement) { + // 약관동의 안 돼있을 경우 약관동의로 리다이렉트 + window.location.href = "/agreement"; + return Promise.reject(new Error("Agreement required")); + } + return axiosInstance(originalRequest); + } catch (refreshError) { + // 리프레시 토큰 만료 + toast.error("세션이 만료되었습니다. 다시 로그인해주세요."); + + try { + await postLogout(); + } catch (logoutError) { + toast.error("로그아웃 API 호출 실패"); + } finally { + localStorage.removeItem(LOCAL_STORAGE_KEY.accessToken); + window.location.href = "/login"; + } + } + } + return Promise.reject(error); + }, +); diff --git a/src/pages/auth/signup-page.tsx b/src/pages/auth/signup-page.tsx deleted file mode 100644 index f1f66d32..00000000 --- a/src/pages/auth/signup-page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const SignupPage = () => { - return
SignupPage 입니다.
; -}; - -export default SignupPage; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 63bd5b0c..6fd3921e 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,7 +1,6 @@ import { createBrowserRouter } from "react-router-dom"; import MobileLayout from "../layouts/mobile-layout"; import ProtectedRoute from "./protected-route"; -import SignupPage from "../pages/auth/signup-page"; import LoginPage from "../pages/auth/login-page"; import HomePage from "../pages/home/home-page"; import MyPage from "../pages/mypage/my-page"; @@ -47,10 +46,6 @@ export const router = createBrowserRouter([ path: "login/callback", element: , }, - { - path: "signup", - element: , - }, { path: "agreement", element: , diff --git a/vite.config.ts b/vite.config.ts index 4ff4f8fe..68fe53cd 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,8 +1,21 @@ -import { defineConfig } from "vite"; +import { defineConfig, loadEnv } from "vite"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; // https://vite.dev/config/ -export default defineConfig({ - plugins: [react(), tailwindcss()], +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + + return { + plugins: [react(), tailwindcss()], + server: { + proxy: { + // '/api'로 시작하는 모든 요청을 프록시가 가로챔 + "/api": { + target: env.VITE_API_BASE_URL, + changeOrigin: true, + }, + }, + }, + }; });