From 439b2c6e1d58c748c3554a4584daeab8cbc028e6 Mon Sep 17 00:00:00 2001 From: PollyGotACracker Date: Fri, 17 Apr 2026 10:47:37 +0900 Subject: [PATCH 1/3] =?UTF-8?q?design(meetup):=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=20=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(main)/meetup/list/page.tsx | 35 +++++--- .../ListFilters/ListFiltersSkeleton.tsx | 88 +++++++++++++++++++ .../list/components/MeetupCardItems/index.tsx | 29 ++++-- .../MeetupCardList/MeetupCardListSkeleton.tsx | 11 +++ .../list/components/MeetupCardList/index.tsx | 38 +++----- 5 files changed, 155 insertions(+), 46 deletions(-) create mode 100644 features/meetup/list/components/ListFilters/ListFiltersSkeleton.tsx create mode 100644 features/meetup/list/components/MeetupCardList/MeetupCardListSkeleton.tsx diff --git a/app/(main)/meetup/list/page.tsx b/app/(main)/meetup/list/page.tsx index 9b4fdae4..c4006283 100644 --- a/app/(main)/meetup/list/page.tsx +++ b/app/(main)/meetup/list/page.tsx @@ -1,13 +1,15 @@ import { Suspense } from "react"; import { Metadata } from "next"; +import { cn } from "@/utils/cn"; import Container from "@/components/layout/Container"; +import QueryErrorBoundary from "@/components/common/QueryErrorBoundary"; import Banner from "@/features/meetup/list/components/Banner"; +import { MeetupListScrollProvider } from "@/features/meetup/list/providers/MeetupListScrollProvider"; import ListFilters from "@/features/meetup/list/components/ListFilters"; -import CreateOpenButton from "@/features/meetup/create/components/CreateOpenButton"; +import ListFiltersSkeleton from "@/features/meetup/list/components/ListFilters/ListFiltersSkeleton"; import MeetupCardList from "@/features/meetup/list/components/MeetupCardList"; -import { MeetupListScrollProvider } from "@/features/meetup/list/providers/MeetupListScrollProvider"; -import QueryErrorBoundary from "@/components/common/QueryErrorBoundary"; -import { cn } from "@/utils/cn"; +import MeetupCardListSkeleton from "@/features/meetup/list/components/MeetupCardList/MeetupCardListSkeleton"; +import CreateOpenButton from "@/features/meetup/create/components/CreateOpenButton"; export const metadata: Metadata = { title: "모임 찾기", @@ -17,6 +19,8 @@ export const metadata: Metadata = { }, }; +const size = 10; + export default function MeetupListPage() { return ( @@ -26,14 +30,21 @@ export default function MeetupListPage() { "md:min-h-[calc(100vh-88px)] md:gap-y-4 md:p-6 lg:gap-y-6 lg:pt-7", )}> - - - - - - - + }> + +
    + }> + + + + +
@@ -41,3 +52,5 @@ export default function MeetupListPage() {
); } + +const ListFiltersStyle = "mx-0 bg-gray-50 px-6 py-2 md:-mx-4 md:px-6"; diff --git a/features/meetup/list/components/ListFilters/ListFiltersSkeleton.tsx b/features/meetup/list/components/ListFilters/ListFiltersSkeleton.tsx new file mode 100644 index 00000000..449f9aa1 --- /dev/null +++ b/features/meetup/list/components/ListFilters/ListFiltersSkeleton.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { useCategoryStore } from "@/store/category.store"; +import { cn } from "@/utils/cn"; + +interface ListFiltersSkeletonProps { + className?: string; +} + +export default function ListFiltersSkeleton({ className }: ListFiltersSkeletonProps) { + const { categories } = useCategoryStore(); + const typeTabCount = categories.length + 1; + + return ( +
+
+ + +
+ +
+ ); +} + +const tabScrollRowClass = + "overflow-x-auto [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"; +const tabSkeletonShape = "box-border h-9 shrink-0 rounded-[0.875rem] px-4 md:h-10 md:rounded-2xl"; +const dropdownTriggerShape = "h-6 shrink-0 rounded-lg md:h-8"; + +function TabRowSkeleton({ typeTabCount }: { typeTabCount: number }) { + return ( + + ); +} + +function KeywordAreaSkeleton() { + return ( +
+
+
+
+
+
+
+
+
+ ); +} + +function DropdownRowSkeleton() { + return ( +
+ {/* 모든 드롭다운 스켈레톤: 동일한 폭으로 통일 */} + {Array.from({ length: 4 }).map((_, i) => ( +
+ ))} +
+ ); +} diff --git a/features/meetup/list/components/MeetupCardItems/index.tsx b/features/meetup/list/components/MeetupCardItems/index.tsx index 587d16a2..4a8a6d47 100644 --- a/features/meetup/list/components/MeetupCardItems/index.tsx +++ b/features/meetup/list/components/MeetupCardItems/index.tsx @@ -4,6 +4,7 @@ import { useRef, useState } from "react"; import { AnimatePresence, motion } from "motion/react"; import { ErrorBoundary } from "react-error-boundary"; import type { InfiniteData, UseInfiniteQueryResult } from "@tanstack/react-query"; +import { cn } from "@/utils/cn"; import type { MeetupItem, MeetupItemSelected, MeetupListResponse } from "../../../types"; import { useIntersectionObserver } from "@/hooks/useIntersectionObserver"; import useToggle from "@/hooks/useToggle"; @@ -13,10 +14,13 @@ import Empty from "@/components/ui/Empty"; import JoinModal from "../JoinModal"; interface MeetupCardItemsProps { + /** 모임 목록 쿼리 */ query: UseInfiniteQueryResult>; + /** 컴포넌트 추가 클래스 */ + className?: string; } -export default function MeetupCardItems({ query }: MeetupCardItemsProps) { +export default function MeetupCardItems({ query, className }: MeetupCardItemsProps) { const { data, isFetchingNextPage, hasNextPage, fetchNextPage } = query; const [selectedData, setSelectedData] = useState(null); const { isOpen, open, close } = useToggle(); @@ -31,6 +35,7 @@ export default function MeetupCardItems({ query }: MeetupCardItemsProps) { return ( 에러가 발생했습니다.}> page?.data) ?? []} setSelectedData={setSelectedData} openModalFn={open} @@ -59,15 +64,23 @@ interface MeetupCardLoadedItemsProps { data: MeetupItem[] | undefined; setSelectedData: (data: MeetupItemSelected) => void; openModalFn: () => void; + className?: string; } -function MeetupCardLoadedItems({ data, setSelectedData, openModalFn }: MeetupCardLoadedItemsProps) { +function MeetupCardLoadedItems({ + data, + setSelectedData, + openModalFn, + className, +}: MeetupCardLoadedItemsProps) { if (data?.length === 0) { return ( - - 아직 모임이 없어요 -
- 지금 바로 모임을 만들어보세요! -
+
  • + + 아직 모임이 없어요 +
    + 지금 바로 모임을 만들어보세요! +
    +
  • ); } return ( @@ -75,7 +88,7 @@ function MeetupCardLoadedItems({ data, setSelectedData, openModalFn }: MeetupCar {data?.map((item, i) => ( ( +
  • + +
  • + )); +} diff --git a/features/meetup/list/components/MeetupCardList/index.tsx b/features/meetup/list/components/MeetupCardList/index.tsx index 8184f6be..842e2ec6 100644 --- a/features/meetup/list/components/MeetupCardList/index.tsx +++ b/features/meetup/list/components/MeetupCardList/index.tsx @@ -1,39 +1,23 @@ "use client"; -import GroupCard from "@/components/ui/GroupCard"; import { useGetMeetups } from "@/features/meetup/queries"; -import MeetupCardItems from "../MeetupCardItems"; import { cn } from "@/utils/cn"; - -const size = 10; +import MeetupCardItems from "../MeetupCardItems"; +import MeetupCardListSkeleton from "./MeetupCardListSkeleton"; interface MeetupCardListProps { - className?: string; + size: number; } -export default function MeetupCardList({ className }: MeetupCardListProps) { +export default function MeetupCardList({ size }: MeetupCardListProps) { const query = useGetMeetups(size); const isRefetching = query.isFetching && !query.isFetchingNextPage && !query.isPending; - return ( -
      - {query.isPending ? ( - - ) : ( - - )} -
    + return query.isPending ? ( + + ) : ( + ); } - -function MeetupCardSkeletonItems({ size }: { size: number }) { - return Array.from({ length: size }).map((_, i) => ( -
  • - -
  • - )); -} From 075564648658ebeb72887e3217aec585fcf3331a Mon Sep 17 00:00:00 2001 From: PollyGotACracker Date: Fri, 17 Apr 2026 11:00:13 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix(meetup):=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20suspense=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(main)/meetup/list/page.tsx | 16 +++------------- .../list/components/MeetupCardItems/index.tsx | 15 +++------------ .../list/components/MeetupCardList/index.tsx | 17 ++++++++++------- 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/app/(main)/meetup/list/page.tsx b/app/(main)/meetup/list/page.tsx index c4006283..2f75c196 100644 --- a/app/(main)/meetup/list/page.tsx +++ b/app/(main)/meetup/list/page.tsx @@ -8,7 +8,6 @@ import { MeetupListScrollProvider } from "@/features/meetup/list/providers/Meetu import ListFilters from "@/features/meetup/list/components/ListFilters"; import ListFiltersSkeleton from "@/features/meetup/list/components/ListFilters/ListFiltersSkeleton"; import MeetupCardList from "@/features/meetup/list/components/MeetupCardList"; -import MeetupCardListSkeleton from "@/features/meetup/list/components/MeetupCardList/MeetupCardListSkeleton"; import CreateOpenButton from "@/features/meetup/create/components/CreateOpenButton"; export const metadata: Metadata = { @@ -33,18 +32,9 @@ export default function MeetupListPage() { }> -
      - }> - - - - -
    + + + diff --git a/features/meetup/list/components/MeetupCardItems/index.tsx b/features/meetup/list/components/MeetupCardItems/index.tsx index 4a8a6d47..0a140c90 100644 --- a/features/meetup/list/components/MeetupCardItems/index.tsx +++ b/features/meetup/list/components/MeetupCardItems/index.tsx @@ -4,7 +4,6 @@ import { useRef, useState } from "react"; import { AnimatePresence, motion } from "motion/react"; import { ErrorBoundary } from "react-error-boundary"; import type { InfiniteData, UseInfiniteQueryResult } from "@tanstack/react-query"; -import { cn } from "@/utils/cn"; import type { MeetupItem, MeetupItemSelected, MeetupListResponse } from "../../../types"; import { useIntersectionObserver } from "@/hooks/useIntersectionObserver"; import useToggle from "@/hooks/useToggle"; @@ -16,11 +15,9 @@ import JoinModal from "../JoinModal"; interface MeetupCardItemsProps { /** 모임 목록 쿼리 */ query: UseInfiniteQueryResult>; - /** 컴포넌트 추가 클래스 */ - className?: string; } -export default function MeetupCardItems({ query, className }: MeetupCardItemsProps) { +export default function MeetupCardItems({ query }: MeetupCardItemsProps) { const { data, isFetchingNextPage, hasNextPage, fetchNextPage } = query; const [selectedData, setSelectedData] = useState(null); const { isOpen, open, close } = useToggle(); @@ -35,7 +32,6 @@ export default function MeetupCardItems({ query, className }: MeetupCardItemsPro return ( 에러가 발생했습니다.}> page?.data) ?? []} setSelectedData={setSelectedData} openModalFn={open} @@ -66,12 +62,7 @@ interface MeetupCardLoadedItemsProps { openModalFn: () => void; className?: string; } -function MeetupCardLoadedItems({ - data, - setSelectedData, - openModalFn, - className, -}: MeetupCardLoadedItemsProps) { +function MeetupCardLoadedItems({ data, setSelectedData, openModalFn }: MeetupCardLoadedItemsProps) { if (data?.length === 0) { return (
  • @@ -88,7 +79,7 @@ function MeetupCardLoadedItems({ {data?.map((item, i) => ( - ) : ( - + return ( +
      + {query.isPending ? : } +
    ); } From dc0fbbc56fb2caa2d97646f2d7e67352bf480832 Mon Sep 17 00:00:00 2001 From: PollyGotACracker Date: Fri, 17 Apr 2026 11:47:06 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix(meetup):=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=20=ED=95=84=ED=84=B0=20=EC=8A=A4=EC=BC=88?= =?UTF-8?q?=EB=A0=88=ED=86=A4=20=EA=B0=84=EA=B2=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meetup/list/components/ListFilters/ListFiltersSkeleton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/meetup/list/components/ListFilters/ListFiltersSkeleton.tsx b/features/meetup/list/components/ListFilters/ListFiltersSkeleton.tsx index 449f9aa1..5fe2ba65 100644 --- a/features/meetup/list/components/ListFilters/ListFiltersSkeleton.tsx +++ b/features/meetup/list/components/ListFilters/ListFiltersSkeleton.tsx @@ -63,7 +63,7 @@ function KeywordAreaSkeleton() { )} aria-hidden>
    -
    +