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

인기게시글-불러오기-기능-추가 #93

Merged
merged 3 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions client/src/components/post/MainPagePostList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PostCardList from "@/components/post/PostCardList";
import CustomContainer from "@/components/layout/CustomContainer";
import { useState } from "react";
import CustomToggleButtonGroup from "@/components/CustomToggleButtonGroup";
import PopularPostCardList from "./PopularPostCardList";

type Props = {
initialData: AugmentedGetPostListResponse;
Expand All @@ -19,11 +20,13 @@ const MainPagePostList = ({ initialData }: Props) => {
<CustomToggleButtonGroup
value={selectableList}
onChange={setCurrentView}
sx={{ position: "fixed", top: 0, left: 0, right: 0,zIndex:1 }}
sx={{ position: "fixed", top: 0, left: 0, right: 0, zIndex: 1 }}
/>
<CustomContainer mt={5}>
{currentView==="전체 캐스크"&&<PostCardList initialData={initialData} />}
{currentView==="인기"&&<PostCardList sort="likeCount"/>}
{currentView === "전체 캐스크" && (
<PostCardList initialData={initialData} />
)}
{currentView === "인기" && <PopularPostCardList />}
</CustomContainer>
</>
);
jobkaeHenry marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
64 changes: 64 additions & 0 deletions client/src/components/post/PopularPostCardList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use client";

import PostCard from "@/components/post/PostCard";
import useGetPopularPostListInfiniteQuery, {
UseGetPopularPostListQueryInterface,
} from "@/queries/post/useGetPopularPostListInfiniteQuery";
import { useInView } from "react-intersection-observer";
import { useEffect } from "react";
import { Stack } from "@mui/material";
import { useMemo } from "react";
import Image from "next/image";
import NoResult from "@/assets/images/noResult.png";
import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage";
import PostCardSkeleton from "./PostCardSkeleton";

function PopularPostCardList(props: UseGetPopularPostListQueryInterface) {
const {
data,
fetchNextPage,
isFetchingNextPage,
hasNextPage,
isSuccess,
isLoading,
} = useGetPopularPostListInfiniteQuery({
...props,
headers: { Authorization: getTokenFromLocalStorage() },
});

const { ref, inView } = useInView();
useEffect(() => {
if (hasNextPage && inView) fetchNextPage();
}, [inView, hasNextPage]);

const hasResult = useMemo(
() => data && data.pages[0].content.length > 0,
[data]
);

return (
<div>
{hasResult &&
isSuccess &&
// 검색결과가 있을시
data?.pages.map((page) =>
page.content.map((post) => <PostCard {...post} key={post.postNo} />)
)}
{isSuccess && !hasResult && (
// 검색결과 없을 시
<Stack justifyContent="center" alignItems="center" py={8}>
<Image src={NoResult} alt="no result alert" />
</Stack>
)}
{/* 로딩창 */}
{isFetchingNextPage || isLoading ? (
<PostCardSkeleton />
) : (
// 인터섹션옵저버
hasNextPage && <div style={{ height: 60 }} ref={ref}></div>
)}
</div>
);
}

export default PopularPostCardList;
Copy link

Choose a reason for hiding this comment

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

해당 코드는 PopularPostCardList 컴포넌트입니다. 다음은 코드 리뷰 내용입니다:

  1. 코드가 잘 구성되어있으며, 누락된 부분은 없습니다.

  2. 코드에 몇 가지 개선 사항이 있을 수 있습니다:

    • import 문을 알파벳순으로 정렬하는 것이 좋습니다.
    • getTokenFromLocalStorage 함수를 가져오기 전에 import 문을 추가하는 것이 좋습니다.
  3. 잠재적인 버그나 위험 요소는 없어 보입니다.

이상입니다! 혹시 추가 질문이 있으면 알려주세요.

8 changes: 7 additions & 1 deletion client/src/const/serverPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ export const DELETE_COMMENT = (postPk: string, commentPk: string) =>
/**
* 게시물리스트를 받아오거나, 작성하는 Path 버전2 (Breaking Change)
*/
export const POST_LIST_V2 = "/posts/v2" as const;
export const POST_LIST_V2 = "/posts/v2";

/**
* 게시물리스트를 받아오거나, 작성하는 Path 버전2 (Breaking Change)
*/
export const POPULAR_POST_LIST = "/posts/popular";

/**
* ID(pk) 를 입력받아 해당 포스트를 지우는 URL
*/
jobkaeHenry marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
127 changes: 127 additions & 0 deletions client/src/queries/post/useGetPopularPostListInfiniteQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query";
import { PostInterface } from "@/types/post/PostInterface";
import { AxiosRequestConfig } from "axios";
import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage";
import { POPULAR_POST_LIST } from "@/const/serverPath";
import useAxiosPrivate from "@/hooks/useAxiosPrivate";
import Pagenated from "@/types/Pagenated";

export interface UseGetPopularPostListQueryInterface
extends GetPostListOptions {
initialData?: AugmentedGetPostListResponse;
headers?: AxiosRequestConfig["headers"];
}

export const useGetPopularPostListInfiniteQuery = ({
initialData,
size,
sort,
headers,
}: UseGetPopularPostListQueryInterface) => {
return useInfiniteQuery({
queryKey: getPopularPostListInfiniteQueryKey.byKeyword({
sort,
}),

queryFn: async ({ pageParam = 0 }) =>
await getPopularPostListQueryFn({
page: pageParam,
size,
sort,
headers: headers?.Authorization
? headers
: { Authorization: getTokenFromLocalStorage() },
}),

getNextPageParam: ({
currentPage,
hasNextPage,
}: AugmentedGetPostListResponse) =>
hasNextPage ? currentPage + 1 : undefined,

getPreviousPageParam: ({ currentPage }: AugmentedGetPostListResponse) =>
currentPage > 0 ? currentPage - 1 : undefined,
initialPageParam: 0,
initialData: initialData
? { pages: [initialData], pageParams: [0] }
: undefined,
});
};
/**
* 포스트리스트를 받아올 때 Query string으로 사용되는 값
*/
export interface GetPostListOptions {
page?: number;
size?: number;
sort?: string;
}
/**
* 서버응답값 + 무한스크롤을 위해 증강된 값
*/
export interface AugmentedGetPostListResponse extends Pagenated<PostInterface> {
currentPage: number;
hasNextPage: boolean;
}

export const getPopularPostListQueryFn = async ({
page = 0,
size = 10,
sort,
headers,
}: GetPostListOptions & {
headers?: AxiosRequestConfig<any>["headers"];
}): Promise<AugmentedGetPostListResponse> => {
const axiosPrivate = useAxiosPrivate();
const { data } = await axiosPrivate.get<{ data: Pagenated<PostInterface> }>(
POPULAR_POST_LIST,
{
baseURL: process.env.NEXT_PUBLIC_BASE_URL,
params: {
page,
size,
sort: sort ?? "lastModifiedDate,desc",
},
headers,
}
);
return {
...data.data,
currentPage: page,
hasNextPage: data.data.totalElements / ((page + 1) * size) > 1,
};
};

export interface PopularPostListInfiniteQueryKey {
keyword?: string;
userNo?: string;
sort?: string;
}

export const getPopularPostListInfiniteQueryKey = {
all: ["popular_posts"] as const,
byKeyword: ({ sort }: Omit<GetPostListOptions, "page" | "size">) =>
[
"popular_posts",
{
sort,
},
] as const,
};

/**
* 모든 포스트리스트 쿼리를 Invalidate 하는 Hooks
* @returns Invalidate 함수
*/
export const useInvalidatePopularPostList = () => {
/**
* 모든 포스트리스트 쿼리를 Invalidate 하는함수
*/
const queryClinet = useQueryClient();
return () => {
queryClinet.invalidateQueries({
queryKey: getPopularPostListInfiniteQueryKey.all,
});
};
};

export default useGetPopularPostListInfiniteQuery;
jobkaeHenry marked this conversation as resolved.
Show resolved Hide resolved