Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#434] [모임 상세] 독서모임 상세 페이지 api 연결 #441

Merged
merged 12 commits into from
Nov 23, 2023
12 changes: 12 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ const nextConfig = {
port: '',
pathname: '/**',
},
{
protocol: 'http',
hostname: 'k.kakaocdn.net',
port: '',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'blog.kakaocdn.net',
port: '',
pathname: '/**',
},
Comment on lines +37 to +48
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ask;
#440 PR의 코멘트로 이미 남겨두긴 했는데 새로 추가된 두 호스트로 부터 이미지를 받는 경우가 있는건가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네! 일부 도서이미지와 프로필 이미지를 해당 도메인에서 받아오더라구요! 관련 에러가 발생해서 추가했어요.

],
},
};
Expand Down
4 changes: 2 additions & 2 deletions public/icons/post.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/apis/user.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { APIJob, APIJobGroup } from '@/types/job';
import { APIUser } from '@/types/user';
import { APIUser, APIUserProfile } from '@/types/user';
import { publicApi } from './core/axios';

const userAPI = {
getUserProfile: ({ userId }: { userId: APIUser['userId'] }) =>
publicApi.get<APIUser>(`/service-api/users/${userId}/profile`),
publicApi.get<APIUserProfile>(`/service-api/users/${userId}/profile`),

getMyProfile: () => publicApi.get<APIUser>('/service-api/users/me'),

Expand Down
47 changes: 40 additions & 7 deletions src/app/group/[groupId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
'use client';

import { Flex } from '@chakra-ui/react';
import TopNavigation from '@/ui/Base/TopNavigation';
import BookGroupInfo from '@/v1/bookGroup/detail/BookGroupInfo';
import { IconArrowLeft, IconHamburger, IconPost } from '@public/icons';

import GroupDetail from '@/ui/Group/GroupDetail';
import { useBookGroupTitle } from '@/queries/group/useBookGroupQuery';
import CommentList from '@/v1/bookGroup/detail/CommentList';

const GroupDetailPage = ({
const DetailBookGroupPage = ({
params: { groupId },
}: {
params: { groupId: number };
}) => {
return (
<Flex direction="column" justify="center">
<GroupDetail bookGroupId={Number(groupId)} />
</Flex>
<>
<BookGroupNavigation groupId={groupId} />
<div className="flex flex-col gap-[2rem]">
<BookGroupInfo groupId={groupId} />
<div className="flex flex-col gap-[1rem]">
<Heading text="게시글" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ask;
해당 페이지에서 한 번만 사용돼서 컴포넌트로 만들지 않아도 되는 요소로 보이는데 컴포넌트로 분리하신 의도가 궁금해요.

Copy link
Member Author

@gxxrxn gxxrxn Nov 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CommentList 컴포넌트 내부에 있던 요소를 페이지단으로 가져오면서 분리된 형태 그대로 사용했어요. 나중에 멤버목록 컴포넌트를 보여줄 때도 충분히 재사용할 수 있을거라고 생각해요!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gxxrxn 자세한 설명 감사해요. 말씀대로 사용자 목록을 보여줄 때 재사용할 수 있겠네요..!

<CommentList groupId={groupId} />
</div>
</div>
</>
);
};

export default GroupDetailPage;
export default DetailBookGroupPage;

const BookGroupNavigation = ({ groupId }: { groupId: number }) => {
const { data: title } = useBookGroupTitle(groupId);

return (
<TopNavigation>
<TopNavigation.LeftItem>
<IconArrowLeft />
</TopNavigation.LeftItem>
<TopNavigation.CenterItem textAlign="left">
{title}
</TopNavigation.CenterItem>
<TopNavigation.RightItem>
<IconPost />
<IconHamburger />
</TopNavigation.RightItem>
</TopNavigation>
);
};

const Heading = ({ text }: { text: string }) => (
<p className=" text-xl font-bold">{text}</p>
);
4 changes: 2 additions & 2 deletions src/hooks/useQueryWithSuspense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
UseQueryResult,
} from '@tanstack/react-query';

export type useQueryOptionWithOutSuspense<
export type UseQueryOptionWithoutSuspense<
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
Expand All @@ -22,7 +22,7 @@ const useQueryWithSuspense = <
>(
queryKey: TQueryKey,
queryFunction?: QueryFunction<TQueryFnData, TQueryKey>,
queryOptions?: useQueryOptionWithOutSuspense<
queryOptions?: UseQueryOptionWithoutSuspense<
TQueryFnData,
TError,
TData,
Expand Down
12 changes: 12 additions & 0 deletions src/queries/group/key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { APIGroupDetail } from '@/types/group';

const bookGroupKeys = {
all: ['bookGroup'] as const,
details: () => [...bookGroupKeys.all, 'detail'] as const,
detail: (id: APIGroupDetail['bookGroupId']) =>
[...bookGroupKeys.details(), id] as const,
comments: (id: APIGroupDetail['bookGroupId']) =>
[...bookGroupKeys.details(), id, 'comments'] as const,
};

export default bookGroupKeys;
40 changes: 40 additions & 0 deletions src/queries/group/useBookGroupCommentsQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useQuery } from '@tanstack/react-query';
import { QueryOptions } from '@/types/query';
import {
APIGroupCommentPagination,
APIGroupDetail,
BookGroupComment,
} from '@/types/group';

import GroupAPI from '@/apis/group';
import bookGroupKeys from './key';

const transformComments = ({ bookGroupComments }: APIGroupCommentPagination) =>
bookGroupComments.map<BookGroupComment>(comment => ({
id: comment.commentId,
writer: {
id: comment.userId,
profileImageSrc: comment.userProfileImage,
name: comment.nickname,
},
createdAt: comment.createdAt,
content: comment.contents,
}));

const useBookGroupCommentsQuery = <TData = APIGroupCommentPagination>(
groupId: APIGroupDetail['bookGroupId'],
select?: QueryOptions<APIGroupCommentPagination, TData>['select']
) =>
useQuery({
queryKey: bookGroupKeys.comments(groupId),
queryFn: () =>
GroupAPI.getGroupComments({ bookGroupId: groupId }).then(
({ data }) => data
),
select,
});

export default useBookGroupCommentsQuery;

export const useBookGroupComments = (groupId: APIGroupDetail['bookGroupId']) =>
useBookGroupCommentsQuery(groupId, transformComments);
40 changes: 40 additions & 0 deletions src/queries/group/useBookGroupQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useQuery } from '@tanstack/react-query';

import { APIGroupDetail, BookGroupDetail } from '@/types/group';
import { QueryOptions } from '@/types/query';

import GroupAPI from '@/apis/group';
import bookGroupKeys from './key';

const transformBookGroupDetail = (data: APIGroupDetail) =>
({
title: data.title,
description: data.introduce,
bookId: data.book.id,
owner: { isMe: data.isOwner, id: data.owner.id },
date: { start: data.startDate, end: data.endDate },
memberCount: { current: data.currentMemberCount, max: data.maxMemberCount },
isPublic: data.isPublic,
isMember: data.isGroupMember,
} as BookGroupDetail);

export const useBookGroupQuery = <TData = APIGroupDetail>(
groupId: APIGroupDetail['bookGroupId'],
select: QueryOptions<APIGroupDetail, TData>['select']
) =>
useQuery({
queryKey: bookGroupKeys.detail(groupId),
queryFn: () =>
GroupAPI.getGroupDetailInfo({ bookGroupId: groupId }).then(
({ data }) => data
),
select,
});

export default useBookGroupQuery;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ask;
useBookQuery 만 default 로 내보내는 이유가 있으신가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 기본 query들이 모두 default로 export 되고 있어서 맞췄습니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gxxrxn 아하..! 다른 쿼리들은 한 파일에 하나의 useQuery 만 사용하고 있어서 default 가 어색해보이지 않았는데 useBookGroupQuery 는 아래 select 쿼리들이 export 되고 있어서 이상하다고 느꼈던 것 같아요. 이대로 두셔도 괜찮을 것 같아요.


export const useBookGroup = (groupId: APIGroupDetail['bookGroupId']) =>
useBookGroupQuery(groupId, transformBookGroupDetail);

export const useBookGroupTitle = (groupId: APIGroupDetail['bookGroupId']) =>
useBookGroupQuery(groupId, data => data.title);
Comment on lines +36 to +40
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2;
다른 react-query Hook 과의 일관성과 일반 커스텀 훅들과의 차별성을 주기 위해 함수명 뒤에 'Query'를 붙이는건 어떻게 생각하시나요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 오히려 다른 query들과 차이가 있다고 생각해서 Query 라는 suffix를 제거했었어요. select를 통해 필요한 데이터들을 가공해서 제공하고 있기 때문에 custom hook에 더 가깝다고 생각했어요! Tanstack Query의 메인테이너 Tkdodo의 best practice 아티클에서도 비슷한 형태로 사용하고 있어서 참고했었어요. 하지만 함수명에 Query에 의존하고 있음을 표현하는 의도라면 그것도 좋다고 생각해요. 이에 대한 민종님의 의견을 듣고싶어요!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gxxrxn select를 사용한걸 보지 못했었네요. 😅 새로운 쿼리를 호출하는 것이 아니라, 쿼리를 통해 파생된 데이터라면 규란님 의견대로 'query'를 붙일 필요가 없어보이네요 👀

14 changes: 0 additions & 14 deletions src/queries/group/useGroupCommentsQuery/index.tsx

This file was deleted.

10 changes: 10 additions & 0 deletions src/queries/user/key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { APIUser } from '@/types/user';

const userKeys = {
all: ['user'] as const,
details: () => [...userKeys.all, 'detail'] as const,
detail: (id: APIUser['userId']) => [...userKeys.details(), id] as const,
me: () => [...userKeys.details(), 'me'] as const,
};

export default userKeys;
7 changes: 4 additions & 3 deletions src/queries/user/useMyProfileQuery.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import userAPI from '@/apis/user';
import type { APIUser } from '@/types/user';
import useQueryWithSuspense, {
useQueryOptionWithOutSuspense,
UseQueryOptionWithoutSuspense,
} from '@/hooks/useQueryWithSuspense';
import userKeys from './key';

const useMyProfileQuery = (options?: useQueryOptionWithOutSuspense<APIUser>) =>
const useMyProfileQuery = (options?: UseQueryOptionWithoutSuspense<APIUser>) =>
useQueryWithSuspense(
['user', 'me'],
userKeys.me(),
() => userAPI.getMyProfile().then(({ data }) => data),
options
);
Expand Down
12 changes: 7 additions & 5 deletions src/queries/user/useUserProfileQuery.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import userAPI from '@/apis/user';
import type { APIUser, APIUserProfile } from '@/types/user';
import useQueryWithSuspense, {
useQueryOptionWithOutSuspense,
UseQueryOptionWithoutSuspense,
} from '@/hooks/useQueryWithSuspense';
import type { APIUser } from '@/types/user';

import userAPI from '@/apis/user';
import userKeys from './key';

const useUserProfileQuery = (
userId: APIUser['userId'],
options?: useQueryOptionWithOutSuspense<APIUser>
options?: UseQueryOptionWithoutSuspense<APIUserProfile>
) =>
useQueryWithSuspense(
['user', String(userId)],
userKeys.detail(userId),
() => userAPI.getUserProfile({ userId }).then(({ data }) => data),
options
);
Expand Down
40 changes: 0 additions & 40 deletions src/stories/bookGroup/detail/BookGroupInfo.stories.tsx

This file was deleted.

32 changes: 0 additions & 32 deletions src/stories/bookGroup/detail/CommentList.stories.tsx

This file was deleted.

30 changes: 28 additions & 2 deletions src/types/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface APIGroup {
hasJoinPasswd: boolean;
isPublic: boolean;
bookGroupId: number;
memberCount: number;
currentMemberCount: number;
commentCount: number;
book: APIGroupBook;
owner: APIGroupOwner;
Expand Down Expand Up @@ -65,10 +65,36 @@ export interface APIGroupComment {
userProfileImage: APIUser['profileImage'];
createdAt: string;
modifiedAt: string;
nickname: APIUser['nickname'];
nickname: string;
writtenByCurrentUser: boolean;
}

export interface APIGroupCommentPagination extends Pagination {
bookGroup: { isPublic: APIGroup['isPublic'] };
bookGroupComments: APIGroupComment[];
}

export type BookGroupDetail = {
title: APIGroup['title'];
description: APIGroup['introduce'];
bookId: APIBook['bookId'];
owner: { isMe: boolean; id: APIUser['userId'] };
date: { start: APIGroup['startDate']; end: APIGroup['endDate'] };
memberCount: {
current: APIGroup['currentMemberCount'];
max: APIGroup['maxMemberCount'];
};
isPublic: APIGroup['isPublic'];
isMember: APIGroupDetail['isGroupMember'];
};

export type BookGroupComment = {
id: APIGroup['bookGroupId'];
writer: {
id: APIUser['userId'];
profileImageSrc: APIUser['profileImage'];
name: APIUser['nickname'];
};
createdAt: APIGroupComment['createdAt'];
content: APIGroupComment['contents'];
};
Loading