Skip to content

Commit

Permalink
Feat: #80 인터셉터 로직 일부 수정 및 로그인 API 호출 로직에 useMutation 훅 적용
Browse files Browse the repository at this point in the history
  • Loading branch information
Yoonyesol committed Aug 27, 2024
1 parent 9966f54 commit 3993d27
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 21 deletions.
7 changes: 6 additions & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import '@/globals.css';
import MainRouter from '@routes/MainRouter.tsx';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; // QueryClient와 QueryClientProvider 임포트

async function enableMocking() {
if (!import.meta.env.DEV) return;
Expand All @@ -10,10 +11,14 @@ async function enableMocking() {
return worker.start();
}

const queryClient = new QueryClient();

enableMocking().then(() => {
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<MainRouter />
<QueryClientProvider client={queryClient}>
<MainRouter />
</QueryClientProvider>
</React.StrictMode>,
);
});
21 changes: 13 additions & 8 deletions src/pages/user/SignInPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import FooterLinks from '@components/user/auth-form/FooterLinks';
import AuthFormLayout from '@layouts/AuthFormLayout';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { useMutation } from '@tanstack/react-query';
import type { UserSignInForm } from '@/types/UserType';
import { authAxios, defaultAxios } from '@/services/axiosProvider';
import { authAxios } from '@/services/axiosProvider';
import useToast from '@/hooks/useToast';
import { useAuthStore } from '@/stores/useAuthStore';
import { login } from '@/services/authService';

export default function SignInPage() {
const { toastError } = useToast();
Expand All @@ -27,24 +29,27 @@ export default function SignInPage() {
},
});

const onSubmit = async (data: UserSignInForm) => {
try {
const response = await defaultAxios.post('user/login', data, { withCredentials: true });
if (response.status !== 200) return toastError('잘못된 응답입니다. 다시 로그인 해주세요.');

const signIn = useMutation({
mutationFn: (data: UserSignInForm) => login(data),
onSuccess: (response) => {
const accessToken = response.headers.authorization;
if (!accessToken) return toastError('로그인에 실패했습니다.');

authAxios.defaults.headers.Authorization = accessToken;
useAuthStore.getState().Login(accessToken.replace('Bearer ', ''));

navigate('/', { replace: true });
} catch (error) {
},
onError: (error: Error) => {
if (axios.isAxiosError(error) && error.response?.status === 400) {
return toastError('아이디와 비밀번호를 한번 더 확인해 주세요.');
}
toastError(`로그인 도중 오류가 발생했습니다: ${error}`);
}
},
});

const onSubmit = async (data: UserSignInForm) => {
signIn.mutate(data);
};

return (
Expand Down
14 changes: 14 additions & 0 deletions src/services/authService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defaultAxios } from '@services/axiosProvider';
import type { User, UserSignInForm } from '@/types/UserType';

/**
* 사용자 로그인 API
*
* @export
* @async
* @param {UserSignInForm} loginForm - 로그인 폼 데이터
* @returns {Promise<AxiosResponse<User>>}
*/
export async function login(loginForm: UserSignInForm) {
return defaultAxios.post<User>('user/login', loginForm, { withCredentials: true });
}
22 changes: 11 additions & 11 deletions src/services/axiosProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import axios from 'axios';
import { SECOND } from '@constants/units';
import { JWT_TOKEN_DUMMY } from '@mocks/mockData';
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
import { useNavigate } from 'react-router-dom';
import { useAuthStore } from '@/stores/useAuthStore';
import useToast from '@/hooks/useToast';

Expand Down Expand Up @@ -32,7 +31,7 @@ authAxios.interceptors.request.use(
(config) => {
const modifiedConfig = { ...config };

const accessToken = useAuthStore.getState();
const { accessToken } = useAuthStore.getState();
if (accessToken) modifiedConfig.headers.Authorization = `Bearer ${accessToken}`;

return modifiedConfig;
Expand All @@ -47,31 +46,32 @@ authAxios.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
const nav = useNavigate();
const { toastError } = useToast();

// Access token 만료 시 처리
if (error.response?.status === 401) {
try {
// Refresh token을 이용해 새로운 Access token 발급
const refreshResponse = await defaultAxios.post('user/login/refresh', null, { withCredentials: true });
const newAccessToken = refreshResponse.headers.Authorization; // 응답값: `Bearer ${newAccessToken}`

if (!newAccessToken) {
toastError('토큰 발급에 실패했습니다. 다시 로그인 해주세요.');
useAuthStore.getState().Logout();
return;
}

authAxios.defaults.headers.Authorization = newAccessToken;
useAuthStore.getState().setAccessToken(newAccessToken.replace('Bearer ', ''));

// 기존 요청에 새로운 Access token 적용
originalRequest.headers.Authorization = newAccessToken;
return await axios(originalRequest);
} catch (refreshError) {
// Refresh token 에러/만료 시 처리
// Refresh token 에러 시 처리
console.error('Refresh token error:', refreshError);

toastError('로그인 정보가 만료되었습니다. 다시 로그인 해주세요.');

// 3초 후 페이지 이동
setTimeout(() => {
useAuthStore.getState().Logout();
nav('/signin');
}, 3000);
useAuthStore.getState().Logout();
return Promise.reject(refreshError);
}
}
Expand Down
1 change: 0 additions & 1 deletion src/stores/useAuthStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const useAuthStore = createStore(
},
Logout: () => {
set({ isAuthenticated: false, accessToken: null });
// TODO: 로그아웃 로직 요청 코드 추가
},
}),
{
Expand Down

0 comments on commit 3993d27

Please sign in to comment.