From a82a82b2a134c7c4a8487c9ef8cbf36e9440ed2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=AF=BC=EC=84=9D?= Date: Fri, 23 Jan 2026 15:59:44 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20suspense,=EC=8A=A4=EC=BC=88?= =?UTF-8?q?=EB=A0=88=ED=86=A4=20=EB=A1=9C=EB=94=A9=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=ED=99=9C=EC=9A=A9=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EA=B2=BD=ED=97=98=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/use-get-gathering-mine-list.ts | 9 ++-- .../user/query/use-get-user-info-suspense.ts | 21 ++++++++ src/app/(root)/(main)/my-page/page.tsx | 22 +++++++-- src/components/layout/header.tsx | 6 ++- .../section/fallback/auth-status-skeleton.tsx | 7 +++ .../fallback/gathering-list-skeleton.tsx | 12 +++-- .../fallback/user-profile-skeleton.tsx | 23 +++++++++ src/components/section/user/gatherings.tsx | 49 ++++++++++--------- src/components/section/user/user-profile.tsx | 11 ++--- 9 files changed, 117 insertions(+), 43 deletions(-) create mode 100644 src/apis/user/query/use-get-user-info-suspense.ts create mode 100644 src/components/section/fallback/auth-status-skeleton.tsx create mode 100644 src/components/section/fallback/user-profile-skeleton.tsx diff --git a/src/apis/gathering-list/query/use-get-gathering-mine-list.ts b/src/apis/gathering-list/query/use-get-gathering-mine-list.ts index bbd16ae1..c04097fb 100644 --- a/src/apis/gathering-list/query/use-get-gathering-mine-list.ts +++ b/src/apis/gathering-list/query/use-get-gathering-mine-list.ts @@ -1,10 +1,13 @@ import queryKeys from "@/apis/query-keys"; import { GetGatheringMineListRequest } from "@/types/gathering-list"; -import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; +import { + useSuspenseInfiniteQuery, + useSuspenseQuery, +} from "@tanstack/react-query"; import { getGatheringMineList } from "../gathering-list.api"; const useGetGatheringMineList = (params: GetGatheringMineListRequest) => { - return useQuery({ + return useSuspenseQuery({ queryKey: queryKeys.gatheringList.mine(params), queryFn: () => getGatheringMineList(params), }); @@ -13,7 +16,7 @@ const useGetGatheringMineList = (params: GetGatheringMineListRequest) => { const useGetGatheringMineListInfinite = ( params: Omit ) => { - return useInfiniteQuery({ + return useSuspenseInfiniteQuery({ queryKey: queryKeys.gatheringList.mineInfinite({ ...params, page: 0 }), queryFn: ({ pageParam }) => getGatheringMineList({ ...params, page: pageParam }), diff --git a/src/apis/user/query/use-get-user-info-suspense.ts b/src/apis/user/query/use-get-user-info-suspense.ts new file mode 100644 index 00000000..64d6a9d4 --- /dev/null +++ b/src/apis/user/query/use-get-user-info-suspense.ts @@ -0,0 +1,21 @@ +import queryKeys from "@/apis/query-keys"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import { getUserInfo } from "../user.api"; + +const useGetUserInfoSuspense = () => { + return useSuspenseQuery({ + queryKey: queryKeys.user.all, + queryFn: getUserInfo, + retry: false, + staleTime: 1000 * 60 * 5, + select: (response) => ({ + ...response, + profileImageUrl: response.profileImageUrl + ? `${response.profileImageUrl}?date=${Date.now()}` + : "", + }), + structuralSharing: false, + }); +}; + +export default useGetUserInfoSuspense; diff --git a/src/app/(root)/(main)/my-page/page.tsx b/src/app/(root)/(main)/my-page/page.tsx index ba14dc7e..234baf51 100644 --- a/src/app/(root)/(main)/my-page/page.tsx +++ b/src/app/(root)/(main)/my-page/page.tsx @@ -1,10 +1,26 @@ -import { Gatherings, UserProfile } from "@/components/section"; +import { GatheringListSkeleton, UserProfile } from "@/components/section"; +import UserProfileSkeleton from "@/components/section/fallback/user-profile-skeleton"; +import { + HostGatherings, + MemberGatherings, +} from "@/components/section/user/gatherings"; +import { Suspense } from "react"; const MyPage = () => { return (
- - + }> + + + +
+ }> + + + }> + + +
); }; diff --git a/src/components/layout/header.tsx b/src/components/layout/header.tsx index ca98a876..561bd110 100644 --- a/src/components/layout/header.tsx +++ b/src/components/layout/header.tsx @@ -3,6 +3,8 @@ import { Logo, LogoTypo } from "@/assets/icons-colored"; import useSideMenuStore from "@/store/side-menu-store"; import { cn } from "@/utils/cn"; import Link from "next/link"; +import { Suspense } from "react"; +import AuthStatusSkeleton from "../section/fallback/auth-status-skeleton"; import { AuthStatusButton, GnbTabButton, HamburgerMenuButton } from "../ui"; import SideMenu from "../ui/side-menu/side-menu"; @@ -37,7 +39,9 @@ const Header = ({ className }: HeaderProps) => { 모임 리스트 - + }> + + { + return ( +
+ ); +}; + +export default AuthStatusSkeleton; diff --git a/src/components/section/fallback/gathering-list-skeleton.tsx b/src/components/section/fallback/gathering-list-skeleton.tsx index c316f1f2..b99dc77e 100644 --- a/src/components/section/fallback/gathering-list-skeleton.tsx +++ b/src/components/section/fallback/gathering-list-skeleton.tsx @@ -1,4 +1,4 @@ -const GatheringListSkeleton = () => { +const GatheringListSkeleton = ({ subTitle = true }: { subTitle?: boolean }) => { const skeletonCards = Array.from({ length: 4 }, (_, index) => (
  • @@ -7,13 +7,13 @@ const GatheringListSkeleton = () => { {/* 본문 */}
    {/* 모임 명 */} -
    +
    {/* 카테고리 뱃지 및 인원 수 */}
    {/* 카테고리 뱃지 */} -
    +
    {/* 인원 수 */} -
    +
    @@ -26,7 +26,9 @@ const GatheringListSkeleton = () => {
    -
    + {subTitle && ( +
    + )}
    diff --git a/src/components/section/fallback/user-profile-skeleton.tsx b/src/components/section/fallback/user-profile-skeleton.tsx new file mode 100644 index 00000000..1551c02a --- /dev/null +++ b/src/components/section/fallback/user-profile-skeleton.tsx @@ -0,0 +1,23 @@ +const UserProfileSkeleton = () => { + return ( +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + ); +}; + +export default UserProfileSkeleton; diff --git a/src/components/section/user/gatherings.tsx b/src/components/section/user/gatherings.tsx index fd395cc4..4c67ec90 100644 --- a/src/components/section/user/gatherings.tsx +++ b/src/components/section/user/gatherings.tsx @@ -3,45 +3,46 @@ import { useGetGatheringMineList } from "@/apis/gathering-list/query/use-get-gathering-mine-list"; import GatheringList from "../gathering/list/gathering-list"; -const Gatherings = () => { - const { - data: hostGathering, - isPending: isHostLoading, - isError: isHostError, - } = useGetGatheringMineList({ +const HostGatherings = () => { + const { data: hostGathering } = useGetGatheringMineList({ role: "HOST", size: 10, page: 0, }); - const { - data: memberGathering, - isPending: isMemberLoading, - isError: isMemberError, - } = useGetGatheringMineList({ + return ( + + ); +}; + +const MemberGatherings = () => { + const { data: memberGathering } = useGetGatheringMineList({ role: "MEMBER", size: 10, page: 0, }); - if (isHostLoading || isMemberLoading) return
    Loading...
    ; - - if (isHostError || isMemberError) return
    Error
    ; + return ( + + ); +}; +const Gatherings = () => { return (
    - - + +
    ); }; export default Gatherings; +export { HostGatherings, MemberGatherings }; diff --git a/src/components/section/user/user-profile.tsx b/src/components/section/user/user-profile.tsx index df06f7a8..a481ce0c 100644 --- a/src/components/section/user/user-profile.tsx +++ b/src/components/section/user/user-profile.tsx @@ -1,16 +1,13 @@ "use client"; -import useGetUserInfo from "@/apis/user/query/use-get-user-info"; +import useGetUserInfoSuspense from "@/apis/user/query/use-get-user-info-suspense"; import { PasswordEditModal, Profile, ProfileEditModal } from "@/components/ui"; const UserProfile = () => { - const { data, isPending, isError } = useGetUserInfo(); - - if (isPending) return
    Loading...
    ; - if (isError) return
    Error
    ; + const { data } = useGetUserInfoSuspense(); return (
    -
    +
    { />
    - + {data.nickname} From 966743370a8c2b4bf473d443a6ec9126c8c2a176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=AF=BC=EC=84=9D?= Date: Mon, 2 Feb 2026 10:43:28 +0900 Subject: [PATCH 2/4] =?UTF-8?q?ui:=20=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=9C=A0=EC=A0=80=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=20=EB=86=92=EC=9D=B4=20=ED=82=A4=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/section/user/user-profile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/section/user/user-profile.tsx b/src/components/section/user/user-profile.tsx index a481ce0c..7602bafc 100644 --- a/src/components/section/user/user-profile.tsx +++ b/src/components/section/user/user-profile.tsx @@ -7,7 +7,7 @@ const UserProfile = () => { return (
    -
    +
    Date: Tue, 24 Feb 2026 09:26:25 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20AuthStatusButton=EC=97=90=EC=84=9C?= =?UTF-8?q?=20Skeleton=20=EB=A1=9C=EB=94=A9=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20Suspense=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header.tsx | 6 +-- .../ui/button/auth-status-button.tsx | 49 ++++++++++--------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/components/layout/header.tsx b/src/components/layout/header.tsx index 561bd110..ca98a876 100644 --- a/src/components/layout/header.tsx +++ b/src/components/layout/header.tsx @@ -3,8 +3,6 @@ import { Logo, LogoTypo } from "@/assets/icons-colored"; import useSideMenuStore from "@/store/side-menu-store"; import { cn } from "@/utils/cn"; import Link from "next/link"; -import { Suspense } from "react"; -import AuthStatusSkeleton from "../section/fallback/auth-status-skeleton"; import { AuthStatusButton, GnbTabButton, HamburgerMenuButton } from "../ui"; import SideMenu from "../ui/side-menu/side-menu"; @@ -39,9 +37,7 @@ const Header = ({ className }: HeaderProps) => { 모임 리스트
    - }> - - + { const isSignedIn = useAuthStore((state) => state.authStatus); if (isSignedIn) { - return ( - user && ( - - } - items={[ - { - text: "마이페이지", - onClick: () => router.push("/my-page"), - }, - { - text: "로그아웃", - onClick: signOut, - }, - ]} - itemClassName="hover:text-gray-neutral-700 text-gray-neutral-500 justify-center" - contentAlign="end" - /> - ) + return user ? ( + + } + items={[ + { + text: "마이페이지", + onClick: () => router.push("/my-page"), + }, + { + text: "로그아웃", + onClick: signOut, + }, + ]} + itemClassName="hover:text-gray-neutral-700 text-gray-neutral-500 justify-center" + contentAlign="end" + /> + ) : ( + ); } else { return ( From 4bec0e8ce8548a75296420a6558ae443e14319e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=AF=BC=EC=84=9D?= Date: Tue, 24 Feb 2026 09:41:39 +0900 Subject: [PATCH 4/4] =?UTF-8?q?ui:=20UserProfile=20=EB=B0=8F=20UserProfile?= =?UTF-8?q?Skeleton=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20?= =?UTF-8?q?=EB=86=92=EC=9D=B4=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/section/fallback/user-profile-skeleton.tsx | 2 +- src/components/section/user/user-profile.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/section/fallback/user-profile-skeleton.tsx b/src/components/section/fallback/user-profile-skeleton.tsx index 1551c02a..1aaea390 100644 --- a/src/components/section/fallback/user-profile-skeleton.tsx +++ b/src/components/section/fallback/user-profile-skeleton.tsx @@ -1,7 +1,7 @@ const UserProfileSkeleton = () => { return (
    -
    +
    diff --git a/src/components/section/user/user-profile.tsx b/src/components/section/user/user-profile.tsx index 7602bafc..99157d58 100644 --- a/src/components/section/user/user-profile.tsx +++ b/src/components/section/user/user-profile.tsx @@ -7,7 +7,7 @@ const UserProfile = () => { return (
    -
    +