Skip to content

Commit

Permalink
Merge pull request #302 from Team-TenTen/feature/#301/profile-ssr
Browse files Browse the repository at this point in the history
프로필 페이지 SSR 적용
  • Loading branch information
dudwns authored May 13, 2024
2 parents e85e03d + 27d86d6 commit 4b9ae25
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 126 deletions.
16 changes: 13 additions & 3 deletions src/app/(routes)/user/[userId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { UserController } from '@/components'
import { ProfileTap } from '@/components'
import { fetchGetUserProfile } from '@/services/user/profile/profile'
import { Metadata } from 'next'

type Props = {
params: { userId: number }
}

export async function getProfile(userId: number) {
const response = await fetchGetUserProfile({ memberId: userId })
return response
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
const userId = params.userId
const user = await fetchGetUserProfile({ memberId: userId })
const user = await getProfile(userId)

return {
title: user.nickname,
Expand All @@ -19,7 +24,12 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
}

const UserLayout = ({ children }: { children: React.ReactNode }) => {
return <UserController>{children}</UserController>
return (
<>
<ProfileTap />
{children}
</>
)
}

export default UserLayout
133 changes: 16 additions & 117 deletions src/app/(routes)/user/[userId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,20 @@
'use client'

import { Avatar, CategoryListItem, Spinner } from '@/components'
import Button from '@/components/common/Button/Button'
import FollowList from '@/components/common/FollowList/FollowList'
import LoginModal from '@/components/common/Modal/LoginModal'
import { CATEGORIES_RENDER, PROFILE_MSG } from '@/constants'
import { useFollowUser, useModal } from '@/hooks'
import useGetProfile from '@/hooks/useGetProfile'
import {
fetchGetFollowers,
fetchGetFollowing,
} from '@/services/user/follow/follow'
import { cls, getProfileButtonColor, getProfileButtonText } from '@/utils'
import { useRouter } from 'next/navigation'
Avatar,
CategoryListItem,
FollowListButton,
ProfileEditButton,
} from '@/components'
import { CATEGORIES_RENDER, PROFILE_MSG } from '@/constants'
import { getProfile } from './layout'

interface UserPageProps {
params: { userId: number }
}

const UserPage = () => {
const { user, myId, isProfileLoading } = useGetProfile()
const router = useRouter()
const { Modal, isOpen, modalClose, currentModal, handleOpenCurrentModal } =
useModal()
const {
isFollowing,
followingCount,
setFollowingCount,
followerCount,
handleClickFollow,
} = useFollowUser({
memberId: user?.memberId || 0,
isInitFollowing: !!user?.isFollowing,
followingInitCount: user?.followingCount || 0,
followerInitCount: user?.followerCount || 0,
handleOpenCurrentModal,
})
export default async function UserPage({ params: { userId } }: UserPageProps) {
const user = await getProfile(userId)

return isProfileLoading ? (
<Spinner />
) : (
return (
<>
<div className="flex flex-col gap-4 px-4 py-6">
<div className="flex gap-3">
Expand All @@ -55,49 +34,11 @@ const UserPage = () => {
{user?.newsEmail}
</div>
<div className="flex gap-1 text-xs font-medium text-gray6">
<div
className="cursor-pointer hover:font-semibold"
onClick={() => {
handleOpenCurrentModal('following')
}}>
{PROFILE_MSG.FOLLOWING} {followingCount}
</div>
{PROFILE_MSG.LIST_DIVIDER}
<div
className="cursor-pointer hover:font-semibold"
onClick={() => {
handleOpenCurrentModal('follower')
}}>
{PROFILE_MSG.FOLLOWER} {followerCount}
</div>
<FollowListButton user={user} />
</div>
</div>
</div>
<Button
type="button"
onClick={() => {
if (user?.memberId === myId) {
router.push('/user/setting')
} else if (isFollowing) {
handleClickFollow(isFollowing)
} else {
handleClickFollow(isFollowing)
}
}}
className={cls(
'button button-md button-lg',
getProfileButtonColor({
isFollowing,
memberId: user?.memberId,
myId,
}),
)}>
{getProfileButtonText({
isFollowing,
memberId: user?.memberId,
myId,
})}
</Button>
<ProfileEditButton user={user} />
<div className="flex flex-col ">
<div className="py-3 text-sm font-semibold text-gray9">
{PROFILE_MSG.FAVORITE_CATEGORY}
Expand All @@ -121,48 +62,6 @@ const UserPage = () => {
<div className="text-sm font-normal text-gray9">{user?.aboutMe}</div>
</div>
</div>
{currentModal !== 'login' && isOpen && (
<Modal
title={
currentModal === 'following'
? `${PROFILE_MSG.FOLLOWING}`
: `${PROFILE_MSG.FOLLOWER}`
}
onClose={modalClose}
type={'follow'}>
<div className="flex flex-col gap-2">
{currentModal === 'following' && (
<FollowList
memberId={user?.memberId}
fetchFn={fetchGetFollowing}
myId={myId}
type="following"
followingCount={followingCount}
setFollowingCount={setFollowingCount}
/>
)}
{currentModal === 'follower' && (
<FollowList
memberId={user?.memberId}
fetchFn={fetchGetFollowers}
myId={myId}
type="follower"
followingCount={followingCount}
setFollowingCount={setFollowingCount}
/>
)}
</div>
</Modal>
)}
{currentModal === 'login' && (
<LoginModal
Modal={Modal}
isOpen={isOpen}
modalClose={modalClose}
/>
)}
</>
)
}

export default UserPage
2 changes: 1 addition & 1 deletion src/app/api/user/[memberId]/profile/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function GET(
}

try {
const userData = await apiServer.get(path, { cache: 'no-cache' }, headers)
const userData = await apiServer.get(path, {}, headers)
return NextResponse.json(userData)
} catch (error: any) {
return NextResponse.json(
Expand Down
88 changes: 88 additions & 0 deletions src/components/FollowListButton/FollowListButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use client'

import { PROFILE_MSG } from '@/constants'
import { useFollowUser, useModal } from '@/hooks'
import { useCurrentUser } from '@/hooks/useCurrentUser'
import {
fetchGetFollowers,
fetchGetFollowing,
} from '@/services/user/follow/follow'
import { UserProfileResBody } from '@/types'
import FollowList from '../common/FollowList/FollowList'
import LoginModal from '../common/Modal/LoginModal'

const FollowListButton = ({ user }: { user: UserProfileResBody }) => {
const { currentUser } = useCurrentUser()
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 (
<>
<div
className="cursor-pointer hover:font-semibold"
onClick={() => {
handleOpenCurrentModal('following')
}}>
{PROFILE_MSG.FOLLOWING} {followingCount}
</div>
{PROFILE_MSG.LIST_DIVIDER}
<div
className="cursor-pointer hover:font-semibold"
onClick={() => {
handleOpenCurrentModal('follower')
}}>
{PROFILE_MSG.FOLLOWER} {followerCount}
</div>
{currentModal !== 'login' && isOpen && (
<Modal
title={
currentModal === 'following'
? `${PROFILE_MSG.FOLLOWING}`
: `${PROFILE_MSG.FOLLOWER}`
}
onClose={modalClose}
type={'follow'}>
<div className="flex flex-col gap-2">
{currentModal === 'following' && (
<FollowList
memberId={user?.memberId}
fetchFn={fetchGetFollowing}
myId={myId}
type="following"
followingCount={followingCount}
setFollowingCount={setFollowingCount}
/>
)}
{currentModal === 'follower' && (
<FollowList
memberId={user?.memberId}
fetchFn={fetchGetFollowers}
myId={myId}
type="follower"
followingCount={followingCount}
setFollowingCount={setFollowingCount}
/>
)}
</div>
</Modal>
)}
{currentModal === 'login' && (
<LoginModal
Modal={Modal}
isOpen={isOpen}
modalClose={modalClose}
/>
)}
</>
)
}

export default FollowListButton
55 changes: 55 additions & 0 deletions src/components/ProfileEditButton/ProfileEditButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client'

import { useFollowUser, useModal } from '@/hooks'
import { useCurrentUser } from '@/hooks/useCurrentUser'
import { UserProfileResBody } from '@/types'
import { cls, getProfileButtonColor, getProfileButtonText } from '@/utils'
import { useRouter } from 'next/navigation'
import Button from '../common/Button/Button'
import Spinner from '../common/Spinner/Spinner'

const ProfileEditButton = ({ user }: { user: UserProfileResBody }) => {
const router = useRouter()
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,
handleOpenCurrentModal,
})

return myId ? (
<Button
type="button"
onClick={() => {
if (user?.memberId === myId) {
router.push('/user/setting')
} else if (isFollowing) {
handleClickFollow(isFollowing)
} else {
handleClickFollow(isFollowing)
}
}}
className={cls(
'button button-md button-lg',
getProfileButtonColor({
isFollowing,
memberId: user?.memberId,
myId,
}),
)}>
{getProfileButtonText({
isFollowing,
memberId: user?.memberId,
myId,
})}
</Button>
) : (
<Spinner />
)
}

export default ProfileEditButton
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import TabItem from '@/components/common/Tab/TabItem'
import useTab from '@/components/common/Tab/hooks/useTab'
import useGetProfile from '@/hooks/useGetProfile'

const UserController = ({ children }: { children: React.ReactNode }) => {
const ProfileTap = () => {
const { user, myId, isProfileLoading } = useGetProfile()
const { currentTab, tabList } = useTab({
type: 'user',
Expand All @@ -28,9 +28,8 @@ const UserController = ({ children }: { children: React.ReactNode }) => {
))}
</Tab>
)}
{children}
</>
)
}

export default UserController
export default ProfileTap
1 change: 1 addition & 0 deletions src/components/UserInfoForm/UserInfoForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ const UserInfoForm = ({ userData, formType }: UserInfoFormProps) => {
userData?.memberId &&
(await fetchPostUserProfile(userData?.memberId, data, imageFile))
notify('success', '수정되었습니다.')
router.refresh()
router.back()
} catch (e) {
notify('error', '수정에 실패했습니다.')
Expand Down
2 changes: 2 additions & 0 deletions src/components/common/CategoryList/CategoryListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client'

import { cls } from '@/utils'
import Link from 'next/link'
import { usePathname, useSearchParams } from 'next/navigation'
Expand Down
4 changes: 3 additions & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ export { default as SpaceList } from './SpaceList/SpaceList'
export { default as Spinner } from './common/Spinner/Spinner'
export { default as SearchController } from './SearchController/SearchController'
export { default as NotificationController } from './NotificationController/NotificationController'
export { default as UserController } from './UserController/UserController'
export { default as ProfileTap } from './ProfileTap/ProfileTap'
export { default as SettingController } from './SettingController/SettingController'
export { default as FollowListButton } from './FollowListButton/FollowListButton'
export { default as ProfileEditButton } from './ProfileEditButton/ProfileEditButton'
2 changes: 1 addition & 1 deletion src/services/user/profile/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const fetchGetUserProfile = async ({ memberId }: FetchGetUserProfileProps) => {
const path = `/api/user/${memberId}/profile`

try {
const response = await apiClient.get(path)
const response = await apiClient.get(path, { cache: 'no-store' })
return response
} catch (e) {
if (e instanceof Error) throw new Error(e.message)
Expand Down

0 comments on commit 4b9ae25

Please sign in to comment.