From 4d2b6c93db5dce0686a48741f7d9b55b773418f6 Mon Sep 17 00:00:00 2001 From: manNomi Date: Sat, 14 Feb 2026 19:01:49 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=9A=80=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B2=8C=EC=8B=9C=ED=8C=90=20CSR=20=EC=A0=84?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/app/community/[boardCode]/page.tsx | 38 ++----------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/apps/web/src/app/community/[boardCode]/page.tsx b/apps/web/src/app/community/[boardCode]/page.tsx index ba19d791..1b60046f 100644 --- a/apps/web/src/app/community/[boardCode]/page.tsx +++ b/apps/web/src/app/community/[boardCode]/page.tsx @@ -1,56 +1,24 @@ -import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query"; import type { Metadata } from "next"; -import { CommunityQueryKeys } from "@/apis/community/api"; -import { getPostListServer } from "@/apis/community/server"; import TopDetailNavigation from "@/components/layout/TopDetailNavigation"; -import { COMMUNITY_BOARDS } from "@/constants/community"; import CommunityPageContent from "./CommunityPageContent"; export const metadata: Metadata = { title: "커뮤니티", }; -// ISR: 정적 경로 생성 -export async function generateStaticParams() { - return COMMUNITY_BOARDS.map((board) => ({ - boardCode: board.code, - })); -} - -// ISR: 자동 재생성 비활성화 (수동 revalidate만 사용) -export const revalidate = false; - interface CommunityPageProps { params: { boardCode: string; }; } -const CommunityPage = async ({ params }: CommunityPageProps) => { +const CommunityPage = ({ params }: CommunityPageProps) => { const { boardCode } = params; - // QueryClient 생성 (서버 컴포넌트에서만 사용) - const queryClient = new QueryClient(); - - // 기본 카테고리 - const defaultCategory = "전체"; - - // 서버에서 데이터 prefetch (ISR - 수동 revalidate만) - const result = await getPostListServer({ boardCode, category: defaultCategory, revalidate: false }); - - if (result.ok) { - // React Query 캐시에 데이터 설정 (서버 fetch와 동일한 category 사용) - queryClient.setQueryData([CommunityQueryKeys.postList, boardCode, defaultCategory], { - data: result.data, - }); - } - return (
- - - - + +
); }; From 9d5c0e1bbe70536d6f062f51d8f9ad323c35f22e Mon Sep 17 00:00:00 2001 From: manNomi Date: Sat, 14 Feb 2026 19:01:57 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20=EC=8B=A0=EA=B3=A0/=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=20=EB=A1=9C=EC=BB=AC=20=EC=83=81=ED=83=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=B0=8F=20=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=95=84=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[boardCode]/CommunityPageContent.tsx | 38 ++++++++++++- .../src/lib/zustand/useReportedPostsStore.ts | 55 +++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/lib/zustand/useReportedPostsStore.ts diff --git a/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx b/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx index d1177b4f..4d0ac283 100644 --- a/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx +++ b/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx @@ -1,14 +1,22 @@ "use client"; import { useRouter } from "next/navigation"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { useGetPostList } from "@/apis/community"; import ButtonTab from "@/components/ui/ButtonTab"; import { COMMUNITY_BOARDS, COMMUNITY_CATEGORIES } from "@/constants/community"; +import useReportedPostsStore from "@/lib/zustand/useReportedPostsStore"; +import type { ListPost } from "@/types/community"; import CommunityRegionSelector from "./CommunityRegionSelector"; import PostCards from "./PostCards"; import PostWriteButton from "./PostWriteButton"; +type ListPostWithAuthor = ListPost & { + postFindSiteUserResponse?: { + id: number; + }; +}; + interface CommunityPageContentProps { boardCode: string; } @@ -16,13 +24,37 @@ interface CommunityPageContentProps { const CommunityPageContent = ({ boardCode }: CommunityPageContentProps) => { const router = useRouter(); const [category, setCategory] = useState("전체"); + const reportedPostIds = useReportedPostsStore((state) => state.reportedPostIds); + const blockedUserIds = useReportedPostsStore((state) => state.blockedUserIds); - // HydrationBoundary로부터 자동으로 prefetch된 데이터 사용 const { data: posts = [] } = useGetPostList({ boardCode, category, }); + const visiblePosts = useMemo(() => { + if (reportedPostIds.length === 0 && blockedUserIds.length === 0) { + return posts; + } + + const reportedIdSet = new Set(reportedPostIds); + const blockedUserIdSet = new Set(blockedUserIds); + + return posts.filter((post) => { + if (reportedIdSet.has(post.id)) { + return false; + } + + const authorId = (post as ListPostWithAuthor).postFindSiteUserResponse?.id; + + if (typeof authorId === "number" && blockedUserIdSet.has(authorId)) { + return false; + } + + return true; + }); + }, [posts, reportedPostIds, blockedUserIds]); + const handleBoardChange = (newBoard: string) => { router.push(`/community/${newBoard}`); }; @@ -49,7 +81,7 @@ const CommunityPageContent = ({ boardCode }: CommunityPageContentProps) => { setChoice={setCategory} style={{ padding: "10px 0 10px 18px" }} /> - {} + {} ); diff --git a/apps/web/src/lib/zustand/useReportedPostsStore.ts b/apps/web/src/lib/zustand/useReportedPostsStore.ts new file mode 100644 index 00000000..594c582b --- /dev/null +++ b/apps/web/src/lib/zustand/useReportedPostsStore.ts @@ -0,0 +1,55 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +interface ReportedPostsState { + reportedPostIds: number[]; + blockedUserIds: number[]; + addReportedPost: (postId: number) => void; + addBlockedUser: (userId: number) => void; + removeBlockedUser: (userId: number) => void; +} + +const useReportedPostsStore = create()( + persist( + (set) => ({ + reportedPostIds: [], + blockedUserIds: [], + addReportedPost: (postId) => { + set((state) => { + if (state.reportedPostIds.includes(postId)) { + return state; + } + + return { + reportedPostIds: [...state.reportedPostIds, postId], + }; + }); + }, + addBlockedUser: (userId) => { + set((state) => { + if (state.blockedUserIds.includes(userId)) { + return state; + } + + return { + blockedUserIds: [...state.blockedUserIds, userId], + }; + }); + }, + removeBlockedUser: (userId) => { + set((state) => ({ + blockedUserIds: state.blockedUserIds.filter((id) => id !== userId), + })); + }, + }), + { + name: "reported-community-posts-storage", + partialize: (state) => ({ + reportedPostIds: state.reportedPostIds, + blockedUserIds: state.blockedUserIds, + }), + }, + ), +); + +export default useReportedPostsStore; From c7d4c4c77b0c0f215a0efa80f8e047210dac2cd4 Mon Sep 17 00:00:00 2001 From: manNomi Date: Sat, 14 Feb 2026 19:02:05 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20=EC=8B=A0=EA=B3=A0=20?= =?UTF-8?q?=EC=8B=9C=20=EC=9E=91=EC=84=B1=EC=9E=90=20=EC=A6=89=EC=8B=9C=20?= =?UTF-8?q?=EC=B0=A8=EB=8B=A8=20=EB=B0=8F=20=EC=83=81=EC=84=B8=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=20=EC=B0=A8=EB=8B=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/apis/reports/postReport.ts | 3 - .../[boardCode]/[postId]/KebabMenu.tsx | 5 +- .../[boardCode]/[postId]/PostPageContent.tsx | 31 +++++++++- .../_hooks/useSelectReportHandler.ts | 56 ++++++++++++++++--- .../src/components/ui/ReportPanel/index.tsx | 5 +- 5 files changed, 85 insertions(+), 15 deletions(-) diff --git a/apps/web/src/apis/reports/postReport.ts b/apps/web/src/apis/reports/postReport.ts index acb3f013..1a5d4beb 100644 --- a/apps/web/src/apis/reports/postReport.ts +++ b/apps/web/src/apis/reports/postReport.ts @@ -1,7 +1,6 @@ import { useMutation } from "@tanstack/react-query"; import type { AxiosError } from "axios"; -import { useRouter } from "next/navigation"; import { toast } from "@/lib/zustand/useToastStore"; import { reportsApi, type UsePostReportsRequest } from "./api"; @@ -10,12 +9,10 @@ import { reportsApi, type UsePostReportsRequest } from "./api"; * @description 신고 등록 훅 */ const usePostReports = () => { - const router = useRouter(); return useMutation, UsePostReportsRequest>({ mutationFn: reportsApi.postReport, onSuccess: () => { toast.success("신고가 성공적으로 등록되었습니다."); - router.back(); }, onError: (_error) => { toast.error("신고 등록에 실패했습니다. 잠시 후 다시 시도해주세요."); diff --git a/apps/web/src/app/community/[boardCode]/[postId]/KebabMenu.tsx b/apps/web/src/app/community/[boardCode]/[postId]/KebabMenu.tsx index 969fd10b..5582f8dc 100644 --- a/apps/web/src/app/community/[boardCode]/[postId]/KebabMenu.tsx +++ b/apps/web/src/app/community/[boardCode]/[postId]/KebabMenu.tsx @@ -45,9 +45,10 @@ type KebabMenuProps = { postId: number; boardCode: string; isOwner?: boolean; + authorId?: number; }; -const KebabMenu = ({ postId, boardCode, isOwner = false }: KebabMenuProps) => { +const KebabMenu = ({ postId, boardCode, isOwner = false, authorId }: KebabMenuProps) => { const dropdownRef = useRef(null); const { mutate: deletePost } = useDeletePost(); const router = useRouter(); @@ -87,7 +88,7 @@ const KebabMenu = ({ postId, boardCode, isOwner = false }: KebabMenuProps) => {
  • - +