diff --git a/client/src/components/post/MainPagePostList.tsx b/client/src/components/post/MainPagePostList.tsx
index 1d49b32..b770429 100644
--- a/client/src/components/post/MainPagePostList.tsx
+++ b/client/src/components/post/MainPagePostList.tsx
@@ -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;
@@ -19,11 +20,13 @@ const MainPagePostList = ({ initialData }: Props) => {
- {currentView==="전체 캐스크"&&}
- {currentView==="인기"&&}
+ {currentView === "전체 캐스크" && (
+
+ )}
+ {currentView === "인기" && }
>
);
diff --git a/client/src/components/post/PopularPostCardList.tsx b/client/src/components/post/PopularPostCardList.tsx
new file mode 100644
index 0000000..7d17dc3
--- /dev/null
+++ b/client/src/components/post/PopularPostCardList.tsx
@@ -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 (
+
+ {hasResult &&
+ isSuccess &&
+ // 검색결과가 있을시
+ data?.pages.map((page) =>
+ page.content.map((post) =>
)
+ )}
+ {isSuccess && !hasResult && (
+ // 검색결과 없을 시
+
+
+
+ )}
+ {/* 로딩창 */}
+ {isFetchingNextPage || isLoading ? (
+
+ ) : (
+ // 인터섹션옵저버
+ hasNextPage &&
+ )}
+
+ );
+}
+
+export default PopularPostCardList;
diff --git a/client/src/const/serverPath.ts b/client/src/const/serverPath.ts
index 85d9a98..0703103 100644
--- a/client/src/const/serverPath.ts
+++ b/client/src/const/serverPath.ts
@@ -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
*/
diff --git a/client/src/queries/post/useGetPopularPostListInfiniteQuery.ts b/client/src/queries/post/useGetPopularPostListInfiniteQuery.ts
new file mode 100644
index 0000000..d7daba9
--- /dev/null
+++ b/client/src/queries/post/useGetPopularPostListInfiniteQuery.ts
@@ -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 {
+ currentPage: number;
+ hasNextPage: boolean;
+}
+
+export const getPopularPostListQueryFn = async ({
+ page = 0,
+ size = 10,
+ sort,
+ headers,
+}: GetPostListOptions & {
+ headers?: AxiosRequestConfig["headers"];
+}): Promise => {
+ const axiosPrivate = useAxiosPrivate();
+ const { data } = await axiosPrivate.get<{ data: Pagenated }>(
+ 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) =>
+ [
+ "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;