From 8be4ad2a6d452f027577ac17f517d7a3f245647f Mon Sep 17 00:00:00 2001 From: Yeongjun Kim Date: Thu, 3 Apr 2025 19:25:32 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=ED=8C=94?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EC=8B=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EA=B0=80=20=EB=B0=98=EC=98=81=EB=90=98=EB=8F=84=EB=A1=9D=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 --- .../FollowListButton/FollowListButton.tsx | 19 ++---- .../ProfileEditButton/ProfileEditButton.tsx | 25 ++++---- src/components/common/User/User.tsx | 31 +++------- src/hooks/useFollowUser.ts | 60 ++++++------------- src/services/users/useUsers.ts | 46 +++++++++++--- 5 files changed, 80 insertions(+), 101 deletions(-) diff --git a/src/components/FollowListButton/FollowListButton.tsx b/src/components/FollowListButton/FollowListButton.tsx index 5bcf48ee..cb67d9da 100644 --- a/src/components/FollowListButton/FollowListButton.tsx +++ b/src/components/FollowListButton/FollowListButton.tsx @@ -1,7 +1,7 @@ 'use client' import { PROFILE_MSG } from '@/constants' -import { useFollowUser, useModal } from '@/hooks' +import { useModal } from '@/hooks' import { useCurrentUser } from '@/hooks/useCurrentUser' import { fetchGetFollowers, fetchGetFollowing } from '@/services/users/useUsers' import { UserProfileResBody } from '@/types' @@ -13,13 +13,6 @@ const FollowListButton = ({ user }: { user: UserProfileResBody }) => { const myId = currentUser?.memberId const { Modal, isOpen, modalClose, currentModal, handleOpenCurrentModal } = useModal() - const { followingCount, setFollowingCount, followerCount } = useFollowUser({ - memberId: user?.memberId || 0, - isInitFollowing: !!user?.isFollowing, - followingInitCount: user?.followingCount || 0, - followerInitCount: user?.followerCount || 0, - handleOpenCurrentModal, - }) return ( <> @@ -28,7 +21,7 @@ const FollowListButton = ({ user }: { user: UserProfileResBody }) => { onClick={() => { handleOpenCurrentModal('following') }}> - {PROFILE_MSG.FOLLOWING} {followingCount} + {PROFILE_MSG.FOLLOWING} {user?.followingCount} {PROFILE_MSG.LIST_DIVIDER}
{ onClick={() => { handleOpenCurrentModal('follower') }}> - {PROFILE_MSG.FOLLOWER} {followerCount} + {PROFILE_MSG.FOLLOWER} {user?.followerCount}
{currentModal !== 'login' && isOpen && ( { fetchFn={fetchGetFollowing} myId={myId} type="following" - followingCount={followingCount} - setFollowingCount={setFollowingCount} + followingCount={user?.followingCount} /> )} {currentModal === 'follower' && ( @@ -64,8 +56,7 @@ const FollowListButton = ({ user }: { user: UserProfileResBody }) => { fetchFn={fetchGetFollowers} myId={myId} type="follower" - followingCount={followingCount} - setFollowingCount={setFollowingCount} + followingCount={user?.followingCount} /> )} diff --git a/src/components/ProfileEditButton/ProfileEditButton.tsx b/src/components/ProfileEditButton/ProfileEditButton.tsx index e2ff536d..01c9e281 100644 --- a/src/components/ProfileEditButton/ProfileEditButton.tsx +++ b/src/components/ProfileEditButton/ProfileEditButton.tsx @@ -14,44 +14,39 @@ const ProfileEditButton = ({ user }: { user: UserProfileResBody }) => { const { currentUser } = useCurrentUser() const myId = currentUser?.memberId const { handleOpenCurrentModal } = useModal() - const { isFollowing, handleClickFollow } = useFollowUser({ - memberId: user?.memberId || 0, - isInitFollowing: !!user?.isFollowing, - followingInitCount: user?.followingCount || 0, - followerInitCount: user?.followerCount || 0, + const { handleClickFollow } = useFollowUser({ + profileId: user?.memberId || 0, + memberId: currentUser?.memberId || 0, + myId: myId || 0, handleOpenCurrentModal, }) - return myId ? ( + return ( - ) : ( - - - ) } diff --git a/src/components/common/User/User.tsx b/src/components/common/User/User.tsx index 213207f9..d7be0118 100644 --- a/src/components/common/User/User.tsx +++ b/src/components/common/User/User.tsx @@ -1,6 +1,5 @@ 'use client' -import { Dispatch, SetStateAction } from 'react' import { PROFILE_MSG } from '@/constants' import { useFollowUser, useModal } from '@/hooks' import { cls } from '@/utils' @@ -14,12 +13,11 @@ interface UserProps { nickname: string profileImagePath: string aboutMe?: string - isFollowing?: boolean + isFollowing: boolean isAuth?: boolean followingCount?: number myId?: number profileId?: number - setFollowingCount?: Dispatch> } const User = ({ @@ -29,20 +27,16 @@ const User = ({ aboutMe, isFollowing, isAuth, - followingCount, myId, profileId, - setFollowingCount, }: UserProps) => { const { Modal, isOpen, modalClose, currentModal, handleOpenCurrentModal } = useModal() - const { isFollowing: isFollowingValue, handleClickListInFollow } = - useFollowUser({ - profileId: profileId || 0, - memberId: memberId || 0, - isInitFollowing: !!isFollowing, - followerInitCount: followingCount || 0, - }) + const { handleClickListInFollow } = useFollowUser({ + profileId: profileId || 0, + memberId: memberId || 0, + myId: myId || 0, + }) return ( <> @@ -70,23 +64,16 @@ const User = ({ type="button" className={cls( 'button px-2.5 py-1.5 text-sm', - isFollowingValue ? 'button-white' : 'button-emerald', + isFollowing ? 'button-white' : 'button-emerald', )} onClick={() => { if (myId) { - handleClickListInFollow(isFollowingValue) - if (isFollowingValue) { - profileId === myId && - setFollowingCount?.((prev) => prev! - 1) - } else { - profileId === myId && - setFollowingCount?.((prev) => prev! + 1) - } + handleClickListInFollow(isFollowing) } else { handleOpenCurrentModal('login') } }}> - {isFollowingValue ? PROFILE_MSG.FOLLOWING : PROFILE_MSG.FOLLOW} + {isFollowing ? PROFILE_MSG.FOLLOWING : PROFILE_MSG.FOLLOW} )} diff --git a/src/hooks/useFollowUser.ts b/src/hooks/useFollowUser.ts index 94acef6d..039c0983 100644 --- a/src/hooks/useFollowUser.ts +++ b/src/hooks/useFollowUser.ts @@ -1,75 +1,56 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useMemo } from 'react' import { useDeleteFollow, usePostFollow } from '@/services/users/useUsers' -import { useQueryClient } from '@tanstack/react-query' import { debounce } from 'lodash' import { useCurrentUser } from './useCurrentUser' export interface UseFollowUserProps { profileId?: number memberId: number - isInitFollowing: boolean - followingInitCount?: number - followerInitCount: number + myId?: number handleOpenCurrentModal?: (current: string) => void } const useFollowUser = ({ profileId, memberId, - isInitFollowing, - followerInitCount, - followingInitCount, + myId, handleOpenCurrentModal, }: UseFollowUserProps) => { - const queryClient = useQueryClient() const { isLoggedIn } = useCurrentUser() - const [isFollowing, setIsFollowing] = useState(isInitFollowing) - const [followingCount, setFollowingCount] = useState(followingInitCount) - const [followerCount, setFollowerCount] = useState(followerInitCount) - const { mutateAsync: postFollow } = usePostFollow(profileId) - const { mutateAsync: deleteFollow } = useDeleteFollow(profileId) - - useEffect(() => { - setIsFollowing(isInitFollowing) - }, [isInitFollowing]) - - useEffect(() => { - setFollowingCount(followingInitCount) - }, [followingInitCount]) - - useEffect(() => { - setFollowerCount(followerInitCount) - }, [followerInitCount]) + const { mutateAsync: postFollow } = usePostFollow(profileId, myId) + const { mutateAsync: deleteFollow } = useDeleteFollow(profileId, myId) + const targetMemberId = profileId + ? profileId === myId + ? memberId + : profileId + : memberId const debounceUnFollowUser = useMemo( () => debounce(async () => { - if (memberId) { - await deleteFollow({ memberId }) - } + await deleteFollow({ + memberId: targetMemberId, + }) }, 300), - [memberId, deleteFollow], + [targetMemberId, deleteFollow], ) const debounceFollowUser = useMemo( () => debounce(async () => { - if (memberId) { - await postFollow({ memberId }) - } + await postFollow({ + memberId: targetMemberId, + }) }, 300), - [memberId, postFollow], + [targetMemberId, postFollow], ) const handleClickFollow = useCallback( (isFollowing: boolean) => { if (isLoggedIn) { - setIsFollowing((prev) => !prev) if (isFollowing) { - setFollowerCount((prev) => prev - 1) debounceUnFollowUser() } else { - setFollowerCount((prev) => prev + 1) debounceFollowUser() } } else { @@ -86,7 +67,6 @@ const useFollowUser = ({ const handleClickListInFollow = useCallback( (isFollowing: boolean) => { - setIsFollowing((prev) => !prev) if (isFollowing) { debounceUnFollowUser() } else { @@ -97,10 +77,6 @@ const useFollowUser = ({ ) return { - isFollowing, - followingCount, - setFollowingCount, - followerCount, handleClickFollow, handleClickListInFollow, } diff --git a/src/services/users/useUsers.ts b/src/services/users/useUsers.ts index ba73c8f9..79308165 100644 --- a/src/services/users/useUsers.ts +++ b/src/services/users/useUsers.ts @@ -71,6 +71,8 @@ export const usePutUserProfile = (memberId: number) => { }, }) } + +// 멤버 프로필 수정 서버 함수 export const fetchPostUserProfile = async ( userId: number, data: RegisterReqBody, @@ -135,7 +137,7 @@ export const fetchGetFollowers = async ({ } // 팔로우 추가 -export const usePostFollow = (profileId?: number) => { +export const usePostFollow = (profileId?: number, myId?: number) => { const queryClient = useQueryClient() return useMutation({ @@ -145,14 +147,28 @@ export const usePostFollow = (profileId?: number) => { }, onSuccess: () => { queryClient.invalidateQueries({ - queryKey: [QUERY_KEYS.MEMBERS, profileId], + queryKey: [QUERY_KEYS.MEMBERS, profileId || myId], + }) + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.FOLLOWING, profileId || myId], }) queryClient.invalidateQueries({ - queryKey: [QUERY_KEYS.FOLLOWING, profileId], + queryKey: [QUERY_KEYS.FOLLOWERS, profileId || myId], }) queryClient.invalidateQueries({ - queryKey: [QUERY_KEYS.FOLLOWERS, profileId], + queryKey: [QUERY_KEYS.MEMBERS], }) + if (profileId !== myId) { + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.MEMBERS, myId], + }) + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.FOLLOWING, myId], + }) + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.FOLLOWERS, myId], + }) + } }, onError: (error: Error) => { console.log(error) @@ -161,7 +177,7 @@ export const usePostFollow = (profileId?: number) => { } // 팔로우 삭제 -export const useDeleteFollow = (profileId?: number) => { +export const useDeleteFollow = (profileId?: number, myId?: number) => { const queryClient = useQueryClient() return useMutation({ @@ -171,14 +187,28 @@ export const useDeleteFollow = (profileId?: number) => { }, onSuccess: () => { queryClient.invalidateQueries({ - queryKey: [QUERY_KEYS.MEMBERS, profileId], + queryKey: [QUERY_KEYS.MEMBERS, profileId || myId], }) queryClient.invalidateQueries({ - queryKey: [QUERY_KEYS.FOLLOWING, profileId], + queryKey: [QUERY_KEYS.FOLLOWING, profileId || myId], }) queryClient.invalidateQueries({ - queryKey: [QUERY_KEYS.FOLLOWERS, profileId], + queryKey: [QUERY_KEYS.FOLLOWERS, profileId || myId], }) + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.MEMBERS], + }) + if (profileId !== myId) { + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.MEMBERS, myId], + }) + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.FOLLOWING, myId], + }) + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.FOLLOWERS, myId], + }) + } }, onError: (error: Error) => { console.log(error) From f8096b99802e35c4f0c77c19cde9eef732fb9c90 Mon Sep 17 00:00:00 2001 From: Yeongjun Kim Date: Thu, 3 Apr 2025 19:27:46 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EC=BF=BC=EB=A6=AC=20=EB=AC=B4?= =?UTF-8?q?=ED=9A=A8=ED=99=94=20=EC=8B=9C=20Spinner=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=A0=8C=EB=8D=94=EB=A7=81=20=EB=90=98?= =?UTF-8?q?=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 --- src/app/(routes)/user/[userId]/page.tsx | 97 ++++++++++--------- .../common/FollowList/FollowList.tsx | 13 +-- .../common/FollowList/hooks/useFollowQuery.ts | 4 +- 3 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/app/(routes)/user/[userId]/page.tsx b/src/app/(routes)/user/[userId]/page.tsx index e7c0c9f1..df44e786 100644 --- a/src/app/(routes)/user/[userId]/page.tsx +++ b/src/app/(routes)/user/[userId]/page.tsx @@ -1,66 +1,75 @@ +'use client' + import { Avatar, CategoryListItem, FollowListButton, ProfileEditButton, + Spinner, } from '@/components' import { CATEGORIES_RENDER, PROFILE_MSG } from '@/constants' -import { fetchGetUserProfile } from '@/services/users/useUsers' +import { useGetUserProfile } from '@/services/users/useUsers' import { UserLayoutProps } from './layout' -export default async function UserPage({ - params: { userId }, -}: UserLayoutProps) { - const user = await fetchGetUserProfile({ memberId: userId }) +export default function UserPage({ params: { userId } }: UserLayoutProps) { + const { data: user, isFetching: isUserLoading } = useGetUserProfile( + Number(userId), + ) return ( <> -
-
- {user?.profileImagePath && ( -
- + {isUserLoading ? ( + + ) : ( +
+
+ {user?.profileImagePath && ( +
+ +
+ )} +
+
+ {user?.nickname} +
+
+ {user?.newsEmail} +
+
+ +
- )} -
-
- {user?.nickname} -
-
- {user?.newsEmail} +
+ +
+
+ {PROFILE_MSG.FAVORITE_CATEGORY}
-
- +
+
-
- -
-
- {PROFILE_MSG.FAVORITE_CATEGORY} -
- -
-
-
-
- {PROFILE_MSG.DESCRIPTION} +
+ {PROFILE_MSG.DESCRIPTION} +
+
+ {user?.aboutMe} +
-
{user?.aboutMe}
-
+ )} ) } diff --git a/src/components/common/FollowList/FollowList.tsx b/src/components/common/FollowList/FollowList.tsx index 5fcb9cb7..9739f381 100644 --- a/src/components/common/FollowList/FollowList.tsx +++ b/src/components/common/FollowList/FollowList.tsx @@ -1,4 +1,4 @@ -import { Dispatch, Fragment, SetStateAction } from 'react' +import { Fragment, useEffect } from 'react' import { Spinner } from '@/components' import useFollowQuery from '@/components/common/FollowList/hooks/useFollowQuery' import useInfiniteScroll from '@/hooks/useInfiniteScroll' @@ -12,7 +12,6 @@ export interface FollowListProps { myId?: number type?: string followingCount?: number - setFollowingCount?: Dispatch> } export interface FollowUserProps { @@ -29,7 +28,6 @@ const FollowList = ({ myId, type, followingCount, - setFollowingCount, }: FollowListProps) => { const { followList, fetchNextPage, hasNextPage, isFollowLoading } = useFollowQuery({ @@ -38,11 +36,11 @@ const FollowList = ({ type, }) const { target } = useInfiniteScroll({ hasNextPage, fetchNextPage }) - + useEffect(() => { + console.log(isFollowLoading) + }, [isFollowLoading]) return isFollowLoading ? ( - - - + ) : (
    {followList && @@ -60,7 +58,6 @@ const FollowList = ({ followingCount={followingCount} myId={myId} profileId={memberId} - setFollowingCount={setFollowingCount} /> ))} diff --git a/src/components/common/FollowList/hooks/useFollowQuery.ts b/src/components/common/FollowList/hooks/useFollowQuery.ts index 7ca6ada3..3e6942be 100644 --- a/src/components/common/FollowList/hooks/useFollowQuery.ts +++ b/src/components/common/FollowList/hooks/useFollowQuery.ts @@ -5,7 +5,7 @@ import { useInfiniteQuery } from '@tanstack/react-query' const useFollowQuery = ({ memberId, fetchFn, type }: FollowListProps) => { const queryKey = type === 'following' ? QUERY_KEYS.FOLLOWING : QUERY_KEYS.FOLLOWERS - const { data, fetchNextPage, hasNextPage, isLoading } = useInfiniteQuery({ + const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery({ queryKey: [queryKey, memberId], queryFn: ({ pageParam }) => fetchFn({ @@ -22,7 +22,7 @@ const useFollowQuery = ({ memberId, fetchFn, type }: FollowListProps) => { followList: data, fetchNextPage, hasNextPage, - isFollowLoading: isLoading, + isFollowLoading: isFetching, } }