From d497069af6ee9aff7e4b5fde23bee6c8d5ccbda7 Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Mon, 13 Oct 2025 12:24:38 +0900 Subject: [PATCH 01/10] =?UTF-8?q?=E2=9C=A8feat:=20useSocketStore=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/useSocketStore.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/store/useSocketStore.ts diff --git a/src/store/useSocketStore.ts b/src/store/useSocketStore.ts new file mode 100644 index 00000000..43388b87 --- /dev/null +++ b/src/store/useSocketStore.ts @@ -0,0 +1,20 @@ +import * as StompJS from '@stomp/stompjs'; +import { create } from 'zustand'; + +interface SocketStoreProps { + sendMessage: + | ((params: Omit & { body?: T }) => void) + | null; + setSendMessage: ( + sendFn: ( + params: Omit & { body?: T } + ) => void + ) => void; +} + +const useSocketStore = create((set) => ({ + sendMessage: null, + setSendMessage: (sendFn) => set({ sendMessage: sendFn }), +})); + +export default useSocketStore; From bf89f85c14e5e74a9178d0d682d826fc542101b2 Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Mon, 13 Oct 2025 12:36:25 +0900 Subject: [PATCH 02/10] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20=EB=8B=89?= =?UTF-8?q?=EB=84=A4=EC=9E=84=20=EB=B3=80=EA=B2=BD=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=EC=97=90=EC=84=9C=20sendMessage=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/GameRoom/GameRoomController.tsx | 17 ++++++++--------- .../ModalList/CreateUserNameModal.tsx | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/components/Common/GameRoom/GameRoomController.tsx b/src/components/Common/GameRoom/GameRoomController.tsx index 5573723e..b4dfe35a 100644 --- a/src/components/Common/GameRoom/GameRoomController.tsx +++ b/src/components/Common/GameRoom/GameRoomController.tsx @@ -12,6 +12,7 @@ import { useFetchRoomDetail } from '@/hooks/queries'; import { useToast } from '@/hooks/useToast'; import { EnterRoomProps } from '@/hooks/useWebSocket'; import useRoomStore from '@/store/useRoomStore'; +import useSocketStore from '@/store/useSocketStore'; import { ChatMessage } from '@/types'; import GameRoomView from './GameRoomView'; @@ -38,6 +39,7 @@ const GameRoomController = ({ const { data: roomDetail, error, isError } = useFetchRoomDetail(roomId); const { myName, setHostName, gameId } = useRoomStore(); + const { setSendMessage } = useSocketStore(); const isRoomManager = roomDetail.players.some( (player) => player.name === myName && player.isHost @@ -45,6 +47,12 @@ const GameRoomController = ({ const isSelfInPlayers = roomDetail.players.findIndex((user) => user.name === myName) !== -1; + useEffect(() => { + if (sendMessage) { + setSendMessage(sendMessage); + } + }, [sendMessage, setSendMessage]); + useEffect(() => { if (roomDetail.players.length > 0) { const host = roomDetail.players.find((player) => player.isHost); @@ -69,15 +77,6 @@ const GameRoomController = ({ } }, [myName, roomDetail]); - useEffect(() => { - sendMessage({ - destination: `${SOCKET.ROOM.CHANGE_PLAYER_NAME}`, - body: { - name: myName, - }, - }); - }, [myName]); - useEffect(() => { sendMessage({ destination: `${SOCKET.ROOM.CHANGE_GAME}`, diff --git a/src/components/ModalList/CreateUserNameModal.tsx b/src/components/ModalList/CreateUserNameModal.tsx index 63fbccb4..78b6d4c3 100644 --- a/src/components/ModalList/CreateUserNameModal.tsx +++ b/src/components/ModalList/CreateUserNameModal.tsx @@ -15,15 +15,18 @@ import { ModalShell, } from '@/components'; import { STORAGE_KEY } from '@/constants/storage'; +import { SOCKET } from '@/constants/websocket'; import { useLocalStorage } from '@/hooks/useLocalStorage'; import { useToast } from '@/hooks/useToast'; import useModalStore from '@/store/useModalStore'; import useRoomStore from '@/store/useRoomStore'; +import useSocketStore from '@/store/useSocketStore'; const CreateUserNameModal = () => { const { closeModal } = useModalStore(); const { setItem } = useLocalStorage(); const { myName, setMyName } = useRoomStore(); + const { sendMessage } = useSocketStore(); const { toast } = useToast(); const formSchema = z.object({ @@ -52,14 +55,24 @@ const CreateUserNameModal = () => { description: '기존 닉네임과 다른 닉네임을 입력해주세요.', }); } else { + setMyName(values.username); + setItem(STORAGE_KEY.NICKNAME, values.username); + closeModal(); + + if (sendMessage) { + sendMessage({ + destination: `${SOCKET.ROOM.CHANGE_PLAYER_NAME}`, + body: { + name: values.username, + }, + }); + } + toast({ variant: 'success', title: '닉네임 변경 성공', description: `${values.username}님, 그루파이별에 오신 것을 환영해요!`, }); - setMyName(values.username); - setItem(STORAGE_KEY.NICKNAME, values.username); - closeModal(); } }; From 8e793b48145228fab1928b45fbf0edd022ae9067 Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Mon, 13 Oct 2025 12:37:05 +0900 Subject: [PATCH 03/10] =?UTF-8?q?=F0=9F=8E=A8format:=20setSendMessage=20nu?= =?UTF-8?q?ll=EB=8F=84=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=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 --- src/store/useSocketStore.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/store/useSocketStore.ts b/src/store/useSocketStore.ts index 43388b87..1b21273a 100644 --- a/src/store/useSocketStore.ts +++ b/src/store/useSocketStore.ts @@ -6,9 +6,11 @@ interface SocketStoreProps { | ((params: Omit & { body?: T }) => void) | null; setSendMessage: ( - sendFn: ( - params: Omit & { body?: T } - ) => void + sendFn: + | (( + params: Omit & { body?: T } + ) => void) + | null ) => void; } From c5fa60a3b41e7ebd59dcc2327703415a7c78b89a Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Mon, 13 Oct 2025 12:43:24 +0900 Subject: [PATCH 04/10] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EB=B3=80=EA=B2=BD=20=ED=95=B8=EB=93=A4=EB=9F=AC?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=A7=81=EC=A0=91=20sendMessage=20?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/GameListCarousel/GameListCarousel.tsx | 16 ++++++++++++++-- .../Common/GameRoom/GameRoomController.tsx | 12 +----------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/components/Common/GameListCarousel/GameListCarousel.tsx b/src/components/Common/GameListCarousel/GameListCarousel.tsx index faa5e6e6..2db135c4 100644 --- a/src/components/Common/GameListCarousel/GameListCarousel.tsx +++ b/src/components/Common/GameListCarousel/GameListCarousel.tsx @@ -13,11 +13,13 @@ import { GameListCard, } from '@/components'; import { PATH } from '@/constants/router'; +import { SOCKET } from '@/constants/websocket'; import { useToast } from '@/hooks/useToast'; import { cn } from '@/lib/utils'; import { createRoom } from '@/services/rooms'; import useModalStore from '@/store/useModalStore'; import useRoomStore from '@/store/useRoomStore'; +import useSocketStore from '@/store/useSocketStore'; import { GameResponse } from '@/types/api'; interface GameListCarouselProps { @@ -29,8 +31,9 @@ const GameListCarousel = ({ games }: GameListCarouselProps) => { const router = useRouter(); const { toast } = useToast(); - const { setRoomId, setGameId } = useRoomStore(); + const { setRoomId } = useRoomStore(); const { closeModal } = useModalStore(); + const { sendMessage } = useSocketStore(); const [isClicked, setIsClicked] = useState(false); @@ -62,7 +65,16 @@ const GameListCarousel = ({ games }: GameListCarouselProps) => { const handleChangeGame = (gameId: string) => { if (isClicked) return; - setGameId(gameId); + + if (sendMessage) { + sendMessage({ + destination: `${SOCKET.ROOM.CHANGE_GAME}`, + body: { + gameId, + }, + }); + } + closeModal(); setIsClicked(true); }; diff --git a/src/components/Common/GameRoom/GameRoomController.tsx b/src/components/Common/GameRoom/GameRoomController.tsx index b4dfe35a..d35e8d74 100644 --- a/src/components/Common/GameRoom/GameRoomController.tsx +++ b/src/components/Common/GameRoom/GameRoomController.tsx @@ -7,7 +7,6 @@ import { useEffect } from 'react'; import { Spinner } from '@/components'; import { PATH } from '@/constants/router'; -import { SOCKET } from '@/constants/websocket'; import { useFetchRoomDetail } from '@/hooks/queries'; import { useToast } from '@/hooks/useToast'; import { EnterRoomProps } from '@/hooks/useWebSocket'; @@ -38,7 +37,7 @@ const GameRoomController = ({ const { data: roomDetail, error, isError } = useFetchRoomDetail(roomId); - const { myName, setHostName, gameId } = useRoomStore(); + const { myName, setHostName } = useRoomStore(); const { setSendMessage } = useSocketStore(); const isRoomManager = roomDetail.players.some( @@ -77,15 +76,6 @@ const GameRoomController = ({ } }, [myName, roomDetail]); - useEffect(() => { - sendMessage({ - destination: `${SOCKET.ROOM.CHANGE_GAME}`, - body: { - gameId, - }, - }); - }, [gameId]); - // @TODO: 더 선언적으로 error를 처리할 수 있는 방법 찾기 useEffect(() => { if (isError) { From 79bc04c37ee09185f0f83de424b46cf5d099f69f Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Mon, 13 Oct 2025 12:46:04 +0900 Subject: [PATCH 05/10] =?UTF-8?q?=F0=9F=8E=A8format:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20set=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/useBalanceGameStore.ts | 10 ---------- src/store/useQnaGameStore.ts | 8 -------- src/store/useRoomStore.ts | 2 -- 3 files changed, 20 deletions(-) diff --git a/src/store/useBalanceGameStore.ts b/src/store/useBalanceGameStore.ts index 66c9e662..e268adfd 100644 --- a/src/store/useBalanceGameStore.ts +++ b/src/store/useBalanceGameStore.ts @@ -6,7 +6,6 @@ interface BalanceGameStoreProps { round: BalanceGameRoundResponse; selectedPlayers: string[]; setRound: (round: BalanceGameRoundResponse) => void; - setTotalRounds: (count: number) => void; // @TODO: 추후에 완전 삭제 필요 addSelectedPlayers: (player: string) => void; resetSelectedPlayers: () => void; } @@ -20,20 +19,11 @@ const useBalanceGameStore = create((set) => ({ currentRound: 0, playSeconds: 0, }, - selectedPlayers: [], setRound: (round: BalanceGameRoundResponse) => set({ round, }), - - setTotalRounds: (count) => - set((state) => ({ - round: { - ...state.round, - totalRounds: count, - }, - })), addSelectedPlayers: (player) => set((state) => ({ selectedPlayers: [...state.selectedPlayers, player], diff --git a/src/store/useQnaGameStore.ts b/src/store/useQnaGameStore.ts index 8f6020d1..8b21d404 100644 --- a/src/store/useQnaGameStore.ts +++ b/src/store/useQnaGameStore.ts @@ -6,7 +6,6 @@ interface BalanceGameStoreProps { round: QnaGameRoundResponse; submittedPlayers: string[]; setRound: (round: QnaGameRoundResponse) => void; - setTotalRounds: (count: number) => void; // @TODO: 추후에 완전 삭제 필요 addSubmittedPlayer: (playerId: string) => void; clearSubmittedPlayers: () => void; reset: () => void; @@ -23,13 +22,6 @@ const useQnaGameStore = create((set) => ({ set({ round, }), - setTotalRounds: (count) => - set((state) => ({ - round: { - ...state.round, - totalRounds: count, - }, - })), addSubmittedPlayer: (playerId) => set((state) => ({ submittedPlayers: [...state.submittedPlayers, playerId], diff --git a/src/store/useRoomStore.ts b/src/store/useRoomStore.ts index 3ff75511..ce1b696d 100644 --- a/src/store/useRoomStore.ts +++ b/src/store/useRoomStore.ts @@ -11,7 +11,6 @@ interface RoomStoreProps { myName: string; setRoomStatus: (status: roomStatusType) => void; setRoomId: (id: string) => void; - setGameId: (id: string) => void; setHostName: (name: string) => void; setMyName: (name: string) => void; getHostName: () => string | null; @@ -26,7 +25,6 @@ const useRoomStore = create((set, get) => ({ myName: '', setRoomStatus: (status) => set({ roomStatus: status }), setRoomId: (id) => set({ roomId: id }), - setGameId: (id) => set({ gameId: id }), setHostName: (name) => set({ hostName: name }), setMyName: (name) => set({ myName: name }), getHostName: () => get().hostName, From fa19bd1f86ee8fb49e2c9fec5706290189aa4946 Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Mon, 13 Oct 2025 13:18:51 +0900 Subject: [PATCH 06/10] =?UTF-8?q?=F0=9F=8E=A8format:=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=B0=B0=EC=97=B4=EC=97=90=EC=84=9C=20setSendMessa?= =?UTF-8?q?ge=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Common/GameRoom/GameRoomController.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Common/GameRoom/GameRoomController.tsx b/src/components/Common/GameRoom/GameRoomController.tsx index d35e8d74..29ff1060 100644 --- a/src/components/Common/GameRoom/GameRoomController.tsx +++ b/src/components/Common/GameRoom/GameRoomController.tsx @@ -50,7 +50,7 @@ const GameRoomController = ({ if (sendMessage) { setSendMessage(sendMessage); } - }, [sendMessage, setSendMessage]); + }, [sendMessage]); useEffect(() => { if (roomDetail.players.length > 0) { From 933b197ea954a80289286f151c7265e7fcb03353 Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Mon, 13 Oct 2025 13:19:57 +0900 Subject: [PATCH 07/10] =?UTF-8?q?=F0=9F=8E=A8format:=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=B0=B0=EC=97=B4=EC=97=90=EC=84=9C=20setHostName?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Common/GameRoom/GameRoomController.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Common/GameRoom/GameRoomController.tsx b/src/components/Common/GameRoom/GameRoomController.tsx index 29ff1060..040e7b6e 100644 --- a/src/components/Common/GameRoom/GameRoomController.tsx +++ b/src/components/Common/GameRoom/GameRoomController.tsx @@ -59,7 +59,7 @@ const GameRoomController = ({ setHostName(host.name); } } - }, [roomDetail.players, setHostName]); + }, [roomDetail.players]); useEffect(() => { if (roomDetail && !isSelfInPlayers) { From 689ab22757a39425be561e1961ed67791c86c831 Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Mon, 20 Oct 2025 17:34:50 +0900 Subject: [PATCH 08/10] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20sendMessage?= =?UTF-8?q?=20useCallback=EC=9C=BC=EB=A1=9C=20=EA=B0=90=EC=8B=B8=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useWebSocket.ts | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/hooks/useWebSocket.ts b/src/hooks/useWebSocket.ts index eaa8de39..c8fa9d2c 100644 --- a/src/hooks/useWebSocket.ts +++ b/src/hooks/useWebSocket.ts @@ -4,7 +4,7 @@ import * as StompJS from '@stomp/stompjs'; import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/navigation'; -import { useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { DEFAULT_ERROR_MESSAGE, ERROR_MESSAGE } from '@/constants/error'; import { QUERYKEY } from '@/constants/querykey'; @@ -109,22 +109,23 @@ export function useWebSocket() { client.current = null; }; - const sendMessage = ( - params: Omit & { body?: T } - ) => { - if (!client.current || !client.current.connected) { - return; - } + const sendMessage = useCallback( + (params: Omit & { body?: T }) => { + if (!client.current || !client.current.connected) { + return; + } - const { destination, body } = params; - const text = JSON.stringify(body); + const { destination, body } = params; + const text = JSON.stringify(body); - client.current?.publish({ - ...params, - destination: `${SOCKET.PUBLICATION}${destination}`, - body: text, - }); - }; + client.current?.publish({ + ...params, + destination: `${SOCKET.PUBLICATION}${destination}`, + body: text, + }); + }, + [] + ); const receiveMessage = (message: string) => { const { type, sender, content } = JSON.parse(message); From 9889e674deec69a80c117ec5bf25ce7c256beccc Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Wed, 22 Oct 2025 11:08:02 +0900 Subject: [PATCH 09/10] =?UTF-8?q?=F0=9F=8E=A8format:=20closeModal=20?= =?UTF-8?q?=EB=A7=A8=20=EC=95=84=EB=9E=98=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ModalList/CreateUserNameModal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ModalList/CreateUserNameModal.tsx b/src/components/ModalList/CreateUserNameModal.tsx index 78b6d4c3..ea565b2b 100644 --- a/src/components/ModalList/CreateUserNameModal.tsx +++ b/src/components/ModalList/CreateUserNameModal.tsx @@ -57,7 +57,6 @@ const CreateUserNameModal = () => { } else { setMyName(values.username); setItem(STORAGE_KEY.NICKNAME, values.username); - closeModal(); if (sendMessage) { sendMessage({ @@ -73,6 +72,8 @@ const CreateUserNameModal = () => { title: '닉네임 변경 성공', description: `${values.username}님, 그루파이별에 오신 것을 환영해요!`, }); + + closeModal(); } }; From a00f0e41e78d48dc923b9dcf9a5db6a6d7105a33 Mon Sep 17 00:00:00 2001 From: kim-hyunjoo Date: Wed, 22 Oct 2025 11:14:28 +0900 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=90=9Bfix:=20=EB=88=84=EB=9D=BD?= =?UTF-8?q?=EB=90=9C=20=EB=B3=91=ED=95=A9=20=EA=B3=BC=EC=A0=95=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Common/GameRoom/GameRoomController.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/components/Common/GameRoom/GameRoomController.tsx b/src/components/Common/GameRoom/GameRoomController.tsx index 040e7b6e..c617e906 100644 --- a/src/components/Common/GameRoom/GameRoomController.tsx +++ b/src/components/Common/GameRoom/GameRoomController.tsx @@ -35,7 +35,7 @@ const GameRoomController = ({ const { toast } = useToast(); - const { data: roomDetail, error, isError } = useFetchRoomDetail(roomId); + const { data: roomDetail, isError } = useFetchRoomDetail(roomId); const { myName, setHostName } = useRoomStore(); const { setSendMessage } = useSocketStore(); @@ -76,13 +76,6 @@ const GameRoomController = ({ } }, [myName, roomDetail]); - // @TODO: 더 선언적으로 error를 처리할 수 있는 방법 찾기 - useEffect(() => { - if (isError) { - throw error; - } - }, [isError]); - if (!isSelfInPlayers) { return ; }