diff --git a/frontend/package.json b/frontend/package.json index 4cecf260..19dc2085 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "@octokit/rest": "^21.0.1", "@sentry/react": "^8.22.0", "@sentry/webpack-plugin": "^2.21.1", + "@stomp/stompjs": "^7.0.0", "@tanstack/react-query": "^5.51.11", "dotenv-webpack": "^8.1.0", "react": "^18.3.1", @@ -27,6 +28,7 @@ "react-icons": "^5.3.0", "react-router-dom": "^6.25.1", "styled-components": "^6.1.12", + "ws": "^8.18.0", "zustand": "^4.5.4" }, "devDependencies": { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 733f73b6..b75038cf 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -25,8 +25,8 @@ import HowToPair from '@/components/Landing/HowToPair/HowToPair'; import useUserStore from '@/stores/userStore'; -import { getMember } from '@/apis/member'; -import { getIsUserLoggedIn } from '@/apis/oauth'; +import { getMember } from '@/apis/http/member'; +import { getIsUserLoggedIn } from '@/apis/http/oauth'; import GlobalStyles from './styles/Global.style'; import { theme } from './styles/theme'; diff --git a/frontend/src/apis/category.ts b/frontend/src/apis/http/category.ts similarity index 97% rename from frontend/src/apis/category.ts rename to frontend/src/apis/http/category.ts index 6a9e38c5..4ecd8d83 100644 --- a/frontend/src/apis/category.ts +++ b/frontend/src/apis/http/category.ts @@ -1,4 +1,4 @@ -import fetcher from '@/apis/fetcher'; +import fetcher from '@/apis/http/fetcher'; import { ERROR_MESSAGES } from '@/constants/message'; diff --git a/frontend/src/apis/fetcher.ts b/frontend/src/apis/http/fetcher.ts similarity index 100% rename from frontend/src/apis/fetcher.ts rename to frontend/src/apis/http/fetcher.ts diff --git a/frontend/src/apis/github.ts b/frontend/src/apis/http/github.ts similarity index 100% rename from frontend/src/apis/github.ts rename to frontend/src/apis/http/github.ts diff --git a/frontend/src/apis/member.ts b/frontend/src/apis/http/member.ts similarity index 95% rename from frontend/src/apis/member.ts rename to frontend/src/apis/http/member.ts index 1e4f3e53..f0a0639a 100644 --- a/frontend/src/apis/member.ts +++ b/frontend/src/apis/http/member.ts @@ -1,5 +1,5 @@ -import fetcher from '@/apis/fetcher'; -import type { PairRoomStatus } from '@/apis/pairRoom'; +import fetcher from '@/apis/http/fetcher'; +import type { PairRoomStatus } from '@/apis/http/pairRoom'; import { ERROR_MESSAGES } from '@/constants/message'; diff --git a/frontend/src/apis/oauth.ts b/frontend/src/apis/http/oauth.ts similarity index 96% rename from frontend/src/apis/oauth.ts rename to frontend/src/apis/http/oauth.ts index a5d417e4..65305e92 100644 --- a/frontend/src/apis/oauth.ts +++ b/frontend/src/apis/http/oauth.ts @@ -1,4 +1,4 @@ -import fetcher from '@/apis/fetcher'; +import fetcher from '@/apis/http/fetcher'; import { ERROR_MESSAGES } from '@/constants/message'; diff --git a/frontend/src/apis/pairRoom.ts b/frontend/src/apis/http/pairRoom.ts similarity index 93% rename from frontend/src/apis/pairRoom.ts rename to frontend/src/apis/http/pairRoom.ts index 28d9ba64..e86aec4a 100644 --- a/frontend/src/apis/pairRoom.ts +++ b/frontend/src/apis/http/pairRoom.ts @@ -1,8 +1,8 @@ import { Category } from '@/components/PairRoom/ReferenceCard/ReferenceCard.type'; -import fetcher from '@/apis/fetcher'; -import { Reference } from '@/apis/referenceLink'; -import { Todo } from '@/apis/todo'; +import fetcher from '@/apis/http/fetcher'; +import { Reference } from '@/apis/http/referenceLink'; +import { Todo } from '@/apis/http/todo'; import { ERROR_MESSAGES } from '@/constants/message'; @@ -100,7 +100,7 @@ export const updatePairRoomStatus = async ({ accessCode }: UpdatePairRoomStatusR }; export const deletePairRoom = async ({ accessCode }: { accessCode: string }) => { - await fetcher.delete({ + await fetcher.patch({ url: `${API_URL}/pair-room/${accessCode}`, errorMessage: ERROR_MESSAGES.DELETE_PAIR_ROOM, }); diff --git a/frontend/src/apis/referenceLink.ts b/frontend/src/apis/http/referenceLink.ts similarity index 97% rename from frontend/src/apis/referenceLink.ts rename to frontend/src/apis/http/referenceLink.ts index 7600d8e2..36c915fc 100644 --- a/frontend/src/apis/referenceLink.ts +++ b/frontend/src/apis/http/referenceLink.ts @@ -1,4 +1,4 @@ -import fetcher from '@/apis/fetcher'; +import fetcher from '@/apis/http/fetcher'; import { ERROR_MESSAGES } from '@/constants/message'; diff --git a/frontend/src/apis/retrospect.ts b/frontend/src/apis/http/retrospect.ts similarity index 95% rename from frontend/src/apis/retrospect.ts rename to frontend/src/apis/http/retrospect.ts index 43326d19..5906e019 100644 --- a/frontend/src/apis/retrospect.ts +++ b/frontend/src/apis/http/retrospect.ts @@ -1,4 +1,4 @@ -import fetcher from '@/apis/fetcher'; +import fetcher from '@/apis/http/fetcher'; import { ERROR_MESSAGES } from '@/constants/message'; diff --git a/frontend/src/apis/timer.ts b/frontend/src/apis/http/timer.ts similarity index 76% rename from frontend/src/apis/timer.ts rename to frontend/src/apis/http/timer.ts index 7a99e007..8b08458a 100644 --- a/frontend/src/apis/timer.ts +++ b/frontend/src/apis/http/timer.ts @@ -1,11 +1,6 @@ -import fetcher from '@/apis/fetcher'; +import fetcher from '@/apis/http/fetcher'; const API_URL = process.env.REACT_APP_API_URL; -const SOCKET_URL = process.env.REACT_SOCKET_API_URL; - -export const getConnection = (accessCode: string) => { - return new WebSocket(`${SOCKET_URL}/ws-connect?accesscode=${accessCode}`); -}; interface UpdateDurationRequest { duration: string; diff --git a/frontend/src/apis/todo.ts b/frontend/src/apis/http/todo.ts similarity index 97% rename from frontend/src/apis/todo.ts rename to frontend/src/apis/http/todo.ts index fe021ae4..6c66c9fc 100644 --- a/frontend/src/apis/todo.ts +++ b/frontend/src/apis/http/todo.ts @@ -1,4 +1,4 @@ -import fetcher from '@/apis/fetcher'; +import fetcher from '@/apis/http/fetcher'; import { ERROR_MESSAGES } from '@/constants/message'; diff --git a/frontend/src/apis/websocket/todo.ts b/frontend/src/apis/websocket/todo.ts new file mode 100644 index 00000000..80cfd3b7 --- /dev/null +++ b/frontend/src/apis/websocket/todo.ts @@ -0,0 +1,21 @@ +import { Client } from '@stomp/stompjs'; + +import { publishMessage } from '@/apis/websocket/websocket'; + +export const publishTodoMessage = { + add: (client: Client | null, accessCode: string, contents: string) => { + publishMessage(client, `/send/${accessCode}/todo/add`, { contents }); + }, + updateContents: (client: Client | null, accessCode: string, todoId: number, contents: string) => { + publishMessage(client, `/send/${accessCode}/todo/update/${todoId}/contents`, { contents }); + }, + updateOrder: (client: Client | null, accessCode: string, todoId: number, order: number) => { + publishMessage(client, `/send/${accessCode}/todo/update/${todoId}/order`, { order }); + }, + updateChecked: (client: Client | null, accessCode: string, todoId: number) => { + publishMessage(client, `/send/${accessCode}/todo/update/${todoId}/checked`); + }, + delete: (client: Client | null, accessCode: string, todoId: number) => { + publishMessage(client, `/send/${accessCode}/todo/delete/${todoId}`); + }, +}; diff --git a/frontend/src/apis/websocket/websocket.ts b/frontend/src/apis/websocket/websocket.ts new file mode 100644 index 00000000..ddfa66b7 --- /dev/null +++ b/frontend/src/apis/websocket/websocket.ts @@ -0,0 +1,31 @@ +import { Client, Message } from '@stomp/stompjs'; + +const SOCKET_URL = process.env.REACT_SOCKET_API_URL; + +export const getConnection = () => { + return new Client({ brokerURL: `${SOCKET_URL}/ws-connect` }); +}; + +export const subscribeTopic = (client: Client | null, destination: string, handler: (body: T) => void) => { + if (!client?.connected) { + console.error('웹소켓 연결에 실패했습니다.'); + return; + } + + client.subscribe(destination, (message: Message) => { + const body: T = JSON.parse(message.body); + handler(body); + }); +}; + +export const publishMessage = (client: Client | null, destination: string, contents?: object) => { + if (!client?.connected) { + console.error('[ERROR] 서버와 통신에 실패했습니다.'); + return; + } + + client.publish({ + destination: destination, + body: JSON.stringify(contents), + }); +}; diff --git a/frontend/src/components/CompletedPairRoom/ReferenceCard/ReferenceList/ReferenceList.tsx b/frontend/src/components/CompletedPairRoom/ReferenceCard/ReferenceList/ReferenceList.tsx index 11452412..cfff8896 100644 --- a/frontend/src/components/CompletedPairRoom/ReferenceCard/ReferenceList/ReferenceList.tsx +++ b/frontend/src/components/CompletedPairRoom/ReferenceCard/ReferenceList/ReferenceList.tsx @@ -1,6 +1,6 @@ import { Link } from 'react-router-dom'; -import type { Reference } from '@/apis/referenceLink'; +import type { Reference } from '@/apis/http/referenceLink'; import * as S from './ReferenceList.styles'; diff --git a/frontend/src/components/CompletedPairRoom/TodoListCard/TodoItem/TodoItem.tsx b/frontend/src/components/CompletedPairRoom/TodoListCard/TodoItem/TodoItem.tsx index 8fb0bb13..9b6eda59 100644 --- a/frontend/src/components/CompletedPairRoom/TodoListCard/TodoItem/TodoItem.tsx +++ b/frontend/src/components/CompletedPairRoom/TodoListCard/TodoItem/TodoItem.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; -import { Todo } from '@/apis/todo'; +import { Todo } from '@/apis/http/todo'; import useCopyClipBoard from '@/hooks/_common/useCopyClipboard'; diff --git a/frontend/src/components/Main/PairRoomCreateModal/PairRoomCreateModal.stories.tsx b/frontend/src/components/Main/PairRoomCreateModal/PairRoomCreateModal.stories.tsx index 5e4123e2..a24d529c 100644 --- a/frontend/src/components/Main/PairRoomCreateModal/PairRoomCreateModal.stories.tsx +++ b/frontend/src/components/Main/PairRoomCreateModal/PairRoomCreateModal.stories.tsx @@ -13,7 +13,7 @@ type Story = StoryObj; export const Default: Story = { render: () => { - return console.log()} />; + return alert('닫기')} />; }, args: { isOpen: true, diff --git a/frontend/src/components/Main/PairRoomEntryModal/PairRoomEntryModal.tsx b/frontend/src/components/Main/PairRoomEntryModal/PairRoomEntryModal.tsx index 04dbf0a1..6461e6af 100644 --- a/frontend/src/components/Main/PairRoomEntryModal/PairRoomEntryModal.tsx +++ b/frontend/src/components/Main/PairRoomEntryModal/PairRoomEntryModal.tsx @@ -6,7 +6,7 @@ import { Modal } from '@/components/_common/Modal'; import useToastStore from '@/stores/toastStore'; -import { getPairRoomExists } from '@/apis/pairRoom'; +import { getPairRoomExists } from '@/apis/http/pairRoom'; import useClickEnterKey from '@/hooks/_common/customEvent/useClickEnterKey'; import useInput from '@/hooks/_common/useInput'; diff --git a/frontend/src/components/MyPage/PairRoomButton/PairRoomButton.styles.ts b/frontend/src/components/MyPage/PairRoomButton/PairRoomButton.styles.ts index 0bc01df2..eb9c9f08 100644 --- a/frontend/src/components/MyPage/PairRoomButton/PairRoomButton.styles.ts +++ b/frontend/src/components/MyPage/PairRoomButton/PairRoomButton.styles.ts @@ -3,7 +3,7 @@ import { Link } from 'react-router-dom'; import { FaTrashAlt } from 'react-icons/fa'; import styled, { keyframes, css } from 'styled-components'; -import type { PairRoomStatus } from '@/apis/pairRoom'; +import type { PairRoomStatus } from '@/apis/http/pairRoom'; import { Z_INDEX } from '@/constants/style'; diff --git a/frontend/src/components/MyPage/PairRoomButton/PairRoomButton.tsx b/frontend/src/components/MyPage/PairRoomButton/PairRoomButton.tsx index 88da4fec..f9566934 100644 --- a/frontend/src/components/MyPage/PairRoomButton/PairRoomButton.tsx +++ b/frontend/src/components/MyPage/PairRoomButton/PairRoomButton.tsx @@ -3,7 +3,7 @@ import { IoIosArrowForward } from 'react-icons/io'; import ConfirmModal from '@/components/_common/ConfirmModal/ConfirmModal'; import Spinner from '@/components/_common/Spinner/Spinner'; -import type { PairRoomStatus } from '@/apis/pairRoom'; +import type { PairRoomStatus } from '@/apis/http/pairRoom'; import useModal from '@/hooks/_common/useModal'; diff --git a/frontend/src/components/PairRoom/ReferenceCard/ReferenceCard.stories.tsx b/frontend/src/components/PairRoom/ReferenceCard/ReferenceCard.stories.tsx index d50c27e7..4d252e29 100644 --- a/frontend/src/components/PairRoom/ReferenceCard/ReferenceCard.stories.tsx +++ b/frontend/src/components/PairRoom/ReferenceCard/ReferenceCard.stories.tsx @@ -12,7 +12,5 @@ export default meta; type Story = StoryObj; export const Default: Story = { - render: () => ( - {}} references={[]} categories={[]} /> - ), + render: () => {}} references={[]} categories={[]} />, }; diff --git a/frontend/src/components/PairRoom/ReferenceCard/ReferenceCard.tsx b/frontend/src/components/PairRoom/ReferenceCard/ReferenceCard.tsx index 3551f9a3..96b867c5 100644 --- a/frontend/src/components/PairRoom/ReferenceCard/ReferenceCard.tsx +++ b/frontend/src/components/PairRoom/ReferenceCard/ReferenceCard.tsx @@ -7,7 +7,9 @@ import Header from '@/components/PairRoom/ReferenceCard/Header/Header'; import { Category } from '@/components/PairRoom/ReferenceCard/ReferenceCard.type'; import ReferenceList from '@/components/PairRoom/ReferenceCard/ReferenceList/ReferenceList'; -import { Reference } from '@/apis/referenceLink'; +import useSocketStore from '@/stores/socketStore'; + +import { Reference } from '@/apis/http/referenceLink'; import useModal from '@/hooks/_common/useModal'; @@ -18,14 +20,15 @@ import { findValueById } from '@/utils/findOption'; import * as S from './ReferenceCard.styles'; interface ReferenceCardProps { - accessCode: string; isOpen: boolean; toggleIsOpen: () => void; references: Reference[]; categories: Category[]; } -const ReferenceCard = ({ accessCode, isOpen, toggleIsOpen, references, categories }: ReferenceCardProps) => { +const ReferenceCard = ({ isOpen, toggleIsOpen, references, categories }: ReferenceCardProps) => { + const { accessCode } = useSocketStore(); + const [selectedCategoryId, setSelectedCategoryId] = useState(DEFAULT_CATEGORY_ID); const { isModalOpen, openModal, closeModal } = useModal(); diff --git a/frontend/src/components/PairRoom/ReferenceCard/ReferenceList/ReferenceList.tsx b/frontend/src/components/PairRoom/ReferenceCard/ReferenceList/ReferenceList.tsx index 7b85877e..ce293226 100644 --- a/frontend/src/components/PairRoom/ReferenceCard/ReferenceList/ReferenceList.tsx +++ b/frontend/src/components/PairRoom/ReferenceCard/ReferenceList/ReferenceList.tsx @@ -1,6 +1,6 @@ import { Link } from 'react-router-dom'; -import type { Reference } from '@/apis/referenceLink'; +import type { Reference } from '@/apis/http/referenceLink'; import useReferencesMutation from '@/queries/PairRoom/useReferencesMutation'; diff --git a/frontend/src/components/PairRoom/TimerCard/TimerCard.tsx b/frontend/src/components/PairRoom/TimerCard/TimerCard.tsx index 296fd9ee..a332e9d9 100644 --- a/frontend/src/components/PairRoom/TimerCard/TimerCard.tsx +++ b/frontend/src/components/PairRoom/TimerCard/TimerCard.tsx @@ -16,19 +16,13 @@ import { theme } from '@/styles/theme'; import * as S from './TimerCard.styles'; interface TimerCardProps { - accessCode: string; defaultTime: number; - defaultTimeleft: number; + defaultTimeLeft: number; onTimerStop: () => void; } -const TimerCard = ({ accessCode, defaultTime, defaultTimeleft, onTimerStop }: TimerCardProps) => { - const { timeLeft, isActive, handleStart, handlePause } = useTimer( - accessCode, - defaultTime, - defaultTimeleft, - onTimerStop, - ); +const TimerCard = ({ defaultTime, defaultTimeLeft, onTimerStop }: TimerCardProps) => { + const { timeLeft, isActive, handleStart, handlePause } = useTimer(defaultTime, defaultTimeLeft, onTimerStop); const timeLeftRef = useRef(timeLeft); timeLeftRef.current = timeLeft; diff --git a/frontend/src/components/PairRoom/TodoListCard/TodoItem/TodoItem.tsx b/frontend/src/components/PairRoom/TodoListCard/TodoItem/TodoItem.tsx index 40d39170..ac7cd4b4 100644 --- a/frontend/src/components/PairRoom/TodoListCard/TodoItem/TodoItem.tsx +++ b/frontend/src/components/PairRoom/TodoListCard/TodoItem/TodoItem.tsx @@ -2,11 +2,12 @@ import { useState } from 'react'; import CheckBox from '@/components/_common/CheckBox/CheckBox'; -import { Todo } from '@/apis/todo'; +import useSocketStore from '@/stores/socketStore'; -import useCopyClipBoard from '@/hooks/_common/useCopyClipboard'; +import { Todo } from '@/apis/http/todo'; +import { publishTodoMessage } from '@/apis/websocket/todo'; -import useTodosMutation from '@/queries/PairRoom/useTodosMutation'; +import useCopyClipBoard from '@/hooks/_common/useCopyClipboard'; import * as S from './TodoItem.styles'; @@ -22,7 +23,7 @@ const TodoItem = ({ todo, isDraggedOver, onDragStart, onDragEnter, onDrop }: Tod const [isIconHovered, setIsIconHovered] = useState(false); const [, onCopy] = useCopyClipBoard(); - const { updateCheckedMutation, deleteTodoMutation } = useTodosMutation(); + const { client, accessCode } = useSocketStore(); const { id, isChecked, content } = todo; @@ -38,7 +39,7 @@ const TodoItem = ({ todo, isDraggedOver, onDragStart, onDragEnter, onDrop }: Tod onDragEnd={onDrop} > - updateCheckedMutation({ todoId: id })} /> + publishTodoMessage.updateChecked(client, accessCode, id)} />

{content}

@@ -52,7 +53,7 @@ const TodoItem = ({ todo, isDraggedOver, onDragStart, onDragEnter, onDrop }: Tod $isChecked={isChecked} onMouseEnter={() => setIsIconHovered(true)} onMouseLeave={() => setIsIconHovered(false)} - onClick={() => deleteTodoMutation({ todoId: id })} + onClick={() => publishTodoMessage.delete(client, accessCode, id)} /> diff --git a/frontend/src/components/PairRoom/TodoListCard/TodoList/TodoList.tsx b/frontend/src/components/PairRoom/TodoListCard/TodoList/TodoList.tsx index a035ed49..ae01ad37 100644 --- a/frontend/src/components/PairRoom/TodoListCard/TodoList/TodoList.tsx +++ b/frontend/src/components/PairRoom/TodoListCard/TodoList/TodoList.tsx @@ -1,10 +1,11 @@ import TodoItem from '@/components/PairRoom/TodoListCard/TodoItem/TodoItem'; -import { Todo } from '@/apis/todo'; +import useSocketStore from '@/stores/socketStore'; -import useDragAndDrop from '@/hooks/PairRoom/useDragAndDrop'; +import { Todo } from '@/apis/http/todo'; +import { publishTodoMessage } from '@/apis/websocket/todo'; -import useTodosMutation from '@/queries/PairRoom/useTodosMutation'; +import useDragAndDrop from '@/hooks/PairRoom/useDragAndDrop'; import * as S from './TodoList.styles'; @@ -13,10 +14,10 @@ interface TodoListProps { } const TodoList = ({ todos }: TodoListProps) => { - const { updateOrderMutation } = useTodosMutation(); + const { client, accessCode } = useSocketStore(); const handleUpdateOrder = (todoId: number, order: number) => { - updateOrderMutation({ todoId, order }); + publishTodoMessage.updateOrder(client, accessCode, todoId, order); }; const { dragOverItem, handleDragStart, handleDragEnter, handleDrop } = useDragAndDrop(todos, handleUpdateOrder); diff --git a/frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx b/frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx index 20d1345f..89343c30 100644 --- a/frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx +++ b/frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx @@ -1,5 +1,3 @@ -import { useParams } from 'react-router-dom'; - import { LuPlus } from 'react-icons/lu'; import Button from '@/components/_common/Button/Button'; @@ -8,29 +6,32 @@ import { PairRoomCard } from '@/components/PairRoom/PairRoomCard'; import Header from '@/components/PairRoom/TodoListCard/Header/Header'; import TodoList from '@/components/PairRoom/TodoListCard/TodoList/TodoList'; -import { Todo } from '@/apis/todo'; +import useSocketStore from '@/stores/socketStore'; -import useInput from '@/hooks/_common/useInput'; +import { Todo } from '@/apis/http/todo'; +import { publishTodoMessage } from '@/apis/websocket/todo'; -import useTodosMutation from '@/queries/PairRoom/useTodosMutation'; +import useInput from '@/hooks/_common/useInput'; +import useTodo from '@/hooks/PairRoom/useTodo'; import * as S from './TodoListCard.styles'; interface TodoListCardProps { isOpen: boolean; toggleIsOpen: () => void; - todos: Todo[]; + defaultTodos: Todo[]; } -const TodoListCard = ({ isOpen, toggleIsOpen, todos }: TodoListCardProps) => { - const { accessCode } = useParams(); - +const TodoListCard = ({ isOpen, toggleIsOpen, defaultTodos }: TodoListCardProps) => { + const { todos } = useTodo(defaultTodos); const { value, handleChange, resetValue } = useInput(); - const { addTodosMutation } = useTodosMutation(); + + const { client, accessCode } = useSocketStore(); const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); - addTodosMutation({ content: value, accessCode: accessCode || '' }, { onSuccess: resetValue }); + publishTodoMessage.add(client, accessCode, value); + resetValue(); }; return ( diff --git a/frontend/src/components/PairRoom/TodoListCard/TodoListCard.type.ts b/frontend/src/components/PairRoom/TodoListCard/TodoListCard.type.ts deleted file mode 100644 index 08d86cc8..00000000 --- a/frontend/src/components/PairRoom/TodoListCard/TodoListCard.type.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Todo { - id: number; - content: string; - isChecked: boolean; - sort: number; -} diff --git a/frontend/src/components/PairRoomOnboarding/AddPairModal/AddPairModal.tsx b/frontend/src/components/PairRoomOnboarding/AddPairModal/AddPairModal.tsx index e78609db..be404a08 100644 --- a/frontend/src/components/PairRoomOnboarding/AddPairModal/AddPairModal.tsx +++ b/frontend/src/components/PairRoomOnboarding/AddPairModal/AddPairModal.tsx @@ -6,7 +6,7 @@ import { Modal } from '@/components/_common/Modal'; import useToastStore from '@/stores/toastStore'; -import { getMemberName } from '@/apis/member'; +import { getMemberName } from '@/apis/http/member'; import useClickEnterKey from '@/hooks/_common/customEvent/useClickEnterKey'; import useInput from '@/hooks/_common/useInput'; diff --git a/frontend/src/hooks/PairRoom/useDragAndDrop.ts b/frontend/src/hooks/PairRoom/useDragAndDrop.ts index 5a05a33f..31cedb97 100644 --- a/frontend/src/hooks/PairRoom/useDragAndDrop.ts +++ b/frontend/src/hooks/PairRoom/useDragAndDrop.ts @@ -1,6 +1,6 @@ import { useState } from 'react'; -import { Todo } from '@/apis/todo'; +import { Todo } from '@/apis/http/todo'; export type DragPosition = 'ABOVE' | 'BELOW'; diff --git a/frontend/src/hooks/PairRoom/usePairRoom.ts b/frontend/src/hooks/PairRoom/usePairRoom.ts new file mode 100644 index 00000000..4e40f08f --- /dev/null +++ b/frontend/src/hooks/PairRoom/usePairRoom.ts @@ -0,0 +1,49 @@ +import { useEffect } from 'react'; + +import useSocketStore from '@/stores/socketStore'; +import useToastStore from '@/stores/toastStore'; + +import { getConnection } from '@/apis/websocket/websocket'; + +const usePairRoom = () => { + const { setClient, setIsConnected } = useSocketStore(); + const { addToast } = useToastStore(); + + const handleBeforeUnload = (event: BeforeUnloadEvent) => { + event.preventDefault(); + }; + + useEffect(() => { + const client = getConnection(); + + client.onConnect = () => { + setClient(client); + setIsConnected(true); + }; + + client.onDisconnect = () => { + setClient(null); + setIsConnected(false); + }; + + client.onStompError = (error) => { + console.error(error); + addToast({ status: 'ERROR', message: `웹소켓 연결 과정에서 오류가 발생했습니다. ${error}` }); + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + client.activate(); + + return () => { + client.deactivate(); + setClient(null); + setIsConnected(false); + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + return {}; +}; + +export default usePairRoom; diff --git a/frontend/src/hooks/PairRoom/usePairRoomValid.ts b/frontend/src/hooks/PairRoom/usePairRoomValid.ts index 5ab4ea3f..4174e645 100644 --- a/frontend/src/hooks/PairRoom/usePairRoomValid.ts +++ b/frontend/src/hooks/PairRoom/usePairRoomValid.ts @@ -3,7 +3,7 @@ import { useNavigate, useLocation } from 'react-router-dom'; import useToastStore from '@/stores/toastStore'; -import { getPairRoomExists } from '@/apis/pairRoom'; +import { getPairRoomExists } from '@/apis/http/pairRoom'; const usePairRoomValid = (accessCode: string) => { const location = useLocation(); diff --git a/frontend/src/hooks/PairRoom/useTimer.ts b/frontend/src/hooks/PairRoom/useTimer.ts index 3641cdbf..1398361e 100644 --- a/frontend/src/hooks/PairRoom/useTimer.ts +++ b/frontend/src/hooks/PairRoom/useTimer.ts @@ -1,41 +1,39 @@ -import { useRef, useState, useEffect } from 'react'; +import { useState, useRef, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useQueryClient } from '@tanstack/react-query'; import { AlarmSound } from '@/assets'; +import useSocketStore from '@/stores/socketStore'; import useToastStore from '@/stores/toastStore'; -import { getConnection, startTimer, stopTimer } from '@/apis/timer'; +import { startTimer, stopTimer } from '@/apis/http/timer'; +import { subscribeTopic } from '@/apis/websocket/websocket'; import useNotification from '@/hooks/PairRoom/useNotification'; import { QUERY_KEYS } from '@/constants/queryKeys'; -const EVENT_NAMES = { - TIMER: 'timer', - REMAINING_TIME: 'remaining-time', -}; +enum TimerStatus { + COMPLETE = 'complete', + START = 'start', + RUNNING = 'running', + PAUSE = 'pause', + UPDATE = 'update', +} -const MESSAGES = { - COMPLETE: 'complete', - START: 'start', - RUNNING: 'running', - PAUSE: 'pause', - UPDATE: 'update', -}; +const STATUS = TimerStatus; -const useTimer = (accessCode: string, defaultTime: number, defaultTimeleft: number, onTimerStop: () => void) => { - const navigate = useNavigate(); +const useTimer = (defaultTime: number, defaultTimeLeft: number, onTimerStop: () => void) => { + const { client, isConnected, accessCode } = useSocketStore(); - const queryClient = useQueryClient(); + const [timeLeft, setTimeLeft] = useState(defaultTimeLeft); + const [isActive, setIsActive] = useState(false); + const navigate = useNavigate(); + const queryClient = useQueryClient(); const alarmAudio = useRef(new Audio(AlarmSound)); - // const timeoutCount = useRef(0); - - const [timeLeft, setTimeLeft] = useState(defaultTimeleft); - const [isActive, setIsActive] = useState(false); const { addToast } = useToastStore(); const { fireNotification } = useNotification(); @@ -45,100 +43,85 @@ const useTimer = (accessCode: string, defaultTime: number, defaultTimeleft: numb }; const handlePause = () => { - stopTimer(accessCode); + if (isActive) stopTimer(accessCode); }; const handleStop = () => { - addToast({ status: 'SUCCESS', message: '타이머가 종료되었습니다.' }); - setIsActive(false); setTimeLeft(defaultTime); onTimerStop(); + alarmAudio.current.play(); + fireNotification('타이머가 끝났어요!', '드라이버 / 내비게이터 역할을 바꿔 주세요!', { + requireInteraction: true, + }); + + addToast({ status: 'SUCCESS', message: '타이머가 종료되었습니다.' }); addToast({ status: 'INFO', message: '드라이버 / 내비게이터 역할을 바꿔 주세요!' }); }; - const handleEvent = (eventName: string, eventData: string) => { - switch (eventName) { - case EVENT_NAMES.TIMER: - handleTimerEvent(eventData); - break; - case EVENT_NAMES.REMAINING_TIME: - handleRemainingTimeEvent(eventData); - break; - default: - console.warn(`Unhandled event: ${eventName}`); + const handleTimerEvent = (timeLeft: number) => { + if (timeLeft === 0) { + handleStop(); + return; } + + setTimeLeft(timeLeft); }; - const handleTimerEvent = (eventData: string) => { - switch (eventData) { - case MESSAGES.COMPLETE: + const handleTimerStatusEvent = (status: TimerStatus) => { + switch (status) { + case STATUS.COMPLETE: navigate(`/room/${accessCode}/retrospectForm`, { state: { valid: true } }); addToast({ status: 'WARNING', message: '페어룸이 종료되었습니다.' }); break; - case MESSAGES.START: - case MESSAGES.RUNNING: + + case STATUS.START: + case STATUS.RUNNING: setIsActive(true); addToast({ status: 'SUCCESS', message: '타이머가 시작되었습니다.' }); break; - case MESSAGES.PAUSE: + + case STATUS.PAUSE: setIsActive(false); addToast({ status: 'WARNING', message: '타이머가 일시 정지되었습니다.' }); break; - case MESSAGES.UPDATE: + + case STATUS.UPDATE: queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.GET_PAIR_ROOM_TIMER] }); addToast({ status: 'WARNING', message: '타이머 시간이 변경되었습니다.' }); break; - default: - console.warn(`Unhandled timer event data: ${eventData}`); - } - }; - const handleRemainingTimeEvent = (eventData: string) => { - if (eventData === '0') { - handleStop(); - alarmAudio.current.play(); - fireNotification('타이머가 끝났어요!', '드라이버 / 내비게이터 역할을 바꿔 주세요!', { - requireInteraction: true, - }); - } else { - setTimeLeft(Number(eventData)); + default: + addToast({ status: 'ERROR', message: '예상하지 못한 에러가 발생했습니다.' }); } }; useEffect(() => { - const socket = getConnection(accessCode); - - const handleMessage = (event: MessageEvent) => { - console.log('Received event:', event.data); - const parsedData = JSON.parse(event.data); - handleEvent(parsedData.event, parsedData.data); - }; - - const handleBeforeUnload = (event: BeforeUnloadEvent) => { - event.preventDefault(); - }; - - socket.onopen = () => { - console.log('WebSocket connection opened'); - socket.addEventListener('message', handleMessage as EventListener); - }; - - socket.onerror = (error) => { - console.error('WebSocket connection error:', error); - }; - - window.addEventListener('beforeunload', handleBeforeUnload); + if (client && isConnected) { + // 타이머 남은 시간 + subscribeTopic<{ data: number }>(client, `/topic/${accessCode}/timer`, (body) => handleTimerEvent(body.data)); + + // 타이머 상태 + subscribeTopic<{ data: TimerStatus }>(client, `/topic/${accessCode}/timer/status`, (body) => + handleTimerStatusEvent(body.data), + ); + } return () => { - socket.removeEventListener('message', handleMessage as EventListener); - socket.close(); - window.removeEventListener('beforeunload', handleBeforeUnload); + if (client && isConnected) { + client.unsubscribe('/timer'); + client.unsubscribe('/timer/status'); + } }; - }, []); + }, [client]); - return { timeLeft, isActive, handleStart, handlePause }; + return { + timeLeft, + isActive, + handleStart, + handlePause, + }; }; export default useTimer; diff --git a/frontend/src/hooks/PairRoom/useTodo.ts b/frontend/src/hooks/PairRoom/useTodo.ts new file mode 100644 index 00000000..00dceb3c --- /dev/null +++ b/frontend/src/hooks/PairRoom/useTodo.ts @@ -0,0 +1,30 @@ +import { useState, useEffect } from 'react'; + +import useSocketStore from '@/stores/socketStore'; + +import { Todo } from '@/apis/http/todo'; +import { subscribeTopic } from '@/apis/websocket/websocket'; + +const useTodo = (defaultTodos: Todo[]) => { + const [todos, setTodos] = useState(defaultTodos); + + const { client, isConnected, accessCode } = useSocketStore(); + + const handleTodos = (todos: Todo[]) => setTodos(todos); + + useEffect(() => { + if (client && isConnected) { + subscribeTopic(client, `/topic/${accessCode}/todo`, handleTodos); + } + + return () => { + if (client && isConnected) { + client.unsubscribe(`/topic/${accessCode}/todo`); + } + }; + }, [client]); + + return { todos }; +}; + +export default useTodo; diff --git a/frontend/src/hooks/_common/member/useSignInHandler.ts b/frontend/src/hooks/_common/member/useSignInHandler.ts index 1670106d..d033ecc2 100644 --- a/frontend/src/hooks/_common/member/useSignInHandler.ts +++ b/frontend/src/hooks/_common/member/useSignInHandler.ts @@ -1,4 +1,4 @@ -import { getSignInGithub } from '@/apis/oauth'; +import { getSignInGithub } from '@/apis/http/oauth'; const useSignInHandler = () => { const handleSignInGithub = async () => { diff --git a/frontend/src/hooks/_common/member/useSignOutHandler.ts b/frontend/src/hooks/_common/member/useSignOutHandler.ts index 6f451360..383fef63 100644 --- a/frontend/src/hooks/_common/member/useSignOutHandler.ts +++ b/frontend/src/hooks/_common/member/useSignOutHandler.ts @@ -2,7 +2,7 @@ import { useNavigate } from 'react-router-dom'; import useUserStore from '@/stores/userStore'; -import { getSignOut } from '@/apis/member'; +import { getSignOut } from '@/apis/http/member'; const useSignOutHandler = () => { const navigate = useNavigate(); diff --git a/frontend/src/hooks/_common/member/useSignUpHandler.ts b/frontend/src/hooks/_common/member/useSignUpHandler.ts index bac0252a..7444e910 100644 --- a/frontend/src/hooks/_common/member/useSignUpHandler.ts +++ b/frontend/src/hooks/_common/member/useSignUpHandler.ts @@ -2,7 +2,7 @@ import { useNavigate } from 'react-router-dom'; import useUserStore from '@/stores/userStore'; -import { addSignUp } from '@/apis/oauth'; +import { addSignUp } from '@/apis/http/oauth'; const useSignUpHandler = () => { const navigate = useNavigate(); diff --git a/frontend/src/pages/Callback/Callback.tsx b/frontend/src/pages/Callback/Callback.tsx index d83dd551..c10261a5 100644 --- a/frontend/src/pages/Callback/Callback.tsx +++ b/frontend/src/pages/Callback/Callback.tsx @@ -5,8 +5,8 @@ import Spinner from '@/components/_common/Spinner/Spinner'; import useUserStore from '@/stores/userStore'; -import { getMember } from '@/apis/member'; -import { getSignInCallback } from '@/apis/oauth'; +import { getMember } from '@/apis/http/member'; +import { getSignInCallback } from '@/apis/http/oauth'; import * as S from './Callback.styles'; diff --git a/frontend/src/pages/PairRoom/PairRoom.tsx b/frontend/src/pages/PairRoom/PairRoom.tsx index 634658db..601ed88c 100644 --- a/frontend/src/pages/PairRoom/PairRoom.tsx +++ b/frontend/src/pages/PairRoom/PairRoom.tsx @@ -10,7 +10,10 @@ import ReferenceCard from '@/components/PairRoom/ReferenceCard/ReferenceCard'; import TimerCard from '@/components/PairRoom/TimerCard/TimerCard'; import TodoListCard from '@/components/PairRoom/TodoListCard/TodoListCard'; +import useSocketStore from '@/stores/socketStore'; + import useModal from '@/hooks/_common/useModal'; +import usePairRoom from '@/hooks/PairRoom/usePairRoom'; import usePairRoomMutation from '@/queries/PairRoom/usePairRoomMutation'; import usePairRoomQuery from '@/queries/PairRoom/usePairRoomQuery'; @@ -25,7 +28,8 @@ const PairRoom = () => { const [navigator, setNavigator] = useState(''); const [isCardOpen, setIsCardOpen] = useState(false); - const { isModalOpen, closeModal } = useModal(true); + usePairRoom(); + const { isConnected } = useSocketStore(); const { driver: latestDriver, @@ -42,6 +46,8 @@ const PairRoom = () => { const { updatePairRoleMutation } = usePairRoomMutation(); + const { isModalOpen, closeModal } = useModal(true); + useEffect(() => { if (status === 'COMPLETED') navigate(`/room/${accessCode}/completed`, { state: { valid: true }, replace: true }); }, [status]); @@ -51,7 +57,7 @@ const PairRoom = () => { setNavigator(latestNavigator); }, [latestDriver, latestNavigator]); - if (isFetching) { + if (isFetching || !isConnected) { return ; } @@ -61,16 +67,14 @@ const PairRoom = () => { updatePairRoleMutation({ accessCode: accessCode || '' })} /> - setIsCardOpen(false)} todos={todos} /> + setIsCardOpen(false)} defaultTodos={todos} /> setIsCardOpen(true)} references={references} diff --git a/frontend/src/pages/PrivateRoutes.tsx b/frontend/src/pages/PrivateRoutes.tsx index 7c399eb0..847de8bd 100644 --- a/frontend/src/pages/PrivateRoutes.tsx +++ b/frontend/src/pages/PrivateRoutes.tsx @@ -3,9 +3,10 @@ import { Navigate, Outlet, useLocation, useParams } from 'react-router-dom'; import Loading from '@/pages/Loading/Loading'; +import useSocketStore from '@/stores/socketStore'; import useToastStore from '@/stores/toastStore'; -import { getPairRoomExists } from '@/apis/pairRoom'; +import { getPairRoomExists } from '@/apis/http/pairRoom'; const PrivateRoutes = () => { const location = useLocation(); @@ -13,6 +14,7 @@ const PrivateRoutes = () => { const [isValid, setIsValid] = useState(null); + const { setAccessCode } = useSocketStore(); const { addToast } = useToastStore(); const validateAccess = async () => { @@ -45,6 +47,7 @@ const PrivateRoutes = () => { useEffect(() => { validateAccess(); + setAccessCode(accessCode || ''); }, []); if (isValid === null) return ; diff --git a/frontend/src/queries/CompletedPairRoom/useUserIsInPairRoomQuery.ts b/frontend/src/queries/CompletedPairRoom/useUserIsInPairRoomQuery.ts index 445d8032..a84e5160 100644 --- a/frontend/src/queries/CompletedPairRoom/useUserIsInPairRoomQuery.ts +++ b/frontend/src/queries/CompletedPairRoom/useUserIsInPairRoomQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getUserIsInPairRoom } from '@/apis/member'; +import { getUserIsInPairRoom } from '@/apis/http/member'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/CompletedPairRoom/useUserRetrospectExistsQuery.ts b/frontend/src/queries/CompletedPairRoom/useUserRetrospectExistsQuery.ts index fd111da9..0ee1ca96 100644 --- a/frontend/src/queries/CompletedPairRoom/useUserRetrospectExistsQuery.ts +++ b/frontend/src/queries/CompletedPairRoom/useUserRetrospectExistsQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getUserRetrospectExists } from '@/apis/member'; +import { getUserRetrospectExists } from '@/apis/http/member'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/MyPage/useMemberMutation.ts b/frontend/src/queries/MyPage/useMemberMutation.ts index f2c5b15c..c1b1e019 100644 --- a/frontend/src/queries/MyPage/useMemberMutation.ts +++ b/frontend/src/queries/MyPage/useMemberMutation.ts @@ -5,7 +5,7 @@ import { useMutation } from '@tanstack/react-query'; import useToastStore from '@/stores/toastStore'; import useUserStore from '@/stores/userStore'; -import { deleteMember } from '@/apis/member'; +import { deleteMember } from '@/apis/http/member'; const useMemberMutation = () => { const navigate = useNavigate(); diff --git a/frontend/src/queries/MyPage/useMyPairRoomsQuery.ts b/frontend/src/queries/MyPage/useMyPairRoomsQuery.ts index 8cf2e250..7a633b89 100644 --- a/frontend/src/queries/MyPage/useMyPairRoomsQuery.ts +++ b/frontend/src/queries/MyPage/useMyPairRoomsQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getMyPairRooms } from '@/apis/member'; +import { getMyPairRooms } from '@/apis/http/member'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/MyPage/useMyRetrospectsQuery.ts b/frontend/src/queries/MyPage/useMyRetrospectsQuery.ts index acb9c5e2..a5cd9357 100644 --- a/frontend/src/queries/MyPage/useMyRetrospectsQuery.ts +++ b/frontend/src/queries/MyPage/useMyRetrospectsQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getMyRetrospects } from '@/apis/member'; +import { getMyRetrospects } from '@/apis/http/member'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoom/useCategoriesMutation.ts b/frontend/src/queries/PairRoom/useCategoriesMutation.ts index 56381644..00199ed0 100644 --- a/frontend/src/queries/PairRoom/useCategoriesMutation.ts +++ b/frontend/src/queries/PairRoom/useCategoriesMutation.ts @@ -2,7 +2,7 @@ import { useQueryClient, useMutation } from '@tanstack/react-query'; import useToastStore from '@/stores/toastStore'; -import { addCategory, updateCategory, deleteCategory } from '@/apis/category'; +import { addCategory, updateCategory, deleteCategory } from '@/apis/http/category'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoom/useCategoriesQuery.ts b/frontend/src/queries/PairRoom/useCategoriesQuery.ts index b57c931d..9d1f08e6 100644 --- a/frontend/src/queries/PairRoom/useCategoriesQuery.ts +++ b/frontend/src/queries/PairRoom/useCategoriesQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getCategories } from '@/apis/category'; +import { getCategories } from '@/apis/http/category'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoom/usePairRoomMutation.ts b/frontend/src/queries/PairRoom/usePairRoomMutation.ts index 0dbd7f5b..d1410f5d 100644 --- a/frontend/src/queries/PairRoom/usePairRoomMutation.ts +++ b/frontend/src/queries/PairRoom/usePairRoomMutation.ts @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import useToastStore from '@/stores/toastStore'; -import { addPairRoom, updatePairRole, updatePairRoomStatus, deletePairRoom } from '@/apis/pairRoom'; +import { addPairRoom, updatePairRole, updatePairRoomStatus, deletePairRoom } from '@/apis/http/pairRoom'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoom/usePairRoomQuery.ts b/frontend/src/queries/PairRoom/usePairRoomQuery.ts index 445dfb91..50996086 100644 --- a/frontend/src/queries/PairRoom/usePairRoomQuery.ts +++ b/frontend/src/queries/PairRoom/usePairRoomQuery.ts @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { getPairRoom } from '@/apis/pairRoom'; +import { getPairRoom } from '@/apis/http/pairRoom'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoom/useReferencesMutation.ts b/frontend/src/queries/PairRoom/useReferencesMutation.ts index dd4aa2aa..bfb15b1f 100644 --- a/frontend/src/queries/PairRoom/useReferencesMutation.ts +++ b/frontend/src/queries/PairRoom/useReferencesMutation.ts @@ -2,7 +2,7 @@ import { useQueryClient, useMutation } from '@tanstack/react-query'; import useToastStore from '@/stores/toastStore'; -import { addReferenceLink, deleteReferenceLink } from '@/apis/referenceLink'; +import { addReferenceLink, deleteReferenceLink } from '@/apis/http/referenceLink'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoom/useReferencesQuery.ts b/frontend/src/queries/PairRoom/useReferencesQuery.ts index 0ee9685e..2ed07cd9 100644 --- a/frontend/src/queries/PairRoom/useReferencesQuery.ts +++ b/frontend/src/queries/PairRoom/useReferencesQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getReferenceLinks } from '@/apis/referenceLink'; +import { getReferenceLinks } from '@/apis/http/referenceLink'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoom/useTimerMutation.ts b/frontend/src/queries/PairRoom/useTimerMutation.ts index d1840c5b..614d2f26 100644 --- a/frontend/src/queries/PairRoom/useTimerMutation.ts +++ b/frontend/src/queries/PairRoom/useTimerMutation.ts @@ -2,7 +2,7 @@ import { useMutation } from '@tanstack/react-query'; import useToastStore from '@/stores/toastStore'; -import { updateDuration } from '@/apis/timer'; +import { updateDuration } from '@/apis/http/timer'; const useTimerMutation = () => { const { addToast } = useToastStore(); diff --git a/frontend/src/queries/PairRoom/useTodosMutation.ts b/frontend/src/queries/PairRoom/useTodosMutation.ts index 5ddaa84c..68f4f6ed 100644 --- a/frontend/src/queries/PairRoom/useTodosMutation.ts +++ b/frontend/src/queries/PairRoom/useTodosMutation.ts @@ -2,7 +2,7 @@ import { useQueryClient, useMutation } from '@tanstack/react-query'; import useToastStore from '@/stores/toastStore'; -import { addTodos, updateOrder, updateChecked, updateContents, deleteTodo } from '@/apis/todo'; +import { addTodos, updateOrder, updateChecked, updateContents, deleteTodo } from '@/apis/http/todo'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoom/useTodosQuery.ts b/frontend/src/queries/PairRoom/useTodosQuery.ts index 360a1ca2..058e80d4 100644 --- a/frontend/src/queries/PairRoom/useTodosQuery.ts +++ b/frontend/src/queries/PairRoom/useTodosQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getTodos } from '@/apis/todo'; +import { getTodos } from '@/apis/http/todo'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoomOnboarding/useBranchesMutation.ts b/frontend/src/queries/PairRoomOnboarding/useBranchesMutation.ts index 8e86cef0..ffabfac2 100644 --- a/frontend/src/queries/PairRoomOnboarding/useBranchesMutation.ts +++ b/frontend/src/queries/PairRoomOnboarding/useBranchesMutation.ts @@ -2,7 +2,7 @@ import { useMutation } from '@tanstack/react-query'; import useToastStore from '@/stores/toastStore'; -import { addBranch, getSHAforMain } from '@/apis/github'; +import { addBranch, getSHAforMain } from '@/apis/http/github'; const useBranchesMutation = () => { const { addToast } = useToastStore(); diff --git a/frontend/src/queries/PairRoomOnboarding/useBranchesQuery.ts b/frontend/src/queries/PairRoomOnboarding/useBranchesQuery.ts index affb8984..d856283c 100644 --- a/frontend/src/queries/PairRoomOnboarding/useBranchesQuery.ts +++ b/frontend/src/queries/PairRoomOnboarding/useBranchesQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getBranches } from '@/apis/github'; +import { getBranches } from '@/apis/http/github'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/PairRoomOnboarding/useRepositoriesQuery.ts b/frontend/src/queries/PairRoomOnboarding/useRepositoriesQuery.ts index e0ef051f..7bd944a7 100644 --- a/frontend/src/queries/PairRoomOnboarding/useRepositoriesQuery.ts +++ b/frontend/src/queries/PairRoomOnboarding/useRepositoriesQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getRepositories } from '@/apis/github'; +import { getRepositories } from '@/apis/http/github'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/Retrospect/useRetrospectMutation.ts b/frontend/src/queries/Retrospect/useRetrospectMutation.ts index 817ea56e..07da4965 100644 --- a/frontend/src/queries/Retrospect/useRetrospectMutation.ts +++ b/frontend/src/queries/Retrospect/useRetrospectMutation.ts @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import useToastStore from '@/stores/toastStore'; -import { addRetrospect, deleteRetrospect } from '@/apis/retrospect'; +import { addRetrospect, deleteRetrospect } from '@/apis/http/retrospect'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/queries/Retrospect/useRetrospectQuery.ts b/frontend/src/queries/Retrospect/useRetrospectQuery.ts index 9a20b5f4..b23a59ae 100644 --- a/frontend/src/queries/Retrospect/useRetrospectQuery.ts +++ b/frontend/src/queries/Retrospect/useRetrospectQuery.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { getRetrospect } from '@/apis/retrospect'; +import { getRetrospect } from '@/apis/http/retrospect'; import { QUERY_KEYS } from '@/constants/queryKeys'; diff --git a/frontend/src/stores/socketStore.ts b/frontend/src/stores/socketStore.ts new file mode 100644 index 00000000..930b77bc --- /dev/null +++ b/frontend/src/stores/socketStore.ts @@ -0,0 +1,22 @@ +import { Client } from '@stomp/stompjs'; +import { create } from 'zustand'; + +interface SocketStore { + client: Client | null; + isConnected: boolean; + accessCode: string; + setClient: (client: Client | null) => void; + setIsConnected: (isConnected: boolean) => void; + setAccessCode: (accessCode: string) => void; +} + +const useSocketStore = create((set) => ({ + client: null, + isConnected: false, + accessCode: '', + setClient: (client) => set({ client }), + setAccessCode: (accessCode) => set({ accessCode }), + setIsConnected: (isConnected) => set({ isConnected }), +})); + +export default useSocketStore; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d3d09746..3222c202 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1986,6 +1986,11 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@stomp/stompjs@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@stomp/stompjs/-/stompjs-7.0.0.tgz#46b5c454a9dc8262e0b20f3b3dbacaa113993077" + integrity sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw== + "@storybook/addon-actions@8.2.4": version "8.2.4" resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.2.4.tgz#5d752381d3113e573dc9916f43cc7405d28d3a23" @@ -9012,16 +9017,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9107,14 +9103,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -10219,16 +10208,7 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10276,7 +10256,7 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" -ws@^8.11.0, ws@^8.16.0, ws@^8.2.3: +ws@^8.11.0, ws@^8.16.0, ws@^8.18.0, ws@^8.2.3: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==