From b5fb50e336ebe9e2d478d474eebdae998ef12548 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Sun, 12 May 2024 22:51:26 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20Member=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooks/common/socket/useLandingSocket.ts | 61 +++++++++++++++---- frontend/src/types/common/landing.ts | 23 ++++++- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/frontend/src/hooks/common/socket/useLandingSocket.ts b/frontend/src/hooks/common/socket/useLandingSocket.ts index 50d7d0a..a4ad886 100644 --- a/frontend/src/hooks/common/socket/useLandingSocket.ts +++ b/frontend/src/hooks/common/socket/useLandingSocket.ts @@ -12,6 +12,7 @@ import { DEFAULT_VALUE } from "../../../constants/landing"; import { LandingSocketData, LandingSocketDomain, + LandingSocketMemberAction, LandingSocketMemoAction, } from "../../../types/common/landing"; @@ -44,25 +45,56 @@ const useLandingSocket = (socket: Socket) => { ) => { switch (action) { case LandingSocketMemoAction.CREATE: - setMemoList((memoList: LandingMemoDTO[]) => { - return [content, ...memoList]; - }); + setMemoList((memoList: LandingMemoDTO[]) => [content, ...memoList]); break; case LandingSocketMemoAction.DELETE: - setMemoList((memoList: LandingMemoDTO[]) => { - return memoList.filter( - (memo: LandingMemoDTO) => memo.id !== content.id - ); - }); + setMemoList((memoList: LandingMemoDTO[]) => + memoList.filter((memo: LandingMemoDTO) => memo.id !== content.id) + ); break; case LandingSocketMemoAction.COLOR_UPDATE: - setMemoList((memoList: LandingMemoDTO[]) => { - return memoList.map((memo: LandingMemoDTO) => { - if (memo.id !== content.id) return memo; + setMemoList((memoList: LandingMemoDTO[]) => + memoList.map((memo: LandingMemoDTO) => { + if (memo.id !== content.id) { + return memo; + } memo.color = content.color; return memo; - }); - }); + }) + ); + } + }; + + const handleMemberEvent = ( + action: LandingSocketMemberAction, + content: LandingMemberDTO | { id: number } + ) => { + switch (action) { + case LandingSocketMemberAction.CREATE: { + setMember((memberList) => [...memberList, content as LandingMemberDTO]); + + break; + } + case LandingSocketMemberAction.UPDATE: { + setMember((memberList) => + memberList.map((member) => { + if (member.id === content.id) { + member.status = (content as LandingMemberDTO).status; + } + + return member; + }) + ); + + break; + } + case LandingSocketMemberAction.DELETE: { + setMember((memberList) => + memberList.filter(({ id }) => id !== content.id) + ); + + break; + } } }; @@ -74,6 +106,9 @@ const useLandingSocket = (socket: Socket) => { case LandingSocketDomain.MEMO: handleMemoEvent(action, content); break; + case LandingSocketDomain.MEMBER: + handleMemberEvent(action, content); + break; } }; diff --git a/frontend/src/types/common/landing.ts b/frontend/src/types/common/landing.ts index 17034f7..03ee165 100644 --- a/frontend/src/types/common/landing.ts +++ b/frontend/src/types/common/landing.ts @@ -1,4 +1,8 @@ -import { LandingDTO, LandingMemoDTO } from "../DTO/landingDTO"; +import { + LandingDTO, + LandingMemberDTO, + LandingMemoDTO, +} from "../DTO/landingDTO"; export enum LandingSocketDomain { INIT = "landing", @@ -12,6 +16,12 @@ export enum LandingSocketMemoAction { COLOR_UPDATE = "colorUpdate", } +export enum LandingSocketMemberAction { + CREATE = "create", + UPDATE = "update", + DELETE = "delete", +} + interface LandingSocketInitData { domain: LandingSocketDomain.INIT; action: "init"; @@ -24,7 +34,16 @@ interface LandingSocketMemoData { content: LandingMemoDTO; } -export type LandingSocketData = LandingSocketInitData | LandingSocketMemoData; +interface LandingSocketMemberData { + domain: LandingSocketDomain.MEMBER; + action: LandingSocketMemberAction; + content: LandingMemberDTO | { id: number }; +} + +export type LandingSocketData = + | LandingSocketInitData + | LandingSocketMemoData + | LandingSocketMemberData; export enum MemoColorStyle { yellow = "bg-[#FFD966]", From 1f0181310848da7ec7dc7d18b513d35234d35e0d Mon Sep 17 00:00:00 2001 From: surinkwon Date: Sun, 12 May 2024 23:07:42 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=9C=EC=83=9D=20=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자가 상태 변경 버튼을 눌렀을 때 상태변경 이벤트를 서버에 전송 --- .../landing/member/LandingMember.tsx | 29 ++++++++++++++----- .../common/socket/useLandingEmitEvent.ts | 17 +++++++++-- frontend/src/pages/landing/LandingPage.tsx | 4 +-- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/landing/member/LandingMember.tsx b/frontend/src/components/landing/member/LandingMember.tsx index fb9db1c..0fcd825 100644 --- a/frontend/src/components/landing/member/LandingMember.tsx +++ b/frontend/src/components/landing/member/LandingMember.tsx @@ -2,25 +2,33 @@ import { USER_STATUS_WORD, USER_WORD_STATUS } from "../../../constants/landing"; import UserBlock from "./UserBlock"; import useDropdown from "../../../hooks/common/dropdown/useDropdown"; import { memberResponse } from "../../../types/DTO/authDTO"; -import { LandingMemberDTO } from "../../../types/DTO/landingDTO"; +import { LandingMemberDTO, MemberStatus } from "../../../types/DTO/landingDTO"; import { DEFAULT_MEMBER } from "../../../constants/projects"; +import { useEffect } from "react"; + +interface LandingMemberProps { + member: LandingMemberDTO[]; + myInfo: LandingMemberDTO; + inviteLinkIdRef: React.MutableRefObject; + projectTitle: string; + memberSocketEvent: { + emitMemberStatusUpdate: (content: LandingMemberDTO) => void; + }; +} const LandingMember = ({ member, myInfo, inviteLinkIdRef, projectTitle, -}: { - member: LandingMemberDTO[]; - myInfo: LandingMemberDTO; - inviteLinkIdRef: React.MutableRefObject; - projectTitle: string; -}) => { + memberSocketEvent, +}: LandingMemberProps) => { const { Dropdown, selectedOption } = useDropdown({ placeholder: "내 상태", options: ["접속 중", "부재 중", "자리비움"], defaultOption: USER_STATUS_WORD[myInfo.status], }); + const { emitMemberStatusUpdate } = memberSocketEvent; const userData: memberResponse = JSON.parse( window.localStorage.getItem("member") ?? DEFAULT_MEMBER @@ -41,6 +49,13 @@ const LandingMember = ({ }); }; + useEffect(() => { + emitMemberStatusUpdate({ + ...myInfo, + status: selectedOption as MemberStatus, + }); + }, [selectedOption]); + return (
diff --git a/frontend/src/hooks/common/socket/useLandingEmitEvent.ts b/frontend/src/hooks/common/socket/useLandingEmitEvent.ts index 2712f85..5ac5761 100644 --- a/frontend/src/hooks/common/socket/useLandingEmitEvent.ts +++ b/frontend/src/hooks/common/socket/useLandingEmitEvent.ts @@ -1,5 +1,9 @@ import { Socket } from "socket.io-client"; -import { MemoColorType } from "../../../types/common/landing"; +import { + LandingSocketMemberAction, + MemoColorType, +} from "../../../types/common/landing"; +import { LandingMemberDTO } from "../../../types/DTO/landingDTO"; const useLandingEmitEvent = (socket: Socket) => { const memoSocketEvent = { @@ -14,7 +18,16 @@ const useLandingEmitEvent = (socket: Socket) => { }, }; - return { memoSocketEvent }; + const memberSocketEvent = { + emitMemberStatusUpdate: (content: LandingMemberDTO) => { + socket.emit("member", { + action: LandingSocketMemberAction.UPDATE, + content, + }); + }, + }; + + return { memoSocketEvent, memberSocketEvent }; }; export default useLandingEmitEvent; diff --git a/frontend/src/pages/landing/LandingPage.tsx b/frontend/src/pages/landing/LandingPage.tsx index 7ee73c9..51160e1 100644 --- a/frontend/src/pages/landing/LandingPage.tsx +++ b/frontend/src/pages/landing/LandingPage.tsx @@ -17,7 +17,7 @@ const LandingPage = () => { const { socket }: { socket: Socket } = useOutletContext(); const { project, myInfo, member, sprint, link, memoList, inviteLinkIdRef } = useLandingSocket(socket); - const { memoSocketEvent } = useLandingEmitEvent(socket); + const { memoSocketEvent, memberSocketEvent } = useLandingEmitEvent(socket); return (
@@ -28,7 +28,7 @@ const LandingPage = () => {
From 90be1f0e0a04ea5b5380bba27d85ea60491a2010 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 31 May 2024 23:34:38 +0900 Subject: [PATCH 03/18] chore: install zustand --- frontend/package.json | 3 ++- frontend/yarn.lock | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 7310350..0d37525 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,8 @@ "react-router-dom": "^6.22.1", "reusify": "^1.0.4", "socket.io-client": "^4.7.5", - "tailwind-scrollbar-hide": "^1.1.7" + "tailwind-scrollbar-hide": "^1.1.7", + "zustand": "^4.5.2" }, "devDependencies": { "@babel/preset-env": "^7.23.9", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0b76e78..d63893a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5734,6 +5734,11 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-sync-external-store@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5993,3 +5998,10 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zustand@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848" + integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g== + dependencies: + use-sync-external-store "1.2.0" From 8cb28674a2778e245411360d69ce3f2d15432ed9 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 31 May 2024 23:36:23 +0900 Subject: [PATCH 04/18] =?UTF-8?q?refactor:=20myInfo,=20memberList=EB=A5=BC?= =?UTF-8?q?=20=EC=A0=84=EC=97=AD=EC=9C=BC=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 멤버들의 접속 상태는 Landing 페이지에서뿐 아니라 다른 페이지에서도 접근 가능해야 하기 때문에 전역으로 관리하도록 변경 --- .../landing/member/LandingMember.tsx | 6 ++-- .../hooks/common/socket/useLandingSocket.ts | 19 ++++++------ frontend/src/pages/landing/LandingPage.tsx | 13 ++++++-- frontend/src/stores/useMemberStore.ts | 31 +++++++++++++++++++ 4 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 frontend/src/stores/useMemberStore.ts diff --git a/frontend/src/components/landing/member/LandingMember.tsx b/frontend/src/components/landing/member/LandingMember.tsx index 0fcd825..0aead57 100644 --- a/frontend/src/components/landing/member/LandingMember.tsx +++ b/frontend/src/components/landing/member/LandingMember.tsx @@ -7,7 +7,7 @@ import { DEFAULT_MEMBER } from "../../../constants/projects"; import { useEffect } from "react"; interface LandingMemberProps { - member: LandingMemberDTO[]; + memberList: LandingMemberDTO[]; myInfo: LandingMemberDTO; inviteLinkIdRef: React.MutableRefObject; projectTitle: string; @@ -17,7 +17,7 @@ interface LandingMemberProps { } const LandingMember = ({ - member, + memberList, myInfo, inviteLinkIdRef, projectTitle, @@ -83,7 +83,7 @@ const LandingMember = ({ 초대링크 복사
- {member.map((memberData: LandingMemberDTO) => ( + {memberList.map((memberData: LandingMemberDTO) => ( ))}
diff --git a/frontend/src/hooks/common/socket/useLandingSocket.ts b/frontend/src/hooks/common/socket/useLandingSocket.ts index a4ad886..5d885fd 100644 --- a/frontend/src/hooks/common/socket/useLandingSocket.ts +++ b/frontend/src/hooks/common/socket/useLandingSocket.ts @@ -15,24 +15,25 @@ import { LandingSocketMemberAction, LandingSocketMemoAction, } from "../../../types/common/landing"; +import useMemberStore from "../../../stores/useMemberStore"; const useLandingSocket = (socket: Socket) => { const [project, setProject] = useState( DEFAULT_VALUE.PROJECT ); - const [myInfo, setMyInfo] = useState(DEFAULT_VALUE.MY_INFO); - const [member, setMember] = useState([]); const [sprint, setSprint] = useState(null); const [memoList, setMemoList] = useState([]); const [link, setLink] = useState([]); + const { myInfo, memberList, updateMyInfo, updateMemberList, addMember } = + useMemberStore(); const inviteLinkIdRef = useRef(""); const handleInitEvent = (content: LandingDTO) => { const { project, myInfo, member, sprint, memoList, link, inviteLinkId } = content as LandingDTO; setProject(project); - setMyInfo(myInfo); - setMember(member); + updateMyInfo(myInfo); + updateMemberList(member); setSprint(sprint); setMemoList(memoList); setLink(link); @@ -71,12 +72,12 @@ const useLandingSocket = (socket: Socket) => { ) => { switch (action) { case LandingSocketMemberAction.CREATE: { - setMember((memberList) => [...memberList, content as LandingMemberDTO]); + addMember(content as LandingMemberDTO); break; } case LandingSocketMemberAction.UPDATE: { - setMember((memberList) => + updateMemberList( memberList.map((member) => { if (member.id === content.id) { member.status = (content as LandingMemberDTO).status; @@ -89,9 +90,7 @@ const useLandingSocket = (socket: Socket) => { break; } case LandingSocketMemberAction.DELETE: { - setMember((memberList) => - memberList.filter(({ id }) => id !== content.id) - ); + updateMemberList(memberList.filter(({ id }) => id !== content.id)); break; } @@ -124,7 +123,7 @@ const useLandingSocket = (socket: Socket) => { return { project, myInfo, - member, + memberList, sprint, memoList, link, diff --git a/frontend/src/pages/landing/LandingPage.tsx b/frontend/src/pages/landing/LandingPage.tsx index 51160e1..0d2fdbc 100644 --- a/frontend/src/pages/landing/LandingPage.tsx +++ b/frontend/src/pages/landing/LandingPage.tsx @@ -15,8 +15,15 @@ const LandingPage = () => { } const { socket }: { socket: Socket } = useOutletContext(); - const { project, myInfo, member, sprint, link, memoList, inviteLinkIdRef } = - useLandingSocket(socket); + const { + project, + myInfo, + memberList, + sprint, + link, + memoList, + inviteLinkIdRef, + } = useLandingSocket(socket); const { memoSocketEvent, memberSocketEvent } = useLandingEmitEvent(socket); return ( @@ -28,7 +35,7 @@ const LandingPage = () => {
diff --git a/frontend/src/stores/useMemberStore.ts b/frontend/src/stores/useMemberStore.ts new file mode 100644 index 0000000..8251264 --- /dev/null +++ b/frontend/src/stores/useMemberStore.ts @@ -0,0 +1,31 @@ +import { create } from "zustand"; +import { LandingMemberDTO, MemberStatus } from "../types/DTO/landingDTO"; + +interface InitialMemberState { + myInfo: LandingMemberDTO; + memberList: LandingMemberDTO[]; +} + +interface MemberState extends InitialMemberState { + updateMyInfo: (newMyInfo: LandingMemberDTO) => void; + updateMyStatus: (status: MemberStatus) => void; + updateMemberList: (newMember: LandingMemberDTO[]) => void; + addMember: (member: LandingMemberDTO) => void; +} + +const initialState: InitialMemberState = { + myInfo: { id: -1, username: "", imageUrl: "", status: "on" }, + memberList: [], +}; + +const useMemberStore = create((set) => ({ + ...initialState, + updateMyInfo: (newMyInfo) => set(() => ({ myInfo: newMyInfo })), + updateMyStatus: (status) => + set((state) => ({ myInfo: { ...state.myInfo, status } })), + updateMemberList: (newMember) => set({ memberList: newMember }), + addMember: (member) => + set((state) => ({ memberList: [...state.memberList, member] })), +})); + +export default useMemberStore; From 8021980d6c61de9074cbb5c80a5b08f6f2b14e8b Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 31 May 2024 23:38:20 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=20=EC=9E=90?= =?UTF-8?q?=EB=A6=AC=EB=B9=84=EC=9B=80=20=EC=83=81=ED=83=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 최적화 전의 자리비움 상태 변경 기능 추가 --- .../src/hooks/common/useUserAwayStatus.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 frontend/src/hooks/common/useUserAwayStatus.ts diff --git a/frontend/src/hooks/common/useUserAwayStatus.ts b/frontend/src/hooks/common/useUserAwayStatus.ts new file mode 100644 index 0000000..ba2a656 --- /dev/null +++ b/frontend/src/hooks/common/useUserAwayStatus.ts @@ -0,0 +1,61 @@ +import { useEffect, useRef } from "react"; +import { Socket } from "socket.io-client"; +import useLandingEmitEvent from "./socket/useLandingEmitEvent"; +import useMemberStore from "../../stores/useMemberStore"; + +const useUserAwayStatus = (socket: Socket) => { + const timerRef = useRef(null); + const myInfo = useMemberStore((state) => state.myInfo); + const { memberSocketEvent } = useLandingEmitEvent(socket); + const TIME = 1000 * 60 * 10; + + const handleUserAct = () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + } + + if (myInfo.status === "away") { + memberSocketEvent.emitMemberStatusUpdate({ + ...myInfo, + status: "on", + }); + } + + timerRef.current = setTimeout(() => { + memberSocketEvent.emitMemberStatusUpdate({ + ...myInfo, + status: "away", + }); + + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }, TIME); + }; + + const addUserActEventListener = () => { + window.addEventListener("mousemove", handleUserAct); + window.addEventListener("keydown", handleUserAct); + window.addEventListener("scroll", handleUserAct); + window.addEventListener("click", handleUserAct); + }; + + const removeUserActEventListener = () => { + window.removeEventListener("mousemove", handleUserAct); + window.removeEventListener("keydown", handleUserAct); + window.removeEventListener("scroll", handleUserAct); + window.removeEventListener("click", handleUserAct); + }; + + useEffect(() => { + addUserActEventListener(); + + return () => { + removeUserActEventListener(); + }; + }, []); + + return { addUserActEventListener, removeUserActEventListener }; +}; + +export default useUserAwayStatus; From dc981161707c3bd04ffebf7b1a50801e4f5db7ad Mon Sep 17 00:00:00 2001 From: surinkwon Date: Thu, 6 Jun 2024 20:01:58 +0900 Subject: [PATCH 06/18] =?UTF-8?q?fix:=20=EC=A4=91=EB=B3=B5=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20=EC=9A=94=EC=B2=AD=EC=9D=84=20=EB=A7=89=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20useSocket=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit socket이 일반 변수로 관리되고 있어 연결 요청이 두 번 발생하던 현상 수정 disconnect 동작을 추가해 프로젝트 페이지에서 벗어나면 연결 해제되도록 수정 --- frontend/src/hooks/common/socket/useSocket.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/src/hooks/common/socket/useSocket.ts b/frontend/src/hooks/common/socket/useSocket.ts index 258d2ae..de0fada 100644 --- a/frontend/src/hooks/common/socket/useSocket.ts +++ b/frontend/src/hooks/common/socket/useSocket.ts @@ -5,12 +5,15 @@ import { getAccessToken } from "../../../apis/utils/authAPI"; const useSocket = (projectId: string) => { const WS_URL = `${BASE_URL}/project-${projectId}`; - const socket = io(WS_URL, { - path: `/api/socket.io`, - auth: { - accessToken: getAccessToken(), - }, - }); + const [socket] = useState( + io(WS_URL, { + path: `/api/socket.io`, + auth: { + accessToken: getAccessToken(), + }, + autoConnect: false, + }) + ); const [connected, setConnected] = useState(false); useEffect(() => { @@ -21,10 +24,12 @@ const useSocket = (projectId: string) => { setConnected(false); }; + socket.connect(); socket.on("connect", handleOnConnect); socket.on("disconnect", handleOnDisconnect); return () => { + socket.disconnect(); socket.off("connect", handleOnConnect); socket.off("disconnect", handleOnDisconnect); }; From 8030de2d38209ddc577f65d0d1a06239f6f7bc2b Mon Sep 17 00:00:00 2001 From: surinkwon Date: Thu, 6 Jun 2024 21:48:14 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=A0=91?= =?UTF-8?q?=EC=86=8D=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존 useLandingSocket에 있던 멤버 상태 초기화 로직을 LandingMember 내부 훅으로 옮김 멤버 상태 전역 관리 --- .../landing/member/LandingMember.tsx | 32 +++--- .../src/hooks/common/dropdown/useDropdown.tsx | 7 +- .../common/member/useUpdateUserStatus.ts | 98 +++++++++++++++++++ .../common/socket/useLandingEmitEvent.ts | 7 +- .../hooks/common/socket/useLandingSocket.ts | 51 +--------- frontend/src/pages/landing/LandingPage.tsx | 12 +-- 6 files changed, 126 insertions(+), 81 deletions(-) create mode 100644 frontend/src/hooks/common/member/useUpdateUserStatus.ts diff --git a/frontend/src/components/landing/member/LandingMember.tsx b/frontend/src/components/landing/member/LandingMember.tsx index 0aead57..c3d1baf 100644 --- a/frontend/src/components/landing/member/LandingMember.tsx +++ b/frontend/src/components/landing/member/LandingMember.tsx @@ -1,15 +1,15 @@ -import { USER_STATUS_WORD, USER_WORD_STATUS } from "../../../constants/landing"; +import { useEffect } from "react"; +import { useOutletContext } from "react-router-dom"; +import { Socket } from "socket.io-client"; +import { USER_WORD_STATUS } from "../../../constants/landing"; import UserBlock from "./UserBlock"; import useDropdown from "../../../hooks/common/dropdown/useDropdown"; -import { memberResponse } from "../../../types/DTO/authDTO"; -import { LandingMemberDTO, MemberStatus } from "../../../types/DTO/landingDTO"; +import { LandingMemberDTO } from "../../../types/DTO/landingDTO"; +import useUpdateUserStatus from "../../../hooks/common/member/useUpdateUserStatus"; import { DEFAULT_MEMBER } from "../../../constants/projects"; -import { useEffect } from "react"; +import { memberResponse } from "../../../types/DTO/authDTO"; interface LandingMemberProps { - memberList: LandingMemberDTO[]; - myInfo: LandingMemberDTO; - inviteLinkIdRef: React.MutableRefObject; projectTitle: string; memberSocketEvent: { emitMemberStatusUpdate: (content: LandingMemberDTO) => void; @@ -17,24 +17,26 @@ interface LandingMemberProps { } const LandingMember = ({ - memberList, - myInfo, - inviteLinkIdRef, projectTitle, memberSocketEvent, }: LandingMemberProps) => { - const { Dropdown, selectedOption } = useDropdown({ + const { socket }: { socket: Socket } = useOutletContext(); + const { Dropdown, selectedOption, handleChangeSelectedOption } = useDropdown({ placeholder: "내 상태", options: ["접속 중", "부재 중", "자리비움"], - defaultOption: USER_STATUS_WORD[myInfo.status], + defaultOption: "접속 중", }); + const { myInfo, memberList, inviteLinkIdRef } = useUpdateUserStatus( + socket, + handleChangeSelectedOption + ); + const { emitMemberStatusUpdate } = memberSocketEvent; const userData: memberResponse = JSON.parse( window.localStorage.getItem("member") ?? DEFAULT_MEMBER ); - const imageUrl = myInfo.imageUrl ?? userData.imageUrl; - const username = myInfo.username ?? userData.username; + const { imageUrl, username } = myInfo.imageUrl ? myInfo : userData; const handleInviteButtonClick = () => { window.navigator.clipboard @@ -52,7 +54,7 @@ const LandingMember = ({ useEffect(() => { emitMemberStatusUpdate({ ...myInfo, - status: selectedOption as MemberStatus, + status: USER_WORD_STATUS[selectedOption], }); }, [selectedOption]); diff --git a/frontend/src/hooks/common/dropdown/useDropdown.tsx b/frontend/src/hooks/common/dropdown/useDropdown.tsx index 3b8bd6d..9f458c6 100644 --- a/frontend/src/hooks/common/dropdown/useDropdown.tsx +++ b/frontend/src/hooks/common/dropdown/useDropdown.tsx @@ -24,6 +24,9 @@ const useDropdown = ({ defaultOption ? defaultOption : "" ); const dropdownRef = useRef(null); + const handleChangeSelectedOption = (option: string) => { + setSelectedOption(option); + }; const Dropdown = ({ buttonClassName = "", @@ -38,7 +41,7 @@ const useDropdown = ({ }; const handleOptionClick = (option: string) => { - setSelectedOption(option); + handleChangeSelectedOption(option); setOpen(false); }; @@ -100,7 +103,7 @@ const useDropdown = ({ ); }; - return { Dropdown, selectedOption }; + return { Dropdown, selectedOption, handleChangeSelectedOption }; }; export default useDropdown; diff --git a/frontend/src/hooks/common/member/useUpdateUserStatus.ts b/frontend/src/hooks/common/member/useUpdateUserStatus.ts new file mode 100644 index 0000000..0fea475 --- /dev/null +++ b/frontend/src/hooks/common/member/useUpdateUserStatus.ts @@ -0,0 +1,98 @@ +import { Socket } from "socket.io-client"; +import { + LandingSocketData, + LandingSocketDomain, + LandingSocketMemberAction, +} from "../../../types/common/landing"; +import { LandingDTO, LandingMemberDTO } from "../../../types/DTO/landingDTO"; +import useMemberStore from "../../../stores/useMemberStore"; +import { useEffect, useRef } from "react"; +import { USER_STATUS_WORD } from "../../../constants/landing"; + +const useUpdateUserStatus = ( + socket: Socket, + handleChangeStatus: (option: string) => void +) => { + const { + myInfo, + memberList, + updateMyInfo, + updateMyStatus, + updateMemberList, + addMember, + } = useMemberStore(); + const inviteLinkIdRef = useRef(""); + + const handleInitEvent = (content: LandingDTO) => { + const { myInfo, member: memberList, inviteLinkId } = content; + updateMyInfo(myInfo); + updateMemberList(memberList); + inviteLinkIdRef.current = inviteLinkId; + }; + + const handleMemberEvent = ( + action: LandingSocketMemberAction, + content: LandingMemberDTO | { id: number } + ) => { + switch (action) { + case LandingSocketMemberAction.CREATE: { + addMember(content as LandingMemberDTO); + + break; + } + case LandingSocketMemberAction.UPDATE: { + if (content.id === myInfo.id) { + updateMyStatus((content as LandingMemberDTO).status); + handleChangeStatus( + USER_STATUS_WORD[(content as LandingMemberDTO).status] + ); + + break; + } + + updateMemberList( + memberList.map((member) => { + if (member.id === content.id) { + return { + ...member, + status: (content as LandingMemberDTO).status, + }; + } + + return member; + }) + ); + + break; + } + case LandingSocketMemberAction.DELETE: { + updateMemberList(memberList.filter(({ id }) => id !== content.id)); + + break; + } + } + }; + + const handleOnLanding = ({ domain, action, content }: LandingSocketData) => { + switch (domain) { + case LandingSocketDomain.INIT: + handleInitEvent(content); + break; + case LandingSocketDomain.MEMBER: + handleMemberEvent(action, content); + break; + } + }; + + useEffect(() => { + socket.on("landing", handleOnLanding); + + return () => { + socket.off("landing", handleOnLanding); + }; + }, [socket, myInfo, memberList]); + + return { myInfo, memberList, inviteLinkIdRef }; +}; + +export default useUpdateUserStatus; diff --git a/frontend/src/hooks/common/socket/useLandingEmitEvent.ts b/frontend/src/hooks/common/socket/useLandingEmitEvent.ts index 5ac5761..0a5f712 100644 --- a/frontend/src/hooks/common/socket/useLandingEmitEvent.ts +++ b/frontend/src/hooks/common/socket/useLandingEmitEvent.ts @@ -1,8 +1,5 @@ import { Socket } from "socket.io-client"; -import { - LandingSocketMemberAction, - MemoColorType, -} from "../../../types/common/landing"; +import { MemoColorType } from "../../../types/common/landing"; import { LandingMemberDTO } from "../../../types/DTO/landingDTO"; const useLandingEmitEvent = (socket: Socket) => { @@ -21,7 +18,7 @@ const useLandingEmitEvent = (socket: Socket) => { const memberSocketEvent = { emitMemberStatusUpdate: (content: LandingMemberDTO) => { socket.emit("member", { - action: LandingSocketMemberAction.UPDATE, + action: "update", content, }); }, diff --git a/frontend/src/hooks/common/socket/useLandingSocket.ts b/frontend/src/hooks/common/socket/useLandingSocket.ts index 5d885fd..3568540 100644 --- a/frontend/src/hooks/common/socket/useLandingSocket.ts +++ b/frontend/src/hooks/common/socket/useLandingSocket.ts @@ -1,9 +1,8 @@ import { Socket } from "socket.io-client"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import { LandingDTO, LandingLinkDTO, - LandingMemberDTO, LandingMemoDTO, LandingProjectDTO, LandingSprintDTO, @@ -12,10 +11,8 @@ import { DEFAULT_VALUE } from "../../../constants/landing"; import { LandingSocketData, LandingSocketDomain, - LandingSocketMemberAction, LandingSocketMemoAction, } from "../../../types/common/landing"; -import useMemberStore from "../../../stores/useMemberStore"; const useLandingSocket = (socket: Socket) => { const [project, setProject] = useState( @@ -24,20 +21,13 @@ const useLandingSocket = (socket: Socket) => { const [sprint, setSprint] = useState(null); const [memoList, setMemoList] = useState([]); const [link, setLink] = useState([]); - const { myInfo, memberList, updateMyInfo, updateMemberList, addMember } = - useMemberStore(); - const inviteLinkIdRef = useRef(""); const handleInitEvent = (content: LandingDTO) => { - const { project, myInfo, member, sprint, memoList, link, inviteLinkId } = - content as LandingDTO; + const { project, sprint, memoList, link } = content as LandingDTO; setProject(project); - updateMyInfo(myInfo); - updateMemberList(member); setSprint(sprint); setMemoList(memoList); setLink(link); - inviteLinkIdRef.current = inviteLinkId; }; const handleMemoEvent = ( @@ -66,37 +56,6 @@ const useLandingSocket = (socket: Socket) => { } }; - const handleMemberEvent = ( - action: LandingSocketMemberAction, - content: LandingMemberDTO | { id: number } - ) => { - switch (action) { - case LandingSocketMemberAction.CREATE: { - addMember(content as LandingMemberDTO); - - break; - } - case LandingSocketMemberAction.UPDATE: { - updateMemberList( - memberList.map((member) => { - if (member.id === content.id) { - member.status = (content as LandingMemberDTO).status; - } - - return member; - }) - ); - - break; - } - case LandingSocketMemberAction.DELETE: { - updateMemberList(memberList.filter(({ id }) => id !== content.id)); - - break; - } - } - }; - const handleOnLanding = ({ domain, action, content }: LandingSocketData) => { switch (domain) { case LandingSocketDomain.INIT: @@ -105,9 +64,6 @@ const useLandingSocket = (socket: Socket) => { case LandingSocketDomain.MEMO: handleMemoEvent(action, content); break; - case LandingSocketDomain.MEMBER: - handleMemberEvent(action, content); - break; } }; @@ -122,12 +78,9 @@ const useLandingSocket = (socket: Socket) => { return { project, - myInfo, - memberList, sprint, memoList, link, - inviteLinkIdRef, }; }; diff --git a/frontend/src/pages/landing/LandingPage.tsx b/frontend/src/pages/landing/LandingPage.tsx index 0d2fdbc..b8cbd1f 100644 --- a/frontend/src/pages/landing/LandingPage.tsx +++ b/frontend/src/pages/landing/LandingPage.tsx @@ -15,15 +15,7 @@ const LandingPage = () => { } const { socket }: { socket: Socket } = useOutletContext(); - const { - project, - myInfo, - memberList, - sprint, - link, - memoList, - inviteLinkIdRef, - } = useLandingSocket(socket); + const { project, sprint, link, memoList } = useLandingSocket(socket); const { memoSocketEvent, memberSocketEvent } = useLandingEmitEvent(socket); return ( @@ -35,7 +27,7 @@ const LandingPage = () => {
From 22e7d8c357325f049ddf40e0aea65c745833a222 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Thu, 6 Jun 2024 22:23:17 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=20=EC=9E=90?= =?UTF-8?q?=EB=A6=AC=20=EB=B9=84=EC=9B=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 10분 동안 사용자 움직임이 없을 시 자동으로 자리비움 상태로 변환하는 기능 구현 --- .../src/hooks/common/throttle/useThrottle.ts | 22 +++++ frontend/src/hooks/common/useAwayUser.ts | 83 +++++++++++++++++++ .../src/hooks/common/useUserAwayStatus.ts | 61 -------------- frontend/src/pages/main/MainPage.tsx | 9 +- 4 files changed, 112 insertions(+), 63 deletions(-) create mode 100644 frontend/src/hooks/common/throttle/useThrottle.ts create mode 100644 frontend/src/hooks/common/useAwayUser.ts delete mode 100644 frontend/src/hooks/common/useUserAwayStatus.ts diff --git a/frontend/src/hooks/common/throttle/useThrottle.ts b/frontend/src/hooks/common/throttle/useThrottle.ts new file mode 100644 index 0000000..5aa9857 --- /dev/null +++ b/frontend/src/hooks/common/throttle/useThrottle.ts @@ -0,0 +1,22 @@ +import { useRef } from "react"; + +const useThrottle = () => { + const timerRef = useRef(null); + + const throttle = (time: number, callback: () => void) => { + if (!timerRef.current) { + timerRef.current = setTimeout(() => { + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = null; + } + }, time); + + callback(); + } + }; + + return throttle; +}; + +export default useThrottle; diff --git a/frontend/src/hooks/common/useAwayUser.ts b/frontend/src/hooks/common/useAwayUser.ts new file mode 100644 index 0000000..818a6da --- /dev/null +++ b/frontend/src/hooks/common/useAwayUser.ts @@ -0,0 +1,83 @@ +import { useEffect, useRef } from "react"; +import { Socket } from "socket.io-client"; +import useLandingEmitEvent from "./socket/useLandingEmitEvent"; +import useMemberStore from "../../stores/useMemberStore"; +import useThrottle from "./throttle/useThrottle"; + +const useAwayUser = (socket: Socket) => { + const timerRef = useRef(null); + const myInfo = useMemberStore((state) => state.myInfo); + const updateMemberList = useMemberStore((state) => state.updateMemberList); + const throttle = useThrottle(); + const { memberSocketEvent } = useLandingEmitEvent(socket); + const TIME = 1000 * 60 * 10; + const THROTTLE_TIME = 3000; + + const clearTimer = () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + + const setTimer = () => { + timerRef.current = setTimeout(() => { + memberSocketEvent.emitMemberStatusUpdate({ + ...myInfo, + status: "away", + }); + + clearTimer(); + }, TIME); + }; + + const handleUserStatus = () => { + throttle(THROTTLE_TIME, () => { + clearTimer(); + + if (myInfo.status === "away") { + memberSocketEvent.emitMemberStatusUpdate({ + ...myInfo, + status: "on", + }); + + setTimer(); + } + + setTimer(); + }); + }; + + const addUserStatusEventListener = () => { + window.addEventListener("mousemove", handleUserStatus); + window.addEventListener("keydown", handleUserStatus); + window.addEventListener("scroll", handleUserStatus); + window.addEventListener("click", handleUserStatus); + }; + + const removeUserStatusEventListener = () => { + window.removeEventListener("mousemove", handleUserStatus); + window.removeEventListener("keydown", handleUserStatus); + window.removeEventListener("scroll", handleUserStatus); + window.removeEventListener("click", handleUserStatus); + clearTimer(); + }; + + useEffect(() => { + addUserStatusEventListener(); + + return () => { + removeUserStatusEventListener(); + }; + }, [myInfo]); + + useEffect( + () => () => { + updateMemberList([]); + }, + [] + ); + + return { addUserStatusEventListener, removeUserStatusEventListener }; +}; + +export default useAwayUser; diff --git a/frontend/src/hooks/common/useUserAwayStatus.ts b/frontend/src/hooks/common/useUserAwayStatus.ts deleted file mode 100644 index ba2a656..0000000 --- a/frontend/src/hooks/common/useUserAwayStatus.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { useEffect, useRef } from "react"; -import { Socket } from "socket.io-client"; -import useLandingEmitEvent from "./socket/useLandingEmitEvent"; -import useMemberStore from "../../stores/useMemberStore"; - -const useUserAwayStatus = (socket: Socket) => { - const timerRef = useRef(null); - const myInfo = useMemberStore((state) => state.myInfo); - const { memberSocketEvent } = useLandingEmitEvent(socket); - const TIME = 1000 * 60 * 10; - - const handleUserAct = () => { - if (timerRef.current) { - clearTimeout(timerRef.current); - } - - if (myInfo.status === "away") { - memberSocketEvent.emitMemberStatusUpdate({ - ...myInfo, - status: "on", - }); - } - - timerRef.current = setTimeout(() => { - memberSocketEvent.emitMemberStatusUpdate({ - ...myInfo, - status: "away", - }); - - if (timerRef.current) { - clearTimeout(timerRef.current); - } - }, TIME); - }; - - const addUserActEventListener = () => { - window.addEventListener("mousemove", handleUserAct); - window.addEventListener("keydown", handleUserAct); - window.addEventListener("scroll", handleUserAct); - window.addEventListener("click", handleUserAct); - }; - - const removeUserActEventListener = () => { - window.removeEventListener("mousemove", handleUserAct); - window.removeEventListener("keydown", handleUserAct); - window.removeEventListener("scroll", handleUserAct); - window.removeEventListener("click", handleUserAct); - }; - - useEffect(() => { - addUserActEventListener(); - - return () => { - removeUserActEventListener(); - }; - }, []); - - return { addUserActEventListener, removeUserActEventListener }; -}; - -export default useUserAwayStatus; diff --git a/frontend/src/pages/main/MainPage.tsx b/frontend/src/pages/main/MainPage.tsx index 99242b4..40401d1 100644 --- a/frontend/src/pages/main/MainPage.tsx +++ b/frontend/src/pages/main/MainPage.tsx @@ -1,17 +1,22 @@ import { Outlet, useLocation, useParams } from "react-router-dom"; import ProjectSidebar from "../../components/main/ProjectSidebar"; import useSocket from "../../hooks/common/socket/useSocket"; +import useAwayUser from "../../hooks/common/useAwayUser"; const MainPage = () => { const { pathname } = useLocation(); const { projectId } = useParams(); - if (!projectId) throw Error("잘못된 ProjectID 입니다."); + if (!projectId) { + throw Error("잘못된 ProjectID 입니다."); + } const { socket } = useSocket(projectId); + const userStatusEventListener = useAwayUser(socket); + return (
- +
); From b4115a6a247ca0734b414f636fc411018f6b3fbc Mon Sep 17 00:00:00 2001 From: surinkwon Date: Thu, 6 Jun 2024 22:37:29 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=EC=97=90=EC=84=9C=20=EB=82=98=EA=B0=88=20=EC=8B=9C=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EB=A5=BC=20=EB=B6=80=EC=9E=AC=20=EC=A4=91?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useUserLeaveProject 훅을 만들어 내부에 로직 구현 --- .../hooks/common/{ => member}/useAwayUser.ts | 14 +++-------- .../common/member/useUserLeaveProject.ts | 23 +++++++++++++++++++ frontend/src/pages/main/MainPage.tsx | 4 +++- 3 files changed, 29 insertions(+), 12 deletions(-) rename frontend/src/hooks/common/{ => member}/useAwayUser.ts (84%) create mode 100644 frontend/src/hooks/common/member/useUserLeaveProject.ts diff --git a/frontend/src/hooks/common/useAwayUser.ts b/frontend/src/hooks/common/member/useAwayUser.ts similarity index 84% rename from frontend/src/hooks/common/useAwayUser.ts rename to frontend/src/hooks/common/member/useAwayUser.ts index 818a6da..9e190bc 100644 --- a/frontend/src/hooks/common/useAwayUser.ts +++ b/frontend/src/hooks/common/member/useAwayUser.ts @@ -1,13 +1,12 @@ import { useEffect, useRef } from "react"; import { Socket } from "socket.io-client"; -import useLandingEmitEvent from "./socket/useLandingEmitEvent"; -import useMemberStore from "../../stores/useMemberStore"; -import useThrottle from "./throttle/useThrottle"; +import useLandingEmitEvent from "../socket/useLandingEmitEvent"; +import useMemberStore from "../../../stores/useMemberStore"; +import useThrottle from "../throttle/useThrottle"; const useAwayUser = (socket: Socket) => { const timerRef = useRef(null); const myInfo = useMemberStore((state) => state.myInfo); - const updateMemberList = useMemberStore((state) => state.updateMemberList); const throttle = useThrottle(); const { memberSocketEvent } = useLandingEmitEvent(socket); const TIME = 1000 * 60 * 10; @@ -70,13 +69,6 @@ const useAwayUser = (socket: Socket) => { }; }, [myInfo]); - useEffect( - () => () => { - updateMemberList([]); - }, - [] - ); - return { addUserStatusEventListener, removeUserStatusEventListener }; }; diff --git a/frontend/src/hooks/common/member/useUserLeaveProject.ts b/frontend/src/hooks/common/member/useUserLeaveProject.ts new file mode 100644 index 0000000..9ccdb4b --- /dev/null +++ b/frontend/src/hooks/common/member/useUserLeaveProject.ts @@ -0,0 +1,23 @@ +import { useEffect } from "react"; +import { Socket } from "socket.io-client"; +import useMemberStore from "../../../stores/useMemberStore"; +import useLandingEmitEvent from "../socket/useLandingEmitEvent"; + +const useUserLeaveProject = (socket: Socket) => { + const myInfo = useMemberStore((state) => state.myInfo); + const updateMemberList = useMemberStore((state) => state.updateMemberList); + const { memberSocketEvent } = useLandingEmitEvent(socket); + + useEffect( + () => () => { + updateMemberList([]); + memberSocketEvent.emitMemberStatusUpdate({ + ...myInfo, + status: "off", + }); + }, + [] + ); +}; + +export default useUserLeaveProject; diff --git a/frontend/src/pages/main/MainPage.tsx b/frontend/src/pages/main/MainPage.tsx index 40401d1..866d92d 100644 --- a/frontend/src/pages/main/MainPage.tsx +++ b/frontend/src/pages/main/MainPage.tsx @@ -1,7 +1,8 @@ import { Outlet, useLocation, useParams } from "react-router-dom"; import ProjectSidebar from "../../components/main/ProjectSidebar"; import useSocket from "../../hooks/common/socket/useSocket"; -import useAwayUser from "../../hooks/common/useAwayUser"; +import useAwayUser from "../../hooks/common/member/useAwayUser"; +import useUserLeaveProject from "../../hooks/common/member/useUserLeaveProject"; const MainPage = () => { const { pathname } = useLocation(); @@ -11,6 +12,7 @@ const MainPage = () => { } const { socket } = useSocket(projectId); const userStatusEventListener = useAwayUser(socket); + useUserLeaveProject(socket); return (
From 9afb69cdead931e3d26fa379b6a907a9ea5de75c Mon Sep 17 00:00:00 2001 From: surinkwon Date: Thu, 6 Jun 2024 23:36:54 +0900 Subject: [PATCH 10/18] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C?= =?UTF-8?q?=20=EA=B7=B8=20=EC=83=81=ED=83=9C=EB=A5=BC=20=EC=9C=A0=EC=A7=80?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 수동으로 자리비움이나 부재중으로 변경 시 이벤트리스너를 제거해 움직임을 감지하지 못하도록 함 --- .../landing/member/LandingMember.tsx | 32 +++++++++++++++---- .../src/hooks/common/dropdown/useDropdown.tsx | 5 +++ .../src/hooks/common/member/useAwayUser.ts | 18 +++++++++-- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/landing/member/LandingMember.tsx b/frontend/src/components/landing/member/LandingMember.tsx index c3d1baf..f1b893c 100644 --- a/frontend/src/components/landing/member/LandingMember.tsx +++ b/frontend/src/components/landing/member/LandingMember.tsx @@ -1,7 +1,6 @@ -import { useEffect } from "react"; import { useOutletContext } from "react-router-dom"; import { Socket } from "socket.io-client"; -import { USER_WORD_STATUS } from "../../../constants/landing"; +import { USER_STATUS_WORD, USER_WORD_STATUS } from "../../../constants/landing"; import UserBlock from "./UserBlock"; import useDropdown from "../../../hooks/common/dropdown/useDropdown"; import { LandingMemberDTO } from "../../../types/DTO/landingDTO"; @@ -16,11 +15,23 @@ interface LandingMemberProps { }; } +interface useOutletContextValues { + socket: Socket; + addUserStatusEventListener: () => void; + removeUserStatusEventListener: () => void; + handleCanAddStatusEventListener: (havePurpose: boolean) => void; +} + const LandingMember = ({ projectTitle, memberSocketEvent, }: LandingMemberProps) => { - const { socket }: { socket: Socket } = useOutletContext(); + const { + socket, + addUserStatusEventListener, + removeUserStatusEventListener, + handleCanAddStatusEventListener, + }: useOutletContextValues = useOutletContext(); const { Dropdown, selectedOption, handleChangeSelectedOption } = useDropdown({ placeholder: "내 상태", options: ["접속 중", "부재 중", "자리비움"], @@ -51,12 +62,20 @@ const LandingMember = ({ }); }; - useEffect(() => { + function selectStatusOption(option: string) { emitMemberStatusUpdate({ ...myInfo, - status: USER_WORD_STATUS[selectedOption], + status: USER_WORD_STATUS[option], }); - }, [selectedOption]); + + if (option === USER_STATUS_WORD.away || option === USER_STATUS_WORD.off) { + handleCanAddStatusEventListener(false); + removeUserStatusEventListener(); + } else { + handleCanAddStatusEventListener(true); + addUserStatusEventListener(); + } + } return (
@@ -69,6 +88,7 @@ const LandingMember = ({ containerClassName="w-[6rem] bg-white rounded-b-lg overflow-hidden" itemClassName="w-full text-xxxs text-center font-semibold py-2 hover:bg-middle-green hover:text-white hover:font-semibold" iconSize="w-[12px] h-[12px]" + selectOption={selectStatusOption} />
void; } const useDropdown = ({ @@ -33,6 +34,7 @@ const useDropdown = ({ containerClassName = "", itemClassName = "", iconSize = "24", + selectOption, }: DropdownProps) => { const [open, setOpen] = useState(false); @@ -41,6 +43,9 @@ const useDropdown = ({ }; const handleOptionClick = (option: string) => { + if (selectOption) { + selectOption(option); + } handleChangeSelectedOption(option); setOpen(false); }; diff --git a/frontend/src/hooks/common/member/useAwayUser.ts b/frontend/src/hooks/common/member/useAwayUser.ts index 9e190bc..8fe0b70 100644 --- a/frontend/src/hooks/common/member/useAwayUser.ts +++ b/frontend/src/hooks/common/member/useAwayUser.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { Socket } from "socket.io-client"; import useLandingEmitEvent from "../socket/useLandingEmitEvent"; import useMemberStore from "../../../stores/useMemberStore"; @@ -7,6 +7,8 @@ import useThrottle from "../throttle/useThrottle"; const useAwayUser = (socket: Socket) => { const timerRef = useRef(null); const myInfo = useMemberStore((state) => state.myInfo); + const [canAddStatusEventListener, setCanAddStatusEventListener] = + useState(true); const throttle = useThrottle(); const { memberSocketEvent } = useLandingEmitEvent(socket); const TIME = 1000 * 60 * 10; @@ -46,6 +48,10 @@ const useAwayUser = (socket: Socket) => { }); }; + const handleCanAddStatusEventListener = (can: boolean) => { + setCanAddStatusEventListener(can); + }; + const addUserStatusEventListener = () => { window.addEventListener("mousemove", handleUserStatus); window.addEventListener("keydown", handleUserStatus); @@ -62,14 +68,20 @@ const useAwayUser = (socket: Socket) => { }; useEffect(() => { - addUserStatusEventListener(); + if (canAddStatusEventListener) { + addUserStatusEventListener(); + } return () => { removeUserStatusEventListener(); }; }, [myInfo]); - return { addUserStatusEventListener, removeUserStatusEventListener }; + return { + addUserStatusEventListener, + removeUserStatusEventListener, + handleCanAddStatusEventListener, + }; }; export default useAwayUser; From cd40b304ac4aa420e59158f37f1294fdd57a2955 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Thu, 6 Jun 2024 23:44:33 +0900 Subject: [PATCH 11/18] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=97=90=20=EC=A0=91=EC=86=8D=20=ED=9B=84=20?= =?UTF-8?q?=EB=B0=94=EB=A1=9C=20=ED=83=80=EC=9D=B4=EB=A8=B8=EA=B0=80=20?= =?UTF-8?q?=EB=8F=99=EC=9E=91=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eventListener를 add하면서 타이머를 설정하도록 수정 --- frontend/src/hooks/common/member/useAwayUser.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/hooks/common/member/useAwayUser.ts b/frontend/src/hooks/common/member/useAwayUser.ts index 8fe0b70..dff5981 100644 --- a/frontend/src/hooks/common/member/useAwayUser.ts +++ b/frontend/src/hooks/common/member/useAwayUser.ts @@ -20,7 +20,7 @@ const useAwayUser = (socket: Socket) => { } }; - const setTimer = () => { + const rsetTimer = () => { timerRef.current = setTimeout(() => { memberSocketEvent.emitMemberStatusUpdate({ ...myInfo, @@ -40,11 +40,9 @@ const useAwayUser = (socket: Socket) => { ...myInfo, status: "on", }); - - setTimer(); } - setTimer(); + rsetTimer(); }); }; @@ -57,6 +55,7 @@ const useAwayUser = (socket: Socket) => { window.addEventListener("keydown", handleUserStatus); window.addEventListener("scroll", handleUserStatus); window.addEventListener("click", handleUserStatus); + rsetTimer(); }; const removeUserStatusEventListener = () => { From 98c58add5e15de33c54ba9ae4a99abcd64b3d6cb Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 7 Jun 2024 00:53:07 +0900 Subject: [PATCH 12/18] =?UTF-8?q?style:=20rsetTimer=20=EC=98=A4=ED=83=88?= =?UTF-8?q?=EC=9E=90=EB=A5=BC=20resetTimer=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/common/member/useAwayUser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/hooks/common/member/useAwayUser.ts b/frontend/src/hooks/common/member/useAwayUser.ts index dff5981..79b9645 100644 --- a/frontend/src/hooks/common/member/useAwayUser.ts +++ b/frontend/src/hooks/common/member/useAwayUser.ts @@ -20,7 +20,7 @@ const useAwayUser = (socket: Socket) => { } }; - const rsetTimer = () => { + const resetTimer = () => { timerRef.current = setTimeout(() => { memberSocketEvent.emitMemberStatusUpdate({ ...myInfo, @@ -42,7 +42,7 @@ const useAwayUser = (socket: Socket) => { }); } - rsetTimer(); + resetTimer(); }); }; @@ -55,7 +55,7 @@ const useAwayUser = (socket: Socket) => { window.addEventListener("keydown", handleUserStatus); window.addEventListener("scroll", handleUserStatus); window.addEventListener("click", handleUserStatus); - rsetTimer(); + resetTimer(); }; const removeUserStatusEventListener = () => { From 6387955946f3716acd4ae5047877706e6c042930 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 7 Jun 2024 00:54:48 +0900 Subject: [PATCH 13/18] =?UTF-8?q?refactor:=20emitMemberEvent=20=EB=A5=BC?= =?UTF-8?q?=20LandingMember=20=EB=82=B4=EB=B6=80=EB=A1=9C=20=EC=98=AE?= =?UTF-8?q?=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 바뀐 socket 통신 규칙에 맞게 member 관련 로직을 member 내부로 이동 --- .../components/landing/member/LandingMember.tsx | 16 +++------------- .../hooks/common/member/useUpdateUserStatus.ts | 9 ++++++++- frontend/src/pages/landing/LandingPage.tsx | 7 ++----- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/landing/member/LandingMember.tsx b/frontend/src/components/landing/member/LandingMember.tsx index f1b893c..ab9af85 100644 --- a/frontend/src/components/landing/member/LandingMember.tsx +++ b/frontend/src/components/landing/member/LandingMember.tsx @@ -10,9 +10,6 @@ import { memberResponse } from "../../../types/DTO/authDTO"; interface LandingMemberProps { projectTitle: string; - memberSocketEvent: { - emitMemberStatusUpdate: (content: LandingMemberDTO) => void; - }; } interface useOutletContextValues { @@ -22,10 +19,7 @@ interface useOutletContextValues { handleCanAddStatusEventListener: (havePurpose: boolean) => void; } -const LandingMember = ({ - projectTitle, - memberSocketEvent, -}: LandingMemberProps) => { +const LandingMember = ({ projectTitle }: LandingMemberProps) => { const { socket, addUserStatusEventListener, @@ -37,12 +31,8 @@ const LandingMember = ({ options: ["접속 중", "부재 중", "자리비움"], defaultOption: "접속 중", }); - const { myInfo, memberList, inviteLinkIdRef } = useUpdateUserStatus( - socket, - handleChangeSelectedOption - ); - - const { emitMemberStatusUpdate } = memberSocketEvent; + const { myInfo, memberList, inviteLinkIdRef, emitMemberStatusUpdate } = + useUpdateUserStatus(socket, handleChangeSelectedOption); const userData: memberResponse = JSON.parse( window.localStorage.getItem("member") ?? DEFAULT_MEMBER diff --git a/frontend/src/hooks/common/member/useUpdateUserStatus.ts b/frontend/src/hooks/common/member/useUpdateUserStatus.ts index 0fea475..adf1272 100644 --- a/frontend/src/hooks/common/member/useUpdateUserStatus.ts +++ b/frontend/src/hooks/common/member/useUpdateUserStatus.ts @@ -84,6 +84,13 @@ const useUpdateUserStatus = ( } }; + const emitMemberStatusUpdate = (content: LandingMemberDTO) => { + socket.emit("member", { + action: "update", + content, + }); + }; + useEffect(() => { socket.on("landing", handleOnLanding); @@ -92,7 +99,7 @@ const useUpdateUserStatus = ( }; }, [socket, myInfo, memberList]); - return { myInfo, memberList, inviteLinkIdRef }; + return { myInfo, memberList, inviteLinkIdRef, emitMemberStatusUpdate }; }; export default useUpdateUserStatus; diff --git a/frontend/src/pages/landing/LandingPage.tsx b/frontend/src/pages/landing/LandingPage.tsx index b8cbd1f..1c3acbb 100644 --- a/frontend/src/pages/landing/LandingPage.tsx +++ b/frontend/src/pages/landing/LandingPage.tsx @@ -16,7 +16,7 @@ const LandingPage = () => { const { socket }: { socket: Socket } = useOutletContext(); const { project, sprint, link, memoList } = useLandingSocket(socket); - const { memoSocketEvent, memberSocketEvent } = useLandingEmitEvent(socket); + const { memoSocketEvent } = useLandingEmitEvent(socket); return (
@@ -26,10 +26,7 @@ const LandingPage = () => {
- +
From 57b4e8fc704063e50ad5efc02f8cbe0330e7e19c Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 7 Jun 2024 01:23:09 +0900 Subject: [PATCH 14/18] =?UTF-8?q?remove:=20=EC=82=AC=EC=9A=A9=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/hooks/common/socket/useLandingEmitEvent.ts | 0 frontend/src/hooks/common/useWheelDownActive.ts | 13 ------------- 2 files changed, 13 deletions(-) delete mode 100644 frontend/src/hooks/common/socket/useLandingEmitEvent.ts delete mode 100644 frontend/src/hooks/common/useWheelDownActive.ts diff --git a/frontend/src/hooks/common/socket/useLandingEmitEvent.ts b/frontend/src/hooks/common/socket/useLandingEmitEvent.ts deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/hooks/common/useWheelDownActive.ts b/frontend/src/hooks/common/useWheelDownActive.ts deleted file mode 100644 index f65aa70..0000000 --- a/frontend/src/hooks/common/useWheelDownActive.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useState } from "react"; - -const useWheelUpDown = () => { - const [wheelDownActive, setWheelDownActive] = useState(false); - - const changeWheelDownActive = (active: boolean) => { - setWheelDownActive(active); - }; - - return { wheelDownActive, changeWheelDownActive }; -}; - -export default useWheelUpDown; From 90a275474b41f6d9d899ad089a929c9902f8fee3 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 7 Jun 2024 01:56:09 +0900 Subject: [PATCH 15/18] =?UTF-8?q?fix:=20=EC=82=AD=EC=A0=9C=EB=90=9C=20useW?= =?UTF-8?q?heelDownActive=20=EC=9E=AC=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/common/useWheelDownActive.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 frontend/src/hooks/common/useWheelDownActive.ts diff --git a/frontend/src/hooks/common/useWheelDownActive.ts b/frontend/src/hooks/common/useWheelDownActive.ts new file mode 100644 index 0000000..f65aa70 --- /dev/null +++ b/frontend/src/hooks/common/useWheelDownActive.ts @@ -0,0 +1,13 @@ +import { useState } from "react"; + +const useWheelUpDown = () => { + const [wheelDownActive, setWheelDownActive] = useState(false); + + const changeWheelDownActive = (active: boolean) => { + setWheelDownActive(active); + }; + + return { wheelDownActive, changeWheelDownActive }; +}; + +export default useWheelUpDown; From b6d4e4d7b2846a40f7575e318cb6a2f948bc539d Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 7 Jun 2024 01:58:37 +0900 Subject: [PATCH 16/18] =?UTF-8?q?refactor:=20emitMemberStatusUpdate=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=A5=BC=20=EC=9E=AC=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20util?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/landing/member/LandingMember.tsx | 9 ++++++--- frontend/src/hooks/common/member/useAwayUser.ts | 7 +++---- .../src/hooks/common/member/useUpdateUserStatus.ts | 9 +-------- .../src/hooks/common/member/useUserLeaveProject.ts | 5 ++--- frontend/src/utils/emitMemberStatusUpdate.ts | 11 +++++++++++ 5 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 frontend/src/utils/emitMemberStatusUpdate.ts diff --git a/frontend/src/components/landing/member/LandingMember.tsx b/frontend/src/components/landing/member/LandingMember.tsx index ab9af85..56f5905 100644 --- a/frontend/src/components/landing/member/LandingMember.tsx +++ b/frontend/src/components/landing/member/LandingMember.tsx @@ -7,6 +7,7 @@ import { LandingMemberDTO } from "../../../types/DTO/landingDTO"; import useUpdateUserStatus from "../../../hooks/common/member/useUpdateUserStatus"; import { DEFAULT_MEMBER } from "../../../constants/projects"; import { memberResponse } from "../../../types/DTO/authDTO"; +import emitMemberStatusUpdate from "../../../utils/emitMemberStatusUpdate"; interface LandingMemberProps { projectTitle: string; @@ -31,8 +32,10 @@ const LandingMember = ({ projectTitle }: LandingMemberProps) => { options: ["접속 중", "부재 중", "자리비움"], defaultOption: "접속 중", }); - const { myInfo, memberList, inviteLinkIdRef, emitMemberStatusUpdate } = - useUpdateUserStatus(socket, handleChangeSelectedOption); + const { myInfo, memberList, inviteLinkIdRef } = useUpdateUserStatus( + socket, + handleChangeSelectedOption + ); const userData: memberResponse = JSON.parse( window.localStorage.getItem("member") ?? DEFAULT_MEMBER @@ -53,7 +56,7 @@ const LandingMember = ({ projectTitle }: LandingMemberProps) => { }; function selectStatusOption(option: string) { - emitMemberStatusUpdate({ + emitMemberStatusUpdate(socket, { ...myInfo, status: USER_WORD_STATUS[option], }); diff --git a/frontend/src/hooks/common/member/useAwayUser.ts b/frontend/src/hooks/common/member/useAwayUser.ts index 79b9645..a6bcf60 100644 --- a/frontend/src/hooks/common/member/useAwayUser.ts +++ b/frontend/src/hooks/common/member/useAwayUser.ts @@ -1,8 +1,8 @@ import { useEffect, useRef, useState } from "react"; import { Socket } from "socket.io-client"; -import useLandingEmitEvent from "../socket/useLandingEmitEvent"; import useMemberStore from "../../../stores/useMemberStore"; import useThrottle from "../throttle/useThrottle"; +import emitMemberStatusUpdate from "../../../utils/emitMemberStatusUpdate"; const useAwayUser = (socket: Socket) => { const timerRef = useRef(null); @@ -10,7 +10,6 @@ const useAwayUser = (socket: Socket) => { const [canAddStatusEventListener, setCanAddStatusEventListener] = useState(true); const throttle = useThrottle(); - const { memberSocketEvent } = useLandingEmitEvent(socket); const TIME = 1000 * 60 * 10; const THROTTLE_TIME = 3000; @@ -22,7 +21,7 @@ const useAwayUser = (socket: Socket) => { const resetTimer = () => { timerRef.current = setTimeout(() => { - memberSocketEvent.emitMemberStatusUpdate({ + emitMemberStatusUpdate(socket, { ...myInfo, status: "away", }); @@ -36,7 +35,7 @@ const useAwayUser = (socket: Socket) => { clearTimer(); if (myInfo.status === "away") { - memberSocketEvent.emitMemberStatusUpdate({ + emitMemberStatusUpdate(socket, { ...myInfo, status: "on", }); diff --git a/frontend/src/hooks/common/member/useUpdateUserStatus.ts b/frontend/src/hooks/common/member/useUpdateUserStatus.ts index adf1272..0fea475 100644 --- a/frontend/src/hooks/common/member/useUpdateUserStatus.ts +++ b/frontend/src/hooks/common/member/useUpdateUserStatus.ts @@ -84,13 +84,6 @@ const useUpdateUserStatus = ( } }; - const emitMemberStatusUpdate = (content: LandingMemberDTO) => { - socket.emit("member", { - action: "update", - content, - }); - }; - useEffect(() => { socket.on("landing", handleOnLanding); @@ -99,7 +92,7 @@ const useUpdateUserStatus = ( }; }, [socket, myInfo, memberList]); - return { myInfo, memberList, inviteLinkIdRef, emitMemberStatusUpdate }; + return { myInfo, memberList, inviteLinkIdRef }; }; export default useUpdateUserStatus; diff --git a/frontend/src/hooks/common/member/useUserLeaveProject.ts b/frontend/src/hooks/common/member/useUserLeaveProject.ts index 9ccdb4b..438a6b5 100644 --- a/frontend/src/hooks/common/member/useUserLeaveProject.ts +++ b/frontend/src/hooks/common/member/useUserLeaveProject.ts @@ -1,17 +1,16 @@ import { useEffect } from "react"; import { Socket } from "socket.io-client"; import useMemberStore from "../../../stores/useMemberStore"; -import useLandingEmitEvent from "../socket/useLandingEmitEvent"; +import emitMemberStatusUpdate from "../../../utils/emitMemberStatusUpdate"; const useUserLeaveProject = (socket: Socket) => { const myInfo = useMemberStore((state) => state.myInfo); const updateMemberList = useMemberStore((state) => state.updateMemberList); - const { memberSocketEvent } = useLandingEmitEvent(socket); useEffect( () => () => { updateMemberList([]); - memberSocketEvent.emitMemberStatusUpdate({ + emitMemberStatusUpdate(socket, { ...myInfo, status: "off", }); diff --git a/frontend/src/utils/emitMemberStatusUpdate.ts b/frontend/src/utils/emitMemberStatusUpdate.ts new file mode 100644 index 0000000..7ad0252 --- /dev/null +++ b/frontend/src/utils/emitMemberStatusUpdate.ts @@ -0,0 +1,11 @@ +import { Socket } from "socket.io-client"; +import { LandingMemberDTO } from "../types/DTO/landingDTO"; + +const emitMemberStatusUpdate = (socket: Socket, content: LandingMemberDTO) => { + socket.emit("member", { + action: "update", + content, + }); +}; + +export default emitMemberStatusUpdate; From e762eb36f89b9b08ef579f33e6b286fd03e19e75 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 7 Jun 2024 02:22:11 +0900 Subject: [PATCH 17/18] =?UTF-8?q?fix:=20LandingMember=EC=97=90=EC=84=9C=20?= =?UTF-8?q?socket=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=EA=B0=80=20=EB=8F=99=EC=9E=91=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=ED=98=84=EC=83=81=20=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/common/member/useUpdateUserStatus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/hooks/common/member/useUpdateUserStatus.ts b/frontend/src/hooks/common/member/useUpdateUserStatus.ts index 0fea475..3ee508b 100644 --- a/frontend/src/hooks/common/member/useUpdateUserStatus.ts +++ b/frontend/src/hooks/common/member/useUpdateUserStatus.ts @@ -90,7 +90,7 @@ const useUpdateUserStatus = ( return () => { socket.off("landing", handleOnLanding); }; - }, [socket, myInfo, memberList]); + }); return { myInfo, memberList, inviteLinkIdRef }; }; From 837e7a44d362de52c69b610f75c4acceeb15c073 Mon Sep 17 00:00:00 2001 From: surinkwon Date: Fri, 7 Jun 2024 02:40:05 +0900 Subject: [PATCH 18/18] =?UTF-8?q?fix:=20socket=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=95=B8=EB=93=A4=EB=9F=AC=EA=B0=80=20=EC=A0=9C?= =?UTF-8?q?=EB=8C=80=EB=A1=9C=20=EB=8F=99=EC=9E=91=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 매 렌더링마다 landing 이벤트에 달린 모든 이벤트 핸들러가 on, off를 반복하여 머지했을 때 member 관련 로직이 제대로 동작하지 않았다. socket.on과 off는 한 번만 하면 되기 때문에 각 socket 훅의 useEffect에 빈 의존성배열을 넘겨 한 번만 실행되도록 했다. --- .../common/landing/useLandingLinkSocket.ts | 8 ++++-- .../common/landing/useLandingMemoSocket.ts | 28 +++++++++---------- .../common/landing/useLandingProjectSocket.ts | 8 ++++-- .../common/landing/useLandingSprintSocket.ts | 8 ++++-- .../common/member/useUpdateUserStatus.ts | 2 +- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/frontend/src/hooks/common/landing/useLandingLinkSocket.ts b/frontend/src/hooks/common/landing/useLandingLinkSocket.ts index 923de92..62026fa 100644 --- a/frontend/src/hooks/common/landing/useLandingLinkSocket.ts +++ b/frontend/src/hooks/common/landing/useLandingLinkSocket.ts @@ -14,7 +14,9 @@ const useLandingLinkSocket = (socket: Socket) => { }; const handleOnLanding = ({ domain, content }: LandingSocketData) => { - if (domain !== LandingSocketDomain.INIT) return; + if (domain !== LandingSocketDomain.INIT) { + return; + } handleInitEvent(content); }; @@ -22,9 +24,9 @@ const useLandingLinkSocket = (socket: Socket) => { socket.on("landing", handleOnLanding); return () => { - socket.off("landing"); + socket.off("landing", handleOnLanding); }; - }); + }, []); return { link }; }; diff --git a/frontend/src/hooks/common/landing/useLandingMemoSocket.ts b/frontend/src/hooks/common/landing/useLandingMemoSocket.ts index 6bcc32d..09dbeb4 100644 --- a/frontend/src/hooks/common/landing/useLandingMemoSocket.ts +++ b/frontend/src/hooks/common/landing/useLandingMemoSocket.ts @@ -21,24 +21,22 @@ const useLandingMemoSocket = (socket: Socket) => { ) => { switch (action) { case LandingSocketMemoAction.CREATE: - setMemoList((memoList: LandingMemoDTO[]) => { - return [content, ...memoList]; - }); + setMemoList((memoList: LandingMemoDTO[]) => [content, ...memoList]); break; case LandingSocketMemoAction.DELETE: - setMemoList((memoList: LandingMemoDTO[]) => { - return memoList.filter( - (memo: LandingMemoDTO) => memo.id !== content.id - ); - }); + setMemoList((memoList: LandingMemoDTO[]) => + memoList.filter((memo: LandingMemoDTO) => memo.id !== content.id) + ); break; case LandingSocketMemoAction.COLOR_UPDATE: - setMemoList((memoList: LandingMemoDTO[]) => { - return memoList.map((memo: LandingMemoDTO) => { - if (memo.id !== content.id) return memo; + setMemoList((memoList: LandingMemoDTO[]) => + memoList.map((memo: LandingMemoDTO) => { + if (memo.id !== content.id) { + return memo; + } return { ...memo, color: content.color }; - }); - }); + }) + ); } }; const handleOnMemoLanding = ({ @@ -75,9 +73,9 @@ const useLandingMemoSocket = (socket: Socket) => { socket.on("landing", handleOnMemoLanding); return () => { - socket.off("landing"); + socket.off("landing", handleOnMemoLanding); }; - }); + }, []); return { memoList, diff --git a/frontend/src/hooks/common/landing/useLandingProjectSocket.ts b/frontend/src/hooks/common/landing/useLandingProjectSocket.ts index e310e98..a86f4ab 100644 --- a/frontend/src/hooks/common/landing/useLandingProjectSocket.ts +++ b/frontend/src/hooks/common/landing/useLandingProjectSocket.ts @@ -18,7 +18,9 @@ const useLandingProjectSocket = (socket: Socket) => { }; const handleOnLanding = ({ domain, content }: LandingSocketData) => { - if (domain !== LandingSocketDomain.INIT) return; + if (domain !== LandingSocketDomain.INIT) { + return; + } handleInitEvent(content); }; @@ -26,9 +28,9 @@ const useLandingProjectSocket = (socket: Socket) => { socket.on("landing", handleOnLanding); return () => { - socket.off("landing"); + socket.off("landing", handleOnLanding); }; - }); + }, []); return { project }; }; diff --git a/frontend/src/hooks/common/landing/useLandingSprintSocket.ts b/frontend/src/hooks/common/landing/useLandingSprintSocket.ts index 82742be..09c70e6 100644 --- a/frontend/src/hooks/common/landing/useLandingSprintSocket.ts +++ b/frontend/src/hooks/common/landing/useLandingSprintSocket.ts @@ -13,16 +13,18 @@ const useLandingSprintSocket = (socket: Socket) => { setSprint(sprint); }; const handleOnLanding = ({ domain, content }: LandingSocketData) => { - if (domain !== LandingSocketDomain.INIT) return; + if (domain !== LandingSocketDomain.INIT) { + return; + } handleInitEvent(content); }; useEffect(() => { socket.on("landing", handleOnLanding); return () => { - socket.off("landing"); + socket.off("landing", handleOnLanding); }; - }); + }, []); return { sprint }; }; diff --git a/frontend/src/hooks/common/member/useUpdateUserStatus.ts b/frontend/src/hooks/common/member/useUpdateUserStatus.ts index 3ee508b..c3c3e44 100644 --- a/frontend/src/hooks/common/member/useUpdateUserStatus.ts +++ b/frontend/src/hooks/common/member/useUpdateUserStatus.ts @@ -90,7 +90,7 @@ const useUpdateUserStatus = ( return () => { socket.off("landing", handleOnLanding); }; - }); + }, [myInfo, memberList]); return { myInfo, memberList, inviteLinkIdRef }; };