This commit is contained in:
focp212@naver.com
2025-10-31 08:35:50 +09:00
parent 5b28674121
commit 4d40fa1cf7
13 changed files with 92 additions and 90 deletions

View File

@@ -5,7 +5,6 @@ import 'swiper/css';
import { IMAGE_ROOT } from '@/shared/constants/common'; import { IMAGE_ROOT } from '@/shared/constants/common';
import { UserFavorite } from '@/entities/user/model/types'; import { UserFavorite } from '@/entities/user/model/types';
import { useStore } from '@/shared/model/store'; import { useStore } from '@/shared/model/store';
import { useLocation } from 'react-router';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
/* /*

View File

@@ -1,9 +1,7 @@
import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useStore } from '@/shared/model/store'; import { useStore } from '@/shared/model/store';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { UserFavorite } from '@/entities/user/model/types'; import { UserFavorite } from '@/entities/user/model/types';
import { RefObject, useEffect, useState } from 'react'; import { RefObject, useEffect, useState } from 'react';
import { useLocation } from 'react-router';
import { MenuItem } from '../model/types'; import { MenuItem } from '../model/types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { MenuItems } from '@/entities/common/model/constant'; import { MenuItems } from '@/entities/common/model/constant';
@@ -35,7 +33,6 @@ export const MenuCategory = ({
}: MenuCategoryProps) => { }: MenuCategoryProps) => {
const { navigate } = useNavigate(); const { navigate } = useNavigate();
const { i18n } = useTranslation(); const { i18n } = useTranslation();
// const location = useLocation();
const [favoriteItems, setFavoriteItems] = useState<Array<UserFavorite>>([]); const [favoriteItems, setFavoriteItems] = useState<Array<UserFavorite>>([]);
const [menuIds, setMenuIds] = useState<Array<number | undefined>>([]); const [menuIds, setMenuIds] = useState<Array<number | undefined>>([]);
@@ -85,9 +82,7 @@ export const MenuCategory = ({
useStore.getState().UserStore.setUserFavorite(userFavorite); useStore.getState().UserStore.setUserFavorite(userFavorite);
setChangeMenuId(`${menuId}-${checked}`); setChangeMenuId(`${menuId}-${checked}`);
callFavoiteItems(); callFavoiteItems();
// if(location.pathname === PATHS.home){
// }
}; };
const callFavoiteItems = () => { const callFavoiteItems = () => {

View File

@@ -6,7 +6,6 @@ import {
getLocalStorage, getLocalStorage,
setLocalStorage setLocalStorage
} from '@/shared/lib'; } from '@/shared/lib';
import { finalizeConfig } from './utils';
import { appBridge } from '@/utils/appBridge'; import { appBridge } from '@/utils/appBridge';
import { LoginResponse } from '@/entities/user/model/types'; import { LoginResponse } from '@/entities/user/model/types';
import { getHeaderUserAgent } from '@/shared/constants/url'; import { getHeaderUserAgent } from '@/shared/constants/url';
@@ -14,7 +13,16 @@ import { useAppBridge } from '@/hooks';
import { useUserInfo } from '@/entities/user/lib/use-user-info'; import { useUserInfo } from '@/entities/user/lib/use-user-info';
import { useStore } from '@/shared/model/store'; import { useStore } from '@/shared/model/store';
import { login } from '@/entities/user/api/use-login-mutation'; import { login } from '@/entities/user/api/use-login-mutation';
import { useNavigate } from 'react-router'; import { useNavigate } from '@/shared/lib/hooks';
const finalizeConfig = (config: InternalAxiosRequestConfig) => {
const { params, data } = config;
return {
...config,
params,
data,
};
};
const onRequestFulfilled = (config: InternalAxiosRequestConfig) => { const onRequestFulfilled = (config: InternalAxiosRequestConfig) => {
config.headers['Content-Type'] = 'application/json;charset=UTF-8'; config.headers['Content-Type'] = 'application/json;charset=UTF-8';
@@ -37,6 +45,7 @@ const onRequestFulfilled = (config: InternalAxiosRequestConfig) => {
}; };
const onRequestRejected = (error: any) => { const onRequestRejected = (error: any) => {
console.log('onRequestRejected --> ', error);
return Promise.reject(error); return Promise.reject(error);
}; };
@@ -47,14 +56,11 @@ const onResponseFulfilled = (response: AxiosResponse) => {
}; };
}; };
export const onResponseRejected = (error: AxiosError) => { const onResponseRejected = (error: AxiosError) => {
console.log('onResponseRejected --> ', error); console.log('onResponseRejected --> ', error);
console.log('error?.status -> ', error?.status, error?.status === 401)
if(error?.status === 401){ if(error?.status === 401){
if(appBridge.isNativeEnvironment()){ if(appBridge.isNativeEnvironment()){
appBridge.safeCall(() => appBridge.requestRefreshToken()).then((token: LoginResponse) => { return appBridge.safeCall(() => appBridge.requestRefreshToken()).then((token: LoginResponse) => {
setLocalStorage(StorageKeys.TokenType, token.tokenType); setLocalStorage(StorageKeys.TokenType, token.tokenType);
setLocalStorage(StorageKeys.AccessToken, token.accessToken); setLocalStorage(StorageKeys.AccessToken, token.accessToken);
setLocalStorage(StorageKeys.RefreshToken, token.refreshToken); setLocalStorage(StorageKeys.RefreshToken, token.refreshToken);
@@ -81,9 +87,7 @@ export const onResponseRejected = (error: AxiosError) => {
id: 'woowahan5', id: 'woowahan5',
password: 'nictest00' password: 'nictest00'
}; };
login(params).then((result: LoginResponse) => { return login(params).then((result: LoginResponse) => {
result.usrid = params.id;
setLocalStorage(StorageKeys.TokenType, result.tokenType); setLocalStorage(StorageKeys.TokenType, result.tokenType);
setLocalStorage(StorageKeys.AccessToken, result.accessToken); setLocalStorage(StorageKeys.AccessToken, result.accessToken);
setLocalStorage(StorageKeys.RefreshToken, result.refreshToken); setLocalStorage(StorageKeys.RefreshToken, result.refreshToken);
@@ -92,7 +96,7 @@ export const onResponseRejected = (error: AxiosError) => {
setLocalStorage(StorageKeys.MenuGrants, result.menuGrants); setLocalStorage(StorageKeys.MenuGrants, result.menuGrants);
setLocalStorage(StorageKeys.ClientAddressIP, result.clientAddressIP); setLocalStorage(StorageKeys.ClientAddressIP, result.clientAddressIP);
setLocalStorage(StorageKeys.Requires2FA, result.requires2FA); setLocalStorage(StorageKeys.Requires2FA, result.requires2FA);
setLocalStorage(StorageKeys.Usrid, result.usrid); setLocalStorage(StorageKeys.Usrid, params.id);
if(appBridge.isNativeEnvironment()){ if(appBridge.isNativeEnvironment()){
setLocalStorage(StorageKeys.DeviceId, result.deviceId); setLocalStorage(StorageKeys.DeviceId, result.deviceId);
@@ -102,12 +106,13 @@ export const onResponseRejected = (error: AxiosError) => {
useStore.getState().UserStore.setUserInfo({ useStore.getState().UserStore.setUserInfo({
...result ...result
}); });
location.reload(); return error.config? axios.request(error.config): null;
}); });
} }
} }
return new Promise((_resolve, reject) => { return new Promise((_resolve, reject) => {
if(checkIsAxiosError(error)){ if(checkIsAxiosError(error)){
// iOS의 경우 window에서 unload event가 일어날때 네트워크 에러가 발생하곤 해서 이런 케이스를 방지하기 위해 지연시킴 // iOS의 경우 window에서 unload event가 일어날때 네트워크 에러가 발생하곤 해서 이런 케이스를 방지하기 위해 지연시킴

View File

@@ -1,10 +0,0 @@
import { InternalAxiosRequestConfig } from 'axios';
export const finalizeConfig = (config: InternalAxiosRequestConfig) => {
const { params, data } = config;
return {
...config,
params,
data,
};
};

View File

@@ -9,6 +9,7 @@ const globalQueryClient = new QueryClient({
throwOnError: true, throwOnError: true,
// suspense: true, // suspense: true,
networkMode: 'always', networkMode: 'always',
}, },
mutations: { mutations: {
throwOnError: true, throwOnError: true,

View File

@@ -11,8 +11,9 @@ export const CommonErrorBoundary = (props: Props) => {
const { reset } = useQueryErrorResetBoundary(); const { reset } = useQueryErrorResetBoundary();
return ( return (
<ErrorBoundary FallbackComponent={FallbackComponent} onReset={reset}> <ErrorBoundary
{children} FallbackComponent={ FallbackComponent }
</ErrorBoundary> onReset={ reset }
>{ children }</ErrorBoundary>
); );
}; };

View File

@@ -2,7 +2,6 @@ import { useQueryErrorResetBoundary } from '@tanstack/react-query';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { APIError } from '@/widgets/fallbacks/api-error'; import { APIError } from '@/widgets/fallbacks/api-error';
import { KickOutError } from '@/widgets/fallbacks/kick-out-error'; import { KickOutError } from '@/widgets/fallbacks/kick-out-error';
import { checkIsAxiosError, checkIsKickOutError } from '@/shared/lib/error'; import { checkIsAxiosError, checkIsKickOutError } from '@/shared/lib/error';
@@ -15,17 +14,26 @@ import { checkIsAxiosError, checkIsKickOutError } from '@/shared/lib/error';
* - network * - network
* - common * - common
*/ */
function FallbackComponent({ error, resetErrorBoundary }: FallbackProps) { function FallbackComponent({
error,
resetErrorBoundary
}: FallbackProps) {
// api 에러가 아닌 경우 상위 에러 바운더리로 위임 // api 에러가 아닌 경우 상위 에러 바운더리로 위임
if(!checkIsAxiosError(error)){ if(!checkIsAxiosError(error)){
throw error; throw error;
} }
if(checkIsKickOutError(error)){ if(checkIsKickOutError(error)){
return <KickOutError error={error} />; return (
<KickOutError error={ error } />
);
} }
return (
return <APIError error={error} resetErrorBoundary={resetErrorBoundary} />; <APIError
error={ error }
resetErrorBoundary={ resetErrorBoundary }
/>
);
} }
type Props = PropsWithChildren; type Props = PropsWithChildren;
@@ -34,8 +42,10 @@ export const GlobalAPIErrorBoundary = ({ children }: Props) => {
const { reset } = useQueryErrorResetBoundary(); const { reset } = useQueryErrorResetBoundary();
const { key } = useLocation(); const { key } = useLocation();
return ( return (
<ErrorBoundary onReset={reset} resetKeys={[key]} FallbackComponent={FallbackComponent}> <ErrorBoundary
{children} onReset={ reset }
</ErrorBoundary> resetKeys={ [key] }
FallbackComponent={ FallbackComponent }
>{ children }</ErrorBoundary>
); );
}; };

View File

@@ -6,5 +6,7 @@ import { CommonError } from '@/widgets/fallbacks/common-error';
type Props = PropsWithChildren; type Props = PropsWithChildren;
export const GlobalErrorBoundary = ({ children }: Props) => { export const GlobalErrorBoundary = ({ children }: Props) => {
return <CommonErrorBoundary FallbackComponent={CommonError}>{children}</CommonErrorBoundary>; return (
<CommonErrorBoundary FallbackComponent={ CommonError }>{ children }</CommonErrorBoundary>
);
}; };

View File

@@ -7,7 +7,12 @@ import { Dialog, DialogProps } from '@/shared/ui/dialogs/dialog';
type CommonErrorProps = FallbackProps & { type CommonErrorProps = FallbackProps & {
height?: number; height?: number;
}; };
export const APIError = ({ error, resetErrorBoundary }: CommonErrorProps) => { export const APIError = ({
error,
resetErrorBoundary
}: CommonErrorProps) => {
console.log('APIError --> ', error);
const { navigateBack } = useNavigate(); const { navigateBack } = useNavigate();
const msg = useMemo(() => { const msg = useMemo(() => {
let message: Partial<DialogProps> = { let message: Partial<DialogProps> = {

View File

@@ -6,12 +6,17 @@ import { Dialog, DialogProps } from '@/shared/ui/dialogs/dialog';
type CommonErrorProps = FallbackProps & { type CommonErrorProps = FallbackProps & {
height?: number; height?: number;
}; };
export const CommonError = ({ error, resetErrorBoundary }: CommonErrorProps) => { export const CommonError = ({
error,
resetErrorBoundary
}: CommonErrorProps) => {
console.log('CommonError --> ', error);
const [isOpen, setIsOpen] = useState(true); const [isOpen, setIsOpen] = useState(true);
const msg = useMemo(() => { const msg = useMemo(() => {
let message: Partial<DialogProps> = { let message: Partial<DialogProps> = {
title: '일시적인 오류가 발생하였습니다.', title: '일시적인 오류가 발생하였습니다.',
message: '잠시 후 다시 시도해주세요.', message: '잠시 후 다시 시도해주세요.'
}; };
if(error?.response?.data?.message){ if(error?.response?.data?.message){
message = { message: error.response.data.message }; message = { message: error.response.data.message };

View File

@@ -3,7 +3,10 @@ import { useEffect, useMemo } from 'react';
import { NiceAxiosFallbackProps } from '@/shared/@types/error'; import { NiceAxiosFallbackProps } from '@/shared/@types/error';
import { Dialog, DialogProps } from '@/shared/ui/dialogs/dialog'; import { Dialog, DialogProps } from '@/shared/ui/dialogs/dialog';
export const KickOutError = ({ error, resetErrorBoundary }: NiceAxiosFallbackProps) => { export const KickOutError = ({
error,
resetErrorBoundary
}: NiceAxiosFallbackProps) => {
useEffect(() => { useEffect(() => {
console.error('[ErrorBoundary] Kickout Error', JSON.stringify(error.response?.data)); console.error('[ErrorBoundary] Kickout Error', JSON.stringify(error.response?.data));
// clearAuthData(); // clearAuthData();
@@ -15,7 +18,9 @@ export const KickOutError = ({ error, resetErrorBoundary }: NiceAxiosFallbackPro
message: '잠시 후 다시 시도해주세요.', message: '잠시 후 다시 시도해주세요.',
}; };
if(error?.response?.data?.message){ if(error?.response?.data?.message){
message = { message: error.response.data.message }; message = {
message: error.response.data.message
};
} }
return message; return message;
}, [error]); }, [error]);
@@ -37,6 +42,7 @@ export const KickOutError = ({ error, resetErrorBoundary }: NiceAxiosFallbackPro
onConfirmClick={ handleConfirm } onConfirmClick={ handleConfirm }
onCancelClick={ handleCancel } onCancelClick={ handleCancel }
message={ msg.message } message={ msg.message }
buttonLabel={ ['취소', '재시도'] }
/> />
); );
}; };

View File

@@ -1,17 +0,0 @@
import { Suspense } from 'react';
import { Loading } from '@/widgets/loading/loading';
export const Loadable = (Component: React.ComponentType<any>) => {
const LoadableComponent = (props: any) => {
return (
<Suspense fallback={<Loading />}>
<Component {...props} />
</Suspense>
);
};
LoadableComponent.displayName = `Loadable(${Component.displayName || Component.name || 'Component'})`;
return LoadableComponent;
};