Skip to content

Commit

Permalink
Merge pull request #1064 from woowacourse-teams/FE/feature/#1045
Browse files Browse the repository at this point in the history
[FE] Todo API 웹소켓으로 변경
  • Loading branch information
greetings1012 authored Jan 22, 2025
2 parents ef09e8e + 67e1bab commit 164f891
Show file tree
Hide file tree
Showing 61 changed files with 324 additions and 212 deletions.
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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": {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fetcher from '@/apis/fetcher';
import fetcher from '@/apis/http/fetcher';

import { ERROR_MESSAGES } from '@/constants/message';

Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fetcher from '@/apis/fetcher';
import fetcher from '@/apis/http/fetcher';

import { ERROR_MESSAGES } from '@/constants/message';

Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fetcher from '@/apis/fetcher';
import fetcher from '@/apis/http/fetcher';

import { ERROR_MESSAGES } from '@/constants/message';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fetcher from '@/apis/fetcher';
import fetcher from '@/apis/http/fetcher';

import { ERROR_MESSAGES } from '@/constants/message';

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fetcher from '@/apis/fetcher';
import fetcher from '@/apis/http/fetcher';

import { ERROR_MESSAGES } from '@/constants/message';

Expand Down
21 changes: 21 additions & 0 deletions frontend/src/apis/websocket/todo.ts
Original file line number Diff line number Diff line change
@@ -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}`);
},
};
31 changes: 31 additions & 0 deletions frontend/src/apis/websocket/websocket.ts
Original file line number Diff line number Diff line change
@@ -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 = <T>(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),
});
};
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Story = StoryObj<typeof PairRoomCreateModal>;

export const Default: Story = {
render: () => {
return <PairRoomCreateModal isOpen={true} closeModal={() => console.log()} />;
return <PairRoomCreateModal isOpen={true} closeModal={() => alert('닫기')} />;
},
args: {
isOpen: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,5 @@ export default meta;
type Story = StoryObj<typeof ReferenceCard>;

export const Default: Story = {
render: () => (
<ReferenceCard accessCode="1234" isOpen={true} toggleIsOpen={() => {}} references={[]} categories={[]} />
),
render: () => <ReferenceCard isOpen={true} toggleIsOpen={() => {}} references={[]} categories={[]} />,
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
12 changes: 3 additions & 9 deletions frontend/src/components/PairRoom/TimerCard/TimerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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;

Expand All @@ -38,7 +39,7 @@ const TodoItem = ({ todo, isDraggedOver, onDragStart, onDragEnter, onDrop }: Tod
onDragEnd={onDrop}
>
<S.TodoContainer $isChecked={isChecked}>
<CheckBox isChecked={isChecked} onClick={() => updateCheckedMutation({ todoId: id })} />
<CheckBox isChecked={isChecked} onClick={() => publishTodoMessage.updateChecked(client, accessCode, id)} />
<p>{content}</p>
</S.TodoContainer>
<S.IconContainer>
Expand All @@ -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)}
/>
</S.IconContainer>
</S.Layout>
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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);
Expand Down
23 changes: 12 additions & 11 deletions frontend/src/components/PairRoom/TodoListCard/TodoListCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { useParams } from 'react-router-dom';

import { LuPlus } from 'react-icons/lu';

import Button from '@/components/_common/Button/Button';
Expand All @@ -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<HTMLFormElement>) => {
event.preventDefault();
addTodosMutation({ content: value, accessCode: accessCode || '' }, { onSuccess: resetValue });
publishTodoMessage.add(client, accessCode, value);
resetValue();
};

return (
Expand Down

This file was deleted.

Loading

0 comments on commit 164f891

Please sign in to comment.