diff --git a/src/app/(routes)/member/[memberId]/character-create-link.tsx b/src/app/(routes)/member/[memberId]/character-create-link.tsx deleted file mode 100644 index 18fdd80..0000000 --- a/src/app/(routes)/member/[memberId]/character-create-link.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import Link from 'next/link'; - -export default function CharacterCreateLink() { - return ( - - 내 캐릭터 만들러 가기 - - ); -} diff --git a/src/app/(routes)/member/[memberId]/characters-container.tsx b/src/app/(routes)/member/[memberId]/characters-container.tsx new file mode 100644 index 0000000..1834371 --- /dev/null +++ b/src/app/(routes)/member/[memberId]/characters-container.tsx @@ -0,0 +1,34 @@ +'use client'; + +import FixedBottomArea from '@/components/fixed-bottom-area'; +import CTAButton from '@/components/ui/cta-button'; +import { Member } from '@/services/auth/getMember'; +import { useCharactersQuery } from '@/store/query/useCharactersQuery'; +import { Characters } from './characters'; +import { Header } from './header'; +import { MemberData } from './member-data'; + +type Props = { + memberId: Member['memberId']; +}; + +export function CharactersContainer({ memberId }: Props) { + const { + data: { characters, cheerCount, memberNickname, isMe, joinDays, memoCount }, + } = useCharactersQuery(memberId); + + const isMyPage = isMe; + + return ( + <> +
+
+ + +
+ {!isMyPage && ( + } /> + )} + + ); +} diff --git a/src/app/(routes)/member/[memberId]/characters.tsx b/src/app/(routes)/member/[memberId]/characters.tsx new file mode 100644 index 0000000..74ed632 --- /dev/null +++ b/src/app/(routes)/member/[memberId]/characters.tsx @@ -0,0 +1,36 @@ +import { GetCharactersResponse } from '@/services/character/getCharacters'; +import CharacterCard from './character-card'; +import CharacterCreateButton from './character-create-button'; + +type Props = { + isMyPage: boolean; + characters: GetCharactersResponse['characters']; +}; + +const MAXIMUM_CHARACTER = 6; + +export function Characters({ characters, isMyPage }: Props) { + const showCharacterCreateButton = isMyPage && characters.length < MAXIMUM_CHARACTER; + + return ( +
+

성장 기록지

+
+ {characters?.map((character, i) => { + if (isMyPage) + return ( + + ); + return ; + })} + {showCharacterCreateButton && } +
+
+ ); +} diff --git a/src/app/(routes)/member/[memberId]/header.tsx b/src/app/(routes)/member/[memberId]/header.tsx new file mode 100644 index 0000000..785c2cb --- /dev/null +++ b/src/app/(routes)/member/[memberId]/header.tsx @@ -0,0 +1,20 @@ +import { LikeButton, LikeButtonWithTooltip } from './like-button'; +import { GetCharactersResponse } from '@/services/character/getCharacters'; + +type Props = { + isMyPage: boolean; +} & Pick; + +export function Header({ isMyPage, memberNickname, cheerCount }: Props) { + const headerText = `${memberNickname}의 도감 `; + return ( +
+

{headerText}

+ {isMyPage ? ( + + ) : ( + + )} +
+ ); +} diff --git a/src/app/(routes)/member/[memberId]/member-data.tsx b/src/app/(routes)/member/[memberId]/member-data.tsx new file mode 100644 index 0000000..2ad6941 --- /dev/null +++ b/src/app/(routes)/member/[memberId]/member-data.tsx @@ -0,0 +1,49 @@ +import EggImage from '@/assets/images/egg.png'; +import PencilImage from '@/assets/images/pencil.png'; +import { GetCharactersResponse } from '@/services/character/getCharacters'; +import Image from 'next/image'; + +type Props = Pick; +export function MemberData({ joinDays, memoCount }: Props) { + return ( +
+

성장과정

+
+
+
+
성장한지
+
{joinDays}일
+
+
+ +
+
+
+
+
총 메모
+
{memoCount}개
+
+
+ +
+
+
+
+ ); +} diff --git a/src/app/(routes)/member/[memberId]/page.tsx b/src/app/(routes)/member/[memberId]/page.tsx index 154bc34..6f59657 100644 --- a/src/app/(routes)/member/[memberId]/page.tsx +++ b/src/app/(routes)/member/[memberId]/page.tsx @@ -1,112 +1,22 @@ -import EggImage from '@/assets/images/egg.png'; -import PencilImage from '@/assets/images/pencil.png'; -import { PageContainer } from '@/components/ui'; import A2HS from '@/hooks/useA2HS'; -import getMember from '@/services/auth/getMember'; -import getCharacters from '@/services/character/getCharacters'; import { cookies } from 'next/headers'; -import Image from 'next/image'; -import CharacterCard from './character-card'; -import CharacterCreateButton from './character-create-button'; -import CharacterCreateLink from './character-create-link'; -import { LikeButton, LikeButtonWithTooltip } from './like-button'; import PageContainerV2 from '@/components/page-container-v2/page-container-v2'; +import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query'; +import { charactersQueryOptions } from '@/store/query/useCharactersQuery'; +import { CharactersContainer } from './characters-container'; -const MAXIMUM_CHARACTER = 6; +export default async function Home({ params: { memberId } }: { params: { memberId: number } }) { + const accessToken = `${cookies().get('accessToken')?.value}`; -export default async function Home({ params: { memberId } }: { params: { memberId: string } }) { - const accessToken = cookies().get('accessToken')?.value; - - const characters = await getCharacters({ - memberId: Number(memberId), - accessToken, - }); - - const isMyPage = characters?.isMe; - const headerText = `${characters?.memberNickname}의 도감 `; - const showCharacterCreateButton = isMyPage && characters.characters.length < MAXIMUM_CHARACTER; + const queryClient = new QueryClient(); + const { isMe } = await queryClient.fetchQuery(charactersQueryOptions(accessToken, memberId)); return ( - -
-
-

{headerText}

- {isMyPage ? ( - - ) : ( - - )} -
-
-

성장과정

-
-
-
-
성장한지
-
- {characters?.joinDays}일 -
-
-
- -
-
-
-
-
총 메모
-
- {characters?.memoCount}개 -
-
-
- -
-
-
-
-
-

성장 기록지

-
- {characters?.characters?.map((character, i) => { - if (isMyPage) - return ( - - ); - return ; - })} - {showCharacterCreateButton && } -
-
-
- - {!isMyPage && ( -
- -
- )} - -
+ + + + + + ); } diff --git a/src/services/character/getCharacters.ts b/src/services/character/getCharacters.ts index a6b8518..bd6ccd4 100644 --- a/src/services/character/getCharacters.ts +++ b/src/services/character/getCharacters.ts @@ -15,7 +15,7 @@ export type GetCharactersRequest = { accessToken?: string; }; -export default async function getCharacters(request: GetCharactersRequest): Promise { +export default async function getCharacters(request: GetCharactersRequest): Promise { try { const res = await fetch( `${process.env.NEXT_PUBLIC_SERVER_BASE_URL}/api/v1/character/member/${request.memberId}`, diff --git a/src/store/query/useCharactersQuery.ts b/src/store/query/useCharactersQuery.ts new file mode 100644 index 0000000..94547db --- /dev/null +++ b/src/store/query/useCharactersQuery.ts @@ -0,0 +1,19 @@ +import { Member } from '@/services/auth/getMember'; +import getCharacters, { GetCharactersResponse } from '@/services/character/getCharacters'; +import { BasicSuspenseQueryOptions } from '@/types/query'; +import { useSuspenseQuery } from '@tanstack/react-query'; +import { getCookie } from 'cookies-next'; + +export const charactersQueryOptions = ( + accessToken: string, + memberId: Member['memberId'], +): BasicSuspenseQueryOptions => ({ + queryKey: ['characters', memberId], + queryFn: () => getCharacters({ memberId, accessToken }), +}); + +export function useCharactersQuery(memberId: Member['memberId']) { + return useSuspenseQuery({ + ...charactersQueryOptions(`${getCookie('accessToken')}`, memberId), + }); +}