diff --git a/src/pages/RedirectPage/RedirectPage.tsx b/src/pages/RedirectPage/RedirectPage.tsx
index de91dd8e..0fb8d331 100644
--- a/src/pages/RedirectPage/RedirectPage.tsx
+++ b/src/pages/RedirectPage/RedirectPage.tsx
@@ -1,38 +1,31 @@
import { useEffect } from 'react';
-import { useNavigate } from 'react-router-dom';
+import { useLocation, useNavigate } from 'react-router-dom';
+
+import { setAccessToken } from '@/shared/utils/auth';
-// import { useLocation } from 'react-router-dom';
-// import { useSignUp } from '@/shared/apis/auth/queries';
import { ROUTES_CONFIG } from '@/router/routesConfig';
const RedirectPage = () => {
- //Todo: 서버 이슈로 로그인 관련 로직 앱잼 끝나고 사용
- // const params = new URLSearchParams(useLocation().search);
- // const authorizationCode = params.get('code');
+ const { search } = useLocation();
const navigate = useNavigate();
- // const { data, error, isError } = useSignUp(authorizationCode);
-
- // useEffect(() => {
- // if (data) {
- // const { accessToken, refreshToken } = data || {};
- // if (accessToken && refreshToken) {
- // localStorage.setItem('accessToken', accessToken);
- // localStorage.setItem('refreshToken', refreshToken);
- // navigate(ROUTES.home.path);
- // }
- // }
- // if (error) {
- // alert('다시 로그인 해주세요');
- // navigate(ROUTES.login.path, { replace: true });
- // }
- // }, [error, navigate, data]);
-
useEffect(() => {
- navigate(`${ROUTES_CONFIG.onboarding.path}?step=start`, { replace: true });
- }, [navigate]);
+ const params = new URLSearchParams(search);
+ const accessToken = params.get('accessToken');
+ const isSignUp = params.get('isSignUp');
+
+ if (accessToken) {
+ setAccessToken(accessToken);
+
+ if (isSignUp === 'true') {
+ navigate(`${ROUTES_CONFIG.home.path}`, { replace: true });
+ } else {
+ navigate(`${ROUTES_CONFIG.onboarding.path}?step=start`, { replace: true });
+ }
+ }
+ }, [navigate, search]);
- return
RedirectPage
;
+ return <>>;
};
export default RedirectPage;
diff --git a/src/router/ProtectedRoute.tsx b/src/router/ProtectedRoute.tsx
new file mode 100644
index 00000000..146fc7be
--- /dev/null
+++ b/src/router/ProtectedRoute.tsx
@@ -0,0 +1,17 @@
+import { Navigate, Outlet } from 'react-router-dom';
+
+import { getAccessToken } from '@/shared/utils/auth';
+
+import { ROUTES_CONFIG } from './routesConfig';
+
+const ProtectedRoute = () => {
+ const accessToken = getAccessToken();
+ if (!accessToken) {
+ alert('로그인 해주세요.');
+ return ;
+ }
+
+ return ;
+};
+
+export default ProtectedRoute;
diff --git a/src/router/Router.tsx b/src/router/Router.tsx
index b6b90268..6450d1fd 100644
--- a/src/router/Router.tsx
+++ b/src/router/Router.tsx
@@ -8,6 +8,7 @@ import OnboardingPage from '@/pages/OnboardingPage/OnboardingPage';
import RedirectPage from '@/pages/RedirectPage/RedirectPage';
import Layout from '@/shared/layout/Layout';
+import ProtectedRoute from './ProtectedRoute';
import { ROUTES_CONFIG } from './routesConfig';
const LoginPage = lazy(() => import('@/pages/LoginPage/LoginPage'));
@@ -15,16 +16,6 @@ const HomePage = lazy(() => import('@/pages/HomePage/HomePage'));
const TimerPage = lazy(() => import('@/pages/TimerPage/TimerPage'));
const AllowedServicePage = lazy(() => import('@/pages/AllowedServicePage/AllowedServicePage'));
-const ProtectedRoute = () => {
- //Todo: 개발이 진행되면 실제 토큰 상태를 받아서 login page로 이동 시킴
- // const accessToken = getAccessTotken();
- // if (!accessToken) {
- // alert('로그인 해주세요');
- // return ;
- // }
- return ;
-};
-
const router: Router = createBrowserRouter([
{
//public 라우트들
diff --git a/src/router/routesConfig.ts b/src/router/routesConfig.ts
index df653f61..5fac3f1d 100644
--- a/src/router/routesConfig.ts
+++ b/src/router/routesConfig.ts
@@ -17,7 +17,7 @@ export const ROUTES_CONFIG = {
},
redirect: {
title: 'Redirect',
- path: '/redirect',
+ path: 'auth/redirect',
},
timer: {
title: 'Timer',
diff --git a/src/shared/apisV2/auth/auth.api.ts b/src/shared/apisV2/auth/auth.api.ts
new file mode 100644
index 00000000..05a22011
--- /dev/null
+++ b/src/shared/apisV2/auth/auth.api.ts
@@ -0,0 +1,28 @@
+import axios from 'axios';
+
+import { authClient } from '@/shared/apis/client';
+
+import { getAccessToken } from '@/shared/utils/auth';
+
+import { reissueRes } from '@/shared/types/api/auth';
+
+const AUTH_URL = {
+ PATCH_REISSUE_TOKEN: 'api/v2/users/reissue',
+ POST_LOGOUT: 'api/v2/users/logout',
+};
+
+export const patchReissueToken = async (): Promise => {
+ const accessToken = getAccessToken();
+
+ const { data } = await axios.patch(AUTH_URL.PATCH_REISSUE_TOKEN, {
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ });
+
+ return data;
+};
+
+export const postLogout = async () => {
+ await authClient.post(AUTH_URL.POST_LOGOUT);
+};
diff --git a/src/shared/apisV2/auth/auth.queries.ts b/src/shared/apisV2/auth/auth.queries.ts
new file mode 100644
index 00000000..222fad0d
--- /dev/null
+++ b/src/shared/apisV2/auth/auth.queries.ts
@@ -0,0 +1,9 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { postLogout } from './auth.api';
+
+export const usePostLogout = () => {
+ return useMutation({
+ mutationFn: postLogout,
+ });
+};
diff --git a/src/shared/apisV2/client.ts b/src/shared/apisV2/client.ts
new file mode 100644
index 00000000..c05d0e5e
--- /dev/null
+++ b/src/shared/apisV2/client.ts
@@ -0,0 +1,72 @@
+import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
+
+import { getAccessToken, reloginWithoutLogout, setAccessToken } from '@/shared/utils/auth';
+
+import { patchReissueToken } from './auth/auth.api';
+
+const API_URL = `${import.meta.env.VITE_BASE_URL}`;
+
+const defaultConfig: AxiosRequestConfig = {
+ baseURL: API_URL,
+ headers: { 'Content-Type': 'application/json' },
+};
+
+// 기본 설정을 적용한 axios 인스턴스 생성 함수
+const createBaseClient = (additionalConfig: AxiosRequestConfig = {}): AxiosInstance => {
+ const clientConfig = {
+ ...defaultConfig,
+ ...additionalConfig,
+ };
+ const baseClient = axios.create(clientConfig);
+ return baseClient;
+};
+
+// 인증 설정을 추가하는 함수 (토큰)
+const addAuthInterceptor = (axiosClient: AxiosInstance) => {
+ axiosClient.interceptors.request.use(async (config) => {
+ const accessToken = getAccessToken();
+ config.headers.Authorization = `Bearer ${accessToken}`;
+ config.withCredentials = true;
+ return config;
+ });
+
+ axiosClient.interceptors.response.use(
+ (response) => {
+ return response;
+ },
+ async (e) => {
+ const prevRequest = e.config;
+ if (e.response.status === 401 && !prevRequest.sent) {
+ prevRequest.sent = true;
+ // 401 에러가 떴을 때 토큰 재발급
+ try {
+ const { data } = await patchReissueToken();
+ setAccessToken(data.accessToken);
+ return axiosClient(prevRequest);
+ } catch (reissueError) {
+ reloginWithoutLogout();
+ }
+ }
+ return Promise.reject(e);
+ },
+ );
+};
+
+// 클라이언트 생성 함수
+const createAxiosClient = (additionalConfig: AxiosRequestConfig = {}, withAuth: boolean = false): AxiosInstance => {
+ const axiosClient = createBaseClient(additionalConfig);
+
+ if (withAuth) {
+ addAuthInterceptor(axiosClient);
+ }
+
+ return axiosClient;
+};
+
+// 일반 요청 클라이언트 (토큰 불필요)
+const nonAuthClient: AxiosInstance = createAxiosClient();
+
+// 인증 요청 클라이언트 (토큰 필요)
+const authClient: AxiosInstance = createAxiosClient({}, true);
+
+export { authClient, nonAuthClient };
diff --git a/src/shared/apisV2/queryClient.ts b/src/shared/apisV2/queryClient.ts
new file mode 100644
index 00000000..5305ce59
--- /dev/null
+++ b/src/shared/apisV2/queryClient.ts
@@ -0,0 +1,12 @@
+import { QueryClient } from '@tanstack/react-query';
+
+export const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ throwOnError: true,
+ },
+ mutations: {
+ throwOnError: true,
+ },
+ },
+});
diff --git a/src/shared/types/api/auth.ts b/src/shared/types/api/auth.ts
new file mode 100644
index 00000000..5c848197
--- /dev/null
+++ b/src/shared/types/api/auth.ts
@@ -0,0 +1,7 @@
+export interface reissueRes {
+ status: number;
+ message: string;
+ data: {
+ accessToken: string;
+ };
+}
diff --git a/src/shared/utils/auth.ts b/src/shared/utils/auth.ts
new file mode 100644
index 00000000..59a8862c
--- /dev/null
+++ b/src/shared/utils/auth.ts
@@ -0,0 +1,15 @@
+import { ROUTES_CONFIG } from '@/router/routesConfig';
+
+export const getAccessToken = () => {
+ const accessToken = localStorage.getItem('accessToken');
+ return accessToken;
+};
+
+export const setAccessToken = (accessToken: string) => {
+ localStorage.setItem('accessToken', accessToken);
+};
+
+export const reloginWithoutLogout = () => {
+ localStorage.removeItem('accessToken');
+ location.href = ROUTES_CONFIG.login.path;
+};
diff --git a/src/shared/utils/token/index.ts b/src/shared/utils/token/index.ts
deleted file mode 100644
index 0a026cb0..00000000
--- a/src/shared/utils/token/index.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export const getAccessTotken = () => {
- const accessToken = localStorage.getItem('accessToken');
- return accessToken;
-};
-
-export const getRefreshToken = () => {
- const refresh = localStorage.getItem('refreshToken');
- return refresh;
-};
-
-export const getAllToken = () => {
- const accessToken = localStorage.getItem('accessToken');
- const refreshToken = localStorage.getItem('refreshToken');
- return { accessToken, refreshToken };
-};
-
-export const setAllToken = (accessToken: string, refreshToken: string) => {
- localStorage.setItem('accessToken', accessToken);
- localStorage.setItem('refreshToken', refreshToken);
-};
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 11f02fe2..6a8bacbc 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -1 +1,10 @@
///
+
+interface ImportMetaEnv {
+ readonly VITE_BASE_URL: string;
+ readonly VITE_GOOGLE_URL: string;
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv;
+}