From 61c9466836b6d62a00ddc1488edea31fecb5afe0 Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:09:16 +0900 Subject: [PATCH 01/14] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20?= =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=ED=9B=84=20mydashboard=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EB=B0=8F=20=EC=82=AD=EC=A0=9C=20=ED=9B=84=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=20=EB=B0=94=20=EC=BF=BC=EB=A6=AC=20=EA=B0=B1=EC=8B=A0?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=82=AD=EC=A0=9C=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/components/edit/DeleteDashboardButton.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx b/src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx index 29d4460..63ea45b 100644 --- a/src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx +++ b/src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx @@ -3,6 +3,7 @@ import api from '@lib/axios' import { showError, showSuccess } from '@lib/toast' import { useMutation } from '@tanstack/react-query' +import { useQueryClient } from '@tanstack/react-query' import axios from 'axios' import { useRouter } from 'next/navigation' import React from 'react' @@ -16,6 +17,7 @@ export default function DeleteDashboardButton({ dashboardId, }: DeleteDashboardButtonProps) { const router = useRouter() + const queryClient = useQueryClient() const mutation = useMutation({ mutationFn: async () => { @@ -27,7 +29,10 @@ export default function DeleteDashboardButton({ ) }, onSuccess: () => { - router.push('/dashboard') + // 대시보드 삭제 후 사이드 바 대시보드 목록 쿼리 무효화 + queryClient.invalidateQueries({ queryKey: ['dashboards'] }) + + router.push('/mydashboard') showSuccess('대시보드가 삭제되었습니다') }, onError: (error) => { From e015d36a5f548b922d5a8d0f0daffbbdeb8df42e Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:21:35 +0900 Subject: [PATCH 02/14] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20?= =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=97=A4=EB=8D=94=20=EB=B0=98?= =?UTF-8?q?=EC=9D=91=ED=98=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/dashboard/[id]/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/dashboard/[id]/layout.tsx b/src/app/dashboard/[id]/layout.tsx index e1ec752..5c82016 100644 --- a/src/app/dashboard/[id]/layout.tsx +++ b/src/app/dashboard/[id]/layout.tsx @@ -10,7 +10,7 @@ export default function AboutLayout({ return ( <> -
+
{children}
From 9baae8f980147e921f159ffe7f21adaf43bdffce Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:22:11 +0900 Subject: [PATCH 03/14] =?UTF-8?q?=F0=9F=AB=A7=20modify:=20=EB=8C=80?= =?UTF-8?q?=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EC=82=AD=EC=A0=9C=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=9C=84=EC=B9=98=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/dashboard/[id]/edit/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/dashboard/[id]/edit/page.tsx b/src/app/dashboard/[id]/edit/page.tsx index 8d1cd97..5fa2c79 100644 --- a/src/app/dashboard/[id]/edit/page.tsx +++ b/src/app/dashboard/[id]/edit/page.tsx @@ -31,8 +31,8 @@ export default function DashBoardEditPage() {
- {/* 삭제 버튼 영역: 기본 너비 292px, 화면 작으면 100% 최대 292px, 좌측 margin 제거 */} -
+ {/* 삭제 버튼 영역: 기본 너비 292px, 화면 작으면 100% 최대 292px */} +
From 36ea831a6e78ac24eafb8e3e1c7a54e806b6ff1e Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:23:14 +0900 Subject: [PATCH 04/14] =?UTF-8?q?=F0=9F=8E=A8style:=20=EC=B4=88=EB=8C=80?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EB=B0=98=EC=9D=91?= =?UTF-8?q?=ED=98=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/edit/EditInvitation.tsx | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/app/features/dashboard/components/edit/EditInvitation.tsx b/src/app/features/dashboard/components/edit/EditInvitation.tsx index a7fa396..58ea12a 100644 --- a/src/app/features/dashboard/components/edit/EditInvitation.tsx +++ b/src/app/features/dashboard/components/edit/EditInvitation.tsx @@ -32,7 +32,6 @@ export default function EditInvitation() { const dashboardId = params.id as string const queryClient = useQueryClient() - const [currentPage, setCurrentPage] = useState(1) const { @@ -53,7 +52,6 @@ export default function EditInvitation() { retry: false, }) - // length가 0인 경우에도 최소 페이지 1로 보장 const totalPages = Math.max( 1, Math.ceil(invitations.length / INVITATION_SIZE), @@ -86,7 +84,6 @@ export default function EditInvitation() { const handlePrev = () => setCurrentPage((p) => Math.max(p - 1, 1)) const handleNext = () => setCurrentPage((p) => Math.min(p + 1, totalPages)) - // 에러 메시지 정리 const errorMessage = isError && axios.isAxiosError(error) ? error.response?.status === 403 @@ -98,37 +95,55 @@ export default function EditInvitation() { return (
- + {/* Header + 초대 버튼 영역 (데스크탑용) */} +
+ + + {/* 데스크탑에서만 보이는 초대 버튼 */} - +
+ {/* 이메일 입력 및 모바일 전용 버튼 */}
- +
+ + + {/* 모바일/태블릿에서만 보이는 초대 버튼 */} + +
+
{isLoading && (

로딩 중...

)} - {errorMessage && (

{errorMessage}

)} - {!isLoading && !errorMessage && currentItems.map((member, index) => { @@ -150,7 +165,6 @@ export default function EditInvitation() {
- - {children &&
{children}
} ) From e8df89bf564f1a698308504644e9bd3d79df6676 Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:24:37 +0900 Subject: [PATCH 06/14] =?UTF-8?q?=F0=9F=8E=A8style:=20=EB=B0=98=EC=9D=91?= =?UTF-8?q?=ED=98=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EC=A0=95=EB=B3=B4=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/shared/components/common/UserInfo.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/shared/components/common/UserInfo.tsx b/src/app/shared/components/common/UserInfo.tsx index d195133..30e8418 100644 --- a/src/app/shared/components/common/UserInfo.tsx +++ b/src/app/shared/components/common/UserInfo.tsx @@ -22,9 +22,7 @@ export function UserInfo({ nickname, imageUrl, size = 36 }: UserInfoProps) {
{/* Avatar에 nickname, profileImageUrl 모두 넘겨줌 */} - - {displayNickname} - + {displayNickname}
) } From 7831b0792e6b32c6d41738960cab3dce1a94db74 Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:25:24 +0900 Subject: [PATCH 07/14] =?UTF-8?q?=F0=9F=8E=A8style:=20=ED=97=A4=EB=8D=94?= =?UTF-8?q?=20=EC=99=BC=EC=AA=BD=20=EC=A0=9C=EB=AA=A9=20=EB=B6=80=EB=B6=84?= =?UTF-8?q?=20=EB=82=B4=20=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/header/LeftHeaderContent.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/app/shared/components/common/header/LeftHeaderContent.tsx b/src/app/shared/components/common/header/LeftHeaderContent.tsx index 4ce27c4..7775adb 100644 --- a/src/app/shared/components/common/header/LeftHeaderContent.tsx +++ b/src/app/shared/components/common/header/LeftHeaderContent.tsx @@ -8,17 +8,22 @@ export default function LeftHeaderContent() { const { selectedDashboard } = useSelectedDashboardStore() const pathname = usePathname() - if (!selectedDashboard) return null + const isMypage = pathname === '/mypage' + const isMyDashboard = pathname === '/mydashboard' + const showCrown = + selectedDashboard?.createdByMe && !isMypage && !isMyDashboard + + const title = isMypage + ? '계정관리' + : isMyDashboard + ? '내 대시보드' + : selectedDashboard?.title || '내 대시보드' return (
-
- {pathname === '/mypage' - ? '계정관리' - : selectedDashboard.title || '내 대시보드'} -
+
{title}
- {selectedDashboard.createdByMe && pathname !== '/mypage' && ( + {showCrown && (
Date: Sun, 22 Jun 2025 23:47:50 +0900 Subject: [PATCH 08/14] =?UTF-8?q?=F0=9F=8E=A8style:=20dashboardId=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20number=ED=98=95=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=20=EB=B0=8F=20=EA=B7=B8=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/dashboard/components/edit/EditMember.tsx | 10 +++++----- .../common/header/Collaborator/CollaboratorList.tsx | 8 ++++---- src/app/shared/hooks/useMembers.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/app/features/dashboard/components/edit/EditMember.tsx b/src/app/features/dashboard/components/edit/EditMember.tsx index bf43e2e..656d4c7 100644 --- a/src/app/features/dashboard/components/edit/EditMember.tsx +++ b/src/app/features/dashboard/components/edit/EditMember.tsx @@ -24,7 +24,7 @@ async function deleteMember(memberId: number): Promise { export default function EditMember() { const queryClient = useQueryClient() const { id: dashboardId } = useParams() - const dashboardIdStr = String(dashboardId) + const dashboardIdNum = Number(dashboardId) const [currentPage, setCurrentPage] = useState(1) @@ -33,9 +33,9 @@ export default function EditMember() { isLoading, isError, } = useQuery({ - queryKey: ['members', dashboardIdStr], - queryFn: () => fetchMembers(dashboardIdStr), - enabled: !!dashboardIdStr, + queryKey: ['members', dashboardIdNum], + queryFn: () => fetchMembers(dashboardIdNum), + enabled: !!dashboardIdNum, }) // 본인이 구성원으로 들어가기 때문에 0 페이지일 경우 X @@ -55,7 +55,7 @@ export default function EditMember() { mutationFn: (memberId: number) => deleteMember(memberId), onSuccess: () => { showSuccess('삭제에 성공하였습니다.') - queryClient.invalidateQueries({ queryKey: ['members', dashboardIdStr] }) + queryClient.invalidateQueries({ queryKey: ['members', dashboardIdNum] }) }, onError: () => { showError('삭제에 실패했습니다.') diff --git a/src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx b/src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx index 2a66b73..da1b040 100644 --- a/src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx +++ b/src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx @@ -12,16 +12,16 @@ const MAX_COLLABS = 4 export default function CollaboratorList() { const { id: dashboardId } = useParams() - const dashboardIdStr = String(dashboardId) + const dashboardIdNum = Number(dashboardId) const { data: members = [], isLoading, isError, } = useQuery({ - queryKey: ['members', dashboardIdStr], - queryFn: () => fetchMembers(dashboardIdStr), - enabled: !!dashboardIdStr, + queryKey: ['members', dashboardIdNum], + queryFn: () => fetchMembers(dashboardIdNum), + enabled: !!dashboardIdNum, }) if (isLoading && isError) return null diff --git a/src/app/shared/hooks/useMembers.ts b/src/app/shared/hooks/useMembers.ts index 1fd6bf4..65ad403 100644 --- a/src/app/shared/hooks/useMembers.ts +++ b/src/app/shared/hooks/useMembers.ts @@ -14,7 +14,7 @@ export type Member = { const teamId = getTeamId() -export async function fetchMembers(dashboardId: string): Promise { +export async function fetchMembers(dashboardId: number): Promise { const { data } = await authHttpClient.get(`/${teamId}/members`, { params: { page: 1, From a9bb6f465b6c394220cb7cf637f442bd4951707b Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:48:35 +0900 Subject: [PATCH 09/14] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=EA=B0=80=20mydashboard=EC=9D=B8=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC=20=EB=B0=8F=20=EC=B4=88=EB=8C=80=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EB=AF=B8=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/header/RightHeaderNav.tsx | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/app/shared/components/common/header/RightHeaderNav.tsx b/src/app/shared/components/common/header/RightHeaderNav.tsx index 4465675..d53b7f9 100644 --- a/src/app/shared/components/common/header/RightHeaderNav.tsx +++ b/src/app/shared/components/common/header/RightHeaderNav.tsx @@ -2,27 +2,35 @@ import { useModalStore } from '@store/useModalStore' import { useSelectedDashboardStore } from '@store/useSelectedDashboardStore' +import { usePathname } from 'next/navigation' import NavItem from './NavItem' export default function RightHeaderNav() { const { openModal } = useModalStore() const { selectedDashboard } = useSelectedDashboardStore() + const pathname = usePathname() + + const isMyDashboardPage = pathname === '/mydashboard' return ( ) } From aeb23edf5fe0b9e0b77d4b8cd8161f8c7eedce4c Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Mon, 23 Jun 2025 00:28:04 +0900 Subject: [PATCH 10/14] =?UTF-8?q?=F0=9F=8E=A8style:=20mobile-sm=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EC=A6=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tailwind.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tailwind.config.ts b/tailwind.config.ts index c896f70..8e6f713 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -19,6 +19,7 @@ const config: Config = { screens: { mobile: { max: '375px' }, // 0 ~ 375px tablet: { raw: '(min-width: 376px) and (max-width: 744px)' }, // 376 ~ 1919px + 'mobile-sm': { max: '500px' }, // ✅ 0 ~ 500px 'mobile-wide': { raw: '(min-width: 0px) and (max-width: 683px)' }, // 0 ~ 683px 'tablet-wide': { raw: '(min-width: 684px) and (max-width: 1439px)' }, // 684 ~ 1439px }, From 4a25ddaa378aed890ed0411c0d0431741e2e1e1e Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Mon, 23 Jun 2025 00:28:22 +0900 Subject: [PATCH 11/14] =?UTF-8?q?=F0=9F=8E=A8style:=20=EB=8C=80=EC=8B=9C?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=EC=88=98=EC=A0=95=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=B0=98=EC=9D=91=ED=98=95=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/dashboard/[id]/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/dashboard/[id]/layout.tsx b/src/app/dashboard/[id]/layout.tsx index 5c82016..bd8ae61 100644 --- a/src/app/dashboard/[id]/layout.tsx +++ b/src/app/dashboard/[id]/layout.tsx @@ -10,7 +10,7 @@ export default function AboutLayout({ return ( <> -
+
{children}
From 27b749558f2eb302fb697456c77d889288ec3ebb Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Mon, 23 Jun 2025 00:33:52 +0900 Subject: [PATCH 12/14] =?UTF-8?q?=F0=9F=8E=A8style:=20=EA=B5=AC=EC=84=B1?= =?UTF-8?q?=EC=9B=90=20=EC=88=98=EC=A0=95=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EB=B2=84=ED=8A=BC=20=EC=82=AC=EB=9D=BC=EC=A7=80?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=8A=A4=ED=83=80=EC=9D=BC=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 --- src/app/dashboard/[id]/edit/page.tsx | 13 +++++++------ .../dashboard/components/edit/EditMember.tsx | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app/dashboard/[id]/edit/page.tsx b/src/app/dashboard/[id]/edit/page.tsx index 5fa2c79..89d0b6f 100644 --- a/src/app/dashboard/[id]/edit/page.tsx +++ b/src/app/dashboard/[id]/edit/page.tsx @@ -14,9 +14,10 @@ export default function DashBoardEditPage() { const router = useRouter() return ( -
+
+ {/* 돌아가기 버튼 */} - {/* 컨텐츠 박스: 기본 너비 500px, 화면 작으면 100% 최대 500px */} -
+ {/* 콘텐츠 영역 */} +
- {/* 삭제 버튼 영역: 기본 너비 292px, 화면 작으면 100% 최대 292px */} -
+ {/* 삭제 버튼 영역 */} +
diff --git a/src/app/features/dashboard/components/edit/EditMember.tsx b/src/app/features/dashboard/components/edit/EditMember.tsx index 656d4c7..2c05045 100644 --- a/src/app/features/dashboard/components/edit/EditMember.tsx +++ b/src/app/features/dashboard/components/edit/EditMember.tsx @@ -64,7 +64,7 @@ export default function EditMember() { return (
-
+
Date: Mon, 23 Jun 2025 00:45:50 +0900 Subject: [PATCH 13/14] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20=EB=93=9C?= =?UTF-8?q?=EB=A1=AD=EB=8B=A4=EC=9A=B4=20=EC=9C=84=EC=B9=98=20=EB=B7=B0?= =?UTF-8?q?=ED=8F=AC=ED=8A=B8=20=EA=B8=B0=EC=A4=80=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20position=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/Dropdown/Dropdown.tsx | 89 ++++++++----------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/src/app/shared/components/common/Dropdown/Dropdown.tsx b/src/app/shared/components/common/Dropdown/Dropdown.tsx index eb0f953..7a14623 100644 --- a/src/app/shared/components/common/Dropdown/Dropdown.tsx +++ b/src/app/shared/components/common/Dropdown/Dropdown.tsx @@ -1,84 +1,78 @@ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useLayoutEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' -// 드롭다운 컴포넌트 타입 정의 type DropdownProps = { - trigger: React.ReactNode // 드롭다운을 열기 위한 트리거 요소 (버튼, 아이콘 등) - children: React.ReactNode // 드롭다운 내부 콘텐츠 (메뉴 아이템 등) - width?: string // Tailwind 클래스 기반의 너비 설정 (예: 'w-5', 'w-6') - align?: 'left' | 'center' | 'right' // 드롭다운 정렬 방향 - className?: string // 사용자 정의 클래스 + trigger: React.ReactNode + children: React.ReactNode + width?: string + align?: 'left' | 'center' | 'right' + className?: string } -// 드롭다운 컴포넌트 정의 export default function Dropdown({ trigger, children, - width = 'w-6', // 기본 너비 설정 - align = 'left', // 기본 정렬 방향 설정 + width = 'w-6', + align = 'left', + className = '', }: DropdownProps) { - const [open, setOpen] = useState(false) // 드롭다운 열림 여부 상태 - const triggerRef = useRef(null) // 반응형에 따른 위치 조정을 위한 ref 객체 - const menuRef = useRef(null) // 외부 클릭 감지를 위한 드롭다운 메뉴 ref 객체 + const [open, setOpen] = useState(false) + const triggerRef = useRef(null) + const menuRef = useRef(null) const [coords, setCoords] = useState<{ top: number; left: number }>({ top: 0, left: 0, - }) // 드롭다운 메뉴 위치 좌표 상태 + }) - // 드롭다운 열기/닫기 토글 - function toggleOpen() { - setOpen((prev) => !prev) - } - - // Tailwind width 클래스 값을 실제 CSS 너비 값으로 변환 + // Tailwind 너비를 px 값으로 변환 function getWidthValue(width: string): string | undefined { switch (width) { - case 'w-5': // 할 일 카드 모달에서 사용 + case 'w-5': return '5rem' case 'w-6': return '6rem' default: - return undefined // 정의되지 않은 값은 기본 width 적용 + return undefined } } - // 드롭다운이 열릴 때 위치 계산 및 윈도우 이벤트 바인딩 - useEffect(() => { - function updateCoords() { - if (open && triggerRef.current) { - const rect = triggerRef.current.getBoundingClientRect() // 트리거 위치 측정 + // 위치 업데이트 함수 (useLayoutEffect로 변경) + useLayoutEffect(() => { + if (!open) return + function updateCoords() { + if (triggerRef.current) { + const rect = triggerRef.current.getBoundingClientRect() let left = rect.left if (align === 'center') left = rect.left + rect.width / 2 else if (align === 'right') left = rect.right setCoords({ - top: rect.bottom + window.scrollY, // 화면 스크롤 반영 + top: rect.bottom, left, }) } } - if (open) { - updateCoords() - window.addEventListener('resize', updateCoords) - window.addEventListener('scroll', updateCoords) - } + updateCoords() + + window.addEventListener('scroll', updateCoords, { passive: true }) + window.addEventListener('resize', updateCoords) return () => { - window.removeEventListener('resize', updateCoords) window.removeEventListener('scroll', updateCoords) + window.removeEventListener('resize', updateCoords) } }, [open, align]) - // 드롭다운 외부 클릭 시 닫기 + // 외부 클릭 감지해서 닫기 useEffect(() => { - function handleClickOutside(event: MouseEvent) { + function handleClickOutside(e: MouseEvent) { if ( menuRef.current && - !menuRef.current.contains(event.target as Node) && + !menuRef.current.contains(e.target as Node) && triggerRef.current && - !triggerRef.current.contains(event.target as Node) + !triggerRef.current.contains(e.target as Node) ) { setOpen(false) } @@ -86,22 +80,18 @@ export default function Dropdown({ if (open) { document.addEventListener('mousedown', handleClickOutside) - } else { - document.removeEventListener('mousedown', handleClickOutside) } - return () => { document.removeEventListener('mousedown', handleClickOutside) } }, [open]) - // 드롭다운 메뉴 렌더링 (포탈을 이용하여 body로 위치) const menu = open ? createPortal(
{children}
, - document.body, // body에 포탈로 삽입 + document.body, ) : null - // 트리거 요소 + 드롭다운 메뉴 렌더링 return ( <>
setOpen((prev) => !prev)} className="inline-block cursor-pointer" > {trigger} From a525766110b1505ab2c217a54b54d8c085e5f5b6 Mon Sep 17 00:00:00 2001 From: yujin Jeon <101913688+yuj2n@users.noreply.github.com> Date: Mon, 23 Jun 2025 00:57:31 +0900 Subject: [PATCH 14/14] =?UTF-8?q?=F0=9F=8E=A8style:=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EB=8D=95=EC=85=98=20=EC=BD=94=EB=93=9C=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=AA=A8=EC=A7=80=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tailwind.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tailwind.config.ts b/tailwind.config.ts index 8e6f713..1af6392 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -19,7 +19,7 @@ const config: Config = { screens: { mobile: { max: '375px' }, // 0 ~ 375px tablet: { raw: '(min-width: 376px) and (max-width: 744px)' }, // 376 ~ 1919px - 'mobile-sm': { max: '500px' }, // ✅ 0 ~ 500px + 'mobile-sm': { max: '500px' }, // 0 ~ 500px 'mobile-wide': { raw: '(min-width: 0px) and (max-width: 683px)' }, // 0 ~ 683px 'tablet-wide': { raw: '(min-width: 684px) and (max-width: 1439px)' }, // 684 ~ 1439px },