diff --git a/src/apis/user.js b/src/apis/user.js
index afe6f27b..63287509 100644
--- a/src/apis/user.js
+++ b/src/apis/user.js
@@ -1,6 +1,7 @@
import axios from 'axios';
import useLoginStore from '../stores/loginStore';
import qs from 'qs';
+import useErrorStore from '@/stores/errorStore';
axios.defaults.paramsSerializer = (params) => qs.stringify(params, { arrayFormat: 'comma' });
@@ -9,6 +10,26 @@ export const publicAPI = axios.create({
baseURL: import.meta.env.VITE_API_URL,
});
+publicAPI.interceptors.response.use(
+ (response) => {
+ return response;
+ },
+ (error) => {
+ if (error.response) {
+ const errorType = { statusCode: error.response.status };
+ useErrorStore.getState().setError(errorType);
+ } else if (error.message === 'Network Error' || error.message === 'Network Error') {
+ console.log('네트워크 에러 발생');
+ const errorType = { statusCode: error.message };
+ useErrorStore.getState().setError(errorType);
+ } else {
+ const errorType = { statusCode: 'Unknown Error' };
+ useErrorStore.getState().setError(errorType);
+ }
+ return Promise.reject(error);
+ },
+);
+
//인증이 필요한 요청
export const privateAPI = axios.create({
baseURL: import.meta.env.VITE_API_URL,
@@ -89,21 +110,31 @@ privateAPI.interceptors.response.use(
originRequest.headers.Authorization = `Bearer ${newAccessToken}`;
console.log('원래 요청 : ', originRequest);
return axios(originRequest);
- //리프레시 토큰 요청이 실패할때(리프레시 토큰도 만료되었을때 = 재로그인 안내)
} else if (response.data.status === 401) {
+ //리프레시 토큰 요청이 실패할때(리프레시 토큰도 만료되었을때 = 재로그인 안내)
loginStorage.clear();
- alert('세션이 만료되었습니다. 재로그인 해주세요.');
+ const errorType = { statusCode: 401 };
+ useErrorStore.getState().setError(errorType);
} else {
console.log('인터셉터 내부 기타 에러 : ', error);
- alert('에러가 발생했습니다. 관리자에게 문의해주세요!');
+ const errorType = { statusCode: 'Unknown Error' };
+ useErrorStore.getState().setError(errorType);
}
} else {
// 리프레시 토큰이 없는 경우 = 로그인 안내
- console.log(error);
- alert('로그인이 필요한 서비스입니다.');
- return 'need login';
+ const errorType = { statusCode: 401 };
+ useErrorStore.getState().setError(errorType);
}
+ } else {
+ const errorType = { statusCode: 'Unknown Error' };
+ useErrorStore.getState().setError(errorType);
}
+ } else if (error.message === 'Network Error' || error.message === 'Network Error') {
+ const errorType = { statusCode: error.message };
+ useErrorStore.getState().setError(errorType);
+ } else {
+ const errorType = { statusCode: 'Unknown Error' };
+ useErrorStore.getState().setError(errorType);
}
return Promise.reject(error);
},
diff --git a/src/components/AppLayout.jsx b/src/components/AppLayout.jsx
index f997d91c..7ece387d 100644
--- a/src/components/AppLayout.jsx
+++ b/src/components/AppLayout.jsx
@@ -8,9 +8,13 @@ import Header from './common/Header';
import { Outlet } from 'react-router-dom';
import Footer from './common/footer/Footer';
import LoginModal from './modal/LoginModal';
+import APIErrorModal from './modal/APIErrorModal';
+import ModalWrapper from './modal/ModalWrapper';
+import { useErrorStore } from '../stores/errorStore';
export default function AppLayout() {
const { modalOpen } = useLoginModalStore();
+ const { hasError } = useErrorStore();
const [isMobile, setIsMobile] = useState(false);
@@ -48,6 +52,11 @@ export default function AppLayout() {
{modalOpen ? : null}
+ {hasError && (
+
+
+
+ )}
>
)}
diff --git a/src/components/common/card/recreationCard/RecreationCardL.jsx b/src/components/common/card/recreationCard/RecreationCardL.jsx
index b67d2e0d..b807c357 100644
--- a/src/components/common/card/recreationCard/RecreationCardL.jsx
+++ b/src/components/common/card/recreationCard/RecreationCardL.jsx
@@ -47,7 +47,7 @@ export default function RecreationCardL({ content }) {
console.log(response.data);
}
} catch (error) {
- throw new Error('FavBtn Error');
+ throw new Error('FavBtn Error', error);
}
}
};
diff --git a/src/components/common/card/recreationCard/RecreationCardS.jsx b/src/components/common/card/recreationCard/RecreationCardS.jsx
index b9939061..7fde3e47 100644
--- a/src/components/common/card/recreationCard/RecreationCardS.jsx
+++ b/src/components/common/card/recreationCard/RecreationCardS.jsx
@@ -38,7 +38,7 @@ export default function RecreationCardS({ content, refetch }) {
console.log(response.data);
}
} catch (error) {
- throw new Error('FavBtn Error');
+ throw new Error('FavBtn Error', error);
}
}
};
diff --git a/src/components/createFlow/CustomRecreationItem.jsx b/src/components/createFlow/CustomRecreationItem.jsx
index cb86831d..4c6f46f0 100644
--- a/src/components/createFlow/CustomRecreationItem.jsx
+++ b/src/components/createFlow/CustomRecreationItem.jsx
@@ -88,7 +88,7 @@ export default function CustomRecreationItem({ index, register, removeRecreation
{!keywords.length ? (
이곳을 클릭하여 3개의 키워드를 선택해주세요.
diff --git a/src/components/createFlow/FavoriteRecreationsSection.jsx b/src/components/createFlow/FavoriteRecreationsSection.jsx
index fe03d455..76ce49bd 100644
--- a/src/components/createFlow/FavoriteRecreationsSection.jsx
+++ b/src/components/createFlow/FavoriteRecreationsSection.jsx
@@ -42,7 +42,7 @@ export default function FavoriteRecreationsSection({
};
fetchFavoriteRecreations();
- }, [page]);
+ }, [page, totalPages]);
const incrementPage = () => setPage((prev) => prev + 1);
@@ -52,7 +52,7 @@ export default function FavoriteRecreationsSection({
{initialLoading ? (
- ) : !!recreations.length ? (
+ ) : recreations.length ? (
{
+ resetError();
+ window.location.href = '/';
+ };
+
+ if (status === 401) {
+ // 세션 만료
+ return (
+
+
+
+ 세션이 만료되었습니다.
+ 재로그인 해주세요.
+
+
+ toKakaoLogin(pathname)}
+ >
+ 로그인
+
+
+
+
+ );
+ }
+ if (status >= 500) {
+ // 서버 에러
+ return (
+
+
+
+ 서버 에러가 발생했습니다.
+ 잠시 후에 다시 시도해주세요.
+
+
+
+ 홈으로 이동하기
+
+
+
+
+ );
+ } else if (status === 'Network Error') {
+ // 네트워크 에러
+ return (
+
+
+
+ 네트워크 에러가 발생했습니다.
+ 인터넷 연결을 확인해주세요.
+
+
+ window.location.reload()}
+ backgroundColor="main02"
+ color="main05"
+ >
+ 새로고침
+
+
+
+
+ );
+ } else {
+ // 기타 에러
+ return (
+
+
+
+ 알 수 없는 에러가 발생했습니다.
+ 관리자에게 문의해주세요.
+
+
+
+ 홈으로 이동하기
+
+ window.location.reload()}
+ backgroundColor="main05"
+ color="grayscale03"
+ border
+ borderColor="grayscale03"
+ >
+ 새로고침
+
+
+
+
+ );
+ }
+}
diff --git a/src/components/modal/LoginModal.jsx b/src/components/modal/LoginModal.jsx
index 5fb0ab94..e678e4e2 100644
--- a/src/components/modal/LoginModal.jsx
+++ b/src/components/modal/LoginModal.jsx
@@ -2,28 +2,16 @@ import React from 'react';
import { Backdrop } from './modal.style';
import { useLocation } from 'react-router';
import styled from 'styled-components';
-import characterImg from '../../assets/character/kakaoAvb.png';
-import closeIcon from '../../assets/common/x.svg';
+import characterImg from '@/assets/character/kakaoAvb.png';
+import closeIcon from '@/assets/common/x.svg';
-import useLoginModalStore from '../../stores/loginModalStore';
+import useLoginModalStore from '@/stores/loginModalStore';
import Button from '../common/button/Button';
+import toKakaoLogin from '@/utils/kakaoLoginRedirectUtils';
export default function LoginModal() {
const { pathname } = useLocation();
const { modalControl } = useLoginModalStore((state) => state);
- const REST_API_KEY = import.meta.env.VITE_REST_API_KEY;
- let REDIRECT_URI;
-
- if (window.location.href.startsWith('http://localhost:3000/')) {
- REDIRECT_URI = import.meta.env.VITE_REDIRECT_URL_LOCAL;
- } else {
- REDIRECT_URI = import.meta.env.VITE_REDIRECT_URL;
- }
-
- let kakaoURL = `https://kauth.kakao.com/oauth/authorize?&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code&state=${pathname}&prompt=select_account`;
- const toKakaoLogin = () => {
- window.location.href = kakaoURL;
- };
return (
@@ -39,7 +27,12 @@ export default function LoginModal() {
간편하게 로그인을 하고
성공적인 레크레이션을
경험해보세요!
-