Skip to content

Commit c924976

Browse files
authored
Merge pull request #223 from boostcampwm-2024/feat/#208-error-handling
Feat/#208 error handling
2 parents df7c4e2 + f73ab6e commit c924976

File tree

30 files changed

+322
-64
lines changed

30 files changed

+322
-64
lines changed

front/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<html lang="ko">
33
<head>
44
<meta charset="UTF-8" />
5-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
5+
<link rel="icon" type="image/svg+xml" href="./public//favicon/favicon.ico" type="image/x-icon" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>Vite + React + TS</title>
7+
<title>RealTicket</title>
88
</head>
99
<body>
1010
<div id="root"></div>

front/public/favicon/favicon.ico

14.7 KB
Binary file not shown.

front/src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { RouterProvider } from 'react-router-dom';
22

3+
import ToastContainer from '@/components/Toast/ToastContainer.tsx';
4+
35
import AuthProvider from '@/providers/AuthProvider';
46
import QueryProvider from '@/providers/QueryProvider';
57
import router from '@/routes/index';
@@ -9,6 +11,7 @@ function App() {
911
<QueryProvider>
1012
<AuthProvider>
1113
<RouterProvider router={router} />
14+
<ToastContainer />
1215
</AuthProvider>
1316
</QueryProvider>
1417
);

front/src/api/axios.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { toast } from '@/components/Toast/index.ts';
2+
3+
import { ROUTE_URL } from '@/constants/index.ts';
14
import { auth } from '@/events/AuthEvent.ts';
25
import router from '@/routes/index.tsx';
36
import axios, { AxiosError, isAxiosError } from 'axios';
@@ -21,27 +24,49 @@ export const apiClient = axios.create({
2124
});
2225

2326
const EXCLUDING_AUTH_ERROR_REDIRECT_URL_LIST = ['/user'];
24-
const NOT_LOGIN_ERROR_STATUS = 403;
25-
26-
//TODO AuthContext logout 처리 필요, 현재 login상태가 true인데, cookie의 서버 세션이 만료된 경우
27-
const canRedirect = (error: AxiosError) => {
27+
const UN_AUTHENTICATION_ERROR_STATUS = 403;
28+
const UN_AUTHORIZATION_ERROR_STATUS = 401;
29+
const isAuthenticateError = (error: AxiosError) => {
2830
if (
29-
error.status === NOT_LOGIN_ERROR_STATUS &&
31+
error.status === UN_AUTHENTICATION_ERROR_STATUS &&
3032
error.config?.url &&
3133
!EXCLUDING_AUTH_ERROR_REDIRECT_URL_LIST.includes(error.config.url)
3234
)
3335
return true;
3436
return false;
3537
};
38+
39+
const isAuthorizationError = (error: AxiosError) => {
40+
if (error.status === UN_AUTHORIZATION_ERROR_STATUS) {
41+
return true;
42+
}
43+
return false;
44+
};
45+
46+
const isServerError = (error: AxiosError) => {
47+
if (error.status && error.status >= 500 && error.status < 600) {
48+
return true;
49+
}
50+
return false;
51+
};
52+
53+
const isError = (error: unknown) => error && isAxiosError<CustomError>(error);
3654
//TODO 500 에러 처리 필요
3755
apiClient.interceptors.response.use(
3856
(response) => response,
3957
(error) => {
40-
if (error && isAxiosError<CustomError>(error)) {
41-
if (canRedirect(error)) {
42-
alert('로그인이 필요합니다.');
58+
if (isError(error)) {
59+
if (isAuthenticateError(error)) {
60+
toast.error('로그인이 필요합니다.\n로그인 후 이용해주세요.');
4361
auth.logout();
44-
router.navigate('/login', { replace: true });
62+
router.navigate(ROUTE_URL.USER.LOGIN, { replace: true });
63+
}
64+
if (isAuthorizationError(error)) {
65+
toast.error('잘못된 접근입니다.\n다시 시도해주세요.');
66+
router.navigate('/', { replace: true });
67+
}
68+
if (isServerError(error)) {
69+
toast.error('서버에 문제가 있습니다.\n잠시 후 다시 시도해주세요.');
4570
}
4671
throw error;
4772
}
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 1 addition & 1 deletion
Loading

front/src/assets/icons/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ export { default as Ticket } from '@/assets/icons/ticket.svg?react';
1616
export { default as Users } from '@/assets/icons/users.svg?react';
1717
export { default as Square } from '@/assets/icons/square.svg?react';
1818
export { default as CheckSquare } from '@/assets/icons/check-square.svg?react';
19+
export { default as Alert } from '@/assets/icons/alert-triangle.svg?react';
20+
export { default as XCircle } from '@/assets/icons/x-circle.svg?react';
Lines changed: 5 additions & 0 deletions
Loading

front/src/components/Captcha/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ export default function Captcha({ goNextStep }: CaptchaProps) {
2626
};
2727
const validateAndGoNextStep = () => {
2828
if (validateCaptcha(inputData)) {
29-
//TODO 토스트
3029
goNextStep();
3130
} else {
3231
setIsValid(false);
3332
if (InputRef.current) {
34-
(InputRef.current! as HTMLInputElement).focus();
33+
const input = InputRef.current! as HTMLInputElement;
34+
input.value = '';
35+
input.focus();
3536
}
3637
}
3738
};

front/src/components/Navbar/index.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { cx } from 'class-variance-authority';
1818

1919
const POPOVER_WIDTH = 460;
2020

21-
//TODO url 상수화, 자동로그인 추가, 삭제 중인 카드 로딩
2221
const RESERVATION_DELETE_MUTATION_KEY = ['reservation'];
2322

2423
export default function Navbar() {
@@ -35,7 +34,7 @@ export default function Navbar() {
3534
mutationKey: RESERVATION_DELETE_MUTATION_KEY,
3635
mutationFn: deleteReservation,
3736
onSuccess: () => {
38-
return queryClient.invalidateQueries({ queryKey: ['reservation'] });
37+
return queryClient.refetchQueries({ queryKey: ['reservation'] });
3938
},
4039
});
4140

@@ -59,7 +58,7 @@ export default function Navbar() {
5958
const isReservation = reservations && reservations.length > 0;
6059
const widthClass = `w-[${POPOVER_WIDTH}px]`;
6160
return (
62-
<header className="flex w-full justify-between px-8 py-4">
61+
<header className="flex w-full justify-between bg-white px-8 py-4">
6362
<Link to="/" className="flex items-center gap-5">
6463
<Icon iconName="Tickets" size={'big'} color={'primary'} />
6564
<span className="text-heading1 text-primary">RealTicket</span>
@@ -81,7 +80,7 @@ export default function Navbar() {
8180
className={cx(widthClass, `flex flex-col gap-6 rounded-xl border bg-white p-6 shadow-2xl`)}>
8281
<h3 className="px-4 text-left text-heading3">예매 현황</h3>
8382
<Separator direction="row" />
84-
<div className="flex max-h-[800px] flex-col gap-6 overflow-y-scroll">
83+
<div className="flex max-h-[800px] flex-col gap-6 overflow-y-scroll pr-4">
8584
{isReservation ? (
8685
reservations.map((reservation) => (
8786
<ReservationCard

0 commit comments

Comments
 (0)