From 72b7be9d7c1bfcf838a20abd6d16b9013731123a Mon Sep 17 00:00:00 2001 From: rbgksqkr Date: Mon, 18 Nov 2024 17:00:21 +0900 Subject: [PATCH 01/11] =?UTF-8?q?refactor:=20=EB=B0=A9=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EB=B0=A9=20=EC=B0=B8=EC=97=AC=20API=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=97=90=20=EC=93=B0=EB=A1=9C=ED=8B=80?= =?UTF-8?q?=EB=A7=81=EC=9D=84=20=EA=B1=B8=EC=96=B4=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EB=B0=A9=EC=A7=80=20#401?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/useThrottle.ts | 19 +++++++++++++++++++ .../NicknamePage/hooks/useMakeOrEnterRoom.ts | 14 ++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 frontend/src/hooks/useThrottle.ts diff --git a/frontend/src/hooks/useThrottle.ts b/frontend/src/hooks/useThrottle.ts new file mode 100644 index 00000000..36c44317 --- /dev/null +++ b/frontend/src/hooks/useThrottle.ts @@ -0,0 +1,19 @@ +import { useRef } from 'react'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const useThrottle = (func: (...args: any[]) => void, delay = 1000) => { + const isThrottle = useRef(false); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (...args: any[]) => { + if (!isThrottle.current) { + func(...args); + isThrottle.current = true; + setTimeout(() => { + isThrottle.current = false; + }, delay); + } + }; +}; + +export default useThrottle; diff --git a/frontend/src/pages/NicknamePage/hooks/useMakeOrEnterRoom.ts b/frontend/src/pages/NicknamePage/hooks/useMakeOrEnterRoom.ts index 93ce8405..4a319243 100644 --- a/frontend/src/pages/NicknamePage/hooks/useMakeOrEnterRoom.ts +++ b/frontend/src/pages/NicknamePage/hooks/useMakeOrEnterRoom.ts @@ -4,6 +4,7 @@ import { useNavigate, useParams } from 'react-router-dom'; import { enterRoom, createRoom } from '@/apis/room'; import { ROUTES } from '@/constants/routes'; +import useThrottle from '@/hooks/useThrottle'; import { CreateOrEnterRoomResponse } from '@/types/room'; import { CustomError } from '@/utils/error'; @@ -22,6 +23,8 @@ const useMakeOrEnterRoom = () => { }, }); + const throttledCreateRoom = useThrottle(createRoomMutation.mutate); + const enterRoomMutation = useMutation< CreateOrEnterRoomResponse, CustomError, @@ -33,12 +36,19 @@ const useMakeOrEnterRoom = () => { }, }); + const throttledEnterRoom = useThrottle(enterRoomMutation.mutate); + const handleMakeOrEnterRoom = () => { const nickname = nicknameInputRef.current?.value || nicknameInputRef.current?.placeholder || ''; + if (isMaster) { - createRoomMutation.mutate(nickname); + if (createRoomMutation.isPending) return; + + throttledCreateRoom(nickname); } else { - enterRoomMutation.mutate({ nickname, roomUuid: roomUuid || '' }); + if (enterRoomMutation.isPending) return; + + throttledEnterRoom({ nickname, roomUuid: roomUuid || '' }); } }; From 7ec61ae87c93e5ab9525a45f394480f662f7b76e Mon Sep 17 00:00:00 2001 From: rbgksqkr Date: Mon, 18 Nov 2024 17:45:31 +0900 Subject: [PATCH 02/11] =?UTF-8?q?refactor:=20=EB=B0=A9=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EA=B3=BC=20=EB=B0=A9=20=EC=B0=B8=EC=97=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B6=84=EB=A6=AC=20#401?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/NicknamePage/NicknamePage.tsx | 8 +-- .../NicknameInput/NicknameInput.tsx | 6 +- .../NicknameInput/hooks/useNicknameInput.ts | 6 +- .../hooks/useCreateOrEnterRoom.ts | 32 ++++++++++ .../pages/NicknamePage/hooks/useCreateRoom.ts | 37 +++++++++++ .../pages/NicknamePage/hooks/useEnterRoom.ts | 42 +++++++++++++ .../NicknamePage/hooks/useMakeOrEnterRoom.ts | 62 ------------------- 7 files changed, 121 insertions(+), 72 deletions(-) create mode 100644 frontend/src/pages/NicknamePage/hooks/useCreateOrEnterRoom.ts create mode 100644 frontend/src/pages/NicknamePage/hooks/useCreateRoom.ts create mode 100644 frontend/src/pages/NicknamePage/hooks/useEnterRoom.ts delete mode 100644 frontend/src/pages/NicknamePage/hooks/useMakeOrEnterRoom.ts diff --git a/frontend/src/pages/NicknamePage/NicknamePage.tsx b/frontend/src/pages/NicknamePage/NicknamePage.tsx index 99098053..a864df80 100644 --- a/frontend/src/pages/NicknamePage/NicknamePage.tsx +++ b/frontend/src/pages/NicknamePage/NicknamePage.tsx @@ -3,7 +3,7 @@ import { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import NicknameInput from './components/NicknameInput/NicknameInput'; -import useMakeOrEnterRoom from './hooks/useMakeOrEnterRoom'; +import useCreateOrEnterRoom from './hooks/useCreateOrEnterRoom'; import { profileWrapper, profileImg, @@ -21,7 +21,7 @@ import Content from '@/components/layout/Content/Content'; import useButtonHeightOnKeyboard from '@/hooks/useButtonHeightOnKeyboard'; const NicknamePage = () => { - const { nicknameInputRef, handleMakeOrEnterRoom, isLoading } = useMakeOrEnterRoom(); + const { nicknameInputRef, handleCreateOrEnterRoom, isLoading } = useCreateOrEnterRoom(); const { roomUuid } = useParams(); const { bottomButtonHeight } = useButtonHeightOnKeyboard(); @@ -58,10 +58,10 @@ const NicknamePage = () => {
- ); - return (
diff --git a/frontend/src/pages/NicknamePage/hooks/useIsJoinableRoomQuery.ts b/frontend/src/pages/NicknamePage/hooks/useIsJoinableRoomQuery.ts index cd5e78fd..e6b2885d 100644 --- a/frontend/src/pages/NicknamePage/hooks/useIsJoinableRoomQuery.ts +++ b/frontend/src/pages/NicknamePage/hooks/useIsJoinableRoomQuery.ts @@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query'; import { isJoinableRoom } from '@/apis/room'; import { QUERY_KEYS } from '@/constants/queryKeys'; +import { CustomError } from '@/utils/error'; interface useIsJoinableRoomQueryProps { roomUuid?: string; @@ -11,6 +12,11 @@ const useIsJoinableRoomQuery = ({ roomUuid }: useIsJoinableRoomQueryProps) => { const isJoinableRoomQuery = useQuery({ queryKey: [QUERY_KEYS.isJoinable, roomUuid], queryFn: async () => isJoinableRoom(roomUuid || ''), + select: ({ isJoinable }) => { + if (isJoinable === false) { + throw new CustomError({ errorCode: 'CAN_NOT_JOIN_ROOM', status: 400 }); + } + }, enabled: !!roomUuid, }); diff --git a/frontend/src/types/error.ts b/frontend/src/types/error.ts index 18997ab6..7444d412 100644 --- a/frontend/src/types/error.ts +++ b/frontend/src/types/error.ts @@ -32,7 +32,8 @@ export type ErrorCode = | 'INTERNAL_SERVER_ERROR' | 'NOT_FOUND_COOKIE' | 'INVALID_COOKIE' - | 'INVALID_NICKNAME'; + | 'INVALID_NICKNAME' + | 'CAN_NOT_JOIN_ROOM'; export interface UrlParameterError extends ResponseError { errorCode: 'URL_PARAMETER_ERROR'; diff --git a/frontend/src/utils/error.ts b/frontend/src/utils/error.ts index 0f680436..87e64972 100644 --- a/frontend/src/utils/error.ts +++ b/frontend/src/utils/error.ts @@ -3,8 +3,8 @@ import { ErrorCode } from '@/types/error'; interface CustomErrorParams { errorCode: ErrorCode; - message: string; status: number; + message?: string; } export class CustomError extends Error { @@ -14,8 +14,8 @@ export class CustomError extends Error { constructor({ errorCode, status }: CustomErrorParams) { super(); this.errorCode = errorCode; - this.message = ERROR_MESSAGE[errorCode]; this.status = status; + this.message = ERROR_MESSAGE[errorCode]; } } From 89083c755e9a22ac1af25080480561f04a6ef3ec Mon Sep 17 00:00:00 2001 From: rbgksqkr Date: Mon, 18 Nov 2024 20:39:48 +0900 Subject: [PATCH 05/11] =?UTF-8?q?refactor:=20=ED=88=AC=ED=91=9C=20API?= =?UTF-8?q?=EC=97=90=20=EC=93=B0=EB=A1=9C=ED=8B=80=EB=A7=81=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20#401?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/SelectButton/SelectButton.hook.ts | 9 +++++++-- .../GamePage/components/SelectButton/SelectButton.tsx | 11 ++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/src/pages/GamePage/components/SelectButton/SelectButton.hook.ts b/frontend/src/pages/GamePage/components/SelectButton/SelectButton.hook.ts index cb53e714..23585792 100644 --- a/frontend/src/pages/GamePage/components/SelectButton/SelectButton.hook.ts +++ b/frontend/src/pages/GamePage/components/SelectButton/SelectButton.hook.ts @@ -3,11 +3,12 @@ import { useParams } from 'react-router-dom'; import { voteBalanceContent } from '@/apis/balanceContent'; import useGetUserInfo from '@/hooks/useGetUserInfo'; +import useThrottle from '@/hooks/useThrottle'; interface UseSelectCompleteMutationProps { selectedId: number; - contentId?: number; completeSelection: () => void; + contentId?: number; } const useCompleteSelectionMutation = ({ @@ -20,7 +21,7 @@ const useCompleteSelectionMutation = ({ member: { memberId }, } = useGetUserInfo(); - return useMutation({ + const completeSelectionMutation = useMutation({ mutationFn: async () => { if (typeof contentId === 'undefined') { throw new Error('contentId 가 존재하지 않습니다.'); @@ -36,6 +37,10 @@ const useCompleteSelectionMutation = ({ completeSelection(); }, }); + + const throttledVote = useThrottle(completeSelectionMutation.mutate); + + return { ...completeSelectionMutation, vote: throttledVote }; }; export default useCompleteSelectionMutation; diff --git a/frontend/src/pages/GamePage/components/SelectButton/SelectButton.tsx b/frontend/src/pages/GamePage/components/SelectButton/SelectButton.tsx index 7c12282c..4f3716cb 100644 --- a/frontend/src/pages/GamePage/components/SelectButton/SelectButton.tsx +++ b/frontend/src/pages/GamePage/components/SelectButton/SelectButton.tsx @@ -10,21 +10,18 @@ interface SelectButtonProps { } const SelectButton = ({ contentId, selectedId, completeSelection }: SelectButtonProps) => { - const { - data, - isPending, - mutate: vote, - } = useCompleteSelectionMutation({ + const { isSuccess, isPending, vote } = useCompleteSelectionMutation({ selectedId, contentId, completeSelection, }); + return (
From c590db6a3a647d7747874ee483b9a28514962484 Mon Sep 17 00:00:00 2001 From: rbgksqkr Date: Mon, 18 Nov 2024 21:13:42 +0900 Subject: [PATCH 06/11] =?UTF-8?q?refactor:=20=ED=88=AC=ED=91=9C=20API=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=EC=9D=B4=20=EB=93=A4=EC=96=B4=EC=98=A4?= =?UTF-8?q?=EC=9E=90=EB=A7=88=EC=9E=90=20=EC=84=A0=ED=83=9D=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EC=83=81=ED=83=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?#401?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GamePage/components/SelectButton/SelectButton.hook.ts | 3 ++- .../pages/GamePage/components/SelectButton/SelectButton.tsx | 5 +++-- .../GamePage/components/SelectContainer/SelectContainer.tsx | 1 + .../components/SelectContainer/Timer/hooks/useVoteTimer.ts | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/GamePage/components/SelectButton/SelectButton.hook.ts b/frontend/src/pages/GamePage/components/SelectButton/SelectButton.hook.ts index 23585792..4682ad7e 100644 --- a/frontend/src/pages/GamePage/components/SelectButton/SelectButton.hook.ts +++ b/frontend/src/pages/GamePage/components/SelectButton/SelectButton.hook.ts @@ -26,6 +26,7 @@ const useCompleteSelectionMutation = ({ if (typeof contentId === 'undefined') { throw new Error('contentId 가 존재하지 않습니다.'); } + return await voteBalanceContent({ roomId: Number(roomId), optionId: selectedId, @@ -33,7 +34,7 @@ const useCompleteSelectionMutation = ({ memberId: Number(memberId), }); }, - onSuccess: () => { + onMutate: () => { completeSelection(); }, }); diff --git a/frontend/src/pages/GamePage/components/SelectButton/SelectButton.tsx b/frontend/src/pages/GamePage/components/SelectButton/SelectButton.tsx index 4f3716cb..d988c87a 100644 --- a/frontend/src/pages/GamePage/components/SelectButton/SelectButton.tsx +++ b/frontend/src/pages/GamePage/components/SelectButton/SelectButton.tsx @@ -6,10 +6,11 @@ import { bottomButtonLayout } from '@/components/common/Button/Button.styled'; interface SelectButtonProps { contentId: number; selectedId: number; + isVoted: boolean; completeSelection: () => void; } -const SelectButton = ({ contentId, selectedId, completeSelection }: SelectButtonProps) => { +const SelectButton = ({ contentId, selectedId, isVoted, completeSelection }: SelectButtonProps) => { const { isSuccess, isPending, vote } = useCompleteSelectionMutation({ selectedId, contentId, @@ -20,7 +21,7 @@ const SelectButton = ({ contentId, selectedId, completeSelection }: SelectButton
diff --git a/frontend/src/pages/GamePage/components/SelectContainer/Timer/hooks/useVoteTimer.ts b/frontend/src/pages/GamePage/components/SelectContainer/Timer/hooks/useVoteTimer.ts index c0688296..dd123940 100644 --- a/frontend/src/pages/GamePage/components/SelectContainer/Timer/hooks/useVoteTimer.ts +++ b/frontend/src/pages/GamePage/components/SelectContainer/Timer/hooks/useVoteTimer.ts @@ -17,7 +17,7 @@ const useVoteTimer = ({ roomId, selectedId, isVoted, completeSelection }: UseVot const { balanceContent } = useBalanceContentQuery(roomId); const timeLimit = convertMsecToSecond(balanceContent.timeLimit) || DEFAULT_TIME_LIMIT_SEC; - const { mutate: vote } = useCompleteSelectionMutation({ + const { vote } = useCompleteSelectionMutation({ selectedId, contentId: balanceContent.contentId, completeSelection, From 58758976ca135521e3322cc1d6ac90d5cf4a37e7 Mon Sep 17 00:00:00 2001 From: rbgksqkr Date: Tue, 19 Nov 2024 02:22:59 +0900 Subject: [PATCH 07/11] =?UTF-8?q?refactor:=20useThrottle=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EA=B0=95=ED=99=94=20#401?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/useThrottle.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/frontend/src/hooks/useThrottle.ts b/frontend/src/hooks/useThrottle.ts index 36c44317..781b99d2 100644 --- a/frontend/src/hooks/useThrottle.ts +++ b/frontend/src/hooks/useThrottle.ts @@ -1,16 +1,18 @@ import { useRef } from 'react'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -const useThrottle = (func: (...args: any[]) => void, delay = 1000) => { - const isThrottle = useRef(false); +const useThrottle = void>( + func: T, + delay = 3000, +): ((...args: Parameters) => void) => { + const isThrottledRef = useRef(false); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (...args: any[]) => { - if (!isThrottle.current) { + return (...args: Parameters) => { + if (!isThrottledRef.current) { func(...args); - isThrottle.current = true; + isThrottledRef.current = true; setTimeout(() => { - isThrottle.current = false; + isThrottledRef.current = false; }, delay); } }; From 815ef4151a4145d45d4893124510acc731e4d7ee Mon Sep 17 00:00:00 2001 From: rbgksqkr Date: Tue, 19 Nov 2024 02:32:36 +0900 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20mutation=20=EC=84=B1=EA=B3=B5?= =?UTF-8?q?=ED=96=88=EC=9D=84=20=EB=95=8C=EB=8F=84=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?disabled=20=EC=83=81=ED=83=9C=20=EC=B2=98=EB=A6=AC=20#401?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/GameResult/FinalButton/FinalButton.tsx | 6 +++--- .../components/GameResult/FinalButton/FinalButton.utils.ts | 4 ++-- frontend/src/pages/NicknamePage/NicknamePage.tsx | 7 ++++--- .../src/pages/NicknamePage/hooks/useCreateOrEnterRoom.ts | 1 + 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/frontend/src/pages/GameResultPage/components/GameResult/FinalButton/FinalButton.tsx b/frontend/src/pages/GameResultPage/components/GameResult/FinalButton/FinalButton.tsx index 82e99511..7944de0f 100644 --- a/frontend/src/pages/GameResultPage/components/GameResult/FinalButton/FinalButton.tsx +++ b/frontend/src/pages/GameResultPage/components/GameResult/FinalButton/FinalButton.tsx @@ -9,7 +9,7 @@ import useGetUserInfo from '@/hooks/useGetUserInfo'; const FinalButton = () => { const { roomId } = useParams(); - const { mutate: resetRoom, isPending } = useResetRoomMutation(Number(roomId)); + const { mutate: resetRoom, isPending, isSuccess } = useResetRoomMutation(Number(roomId)); const { member: { isMaster }, } = useGetUserInfo(); @@ -18,9 +18,9 @@ const FinalButton = () => {
); diff --git a/frontend/src/pages/GameResultPage/components/GameResult/FinalButton/FinalButton.utils.ts b/frontend/src/pages/GameResultPage/components/GameResult/FinalButton/FinalButton.utils.ts index f5d24ebf..677d3697 100644 --- a/frontend/src/pages/GameResultPage/components/GameResult/FinalButton/FinalButton.utils.ts +++ b/frontend/src/pages/GameResultPage/components/GameResult/FinalButton/FinalButton.utils.ts @@ -1,5 +1,5 @@ -const getFinalButtonText = (isMaster: boolean, isPending: boolean) => { - if (isMaster && isPending) return '로딩중...'; +const getFinalButtonText = (isMaster: boolean, isPending: boolean, isSuccess: boolean) => { + if (isMaster && (isPending || isSuccess)) return '로딩중...'; if (isMaster) return '대기실로 이동'; return '방장이 진행해 주세요'; }; diff --git a/frontend/src/pages/NicknamePage/NicknamePage.tsx b/frontend/src/pages/NicknamePage/NicknamePage.tsx index 1f5d00d9..31247e85 100644 --- a/frontend/src/pages/NicknamePage/NicknamePage.tsx +++ b/frontend/src/pages/NicknamePage/NicknamePage.tsx @@ -13,7 +13,8 @@ import useButtonHeightOnKeyboard from '@/hooks/useButtonHeightOnKeyboard'; const NicknamePage = () => { const { roomUuid } = useParams(); - const { nicknameInputRef, handleCreateOrEnterRoom, isLoading } = useCreateOrEnterRoom(); + const { nicknameInputRef, handleCreateOrEnterRoom, isLoading, isSuccess } = + useCreateOrEnterRoom(); const { bottomButtonHeight } = useButtonHeightOnKeyboard(); useIsJoinableRoomQuery({ roomUuid }); @@ -36,8 +37,8 @@ const NicknamePage = () => { />