From b3461790cd3eadb340dc66d5444f522b8bfda04a Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:10:01 +0900 Subject: [PATCH 01/40] =?UTF-8?q?chore:=20ErrorBoundary=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EC=8B=9C=20=EC=97=90=EB=9F=AC=EA=B0=80=20=EB=9C=A8=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EB=8F=84=EB=A1=9D=20overlay=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=20=EB=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/webpack.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/webpack.config.js b/client/webpack.config.js index 2c706beec..7b5a173a7 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -67,5 +67,8 @@ export default { port: 3000, hot: true, historyApiFallback: true, + client: { + overlay: false, + }, }, }; From db2dffaec9e993201aba11f29c28dd9f5f10d66c Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:11:27 +0900 Subject: [PATCH 02/40] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=A5=BC=20=EC=A0=84=EC=97=AD=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B4=80=EB=9D=BC=ED=95=98=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=9B=85=EA=B3=BC=20=EC=BB=A8=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/ErrorProvider.tsx | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 client/src/ErrorProvider.tsx diff --git a/client/src/ErrorProvider.tsx b/client/src/ErrorProvider.tsx new file mode 100644 index 000000000..b0e5e560a --- /dev/null +++ b/client/src/ErrorProvider.tsx @@ -0,0 +1,55 @@ +import ERROR_MESSAGES from '@constants/errorMessage'; +import React, {createContext, useState, useContext, ReactNode} from 'react'; + +// 에러 컨텍스트 생성 +interface ErrorContextType { + hasError: boolean; + errorMessage: string; + setError: (error: Error) => void; + clearError: () => void; +} + +const ErrorContext = createContext(undefined); + +// 에러 컨텍스트를 제공하는 프로바이더 컴포넌트 +interface ErrorProviderProps { + children: ReactNode; +} + +type ServerError = { + code: string; + message: string; +}; + +export const ErrorProvider: React.FC = ({children}) => { + const [hasError, setHasError] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + + const setError = (error: ServerError) => { + setHasError(false); + setErrorMessage(ERROR_MESSAGES[error.code]); + console.log(ERROR_MESSAGES[error.code]); + setTimeout(() => { + setHasError(true); + setErrorMessage(error.message); + }, 0); // 상태를 빠르게 변경하기 위해 타이머 사용 + }; + + const clearError = () => { + setHasError(false); + setErrorMessage(''); + }; + + return ( + {children} + ); +}; + +// 에러 컨텍스트를 사용하는 커스텀 훅 +export const useError = (): ErrorContextType => { + const context = useContext(ErrorContext); + if (!context) { + throw new Error('useError must be used within an ErrorProvider'); + } + return context; +}; From 743547f76bd8c16819fce53101d01441046368e7 Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:11:46 +0900 Subject: [PATCH 03/40] =?UTF-8?q?feat:=20ErrorProvider=EA=B0=80=20ToastPro?= =?UTF-8?q?vider=EB=A5=BC=20=EA=B0=90=EC=8B=B8=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 849d78420..da01f9780 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,16 +1,23 @@ import {Outlet} from 'react-router-dom'; -import {HDesignProvider, ToastProvider} from 'haengdong-design'; +import {HDesignProvider} from 'haengdong-design'; import {Global} from '@emotion/react'; import {GlobalStyle} from './GlobalStyle'; +import {toastConfig} from 'react-simple-toasts'; +import {ErrorProvider} from './ErrorProvider'; +import {ToastProvider} from '@components/Toast/ToastProvider'; + +toastConfig({theme: 'dark'}); const App: React.FC = () => { return ( - - - + + + + + ); }; From a5a826ce58f1f21c6ef0ecf9c352b0ce925c9848 Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:12:09 +0900 Subject: [PATCH 04/40] =?UTF-8?q?feat:=20api=20=EC=BD=9C=ED=95=A0=20?= =?UTF-8?q?=EB=95=8C=20=EC=9D=B4=20useFetch=ED=9B=85=EC=9D=84=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=20=EC=A0=84=EC=97=AD=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A1=9C=20=EB=A7=8C=EB=93=A4=EC=96=B4=EC=A4=80=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EB=A5=BC=20=ED=95=B8=EB=93=A4=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/useFetch.tsx | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 client/src/apis/useFetch.tsx diff --git a/client/src/apis/useFetch.tsx b/client/src/apis/useFetch.tsx new file mode 100644 index 000000000..549ca0822 --- /dev/null +++ b/client/src/apis/useFetch.tsx @@ -0,0 +1,35 @@ +import {useError} from '../ErrorProvider'; +import {useState} from 'react'; + +interface FetchOptions { + queryFn: () => Promise; +} + +export const useFetch = () => { + const {setError, clearError} = useError(); + const [loading, setLoading] = useState(false); + const [data, setData] = useState(null); + + const request = async ({queryFn}: FetchOptions): Promise => { + setLoading(true); + clearError(); + try { + const result = await queryFn(); + setData(result); + return result; + } catch (error) { + if (error instanceof Error) { + const errorBody = await JSON.parse(error.message); + setError(errorBody); + console.log(error); + } else { + setError(new Error('An unknown error occurred')); + } + return null; + } finally { + setLoading(false); + } + }; + + return {data, loading, request}; +}; From ec1a0e28a31fd35e4f6237c8f17032e87455d67b Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:12:25 +0900 Subject: [PATCH 05/40] =?UTF-8?q?chore:=20=EC=9E=84=EC=8B=9C=EB=A1=9C=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A4=EC=96=B4=EB=91=94=20useFetch=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20=EC=98=88=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/CreateEventPage/SetEventNamePage.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/pages/CreateEventPage/SetEventNamePage.tsx b/client/src/pages/CreateEventPage/SetEventNamePage.tsx index 360db3f17..ebb86646c 100644 --- a/client/src/pages/CreateEventPage/SetEventNamePage.tsx +++ b/client/src/pages/CreateEventPage/SetEventNamePage.tsx @@ -2,10 +2,11 @@ import {useState} from 'react'; import {useNavigate} from 'react-router-dom'; import {FixedButton, MainLayout, LabelInput, Input, Title, TopNav, Back} from 'haengdong-design'; -import {requestPostNewEvent} from '@apis/request/event'; +import {ResponsePostNewEvent, requestPostNewEvent} from '@apis/request/event'; import validateEventName from '@utils/validate/validateEventName'; import {ROUTER_URLS} from '@constants/routerUrls'; +import {useFetch} from '@apis/useFetch'; const SetEventNamePage = () => { const [eventName, setEventName] = useState(''); @@ -13,17 +14,16 @@ const SetEventNamePage = () => { const [canSubmit, setCanSubmit] = useState(false); const navigate = useNavigate(); + const {request} = useFetch(); + const submitEventName = async (event: React.FormEvent) => { event.preventDefault(); - const response = await requestPostNewEvent({eventName}); + const response = await request({queryFn: () => requestPostNewEvent({eventName})}); if (response) { const {eventId} = response; navigate(`${ROUTER_URLS.eventCreateComplete}?${new URLSearchParams({eventId})}`); - } else { - // TODO: (@weadie) - alert('오류님'); } }; From f0cd278a957260c68d9bd8dd0481fe986762a4ec Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:13:15 +0900 Subject: [PATCH 06/40] =?UTF-8?q?chore:=20useFetch=EC=9D=98=20request?= =?UTF-8?q?=EC=97=90=20=EB=84=98=EA=B2=A8=EC=A3=BC=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20response=20=ED=83=80=EC=9E=85=20export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/request/event.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/apis/request/event.ts b/client/src/apis/request/event.ts index fc4708bf7..24f6c44c6 100644 --- a/client/src/apis/request/event.ts +++ b/client/src/apis/request/event.ts @@ -6,7 +6,7 @@ type RequestPostNewEvent = { eventName: string; }; -type ResponsePostNewEvent = { +export type ResponsePostNewEvent = { eventId: string; }; From 714fc3e1245e2c497f1cb284aac133492d34a1ce Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:14:08 +0900 Subject: [PATCH 07/40] =?UTF-8?q?feat:=20Toast=EB=A5=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=ED=95=B4=20=EC=82=AC=EC=9A=A9=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EC=9E=84=EC=8B=9C=EB=A1=9C=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/Toast.style.ts | 67 ++++++++++++++++++ client/src/components/Toast/Toast.tsx | 79 ++++++++++++++++++++++ client/src/components/Toast/Toast.type.ts | 21 ++++++ 3 files changed, 167 insertions(+) create mode 100644 client/src/components/Toast/Toast.style.ts create mode 100644 client/src/components/Toast/Toast.tsx create mode 100644 client/src/components/Toast/Toast.type.ts diff --git a/client/src/components/Toast/Toast.style.ts b/client/src/components/Toast/Toast.style.ts new file mode 100644 index 000000000..52ce2aee7 --- /dev/null +++ b/client/src/components/Toast/Toast.style.ts @@ -0,0 +1,67 @@ +import {css, keyframes} from '@emotion/react'; +import {ToastPosition} from './Toast.type'; + +type ToastMarginStyle = { + position?: ToastPosition; + bottom?: string; + top?: string; +}; + +// 애니메이션 키프레임 정의 +const fadeIn = keyframes` + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +const fadeOut = keyframes` + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(20px); + } +`; + +export const toastMarginStyle = ({position, bottom, top}: ToastMarginStyle) => + css({ + position: 'absolute', + bottom: position === 'bottom' ? `${bottom}` : 'auto', + top: position === 'top' ? `${top}` : 'auto', + left: '50%', + transform: 'translate(-50%)', + + width: '100%', + maxWidth: '48rem', + paddingInline: '1rem', + }); + +export const toastStyle = (isVisible: boolean) => + css({ + width: '100%', + padding: '0.625rem 1rem', + + backgroundColor: 'gray', + boxShadow: '0 8px 12px rgba(0, 0, 0, 0.16);', + + borderRadius: '1.25rem', + + // 애니메이션 추가 + animation: `${isVisible ? fadeIn : fadeOut} 0.5s forwards`, + }); + +export const textStyle = () => + css({ + width: '100%', + + color: 'white', + + whiteSpace: 'pre-line', + }); diff --git a/client/src/components/Toast/Toast.tsx b/client/src/components/Toast/Toast.tsx new file mode 100644 index 000000000..8e10690a0 --- /dev/null +++ b/client/src/components/Toast/Toast.tsx @@ -0,0 +1,79 @@ +/** @jsxImportSource @emotion/react */ +import {createPortal} from 'react-dom'; +import {useState, useEffect} from 'react'; +import {toastStyle, textStyle, toastMarginStyle} from './Toast.style'; +import {ToastProps, ToastType} from './Toast.type'; +import {Button, Flex, Icon, Text} from 'haengdong-design'; + +const renderIcon = (type: ToastType) => { + switch (type) { + case 'error': + return ; + case 'confirm': + return ; + case 'none': + return null; + default: + return null; + } +}; + +const Toast = ({ + type = 'confirm', + top = '0px', + bottom = '0px', + isClickToClose = true, + position = 'bottom', + message, + onUndo, + onClose, + ...htmlProps +}: ToastProps) => { + const [isVisible, setIsVisible] = useState(true); + const styleProps = {position, top, bottom}; + + useEffect(() => { + const timer = setTimeout(() => { + setIsVisible(false); + setTimeout(() => { + if (onClose) onClose(); + }, 500); // fadeOut 애니메이션 시간과 동일하게 설정 + }, 3000); // 토스트가 화면에 보이는 시간 설정 + + return () => { + clearTimeout(timer); + }; + }, [onClose]); + + const handleClickToClose = () => { + if (!isClickToClose || !onClose) return; + + setIsVisible(false); + setTimeout(() => { + onClose(); + }, 500); // fadeOut 애니메이션 시간과 동일하게 설정 + }; + + return createPortal( +
+
+ + + {renderIcon(type)} + + {message} + + + {onUndo && ( + + )} + +
+
, + document.body, + ); +}; + +export default Toast; diff --git a/client/src/components/Toast/Toast.type.ts b/client/src/components/Toast/Toast.type.ts new file mode 100644 index 000000000..12a436c2d --- /dev/null +++ b/client/src/components/Toast/Toast.type.ts @@ -0,0 +1,21 @@ +export type ToastPosition = 'bottom' | 'top'; +export type ToastType = 'error' | 'confirm' | 'none'; + +export interface ToastStyleProps { + bottom?: string; + top?: string; +} + +export interface ToastOptionProps { + position?: ToastPosition; + type?: ToastType; + onUndo?: () => void; + isClickToClose?: boolean; + onClose?: () => void; +} + +export interface ToastRequiredProps { + message: string; +} + +export type ToastProps = React.ComponentProps<'div'> & ToastStyleProps & ToastOptionProps & ToastRequiredProps; From d4363ee4d3b6f80e16185439eb8aa9ba130c7c6a Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:14:53 +0900 Subject: [PATCH 08/40] =?UTF-8?q?feat:=20=EC=A0=84=EC=97=AD=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EB=A5=BC=20=EC=98=B5=EC=A0=80=EB=B9=99=ED=95=98?= =?UTF-8?q?=EB=A9=B4=EC=84=9C=20=EC=97=90=EB=9F=AC=EA=B0=80=20=EC=9E=88?= =?UTF-8?q?=EB=8B=A4=EB=A9=B4=20=ED=86=A0=EC=8A=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EB=9D=84=EC=9A=B0=EB=8F=84=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/ToastProvider.tsx | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 client/src/components/Toast/ToastProvider.tsx diff --git a/client/src/components/Toast/ToastProvider.tsx b/client/src/components/Toast/ToastProvider.tsx new file mode 100644 index 000000000..cdeab0da2 --- /dev/null +++ b/client/src/components/Toast/ToastProvider.tsx @@ -0,0 +1,82 @@ +/** @jsxImportSource @emotion/react */ +import {createContext, useCallback, useContext, useEffect, useState} from 'react'; +import {ToastProps} from './Toast.type'; +import Toast from './Toast'; +import {useError} from '../../ErrorProvider'; + +export const ToastContext = createContext(null); + +interface ToastContextProps { + showToast: (args: ShowToast) => void; +} + +type ShowToast = ToastProps & { + showingTime?: number; + isAlwaysOn?: boolean; +}; + +const ToastProvider = ({children}: React.PropsWithChildren) => { + const [currentToast, setCurrentToast] = useState(null); + const {hasError, errorMessage, clearError} = useError(); + + const showToast = useCallback(({showingTime = 3000, isAlwaysOn = false, ...toastProps}: ShowToast) => { + setCurrentToast({showingTime, isAlwaysOn, ...toastProps}); + }, []); + + const closeToast = () => { + setCurrentToast(null); + }; + + useEffect(() => { + if (hasError) { + showToast({ + message: errorMessage || 'An error occurred', + showingTime: 5000, // 필요에 따라 시간 설정 + isAlwaysOn: false, + position: 'bottom', + bottom: '100px', + }); + + const timer = setTimeout(() => { + clearError(); + }, 5000); // 토스트가 표시되는 시간과 동일하게 설정 + + return () => clearTimeout(timer); + } + + return; + }, [hasError, errorMessage, showToast, clearError]); + + useEffect(() => { + if (!currentToast) return; + + if (!currentToast.isAlwaysOn) { + const timer = setTimeout(() => { + setCurrentToast(null); + }, currentToast.showingTime); + + return () => clearTimeout(timer); + } + + return; + }, [currentToast]); + + return ( + + {currentToast && } + {children} + + ); +}; + +const useToast = () => { + const context = useContext(ToastContext); + + if (!context) { + throw new Error('useToast는 ToastProvider 내에서 사용되어야 합니다.'); + } + + return context; +}; + +export {ToastProvider, useToast}; From bee6989631010c3c9bd9bbb807f578d0efca108a Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:17:34 +0900 Subject: [PATCH 09/40] =?UTF-8?q?fix:=20=ED=83=80=EC=9E=85=EC=97=90=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=B4=20=EC=9D=BC=EC=B9=98=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/ErrorProvider.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/ErrorProvider.tsx b/client/src/ErrorProvider.tsx index b0e5e560a..670b71a02 100644 --- a/client/src/ErrorProvider.tsx +++ b/client/src/ErrorProvider.tsx @@ -5,7 +5,7 @@ import React, {createContext, useState, useContext, ReactNode} from 'react'; interface ErrorContextType { hasError: boolean; errorMessage: string; - setError: (error: Error) => void; + setError: (error: ServerError) => void; clearError: () => void; } @@ -27,12 +27,12 @@ export const ErrorProvider: React.FC = ({children}) => { const setError = (error: ServerError) => { setHasError(false); - setErrorMessage(ERROR_MESSAGES[error.code]); - console.log(ERROR_MESSAGES[error.code]); + setErrorMessage(''); + setTimeout(() => { setHasError(true); - setErrorMessage(error.message); - }, 0); // 상태를 빠르게 변경하기 위해 타이머 사용 + setErrorMessage(ERROR_MESSAGES[error.code] ?? '지금은 에러 코드가 안바뀌어서 에러 메세지가 없어요.'); + }, 0); }; const clearError = () => { From 94fb5a86c3760a7fe58c6777910a9f45db56318a Mon Sep 17 00:00:00 2001 From: pakxe Date: Wed, 7 Aug 2024 17:18:00 +0900 Subject: [PATCH 10/40] =?UTF-8?q?feat:=20=EC=84=9C=EB=B2=84=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20code=EC=99=80=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=EB=A5=BC=20=EB=A7=A4=EC=B9=AD=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=83=81=EC=88=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/constants/errorMessage.ts | 40 +++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/client/src/constants/errorMessage.ts b/client/src/constants/errorMessage.ts index e37220f7f..1868ceaa3 100644 --- a/client/src/constants/errorMessage.ts +++ b/client/src/constants/errorMessage.ts @@ -1,8 +1,36 @@ -const ERROR_MESSAGE = { - eventName: '행사 이름은 30자 이하만 가능해요', - memberName: '참여자 이름은 8자 이하의 한글, 영어만 가능해요', - purchasePrice: '10,000,000원 이하의 숫자만 입력이 가능해요', - purchaseTitle: '지출 이름은 30자 이하의 한글, 영어, 숫자만 가능해요', +type ErrorMessage = Record; + +const ERROR_MESSAGES: ErrorMessage = { + EVENT_NOT_FOUND: '존재하지 않는 행사입니다.', + EVENT_NAME_LENGTH_INVALID: '행사 이름은 2자 이상 30자 이하만 입력 가능합니다.', + EVENT_NAME_CONSECUTIVE_SPACES: '행사 이름에는 공백 문자가 연속될 수 없습니다.', + EVENT_PASSWORD_FORMAT_INVALID: '비밀번호는 4자리 숫자만 가능합니다.', + + ACTION_NOT_FOUND: '존재하지 않는 액션입니다.', + + MEMBER_NAME_LENGTH_INVALID: '멤버 이름은 1자 이상 4자 이하만 입력 가능합니다.', + MEMBER_NAME_DUPLICATE: '중복된 행사 참여 인원 이름이 존재합니다.', + MEMBER_NOT_EXIST: '현재 참여하고 있지 않은 인원이 존재합니다.', + MEMBER_ALREADY_EXIST: '현재 참여하고 있는 인원이 존재합니다.', + + MEMBER_ACTION_NOT_FOUND: '존재하지 않는 멤버 액션입니다.', + MEMBER_ACTION_STATUS_INVALID: '유효하지 않은 멤버 액션 상태입니다.', + + BILL_ACTION_NOT_FOUND: '존재하지 않는 지출 액션입니다.', + BILL_ACTION_TITLE_INVALID: '앞뒤 공백을 제거한 지출 내역 제목은 %d자 ~ %d자여야 합니다.', + BILL_ACTION_PRICE_INVALID: '지출 금액은 10,000,000 이하의 자연수여야 합니다.', + + REQUEST_EMPTY: '입력 값은 공백일 수 없습니다.', + MESSAGE_NOT_READABLE: '읽을 수 없는 요청입니다.', + NO_RESOURCE_REQUEST: '존재하지 않는 자원입니다.', + + INTERNAL_SERVER_ERROR: '서버 내부에 에러가 발생했습니다.', + + PASSWORD_INVALID: '비밀번호가 일치하지 않습니다.', + TOKEN_NOT_FOUND: '토큰이 존재하지 않습니다.', + TOKEN_EXPIRED: '만료된 토큰입니다.', + TOKEN_INVALID: '유효하지 않은 토큰입니다.', + FORBIDDEN: '접근할 수 없는 행사입니다.', }; -export default ERROR_MESSAGE; +export default ERROR_MESSAGES; From 2ab518bf278f9f12dd1f17be135a509b7f11b9a5 Mon Sep 17 00:00:00 2001 From: pakxe Date: Thu, 8 Aug 2024 01:11:47 +0900 Subject: [PATCH 11/40] =?UTF-8?q?chore:=20msw,=20react-error-boundary=20?= =?UTF-8?q?=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/package.json b/client/package.json index 0216028cc..0b9e73310 100644 --- a/client/package.json +++ b/client/package.json @@ -35,6 +35,7 @@ "html-loader": "^5.0.0", "html-webpack-plugin": "^5.6.0", "modify-source-webpack-plugin": "^4.1.0", + "msw": "^2.3.5", "prettier": "3.3.2", "ts-loader": "^9.5.1", "typescript": "^5.5.3", @@ -48,6 +49,7 @@ "haengdong-design": "^0.1.58", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-error-boundary": "^4.0.13", "react-router-dom": "^6.24.1" }, "engines": { From 050172a69c464594670cc4c2311b247682c4ff29 Mon Sep 17 00:00:00 2001 From: pakxe Date: Thu, 8 Aug 2024 01:13:08 +0900 Subject: [PATCH 12/40] =?UTF-8?q?chore:=20=EB=B8=8C=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=EC=A0=80=20=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=20msw=EB=A5=BC?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85=20=ED=8C=8C=EC=9D=BC=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/public/mockServiceWorker.js | 284 +++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 client/public/mockServiceWorker.js diff --git a/client/public/mockServiceWorker.js b/client/public/mockServiceWorker.js new file mode 100644 index 000000000..15751fa19 --- /dev/null +++ b/client/public/mockServiceWorker.js @@ -0,0 +1,284 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const PACKAGE_VERSION = '2.3.5' +const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseClone.body, + headers: Object.fromEntries(responseClone.headers.entries()), + }, + }, + [responseClone.body], + ) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone() + + function passthrough() { + const headers = Object.fromEntries(requestClone.headers.entries()) + + // Remove internal MSW request header so the passthrough request + // complies with any potential CORS preflight checks on the server. + // Some servers forbid unknown request headers. + delete headers['x-msw-intention'] + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const requestBuffer = await request.arrayBuffer() + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, + }, + [requestBuffer], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage( + message, + [channel.port2].concat(transferrables.filter(Boolean)), + ) + }) +} + +async function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} From 88e628ababc718232ca74235659e46bf966ddcab Mon Sep 17 00:00:00 2001 From: pakxe Date: Thu, 8 Aug 2024 01:13:56 +0900 Subject: [PATCH 13/40] =?UTF-8?q?feat:=20msw=EB=A5=BC=20=EC=95=B1=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=EC=A7=80=EC=A0=90=EC=97=90=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/index.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/client/src/index.tsx b/client/src/index.tsx index eeffe86f4..82ccd33c5 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -1,11 +1,18 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import {RouterProvider} from 'react-router-dom'; import router from './router'; +import {RouterProvider} from 'react-router-dom'; + +async function enableMocking() { + const {worker} = await import('./mocks/browser'); //Dynamic import하는 것이 눈에 띄였다. + return worker.start(); +} -ReactDOM.createRoot(document.getElementById('root')!).render( - - - , -); +enableMocking().then(() => { + ReactDOM.createRoot(document.getElementById('root')!).render( + + + , + ); +}); From 4af834499041278c6327daeaa8139536aeeea4d5 Mon Sep 17 00:00:00 2001 From: pakxe Date: Thu, 8 Aug 2024 01:15:29 +0900 Subject: [PATCH 14/40] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EA=B0=84=EB=8B=A8=ED=95=9C=20?= =?UTF-8?q?post=20=EB=AA=A8=ED=82=B9=20=ED=95=A8=EC=88=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/browser.ts | 4 ++++ client/src/mocks/handlers.ts | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 client/src/mocks/browser.ts create mode 100644 client/src/mocks/handlers.ts diff --git a/client/src/mocks/browser.ts b/client/src/mocks/browser.ts new file mode 100644 index 000000000..7f609b74a --- /dev/null +++ b/client/src/mocks/browser.ts @@ -0,0 +1,4 @@ +import {setupWorker} from 'msw/browser'; +import {handlers} from './handlers'; + +export const worker = setupWorker(...handlers); diff --git a/client/src/mocks/handlers.ts b/client/src/mocks/handlers.ts new file mode 100644 index 000000000..75286a6e1 --- /dev/null +++ b/client/src/mocks/handlers.ts @@ -0,0 +1,8 @@ +import {TEMP_PREFIX} from '@apis/tempPrefix'; +import {http, HttpResponse} from 'msw'; + +export const handlers = [ + http.post(`${process.env.API_BASE_URL!}/${TEMP_PREFIX}`, ({request}) => { + return HttpResponse.json({errorCode: 'testCode', message: 'testMessage'}, {status: 404}); + }), +]; From 4608e68d27eaef653524f240832b20cd2ebbc15b Mon Sep 17 00:00:00 2001 From: pakxe Date: Thu, 8 Aug 2024 01:16:37 +0900 Subject: [PATCH 15/40] =?UTF-8?q?feat:=20=ED=95=B8=EB=93=A4=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=97=90=EB=9F=AC=20=EB=98=90?= =?UTF-8?q?=EB=8A=94=20=EC=A7=80=EC=A0=95=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9D=80=20path=EB=A1=9C=20=EC=9D=B4=EB=8F=99=EC=8B=9C=20?= =?UTF-8?q?=EB=B3=B4=EC=97=AC=EC=A4=84=20=EC=97=90=EB=9F=AC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/ErrorPage/ErrorPage.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 client/src/pages/ErrorPage/ErrorPage.tsx diff --git a/client/src/pages/ErrorPage/ErrorPage.tsx b/client/src/pages/ErrorPage/ErrorPage.tsx new file mode 100644 index 000000000..b720710ee --- /dev/null +++ b/client/src/pages/ErrorPage/ErrorPage.tsx @@ -0,0 +1,17 @@ +import {Back, Flex, MainLayout, Title, TopNav} from 'haengdong-design'; + +// import errorPageIcon from '../../assets/error_page_icon.png'; + +// TODO: (@weadie) 임시 에러 페이지입니다. +const ErrorPage = () => { + return ( + + + {/* <Flex width="100%" justifyContent="center"> */} + {/* <img src={errorPageIcon} height="100" width="100" alt="Error Icon"></img> */} + {/* </Flex> */} + </MainLayout> + ); +}; + +export default ErrorPage; From 4d4ea35d3713b546b8aa4d8ff33a324e5d14c8c1 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:18:19 +0900 Subject: [PATCH 16/40] =?UTF-8?q?feat:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20state=EC=A0=9C=EA=B1=B0=EC=99=80=20type=20narrowing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/useFetch.ts | 31 +++++++++++++++++++++++++++++++ client/src/apis/useFetch.tsx | 35 ----------------------------------- 2 files changed, 31 insertions(+), 35 deletions(-) create mode 100644 client/src/apis/useFetch.ts delete mode 100644 client/src/apis/useFetch.tsx diff --git a/client/src/apis/useFetch.ts b/client/src/apis/useFetch.ts new file mode 100644 index 000000000..315b636ce --- /dev/null +++ b/client/src/apis/useFetch.ts @@ -0,0 +1,31 @@ +import {UNHANDLED_ERROR} from '@constants/errorMessage'; +import {ServerError, useError} from '../ErrorProvider'; +import {useState} from 'react'; + +export const useFetch = () => { + const {setError, clearError} = useError(); + const [loading, setLoading] = useState(false); + + const fetch = async <T>(queryFn: () => Promise<T>): Promise<T> => { + setLoading(true); + clearError(); + + try { + const result = await queryFn(); + return result; + } catch (error) { + if (error instanceof Error) { + const errorBody: ServerError = await JSON.parse(error.message); + + setError(errorBody); + throw new Error(errorBody.message); + } else { + throw new Error(UNHANDLED_ERROR); + } + } finally { + setLoading(false); + } + }; + + return {loading, fetch}; +}; diff --git a/client/src/apis/useFetch.tsx b/client/src/apis/useFetch.tsx deleted file mode 100644 index 549ca0822..000000000 --- a/client/src/apis/useFetch.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import {useError} from '../ErrorProvider'; -import {useState} from 'react'; - -interface FetchOptions<T> { - queryFn: () => Promise<T>; -} - -export const useFetch = () => { - const {setError, clearError} = useError(); - const [loading, setLoading] = useState(false); - const [data, setData] = useState<any>(null); - - const request = async <T,>({queryFn}: FetchOptions<T>): Promise<T | null> => { - setLoading(true); - clearError(); - try { - const result = await queryFn(); - setData(result); - return result; - } catch (error) { - if (error instanceof Error) { - const errorBody = await JSON.parse(error.message); - setError(errorBody); - console.log(error); - } else { - setError(new Error('An unknown error occurred')); - } - return null; - } finally { - setLoading(false); - } - }; - - return {data, loading, request}; -}; From eaf75845b557f44c8321cfd71e762e3dd63fbab0 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:19:04 +0900 Subject: [PATCH 17/40] =?UTF-8?q?feat:=20=EB=8B=A4=EB=A4=84=EC=A7=80?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=97=90=EB=9F=AC=EA=B0=80=20?= =?UTF-8?q?=EB=B0=9C=EC=83=9D=ED=96=88=EC=9D=84=20=EC=8B=9C=20=EC=99=B8?= =?UTF-8?q?=EB=B6=80=EB=A1=9C=20=EC=97=90=EB=9F=AC=20=EB=8D=98=EC=A7=80?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/ErrorProvider.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/src/ErrorProvider.tsx b/client/src/ErrorProvider.tsx index 670b71a02..dd38c4a38 100644 --- a/client/src/ErrorProvider.tsx +++ b/client/src/ErrorProvider.tsx @@ -24,6 +24,20 @@ type ServerError = { export const ErrorProvider: React.FC<ErrorProviderProps> = ({children}) => { const [hasError, setHasError] = useState(false); const [errorMessage, setErrorMessage] = useState(''); + const [error, setErrorState] = useState<ServerError | null>(null); + + useEffect(() => { + if (error) { + if (isUnhandledError(error.errorCode)) { + throw new Error(UNHANDLED_ERROR); + } + + setHasError(true); + const message = ERROR_MESSAGES[error.errorCode]; + setErrorMessage(message); + // callback(message); + } + }, [error, callback]); const setError = (error: ServerError) => { setHasError(false); From 92b36654d5f5061fa13a6826386cbf582053c327 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:20:09 +0900 Subject: [PATCH 18/40] =?UTF-8?q?feat:=20=ED=95=B8=EB=93=A4=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EC=97=90=EB=9F=AC=20=ED=8C=90=EB=8B=A8=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=ED=95=A8=EC=88=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/ErrorProvider.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/src/ErrorProvider.tsx b/client/src/ErrorProvider.tsx index dd38c4a38..ca0745eeb 100644 --- a/client/src/ErrorProvider.tsx +++ b/client/src/ErrorProvider.tsx @@ -67,3 +67,9 @@ export const useError = (): ErrorContextType => { } return context; }; + +const isUnhandledError = (errorCode: string) => { + if (errorCode === 'INTERNAL_SERVER_ERROR') return true; + + return ERROR_MESSAGES[errorCode] === undefined; +}; From f002c576fde6a74d332edd124df2b1319c215f54 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:20:33 +0900 Subject: [PATCH 19/40] =?UTF-8?q?feat:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20state,=20setTimeout=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/ErrorProvider.tsx | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/client/src/ErrorProvider.tsx b/client/src/ErrorProvider.tsx index ca0745eeb..db559725f 100644 --- a/client/src/ErrorProvider.tsx +++ b/client/src/ErrorProvider.tsx @@ -1,12 +1,12 @@ -import ERROR_MESSAGES from '@constants/errorMessage'; -import React, {createContext, useState, useContext, ReactNode} from 'react'; +import ERROR_MESSAGES, {UNHANDLED_ERROR} from '@constants/errorMessage'; +import React, {createContext, useState, useContext, useEffect, ReactNode} from 'react'; // 에러 컨텍스트 생성 interface ErrorContextType { hasError: boolean; errorMessage: string; setError: (error: ServerError) => void; - clearError: () => void; + clearError: (ms?: number) => void; } const ErrorContext = createContext<ErrorContextType | undefined>(undefined); @@ -14,14 +14,15 @@ const ErrorContext = createContext<ErrorContextType | undefined>(undefined); // 에러 컨텍스트를 제공하는 프로바이더 컴포넌트 interface ErrorProviderProps { children: ReactNode; + callback?: (message: string) => void; } -type ServerError = { - code: string; +export type ServerError = { + errorCode: string; message: string; }; -export const ErrorProvider: React.FC<ErrorProviderProps> = ({children}) => { +export const ErrorProvider = ({children, callback}: ErrorProviderProps) => { const [hasError, setHasError] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const [error, setErrorState] = useState<ServerError | null>(null); @@ -40,18 +41,17 @@ export const ErrorProvider: React.FC<ErrorProviderProps> = ({children}) => { }, [error, callback]); const setError = (error: ServerError) => { - setHasError(false); + setHasError(true); setErrorMessage(''); - - setTimeout(() => { - setHasError(true); - setErrorMessage(ERROR_MESSAGES[error.code] ?? '지금은 에러 코드가 안바뀌어서 에러 메세지가 없어요.'); - }, 0); + setErrorState(error); }; - const clearError = () => { - setHasError(false); - setErrorMessage(''); + const clearError = (ms: number = 0) => { + setTimeout(() => { + setHasError(false); + setErrorMessage(''); + setErrorState(null); + }, ms); }; return ( From 2652c2e0b333814cdbb610df14da5e9ee2345d92 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:21:01 +0900 Subject: [PATCH 20/40] =?UTF-8?q?feat:=20=EC=A7=80=EC=A0=95=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20path=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EC=8B=9C=20=EC=97=90=EB=9F=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=9D=84=EC=9A=B0=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/router.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/src/router.tsx b/client/src/router.tsx index 75777310a..2d8998caf 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -10,6 +10,8 @@ import {EventPage} from '@pages/EventPage'; import {ROUTER_URLS} from '@constants/routerUrls'; import App from './App'; +import ErrorPage from '@pages/ErrorPage/ErrorPage'; +import UnknownErrorBoundary from './UnknownErrorBoundary'; const router = createBrowserRouter([ { @@ -37,6 +39,10 @@ const router = createBrowserRouter([ {path: ROUTER_URLS.home, element: <HomePage />}, ], }, + { + path: '*', + element: <ErrorPage />, + }, ], }, ]); From c3bb543a7a0206a7897b9122f19709b0ab7f8b4c Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:21:43 +0900 Subject: [PATCH 21/40] =?UTF-8?q?feat:=20=EB=B0=98=EB=B3=B5=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EC=88=AB=EC=9E=90=20=EC=83=81=EC=88=98=ED=99=94?= =?UTF-8?q?=EC=99=80=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20jsx=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/Toast.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/src/components/Toast/Toast.tsx b/client/src/components/Toast/Toast.tsx index 8e10690a0..621ac49e1 100644 --- a/client/src/components/Toast/Toast.tsx +++ b/client/src/components/Toast/Toast.tsx @@ -1,4 +1,3 @@ -/** @jsxImportSource @emotion/react */ import {createPortal} from 'react-dom'; import {useState, useEffect} from 'react'; import {toastStyle, textStyle, toastMarginStyle} from './Toast.style'; @@ -18,12 +17,15 @@ const renderIcon = (type: ToastType) => { } }; +const ANIMATION_TIME = 500; + const Toast = ({ type = 'confirm', top = '0px', bottom = '0px', isClickToClose = true, position = 'bottom', + showingTime, message, onUndo, onClose, @@ -37,8 +39,8 @@ const Toast = ({ setIsVisible(false); setTimeout(() => { if (onClose) onClose(); - }, 500); // fadeOut 애니메이션 시간과 동일하게 설정 - }, 3000); // 토스트가 화면에 보이는 시간 설정 + }, ANIMATION_TIME); // fadeOut 애니메이션 시간과 동일하게 설정 + }, showingTime - ANIMATION_TIME); // 토스트가 내려가는 시간 확보 return () => { clearTimeout(timer); @@ -51,7 +53,7 @@ const Toast = ({ setIsVisible(false); setTimeout(() => { onClose(); - }, 500); // fadeOut 애니메이션 시간과 동일하게 설정 + }, ANIMATION_TIME); // fadeOut 애니메이션 시간과 동일하게 설정 }; return createPortal( From 843179633a5f954a3ded9e0d40e5c15d7cfd1de3 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:23:58 +0900 Subject: [PATCH 22/40] =?UTF-8?q?feat:=20=EC=9E=A1=EC=95=84=EC=84=9C=20?= =?UTF-8?q?=EB=8B=A4=EB=A3=A8=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=B0=94=EC=9A=B4=EB=8D=94=EB=A6=AC=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 20 +++++++++++--------- client/src/UnhandledErrorBoundary.tsx | 9 +++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 client/src/UnhandledErrorBoundary.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index da01f9780..e43d9c5f8 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,21 +3,23 @@ import {HDesignProvider} from 'haengdong-design'; import {Global} from '@emotion/react'; import {GlobalStyle} from './GlobalStyle'; -import {toastConfig} from 'react-simple-toasts'; +// import toast from 'react-simple-toasts'; import {ErrorProvider} from './ErrorProvider'; import {ToastProvider} from '@components/Toast/ToastProvider'; - -toastConfig({theme: 'dark'}); +import UnhandledErrorBoundary from './UnhandledErrorBoundary'; const App: React.FC = () => { return ( <HDesignProvider> - <Global styles={GlobalStyle} /> - <ErrorProvider> - <ToastProvider> - <Outlet /> - </ToastProvider> - </ErrorProvider> + <UnhandledErrorBoundary> + <Global styles={GlobalStyle} /> + <ErrorProvider> + {/* <ErrorProvider callback={toast}> */} + <ToastProvider> + <Outlet /> + </ToastProvider> + </ErrorProvider> + </UnhandledErrorBoundary> </HDesignProvider> ); }; diff --git a/client/src/UnhandledErrorBoundary.tsx b/client/src/UnhandledErrorBoundary.tsx new file mode 100644 index 000000000..fae2a7160 --- /dev/null +++ b/client/src/UnhandledErrorBoundary.tsx @@ -0,0 +1,9 @@ +import ErrorPage from '@pages/ErrorPage/ErrorPage'; +import {StrictPropsWithChildren} from 'haengdong-design/dist/type/strictPropsWithChildren'; +import {ErrorBoundary} from 'react-error-boundary'; + +const UnhandledErrorBoundary = ({children}: StrictPropsWithChildren) => { + return <ErrorBoundary fallback={<ErrorPage />}>{children}</ErrorBoundary>; +}; + +export default UnhandledErrorBoundary; From 7e0478faeb1dae256be5291c90d6c2dac8c2d665 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:25:11 +0900 Subject: [PATCH 23/40] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=98=EA=B2=8C=20=EC=82=AC=EC=9A=A9=EB=90=98=EB=8A=94=20con?= =?UTF-8?q?tentType=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/fetcher.ts | 62 +++++--------------------------------- 1 file changed, 8 insertions(+), 54 deletions(-) diff --git a/client/src/apis/fetcher.ts b/client/src/apis/fetcher.ts index f7ac3b976..1e241834a 100644 --- a/client/src/apis/fetcher.ts +++ b/client/src/apis/fetcher.ts @@ -51,16 +51,11 @@ export const requestPut = ({headers = {}, ...args}: RequestProps) => { return fetcher({method: 'PUT', headers, ...args}); }; -export const requestPost = async <T>({headers = {}, ...args}: RequestProps): Promise<T | undefined> => { +export const requestPost = async <T>({headers = {}, ...args}: RequestProps): Promise<T> => { const response = await fetcher({method: 'POST', headers, ...args}); - const contentType = response!.headers.get('Content-Type'); - if (contentType && contentType.includes('application/json')) { - const data: T = await response!.json(); - return data; - } - - return; + const data: T = await response!.json(); + return data; }; export const requestDelete = ({headers = {}, ...args}: RequestProps) => { @@ -86,52 +81,11 @@ const fetcher = ({baseUrl = API_BASE_URL, method, endpoint, headers, body, query return errorHandler(url, options); }; -// class ErrorWithHeader extends Error { -// header: string; - -// constructor(header: string, message: string) { -// super(message); -// this.name = this.constructor.name; -// this.header = header; -// } -// } - const errorHandler = async (url: string, options: Options) => { - try { - const response = await fetch(url, options); - if (!response.ok) { - const serverErrorMessage = await response.text(); - throw new Error(serverErrorMessage || ''); // 받은 에러 메세지가 없는 경우는 서버에게.. - } - return response; - } catch (error) { - if (error instanceof Error) { - throw new Error(error.message); - } - return; + const response = await fetch(url, options); + if (!response.ok) { + const serverErrorMessage = await response.text(); + throw new Error(serverErrorMessage || ''); // 받은 에러 메세지가 없는 경우는 서버에게.. } + return response; }; - -// export const ERROR_MESSAGE = { -// SYSTEM_FAULT: 'system error. 관리자에게 문의하십시오', -// OFFLINE: '네트워크 연결이 끊어졌습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.', -// UNKNOWN: '알 수 없는 에러입니다. 관리자에게 문의해주세요. (연락처...)', -// }; - -// const getErrorMessage = (error: unknown) => { -// return ERROR_MESSAGE.UNKNOWN; - -// if (error instanceof TypeError) return ERROR_MESSAGE.OFFLINE; - -// if (error instanceof Error) { -// const mappedErrorMessage = Object.entries(SERVER_ERROR_MESSAGE).find(([key]) => { -// const upperCaseErrorMessage = convertToUpperCase(error.message.split(SERVER_ERROR_MESSAGE_DIVIDER)[0]); - -// return upperCaseErrorMessage.includes(key); -// })?.[1]; - -// return mappedErrorMessage ?? ERROR_MESSAGE.UNKNOWN; -// } - -// return ERROR_MESSAGE.UNKNOWN; -// }; From 2b35ee0d5952d8366017851f844781da434701fc Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:25:46 +0900 Subject: [PATCH 24/40] =?UTF-8?q?feat:=20Toast=EC=97=90=20showingTime?= =?UTF-8?q?=EC=9D=84=20=EC=A0=84=EB=8B=AC=ED=95=B4=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=EC=9D=84=20=EC=A7=80=EC=86=8D=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EB=A7=8C=ED=81=BC=20=EC=9C=A0=EC=A7=80?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/Toast.type.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/components/Toast/Toast.type.ts b/client/src/components/Toast/Toast.type.ts index 12a436c2d..31ec64e3d 100644 --- a/client/src/components/Toast/Toast.type.ts +++ b/client/src/components/Toast/Toast.type.ts @@ -12,6 +12,7 @@ export interface ToastOptionProps { onUndo?: () => void; isClickToClose?: boolean; onClose?: () => void; + showingTime: number; } export interface ToastRequiredProps { From fe2be35ad4b9afa95d8d9cda237bbddeff64ca0f Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:26:35 +0900 Subject: [PATCH 25/40] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=8B=9C=EA=B0=84=20=ED=9B=84=EC=97=90=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94=ED=95=98=EB=8A=94=20=EC=B1=85?= =?UTF-8?q?=EC=9E=84=EC=9D=84=20useError=EC=97=90=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/ToastProvider.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/client/src/components/Toast/ToastProvider.tsx b/client/src/components/Toast/ToastProvider.tsx index cdeab0da2..3600d5939 100644 --- a/client/src/components/Toast/ToastProvider.tsx +++ b/client/src/components/Toast/ToastProvider.tsx @@ -3,9 +3,12 @@ import {createContext, useCallback, useContext, useEffect, useState} from 'react import {ToastProps} from './Toast.type'; import Toast from './Toast'; import {useError} from '../../ErrorProvider'; +import ERROR_MESSAGES from '@constants/errorMessage'; export const ToastContext = createContext<ToastContextProps | null>(null); +const DEFAULT_TIME = 3000; + interface ToastContextProps { showToast: (args: ShowToast) => void; } @@ -19,7 +22,7 @@ const ToastProvider = ({children}: React.PropsWithChildren) => { const [currentToast, setCurrentToast] = useState<ShowToast | null>(null); const {hasError, errorMessage, clearError} = useError(); - const showToast = useCallback(({showingTime = 3000, isAlwaysOn = false, ...toastProps}: ShowToast) => { + const showToast = useCallback(({showingTime = DEFAULT_TIME, isAlwaysOn = false, ...toastProps}: ShowToast) => { setCurrentToast({showingTime, isAlwaysOn, ...toastProps}); }, []); @@ -30,22 +33,16 @@ const ToastProvider = ({children}: React.PropsWithChildren) => { useEffect(() => { if (hasError) { showToast({ - message: errorMessage || 'An error occurred', - showingTime: 5000, // 필요에 따라 시간 설정 + message: errorMessage || ERROR_MESSAGES.UNHANDLED, + showingTime: DEFAULT_TIME, // TODO: (@weadie) 나중에 토스트 프로바이더를 제거한 토스트를 만들 것이기 때문에 많이 리펙터링 안함 isAlwaysOn: false, position: 'bottom', bottom: '100px', }); - const timer = setTimeout(() => { - clearError(); - }, 5000); // 토스트가 표시되는 시간과 동일하게 설정 - - return () => clearTimeout(timer); + clearError(DEFAULT_TIME); } - - return; - }, [hasError, errorMessage, showToast, clearError]); + }, [errorMessage, hasError]); useEffect(() => { if (!currentToast) return; From 252fa01854e8f3fb07acbc4e600833509ff676d8 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:26:52 +0900 Subject: [PATCH 26/40] =?UTF-8?q?feat:=20=EC=95=8C=20=EC=88=98=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=97=90=EB=9F=AC=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EB=A9=94=EC=84=B8=EC=A7=80=EC=99=80=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/constants/errorMessage.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/src/constants/errorMessage.ts b/client/src/constants/errorMessage.ts index 1868ceaa3..f30dc31d8 100644 --- a/client/src/constants/errorMessage.ts +++ b/client/src/constants/errorMessage.ts @@ -31,6 +31,10 @@ const ERROR_MESSAGES: ErrorMessage = { TOKEN_EXPIRED: '만료된 토큰입니다.', TOKEN_INVALID: '유효하지 않은 토큰입니다.', FORBIDDEN: '접근할 수 없는 행사입니다.', + + UNHANDLED: '알 수 없는 에러입니다.', }; +export const UNHANDLED_ERROR = 'UNHANDLED'; + export default ERROR_MESSAGES; From 95931550b9fe395671cea9150719511618e28fc3 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:27:26 +0900 Subject: [PATCH 27/40] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=83=88=EB=A1=9C=20=EC=83=9D=EC=84=B1=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=9C=84=ED=95=9C=20api=EC=BD=9C=EC=9D=84=20?= =?UTF-8?q?=EB=8B=B4=EB=8B=B9=ED=95=98=EB=8A=94=20useEvent=ED=9B=85=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useEvent.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 client/src/hooks/useEvent.tsx diff --git a/client/src/hooks/useEvent.tsx b/client/src/hooks/useEvent.tsx new file mode 100644 index 000000000..92e93360b --- /dev/null +++ b/client/src/hooks/useEvent.tsx @@ -0,0 +1,14 @@ +import {ResponsePostNewEvent, requestPostNewEvent} from '@apis/request/event'; +import {useFetch} from '@apis/useFetch'; + +const useEvent = () => { + const {fetch} = useFetch(); + + const createNewEvent = async ({eventName}: {eventName: string}) => { + return await fetch<ResponsePostNewEvent>(() => requestPostNewEvent({eventName})); + }; + + return {createNewEvent}; +}; + +export default useEvent; From dadc181fe71b3c34db2b0316db1069488571a688 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:27:41 +0900 Subject: [PATCH 28/40] =?UTF-8?q?feat:=20useEvent=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/CreateEventPage/SetEventNamePage.tsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/client/src/pages/CreateEventPage/SetEventNamePage.tsx b/client/src/pages/CreateEventPage/SetEventNamePage.tsx index ebb86646c..e2df8c052 100644 --- a/client/src/pages/CreateEventPage/SetEventNamePage.tsx +++ b/client/src/pages/CreateEventPage/SetEventNamePage.tsx @@ -1,30 +1,24 @@ import {useState} from 'react'; import {useNavigate} from 'react-router-dom'; -import {FixedButton, MainLayout, LabelInput, Input, Title, TopNav, Back} from 'haengdong-design'; +import {FixedButton, MainLayout, LabelInput, Title, TopNav, Back} from 'haengdong-design'; -import {ResponsePostNewEvent, requestPostNewEvent} from '@apis/request/event'; import validateEventName from '@utils/validate/validateEventName'; import {ROUTER_URLS} from '@constants/routerUrls'; -import {useFetch} from '@apis/useFetch'; +import useEvent from '@hooks/useEvent'; const SetEventNamePage = () => { const [eventName, setEventName] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const [canSubmit, setCanSubmit] = useState(false); const navigate = useNavigate(); - - const {request} = useFetch(); + const {createNewEvent} = useEvent(); const submitEventName = async (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); + const {eventId} = await createNewEvent({eventName}); - const response = await request<ResponsePostNewEvent | undefined>({queryFn: () => requestPostNewEvent({eventName})}); - - if (response) { - const {eventId} = response; - navigate(`${ROUTER_URLS.eventCreateComplete}?${new URLSearchParams({eventId})}`); - } + navigate(`${ROUTER_URLS.eventCreateComplete}?${new URLSearchParams({eventId})}`); }; const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { @@ -41,6 +35,7 @@ const SetEventNamePage = () => { setErrorMessage(validation.errorMessage ?? ''); } }; + return ( <MainLayout> <TopNav> From 20efacebb9d67b64fd738e714016460b6c95a8e0 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:27:53 +0900 Subject: [PATCH 29/40] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/router.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/router.tsx b/client/src/router.tsx index 2d8998caf..cc50454c1 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -11,7 +11,6 @@ import {ROUTER_URLS} from '@constants/routerUrls'; import App from './App'; import ErrorPage from '@pages/ErrorPage/ErrorPage'; -import UnknownErrorBoundary from './UnknownErrorBoundary'; const router = createBrowserRouter([ { From c65f6e7283c106de2f6753b2d908ae5516ef7493 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:28:14 +0900 Subject: [PATCH 30/40] =?UTF-8?q?chore:=20msw,=20react-error-boundary=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 696 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 696 insertions(+) diff --git a/client/package-lock.json b/client/package-lock.json index 67f32bd58..555504e49 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -13,6 +13,7 @@ "haengdong-design": "^0.1.58", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-error-boundary": "^4.0.13", "react-router-dom": "^6.24.1" }, "devDependencies": { @@ -38,6 +39,7 @@ "html-loader": "^5.0.0", "html-webpack-plugin": "^5.6.0", "modify-source-webpack-plugin": "^4.1.0", + "msw": "^2.3.5", "prettier": "3.3.2", "ts-loader": "^9.5.1", "typescript": "^5.5.3", @@ -1793,6 +1795,43 @@ "node": ">=6.9.0" } }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/cookie/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@bundled-es-modules/tough-cookie": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", + "dev": true, + "dependencies": { + "@types/tough-cookie": "^4.0.5", + "tough-cookie": "^4.1.4" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -2075,6 +2114,143 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@inquirer/confirm": { + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", + "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", + "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.2", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.1.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@inquirer/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@inquirer/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.5.tgz", + "integrity": "sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.2.tgz", + "integrity": "sha512-w9qFkumYDCNyDZmNQjf/n6qQuvQ4dMC3BJesY4oF+yr0CxR5vxujflAVeIcS6U336uzi9GM0kAfZlLrZ9UTkpA==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2232,6 +2408,23 @@ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, + "node_modules/@mswjs/interceptors": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.29.1.tgz", + "integrity": "sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==", + "dev": true, + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2267,6 +2460,28 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2798,6 +3013,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, "node_modules/@types/dotenv-webpack": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/dotenv-webpack/-/dotenv-webpack-7.0.7.tgz", @@ -2898,6 +3119,15 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "22.1.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", @@ -3003,6 +3233,24 @@ "@types/node": "*" } }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true + }, "node_modules/@types/ws": { "version": "8.5.12", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", @@ -3518,6 +3766,33 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-html-community": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", @@ -4178,6 +4453,111 @@ "node": ">=0.10.0" } }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -6277,6 +6657,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -6447,6 +6836,15 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/haengdong-design": { "version": "0.1.58", "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.58.tgz", @@ -6558,6 +6956,12 @@ "he": "bin/he" } }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -7221,6 +7625,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -7976,6 +8386,125 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/msw": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.3.5.tgz", + "integrity": "sha512-+GUI4gX5YC5Bv33epBrD+BGdmDvBg2XGruiWnI3GbIbRmMMBeZ5gs3mJ51OWSGHgJKztZ8AtZeYMMNMVrje2/Q==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^3.0.0", + "@mswjs/interceptors": "^0.29.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.7.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/msw/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/msw/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/msw/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/msw/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/msw/node_modules/path-to-regexp": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "dev": true + }, + "node_modules/msw/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", @@ -7989,6 +8518,15 @@ "multicast-dns": "cli.js" } }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -8275,6 +8813,12 @@ "node": ">= 0.8.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8640,6 +9184,12 @@ "node": ">= 0.10" } }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -8664,6 +9214,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8749,6 +9305,17 @@ "react": "^18.3.1" } }, + "node_modules/react-error-boundary": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz", + "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -8947,6 +9514,15 @@ "strip-ansi": "^6.0.1" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -9565,6 +10141,12 @@ "node": ">= 0.4" } }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -10056,6 +10638,30 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/tree-dump": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", @@ -10236,6 +10842,18 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.23.0.tgz", + "integrity": "sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -10474,6 +11092,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -11165,6 +11793,15 @@ } } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -11178,6 +11815,53 @@ "node": ">= 6" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -11189,6 +11873,18 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } From 559ccc3ecfbc09d398a29560dded1b9908cde0e1 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 01:28:41 +0900 Subject: [PATCH 31/40] =?UTF-8?q?chore:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20import=EC=99=80=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/ErrorPage/ErrorPage.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/client/src/pages/ErrorPage/ErrorPage.tsx b/client/src/pages/ErrorPage/ErrorPage.tsx index b720710ee..f71f5da8e 100644 --- a/client/src/pages/ErrorPage/ErrorPage.tsx +++ b/client/src/pages/ErrorPage/ErrorPage.tsx @@ -1,15 +1,10 @@ -import {Back, Flex, MainLayout, Title, TopNav} from 'haengdong-design'; - -// import errorPageIcon from '../../assets/error_page_icon.png'; +import {MainLayout, Title} from 'haengdong-design'; // TODO: (@weadie) 임시 에러 페이지입니다. const ErrorPage = () => { return ( <MainLayout> <Title title="알 수 없는 오류입니다." description="오류가 난 상황에 대해 {메일}로 연락주시면 소정의 상품을..." /> - {/* <Flex width="100%" justifyContent="center"> */} - {/* <img src={errorPageIcon} height="100" width="100" alt="Error Icon"></img> */} - {/* </Flex> */} </MainLayout> ); }; From a45f84589010b9d0f7ef4dfc98b15eed3123a522 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 02:02:26 +0900 Subject: [PATCH 32/40] =?UTF-8?q?refactor:=20queryFn=20->=20queryFuntion?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/useFetch.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/apis/useFetch.ts b/client/src/apis/useFetch.ts index 315b636ce..b1cd06183 100644 --- a/client/src/apis/useFetch.ts +++ b/client/src/apis/useFetch.ts @@ -6,12 +6,12 @@ export const useFetch = () => { const {setError, clearError} = useError(); const [loading, setLoading] = useState(false); - const fetch = async <T>(queryFn: () => Promise<T>): Promise<T> => { + const fetch = async <T>(queryFunction: () => Promise<T>): Promise<T> => { setLoading(true); clearError(); try { - const result = await queryFn(); + const result = await queryFunction(); return result; } catch (error) { if (error instanceof Error) { From d12979a7d61a898fe537efd051c7b61f95612141 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 02:02:44 +0900 Subject: [PATCH 33/40] =?UTF-8?q?design:=20Toast=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/Toast.style.ts | 15 ++++++--------- client/src/components/Toast/Toast.tsx | 13 +++---------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/client/src/components/Toast/Toast.style.ts b/client/src/components/Toast/Toast.style.ts index 52ce2aee7..bf7500da5 100644 --- a/client/src/components/Toast/Toast.style.ts +++ b/client/src/components/Toast/Toast.style.ts @@ -49,7 +49,7 @@ export const toastStyle = (isVisible: boolean) => padding: '0.625rem 1rem', backgroundColor: 'gray', - boxShadow: '0 8px 12px rgba(0, 0, 0, 0.16);', + boxShadow: '0 0.5rem 0.75rem rgba(0, 0, 0, 0.16);', borderRadius: '1.25rem', @@ -57,11 +57,8 @@ export const toastStyle = (isVisible: boolean) => animation: `${isVisible ? fadeIn : fadeOut} 0.5s forwards`, }); -export const textStyle = () => - css({ - width: '100%', - - color: 'white', - - whiteSpace: 'pre-line', - }); +export const textStyle = css({ + width: '100%', + color: 'white', + whiteSpace: 'pre-line', +}); diff --git a/client/src/components/Toast/Toast.tsx b/client/src/components/Toast/Toast.tsx index 621ac49e1..80f27eb2f 100644 --- a/client/src/components/Toast/Toast.tsx +++ b/client/src/components/Toast/Toast.tsx @@ -5,16 +5,9 @@ import {ToastProps, ToastType} from './Toast.type'; import {Button, Flex, Icon, Text} from 'haengdong-design'; const renderIcon = (type: ToastType) => { - switch (type) { - case 'error': - return <Icon iconType="error" />; - case 'confirm': - return <Icon iconType="confirm" />; - case 'none': - return null; - default: - return null; - } + if (type==='none') return null; + + return <Icon iconType={type} />; }; const ANIMATION_TIME = 500; From 4130ecd04a818c149007910fb0130c64bfdd4282 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 02:04:05 +0900 Subject: [PATCH 34/40] =?UTF-8?q?chore:=20msw=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/index.tsx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/src/index.tsx b/client/src/index.tsx index 82ccd33c5..9bae7a6d8 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -4,15 +4,15 @@ import ReactDOM from 'react-dom/client'; import router from './router'; import {RouterProvider} from 'react-router-dom'; -async function enableMocking() { - const {worker} = await import('./mocks/browser'); //Dynamic import하는 것이 눈에 띄였다. - return worker.start(); -} +// async function enableMocking() { +// const {worker} = await import('./mocks/browser'); //Dynamic import하는 것이 눈에 띄였다. +// return worker.start(); +// } -enableMocking().then(() => { - ReactDOM.createRoot(document.getElementById('root')!).render( - <React.StrictMode> - <RouterProvider router={router} /> - </React.StrictMode>, - ); -}); +// enableMocking().then(() => { +ReactDOM.createRoot(document.getElementById('root')!).render( + <React.StrictMode> + <RouterProvider router={router} /> + </React.StrictMode>, +); +// }); From 892dbb02fdf2c9bf8840eac6be3b650ff0e31203 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 02:15:41 +0900 Subject: [PATCH 35/40] =?UTF-8?q?chore:=20=EB=A6=B0=ED=8A=B8=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 3 ++- client/src/ErrorProvider.tsx | 3 ++- client/src/UnhandledErrorBoundary.tsx | 3 ++- client/src/apis/useFetch.ts | 4 +++- client/src/components/Toast/Toast.style.ts | 1 + client/src/components/Toast/Toast.tsx | 7 ++++--- client/src/components/Toast/ToastProvider.tsx | 7 +++++-- client/src/hooks/useEvent.tsx | 1 + client/src/index.tsx | 2 +- client/src/mocks/browser.ts | 1 + client/src/mocks/handlers.ts | 3 ++- client/src/pages/CreateEventPage/SetEventNamePage.tsx | 3 ++- client/src/router.tsx | 2 +- 13 files changed, 27 insertions(+), 13 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index e43d9c5f8..64de1e68c 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -2,10 +2,11 @@ import {Outlet} from 'react-router-dom'; import {HDesignProvider} from 'haengdong-design'; import {Global} from '@emotion/react'; +import {ToastProvider} from '@components/Toast/ToastProvider'; + import {GlobalStyle} from './GlobalStyle'; // import toast from 'react-simple-toasts'; import {ErrorProvider} from './ErrorProvider'; -import {ToastProvider} from '@components/Toast/ToastProvider'; import UnhandledErrorBoundary from './UnhandledErrorBoundary'; const App: React.FC = () => { diff --git a/client/src/ErrorProvider.tsx b/client/src/ErrorProvider.tsx index db559725f..9d062f18c 100644 --- a/client/src/ErrorProvider.tsx +++ b/client/src/ErrorProvider.tsx @@ -1,6 +1,7 @@ -import ERROR_MESSAGES, {UNHANDLED_ERROR} from '@constants/errorMessage'; import React, {createContext, useState, useContext, useEffect, ReactNode} from 'react'; +import ERROR_MESSAGES, {UNHANDLED_ERROR} from '@constants/errorMessage'; + // 에러 컨텍스트 생성 interface ErrorContextType { hasError: boolean; diff --git a/client/src/UnhandledErrorBoundary.tsx b/client/src/UnhandledErrorBoundary.tsx index fae2a7160..78d017344 100644 --- a/client/src/UnhandledErrorBoundary.tsx +++ b/client/src/UnhandledErrorBoundary.tsx @@ -1,7 +1,8 @@ -import ErrorPage from '@pages/ErrorPage/ErrorPage'; import {StrictPropsWithChildren} from 'haengdong-design/dist/type/strictPropsWithChildren'; import {ErrorBoundary} from 'react-error-boundary'; +import ErrorPage from '@pages/ErrorPage/ErrorPage'; + const UnhandledErrorBoundary = ({children}: StrictPropsWithChildren) => { return <ErrorBoundary fallback={<ErrorPage />}>{children}</ErrorBoundary>; }; diff --git a/client/src/apis/useFetch.ts b/client/src/apis/useFetch.ts index b1cd06183..2a52b583e 100644 --- a/client/src/apis/useFetch.ts +++ b/client/src/apis/useFetch.ts @@ -1,6 +1,8 @@ +import {useState} from 'react'; + import {UNHANDLED_ERROR} from '@constants/errorMessage'; + import {ServerError, useError} from '../ErrorProvider'; -import {useState} from 'react'; export const useFetch = () => { const {setError, clearError} = useError(); diff --git a/client/src/components/Toast/Toast.style.ts b/client/src/components/Toast/Toast.style.ts index bf7500da5..e565741f6 100644 --- a/client/src/components/Toast/Toast.style.ts +++ b/client/src/components/Toast/Toast.style.ts @@ -1,4 +1,5 @@ import {css, keyframes} from '@emotion/react'; + import {ToastPosition} from './Toast.type'; type ToastMarginStyle = { diff --git a/client/src/components/Toast/Toast.tsx b/client/src/components/Toast/Toast.tsx index 80f27eb2f..b93b797c6 100644 --- a/client/src/components/Toast/Toast.tsx +++ b/client/src/components/Toast/Toast.tsx @@ -1,12 +1,13 @@ import {createPortal} from 'react-dom'; import {useState, useEffect} from 'react'; +import {Button, Flex, Icon, Text} from 'haengdong-design'; + import {toastStyle, textStyle, toastMarginStyle} from './Toast.style'; import {ToastProps, ToastType} from './Toast.type'; -import {Button, Flex, Icon, Text} from 'haengdong-design'; const renderIcon = (type: ToastType) => { - if (type==='none') return null; - + if (type === 'none') return null; + return <Icon iconType={type} />; }; diff --git a/client/src/components/Toast/ToastProvider.tsx b/client/src/components/Toast/ToastProvider.tsx index 3600d5939..026f800b3 100644 --- a/client/src/components/Toast/ToastProvider.tsx +++ b/client/src/components/Toast/ToastProvider.tsx @@ -1,9 +1,12 @@ /** @jsxImportSource @emotion/react */ import {createContext, useCallback, useContext, useEffect, useState} from 'react'; + +import ERROR_MESSAGES from '@constants/errorMessage'; + +import {useError} from '../../ErrorProvider'; + import {ToastProps} from './Toast.type'; import Toast from './Toast'; -import {useError} from '../../ErrorProvider'; -import ERROR_MESSAGES from '@constants/errorMessage'; export const ToastContext = createContext<ToastContextProps | null>(null); diff --git a/client/src/hooks/useEvent.tsx b/client/src/hooks/useEvent.tsx index 92e93360b..fddbbc073 100644 --- a/client/src/hooks/useEvent.tsx +++ b/client/src/hooks/useEvent.tsx @@ -1,4 +1,5 @@ import {ResponsePostNewEvent, requestPostNewEvent} from '@apis/request/event'; + import {useFetch} from '@apis/useFetch'; const useEvent = () => { diff --git a/client/src/index.tsx b/client/src/index.tsx index 9bae7a6d8..ee1ffcac9 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -1,8 +1,8 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; +import {RouterProvider} from 'react-router-dom'; import router from './router'; -import {RouterProvider} from 'react-router-dom'; // async function enableMocking() { // const {worker} = await import('./mocks/browser'); //Dynamic import하는 것이 눈에 띄였다. diff --git a/client/src/mocks/browser.ts b/client/src/mocks/browser.ts index 7f609b74a..dd8d73b6c 100644 --- a/client/src/mocks/browser.ts +++ b/client/src/mocks/browser.ts @@ -1,4 +1,5 @@ import {setupWorker} from 'msw/browser'; + import {handlers} from './handlers'; export const worker = setupWorker(...handlers); diff --git a/client/src/mocks/handlers.ts b/client/src/mocks/handlers.ts index 75286a6e1..3bc22d56a 100644 --- a/client/src/mocks/handlers.ts +++ b/client/src/mocks/handlers.ts @@ -1,6 +1,7 @@ -import {TEMP_PREFIX} from '@apis/tempPrefix'; import {http, HttpResponse} from 'msw'; +import {TEMP_PREFIX} from '@apis/tempPrefix'; + export const handlers = [ http.post(`${process.env.API_BASE_URL!}/${TEMP_PREFIX}`, ({request}) => { return HttpResponse.json({errorCode: 'testCode', message: 'testMessage'}, {status: 404}); diff --git a/client/src/pages/CreateEventPage/SetEventNamePage.tsx b/client/src/pages/CreateEventPage/SetEventNamePage.tsx index e2df8c052..bc7c01ee7 100644 --- a/client/src/pages/CreateEventPage/SetEventNamePage.tsx +++ b/client/src/pages/CreateEventPage/SetEventNamePage.tsx @@ -4,9 +4,10 @@ import {FixedButton, MainLayout, LabelInput, Title, TopNav, Back} from 'haengdon import validateEventName from '@utils/validate/validateEventName'; -import {ROUTER_URLS} from '@constants/routerUrls'; import useEvent from '@hooks/useEvent'; +import {ROUTER_URLS} from '@constants/routerUrls'; + const SetEventNamePage = () => { const [eventName, setEventName] = useState(''); const [errorMessage, setErrorMessage] = useState(''); diff --git a/client/src/router.tsx b/client/src/router.tsx index cc50454c1..0e35c8197 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -2,6 +2,7 @@ import {createBrowserRouter} from 'react-router-dom'; import {AdminPage} from '@pages/EventPage/AdminPage'; import {HomePage} from '@pages/EventPage/HomePage'; +import ErrorPage from '@pages/ErrorPage/ErrorPage'; import {CompleteCreateEventPage, SetEventNamePage} from '@pages/CreateEventPage'; import {MainPage} from '@pages/MainPage'; @@ -10,7 +11,6 @@ import {EventPage} from '@pages/EventPage'; import {ROUTER_URLS} from '@constants/routerUrls'; import App from './App'; -import ErrorPage from '@pages/ErrorPage/ErrorPage'; const router = createBrowserRouter([ { From eded4da7e7cbc1806bc64fc8eef1868cb9634a40 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 02:54:44 +0900 Subject: [PATCH 36/40] =?UTF-8?q?fix:=20/=EA=B0=80=20=ED=95=98=EB=82=98=20?= =?UTF-8?q?=EB=8D=94=20=EB=93=A4=EC=96=B4=EA=B0=80=EC=9E=88=EB=8D=98=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/handlers.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/src/mocks/handlers.ts b/client/src/mocks/handlers.ts index 3bc22d56a..c5b88a98b 100644 --- a/client/src/mocks/handlers.ts +++ b/client/src/mocks/handlers.ts @@ -3,7 +3,10 @@ import {http, HttpResponse} from 'msw'; import {TEMP_PREFIX} from '@apis/tempPrefix'; export const handlers = [ - http.post(`${process.env.API_BASE_URL!}/${TEMP_PREFIX}`, ({request}) => { - return HttpResponse.json({errorCode: 'testCode', message: 'testMessage'}, {status: 404}); + http.post(`${process.env.API_BASE_URL!}${TEMP_PREFIX}`, ({request}) => { + return HttpResponse.json( + {errorCode: 'ACTION_NOT_FOUND', message: 'msw모킹함수의 에러메세지입니다.'}, + {status: 404}, + ); }), ]; From 322d21c68be1020d6f3da14ffe309b662c1e87c4 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 02:54:58 +0900 Subject: [PATCH 37/40] =?UTF-8?q?fix:=20=ED=95=A8=EC=88=98=EA=B0=80=20?= =?UTF-8?q?=EC=95=84=EB=8B=8C=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/Toast.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Toast/Toast.tsx b/client/src/components/Toast/Toast.tsx index b93b797c6..d19b11ca3 100644 --- a/client/src/components/Toast/Toast.tsx +++ b/client/src/components/Toast/Toast.tsx @@ -56,7 +56,7 @@ const Toast = ({ <Flex justifyContent="spaceBetween" alignItems="center"> <Flex alignItems="center" gap="0.5rem"> {renderIcon(type)} - <Text size="smallBodyBold" css={textStyle()}> + <Text size="smallBodyBold" css={textStyle}> {message} </Text> </Flex> From 5beb45a02dc0f463ca99b2a3f859608f40d322ff Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 03:07:27 +0900 Subject: [PATCH 38/40] design: px to rem --- client/src/components/Toast/ToastProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Toast/ToastProvider.tsx b/client/src/components/Toast/ToastProvider.tsx index 026f800b3..cac52a2c7 100644 --- a/client/src/components/Toast/ToastProvider.tsx +++ b/client/src/components/Toast/ToastProvider.tsx @@ -40,7 +40,7 @@ const ToastProvider = ({children}: React.PropsWithChildren) => { showingTime: DEFAULT_TIME, // TODO: (@weadie) 나중에 토스트 프로바이더를 제거한 토스트를 만들 것이기 때문에 많이 리펙터링 안함 isAlwaysOn: false, position: 'bottom', - bottom: '100px', + bottom: '6.25rem', }); clearError(DEFAULT_TIME); From 87aae3296c64d2ffc229f71ae70695e5f44cacb1 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 03:10:18 +0900 Subject: [PATCH 39/40] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20useCallback=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/ToastProvider.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/Toast/ToastProvider.tsx b/client/src/components/Toast/ToastProvider.tsx index cac52a2c7..0b6b5bccf 100644 --- a/client/src/components/Toast/ToastProvider.tsx +++ b/client/src/components/Toast/ToastProvider.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @emotion/react */ -import {createContext, useCallback, useContext, useEffect, useState} from 'react'; +import {createContext, useContext, useEffect, useState} from 'react'; import ERROR_MESSAGES from '@constants/errorMessage'; @@ -25,9 +25,9 @@ const ToastProvider = ({children}: React.PropsWithChildren) => { const [currentToast, setCurrentToast] = useState<ShowToast | null>(null); const {hasError, errorMessage, clearError} = useError(); - const showToast = useCallback(({showingTime = DEFAULT_TIME, isAlwaysOn = false, ...toastProps}: ShowToast) => { + const showToast = ({showingTime = DEFAULT_TIME, isAlwaysOn = false, ...toastProps}: ShowToast) => { setCurrentToast({showingTime, isAlwaysOn, ...toastProps}); - }, []); + }; const closeToast = () => { setCurrentToast(null); From 99747d538d8636ba396ede23e565f7a7f383cf70 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Thu, 8 Aug 2024 10:16:46 +0900 Subject: [PATCH 40/40] =?UTF-8?q?chore:=20fadeIn=20->=20fadeInWithTransfor?= =?UTF-8?q?mY=20=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Toast/Toast.style.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/Toast/Toast.style.ts b/client/src/components/Toast/Toast.style.ts index e565741f6..a0b5c7cd4 100644 --- a/client/src/components/Toast/Toast.style.ts +++ b/client/src/components/Toast/Toast.style.ts @@ -9,7 +9,7 @@ type ToastMarginStyle = { }; // 애니메이션 키프레임 정의 -const fadeIn = keyframes` +const fadeInWithTransformY = keyframes` from { opacity: 0; transform: translateY(20px); @@ -20,7 +20,7 @@ const fadeIn = keyframes` } `; -const fadeOut = keyframes` +const fadeOutWithTransformY = keyframes` from { opacity: 1; transform: translateY(0); @@ -55,7 +55,7 @@ export const toastStyle = (isVisible: boolean) => borderRadius: '1.25rem', // 애니메이션 추가 - animation: `${isVisible ? fadeIn : fadeOut} 0.5s forwards`, + animation: `${isVisible ? fadeInWithTransformY : fadeOutWithTransformY} 0.5s forwards`, }); export const textStyle = css({