From 359dd6b5a9ffb9076b21a12c746a6231dc78c648 Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 00:06:16 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=A0=84=EC=B2=B4/=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=95=84=ED=8B=B0=ED=81=B4=20=EA=B0=9C?= =?UTF-8?q?=EC=88=98=20=EC=A1=B0=ED=9A=8C=20api=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/src/pages/myBookmark/apis/axios.ts | 16 ++++++++++++ .../src/pages/myBookmark/apis/queries.ts | 25 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/client/src/pages/myBookmark/apis/axios.ts b/apps/client/src/pages/myBookmark/apis/axios.ts index 15446227..85bd101e 100644 --- a/apps/client/src/pages/myBookmark/apis/axios.ts +++ b/apps/client/src/pages/myBookmark/apis/axios.ts @@ -1,3 +1,4 @@ +import { BookmarkArticlesCountResponse } from '@pages/myBookmark/types/api'; import apiRequest from '@shared/apis/setting/axiosInstance'; export const getBookmarkArticles = async (page: number, size: number) => { @@ -14,6 +15,12 @@ export const getBookmarkUnreadArticles = async (page: number, size: number) => { return data.data; }; +export const getBookmarkArticlesCount = + async (): Promise => { + const { data } = await apiRequest.get(`/api/v3/articles/count`); + return data.data; + }; + export const getCategoryBookmarkArticles = async ( categoryId: string | null, readStatus: boolean | null, @@ -32,3 +39,12 @@ export const getCategoryBookmarkArticles = async ( return data.data; } }; + +export const getCategoryBookmarkArticlesCount = async ( + categoryId: string +): Promise => { + const { data } = await apiRequest.get( + `/api/v3/articles/category/count?category-id=${categoryId}` + ); + return data.data; +}; diff --git a/apps/client/src/pages/myBookmark/apis/queries.ts b/apps/client/src/pages/myBookmark/apis/queries.ts index 2d864ae3..371e0d71 100644 --- a/apps/client/src/pages/myBookmark/apis/queries.ts +++ b/apps/client/src/pages/myBookmark/apis/queries.ts @@ -1,8 +1,14 @@ -import { useSuspenseInfiniteQuery } from '@tanstack/react-query'; +import { + useQuery, + useSuspenseInfiniteQuery, + useSuspenseQuery, +} from '@tanstack/react-query'; import { getBookmarkArticles, + getBookmarkArticlesCount, getBookmarkUnreadArticles, getCategoryBookmarkArticles, + getCategoryBookmarkArticlesCount, } from './axios'; export const useGetBookmarkArticles = () => { @@ -25,6 +31,13 @@ export const useGetBookmarkUnreadArticles = () => { }); }; +export const useGetBookmarkArticlesCount = () => { + return useSuspenseQuery({ + queryKey: ['bookmarkArticlesCount'], + queryFn: getBookmarkArticlesCount, + }); +}; + export const useGetCategoryBookmarkArticles = ( categoryId: string | null, readStatus: boolean | null @@ -44,3 +57,13 @@ export const useGetCategoryBookmarkArticles = ( }, }); }; + +export const useGetCategoryBookmarkArticlesCount = ( + categoryId: string | null +) => { + return useQuery({ + queryKey: ['categoryBookmarkArticlesCount', categoryId], + queryFn: () => getCategoryBookmarkArticlesCount(categoryId!), + enabled: !!categoryId, + }); +}; From 77dacec4c940ad2922a9f3a8a0ffab75496d7549 Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 00:06:43 +0900 Subject: [PATCH 2/9] chore: add BookmarkArticlesCountResponse --- apps/client/src/pages/myBookmark/types/api.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/client/src/pages/myBookmark/types/api.ts b/apps/client/src/pages/myBookmark/types/api.ts index ec05025c..9edab7fa 100644 --- a/apps/client/src/pages/myBookmark/types/api.ts +++ b/apps/client/src/pages/myBookmark/types/api.ts @@ -29,3 +29,10 @@ export interface UnreadBookmarkArticleResponse { } export type CategoryBookmarkArticleResponse = UnreadBookmarkArticleResponse; + +// 나의 북마크 카운트 조회 +export interface BookmarkArticlesCountResponse { + totalArticleCount: number; + readArticleCount: number; + unreadArticleCount: number; +} From db7144575eea384e1d2d178ec676fc63e0f41256 Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 00:07:16 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=A0=84=EC=B2=B4/=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EB=B3=84=20=EC=95=84=ED=8B=B0=ED=81=B4=20?= =?UTF-8?q?=EA=B0=9C=EC=88=98=20=EC=A1=B0=ED=9A=8C=20api=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/myBookmark/MyBookmark.tsx | 4 +++ .../myBookmarkContent/MyBookmarkContent.tsx | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/apps/client/src/pages/myBookmark/MyBookmark.tsx b/apps/client/src/pages/myBookmark/MyBookmark.tsx index 66c87de2..583aea9b 100644 --- a/apps/client/src/pages/myBookmark/MyBookmark.tsx +++ b/apps/client/src/pages/myBookmark/MyBookmark.tsx @@ -52,6 +52,10 @@ const MyBookmark = () => { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['bookmarkReadArticles'] }); queryClient.invalidateQueries({ queryKey: ['bookmarkUnreadArticles'] }); + queryClient.invalidateQueries({ queryKey: ['bookmarkArticlesCount'] }); + queryClient.invalidateQueries({ + queryKey: ['categoryBookmarkArticlesCount'], + }); queryClient.invalidateQueries({ queryKey: ['categoryBookmarkArticles'], }); diff --git a/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx b/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx index e987ff9d..da8c2a60 100644 --- a/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx +++ b/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx @@ -1,5 +1,7 @@ import { useGetBookmarkArticles, + useGetBookmarkArticlesCount, + useGetCategoryBookmarkArticlesCount, useGetBookmarkUnreadArticles, useGetCategoryBookmarkArticles, } from '@pages/myBookmark/apis/queries'; @@ -43,6 +45,10 @@ const MyBookmarkContent = ({ fetchNextPage: fetchNextUnreadArticles, hasNextPage: hasNextUnreadArticles, } = useGetBookmarkUnreadArticles(); + const { data: bookmarkCountData } = useGetBookmarkArticlesCount(); + const { data: categoryCountData } = useGetCategoryBookmarkArticlesCount( + categoryId + ); const { data: categoryArticlesData, @@ -64,13 +70,12 @@ const MyBookmarkContent = ({ ? (articlesData?.pages.flatMap((page) => page.articles) ?? []) : (unreadArticlesData?.pages.flatMap((page) => page.articles) ?? []); - const totalArticle = category - ? categoryArticlesData?.pages?.[0]?.totalArticle - : articlesData?.pages?.[0]?.totalArticle; - - const totalUnread = category - ? categoryArticlesData?.pages?.[0]?.totalUnreadArticle - : articlesData?.pages?.[0]?.totalUnreadArticle; + const totalArticle = categoryId + ? categoryCountData?.totalArticleCount + : bookmarkCountData?.totalArticleCount; + const totalUnread = categoryId + ? categoryCountData?.unreadArticleCount + : bookmarkCountData?.unreadArticleCount; const hasNextPage = category ? hasNextCategoryArticles @@ -93,7 +98,10 @@ const MyBookmarkContent = ({ /** Empty 상태 컴포넌트 */ const EmptyStateComponent = () => { if (articlesToDisplay.length === 0) { - if (articlesData?.pages?.[0]?.totalArticle === 0) return ; + const totalCount = categoryId + ? categoryCountData?.totalArticleCount + : bookmarkCountData?.totalArticleCount; + if ((totalCount ?? 0) === 0) return ; return ; } return null; @@ -135,6 +143,12 @@ const MyBookmarkContent = ({ queryClient.invalidateQueries({ queryKey: ['bookmarkUnreadArticles'], }); + queryClient.invalidateQueries({ + queryKey: ['bookmarkArticlesCount'], + }); + queryClient.invalidateQueries({ + queryKey: ['categoryBookmarkArticlesCount'], + }); queryClient.invalidateQueries({ queryKey: ['categoryBookmarkArticles'], }); From 23a017d799f61da85d1a6838cc38db6b26eea404 Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 00:42:05 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=A0=84=EC=B2=B4/=EC=95=88=EC=9D=BD?= =?UTF-8?q?=EC=9D=8C=20=EC=A1=B0=ED=9A=8C=20api=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EB=A1=9C=20=ED=86=B5=ED=95=A9=EB=90=9C=20v3=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/src/pages/myBookmark/apis/axios.ts | 25 ++++++++------ .../src/pages/myBookmark/apis/queries.ts | 34 +++++++++---------- apps/client/src/pages/myBookmark/types/api.ts | 22 +++++------- 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/apps/client/src/pages/myBookmark/apis/axios.ts b/apps/client/src/pages/myBookmark/apis/axios.ts index 85bd101e..19358905 100644 --- a/apps/client/src/pages/myBookmark/apis/axios.ts +++ b/apps/client/src/pages/myBookmark/apis/axios.ts @@ -1,16 +1,19 @@ -import { BookmarkArticlesCountResponse } from '@pages/myBookmark/types/api'; +import { + BookmarkArticlesCountResponse, + BookmarkArticlesResponse, + CategoryBookmarkArticleResponse, +} from '@pages/myBookmark/types/api'; import apiRequest from '@shared/apis/setting/axiosInstance'; -export const getBookmarkArticles = async (page: number, size: number) => { - const { data } = await apiRequest.get( - `/api/v1/articles?page=${page}&size=${size}` - ); - return data.data; -}; - -export const getBookmarkUnreadArticles = async (page: number, size: number) => { +export const getBookmarkArticles = async ( + readStatus: boolean | null, + page: number, + size: number +): Promise => { + const readStatusQuery = + readStatus === null ? '' : `&read-status=${readStatus}`; const { data } = await apiRequest.get( - `/api/v1/articles/unread?page=${page}&size=${size}` + `/api/v3/articles?page=${page}&size=${size}${readStatusQuery}` ); return data.data; }; @@ -26,7 +29,7 @@ export const getCategoryBookmarkArticles = async ( readStatus: boolean | null, page: number, size: number -) => { +) : Promise => { if (readStatus === null) { const { data } = await apiRequest.get( `/api/v1/articles/category?categoryId=${categoryId}&page=${page}&size=${size}` diff --git a/apps/client/src/pages/myBookmark/apis/queries.ts b/apps/client/src/pages/myBookmark/apis/queries.ts index 371e0d71..571b23e3 100644 --- a/apps/client/src/pages/myBookmark/apis/queries.ts +++ b/apps/client/src/pages/myBookmark/apis/queries.ts @@ -6,25 +6,16 @@ import { import { getBookmarkArticles, getBookmarkArticlesCount, - getBookmarkUnreadArticles, getCategoryBookmarkArticles, getCategoryBookmarkArticlesCount, } from './axios'; +import { BookmarkArticlesResponse } from '../types/api'; -export const useGetBookmarkArticles = () => { +export const useGetBookmarkArticles = (readStatus: boolean | null) => { return useSuspenseInfiniteQuery({ - queryKey: ['bookmarkReadArticles'], - queryFn: ({ pageParam = 0 }) => getBookmarkArticles(pageParam, 20), - initialPageParam: 0, - getNextPageParam: (lastPage, allPages) => - lastPage.articles.length === 0 ? undefined : allPages.length, - }); -}; - -export const useGetBookmarkUnreadArticles = () => { - return useSuspenseInfiniteQuery({ - queryKey: ['bookmarkUnreadArticles'], - queryFn: ({ pageParam = 0 }) => getBookmarkUnreadArticles(pageParam, 20), + queryKey: ['bookmarkArticles', readStatus], + queryFn: ({ pageParam = 0 }) => + getBookmarkArticles(readStatus, Number(pageParam), 20), initialPageParam: 0, getNextPageParam: (lastPage, allPages) => lastPage.articles.length === 0 ? undefined : allPages.length, @@ -44,10 +35,19 @@ export const useGetCategoryBookmarkArticles = ( ) => { return useSuspenseInfiniteQuery({ queryKey: ['categoryBookmarkArticles', readStatus, categoryId], - queryFn: ({ pageParam = 0 }) => { - if (!categoryId) return null; - return getCategoryBookmarkArticles(categoryId, readStatus, pageParam, 20); + if (!categoryId) + return Promise.resolve({ + totalArticleCount: 0, + unreadArticleCount: 0, + articles: [], + }); + return getCategoryBookmarkArticles( + categoryId, + readStatus, + Number(pageParam), + 20 + ); }, initialPageParam: 0, diff --git a/apps/client/src/pages/myBookmark/types/api.ts b/apps/client/src/pages/myBookmark/types/api.ts index 9edab7fa..fc8b9841 100644 --- a/apps/client/src/pages/myBookmark/types/api.ts +++ b/apps/client/src/pages/myBookmark/types/api.ts @@ -7,28 +7,22 @@ interface Category { export interface BookmarkArticle { articleId: number; url: string; - memo: string; + title: string | null; + thumbnailUrl: string | null; + memo: string | null; createdAt: string; isRead: boolean; category: Category; } -// 북마크 전체 조회 -export interface BookmarkArticleResponse { - totalArticle: number; - totalUnreadArticle: number; - isNewUser: boolean; - articles: BookmarkArticle[]; -} - -// 북마크 안 읽음 조회 -export interface UnreadBookmarkArticleResponse { - totalArticle: number; - totalUnreadArticle: number; +// 북마크 조회(v3) +export interface BookmarkArticlesResponse { + totalArticleCount: number; + unreadArticleCount: number; articles: BookmarkArticle[]; } -export type CategoryBookmarkArticleResponse = UnreadBookmarkArticleResponse; +export type CategoryBookmarkArticleResponse = BookmarkArticlesResponse; // 나의 북마크 카운트 조회 export interface BookmarkArticlesCountResponse { From 30237655765461c7414faf72e817b1824e77b388 Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 00:42:41 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=A1=B0=ED=9A=8C=20v3=20api=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/myBookmark/MyBookmark.tsx | 3 +- .../components/fetchCard/FetchCard.tsx | 39 --------------- .../myBookmarkContent/MyBookmarkContent.tsx | 47 ++++++++----------- 3 files changed, 20 insertions(+), 69 deletions(-) delete mode 100644 apps/client/src/pages/myBookmark/components/fetchCard/FetchCard.tsx diff --git a/apps/client/src/pages/myBookmark/MyBookmark.tsx b/apps/client/src/pages/myBookmark/MyBookmark.tsx index 583aea9b..b92d1b92 100644 --- a/apps/client/src/pages/myBookmark/MyBookmark.tsx +++ b/apps/client/src/pages/myBookmark/MyBookmark.tsx @@ -50,8 +50,7 @@ const MyBookmark = () => { const handleDeleteArticle = (id: number) => { deleteArticle(id, { onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['bookmarkReadArticles'] }); - queryClient.invalidateQueries({ queryKey: ['bookmarkUnreadArticles'] }); + queryClient.invalidateQueries({ queryKey: ['bookmarkArticles'] }); queryClient.invalidateQueries({ queryKey: ['bookmarkArticlesCount'] }); queryClient.invalidateQueries({ queryKey: ['categoryBookmarkArticlesCount'], diff --git a/apps/client/src/pages/myBookmark/components/fetchCard/FetchCard.tsx b/apps/client/src/pages/myBookmark/components/fetchCard/FetchCard.tsx deleted file mode 100644 index 1bacbf66..00000000 --- a/apps/client/src/pages/myBookmark/components/fetchCard/FetchCard.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { BookmarkArticle } from '@pages/myBookmark/types/api'; -import { Card } from '@pinback/design-system/ui'; -import { useGetPageMeta } from '@shared/apis/queries'; -import React from 'react'; - -interface FetchCardProps { - article: BookmarkArticle; - onClick: () => void; - onOptionsClick?: (e: React.MouseEvent) => void; -} - -const FetchCard = ({ article, onClick, onOptionsClick }: FetchCardProps) => { - const { data: meta, isPending, isError: error } = useGetPageMeta(article.url); - - if (isPending) { - return ( -
- ); - } - - const displayTitle = !error && meta.title ? meta.title : '제목 없음'; - const displayImageUrl = !error ? meta.image : undefined; - - return ( - - ); -}; - -export default FetchCard; diff --git a/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx b/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx index da8c2a60..f17bb561 100644 --- a/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx +++ b/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx @@ -2,16 +2,14 @@ import { useGetBookmarkArticles, useGetBookmarkArticlesCount, useGetCategoryBookmarkArticlesCount, - useGetBookmarkUnreadArticles, useGetCategoryBookmarkArticles, } from '@pages/myBookmark/apis/queries'; import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll'; -import FetchCard from '@pages/myBookmark/components/fetchCard/FetchCard'; import NoArticles from '@pages/myBookmark/components/NoArticles/NoArticles'; import NoUnreadArticles from '@pages/myBookmark/components/noUnreadArticles/NoUnreadArticles'; import { MutableRefObject } from 'react'; -import { Badge } from '@pinback/design-system/ui'; +import { Badge, Card } from '@pinback/design-system/ui'; interface MyBookmarkContentProps { activeBadge: 'all' | 'notRead'; @@ -34,17 +32,13 @@ const MyBookmarkContent = ({ queryClient, scrollContainerRef, }: MyBookmarkContentProps) => { - const { - data: articlesData, - fetchNextPage: fetchNextArticles, - hasNextPage: hasNextArticles, - } = useGetBookmarkArticles(); + const readStatus = activeBadge === 'notRead' ? false : null; const { - data: unreadArticlesData, - fetchNextPage: fetchNextUnreadArticles, - hasNextPage: hasNextUnreadArticles, - } = useGetBookmarkUnreadArticles(); + data: bookmarkArticlesData, + fetchNextPage: fetchNextBookmarkArticles, + hasNextPage: hasNextBookmarkArticles, + } = useGetBookmarkArticles(readStatus); const { data: bookmarkCountData } = useGetBookmarkArticlesCount(); const { data: categoryCountData } = useGetCategoryBookmarkArticlesCount( categoryId @@ -56,7 +50,7 @@ const MyBookmarkContent = ({ hasNextPage: hasNextCategoryArticles, } = useGetCategoryBookmarkArticles( categoryId, - activeBadge === 'notRead' ? false : null + readStatus ); const categoryList = @@ -66,9 +60,7 @@ const MyBookmarkContent = ({ const articlesToDisplay = category ? categoryList - : activeBadge === 'all' - ? (articlesData?.pages.flatMap((page) => page.articles) ?? []) - : (unreadArticlesData?.pages.flatMap((page) => page.articles) ?? []); + : (bookmarkArticlesData?.pages.flatMap((page) => page.articles) ?? []); const totalArticle = categoryId ? categoryCountData?.totalArticleCount @@ -79,15 +71,11 @@ const MyBookmarkContent = ({ const hasNextPage = category ? hasNextCategoryArticles - : activeBadge === 'all' - ? hasNextArticles - : hasNextUnreadArticles; + : hasNextBookmarkArticles; const fetchNextPage = category ? fetchNextCategoryArticles - : activeBadge === 'all' - ? fetchNextArticles - : fetchNextUnreadArticles; + : fetchNextBookmarkArticles; const observerRef = useInfiniteScroll({ fetchNextPage, @@ -130,18 +118,21 @@ const MyBookmarkContent = ({ className="scrollbar-hide mt-[2.6rem] flex h-screen flex-wrap content-start gap-[1.6rem] overflow-y-auto scroll-smooth" > {articlesToDisplay.map((article) => ( - { window.open(article.url, '_blank'); updateToReadStatus(article.articleId, { onSuccess: () => { queryClient.invalidateQueries({ - queryKey: ['bookmarkReadArticles'], - }); - queryClient.invalidateQueries({ - queryKey: ['bookmarkUnreadArticles'], + queryKey: ['bookmarkArticles'], }); queryClient.invalidateQueries({ queryKey: ['bookmarkArticlesCount'], From fd4c8f630a9f0c99d85f61f659e794b3477a55ee Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 00:43:21 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20=EC=95=84=ED=8B=B0=ED=81=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=8B=9C=20=EB=82=98=EC=9D=98=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?query=20=EB=AC=B4=ED=9A=A8=ED=99=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/shared/components/cardEditModal/CardEditModal.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/client/src/shared/components/cardEditModal/CardEditModal.tsx b/apps/client/src/shared/components/cardEditModal/CardEditModal.tsx index d2e6de21..0013428f 100644 --- a/apps/client/src/shared/components/cardEditModal/CardEditModal.tsx +++ b/apps/client/src/shared/components/cardEditModal/CardEditModal.tsx @@ -115,10 +115,13 @@ export default function CardEditModal({ queryKey: ['remindArticles'], }); queryClient.invalidateQueries({ - queryKey: ['bookmarkReadArticles'], + queryKey: ['bookmarkArticles'], }); queryClient.invalidateQueries({ - queryKey: ['bookmarkUnreadArticles'], + queryKey: ['bookmarkArticlesCount'], + }); + queryClient.invalidateQueries({ + queryKey: ['categoryBookmarkArticlesCount'], }); queryClient.invalidateQueries({ queryKey: ['categoryBookmarkArticles'], From 94df40f60bb772dbe87a67c653943ac111b0a3fd Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 01:16:06 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EB=B3=84=20=EB=B6=81=EB=A7=88=ED=81=AC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20api=20v3=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/src/pages/myBookmark/apis/axios.ts | 19 +++++++------------ .../src/pages/myBookmark/apis/queries.ts | 7 +++++-- apps/client/src/pages/myBookmark/types/api.ts | 9 +++++++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/apps/client/src/pages/myBookmark/apis/axios.ts b/apps/client/src/pages/myBookmark/apis/axios.ts index 19358905..9c799bc1 100644 --- a/apps/client/src/pages/myBookmark/apis/axios.ts +++ b/apps/client/src/pages/myBookmark/apis/axios.ts @@ -29,18 +29,13 @@ export const getCategoryBookmarkArticles = async ( readStatus: boolean | null, page: number, size: number -) : Promise => { - if (readStatus === null) { - const { data } = await apiRequest.get( - `/api/v1/articles/category?categoryId=${categoryId}&page=${page}&size=${size}` - ); - return data.data; - } else { - const { data } = await apiRequest.get( - `/api/v1/articles/category?categoryId=${categoryId}&read-status=${readStatus}&page=${page}&size=${size}` - ); - return data.data; - } +): Promise => { + const readStatusQuery = + readStatus === null ? '' : `&read-status=${readStatus}`; + const { data } = await apiRequest.get( + `/api/v3/articles/category?category-id=${categoryId}&page=${page}&size=${size}${readStatusQuery}` + ); + return data.data; }; export const getCategoryBookmarkArticlesCount = async ( diff --git a/apps/client/src/pages/myBookmark/apis/queries.ts b/apps/client/src/pages/myBookmark/apis/queries.ts index 571b23e3..9b722659 100644 --- a/apps/client/src/pages/myBookmark/apis/queries.ts +++ b/apps/client/src/pages/myBookmark/apis/queries.ts @@ -9,7 +9,9 @@ import { getCategoryBookmarkArticles, getCategoryBookmarkArticlesCount, } from './axios'; -import { BookmarkArticlesResponse } from '../types/api'; +import { + CategoryBookmarkArticleResponse, +} from '../types/api'; export const useGetBookmarkArticles = (readStatus: boolean | null) => { return useSuspenseInfiniteQuery({ @@ -37,9 +39,10 @@ export const useGetCategoryBookmarkArticles = ( queryKey: ['categoryBookmarkArticles', readStatus, categoryId], queryFn: ({ pageParam = 0 }) => { if (!categoryId) - return Promise.resolve({ + return Promise.resolve({ totalArticleCount: 0, unreadArticleCount: 0, + categoryName: '', articles: [], }); return getCategoryBookmarkArticles( diff --git a/apps/client/src/pages/myBookmark/types/api.ts b/apps/client/src/pages/myBookmark/types/api.ts index fc8b9841..bd128829 100644 --- a/apps/client/src/pages/myBookmark/types/api.ts +++ b/apps/client/src/pages/myBookmark/types/api.ts @@ -12,7 +12,7 @@ export interface BookmarkArticle { memo: string | null; createdAt: string; isRead: boolean; - category: Category; + category?: Category; } // 북마크 조회(v3) @@ -22,7 +22,12 @@ export interface BookmarkArticlesResponse { articles: BookmarkArticle[]; } -export type CategoryBookmarkArticleResponse = BookmarkArticlesResponse; +export interface CategoryBookmarkArticleResponse { + totalArticleCount: number; + unreadArticleCount: number; + categoryName: string; + articles: BookmarkArticle[]; +} // 나의 북마크 카운트 조회 export interface BookmarkArticlesCountResponse { From 5e870d9ca38e7eca8ac8bf0247af0687439c6ac3 Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 01:16:35 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EB=B3=84=20=EB=B6=81=EB=A7=88=ED=81=AC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20v3=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../myBookmarkContent/MyBookmarkContent.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx b/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx index f17bb561..dab83741 100644 --- a/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx +++ b/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx @@ -68,6 +68,9 @@ const MyBookmarkContent = ({ const totalUnread = categoryId ? categoryCountData?.unreadArticleCount : bookmarkCountData?.unreadArticleCount; + const categoryNameFromResponse = categoryId + ? categoryArticlesData?.pages?.[0]?.categoryName || category || undefined + : undefined; const hasNextPage = category ? hasNextCategoryArticles @@ -124,8 +127,14 @@ const MyBookmarkContent = ({ title={article.title || '제목 없음'} imageUrl={article.thumbnailUrl || undefined} content={article.memo ?? undefined} - category={article.category.categoryName} - categoryColor={article.category.categoryColor} + category={ + categoryId + ? categoryNameFromResponse + : article.category?.categoryName + } + categoryColor={ + categoryId ? undefined : article.category?.categoryColor + } date={new Date(article.createdAt).toLocaleDateString('ko-KR')} onClick={() => { window.open(article.url, '_blank'); From ea57513f644933daa1161c1c6a87cb80893a1df5 Mon Sep 17 00:00:00 2001 From: constantly-dev Date: Thu, 26 Feb 2026 14:56:40 +0900 Subject: [PATCH 9/9] =?UTF-8?q?refactor:=20useMyBookmarkContentData?= =?UTF-8?q?=EB=A1=9C=20mybookmark=20data=20fetching=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EB=B0=8F=20=EC=83=81=ED=83=9C=20=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EC=83=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../myBookmarkContent/MyBookmarkContent.tsx | 94 +++++-------------- .../hooks/useMyBookmarkContentData.ts | 91 ++++++++++++++++++ 2 files changed, 114 insertions(+), 71 deletions(-) create mode 100644 apps/client/src/pages/myBookmark/hooks/useMyBookmarkContentData.ts diff --git a/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx b/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx index dab83741..64c752da 100644 --- a/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx +++ b/apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx @@ -1,11 +1,4 @@ -import { - useGetBookmarkArticles, - useGetBookmarkArticlesCount, - useGetCategoryBookmarkArticlesCount, - useGetCategoryBookmarkArticles, -} from '@pages/myBookmark/apis/queries'; - -import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll'; +import { useMyBookmarkContentData } from '@pages/myBookmark/hooks/useMyBookmarkContentData'; import NoArticles from '@pages/myBookmark/components/NoArticles/NoArticles'; import NoUnreadArticles from '@pages/myBookmark/components/noUnreadArticles/NoUnreadArticles'; import { MutableRefObject } from 'react'; @@ -32,67 +25,23 @@ const MyBookmarkContent = ({ queryClient, scrollContainerRef, }: MyBookmarkContentProps) => { - const readStatus = activeBadge === 'notRead' ? false : null; - - const { - data: bookmarkArticlesData, - fetchNextPage: fetchNextBookmarkArticles, - hasNextPage: hasNextBookmarkArticles, - } = useGetBookmarkArticles(readStatus); - const { data: bookmarkCountData } = useGetBookmarkArticlesCount(); - const { data: categoryCountData } = useGetCategoryBookmarkArticlesCount( - categoryId - ); - const { - data: categoryArticlesData, - fetchNextPage: fetchNextCategoryArticles, - hasNextPage: hasNextCategoryArticles, - } = useGetCategoryBookmarkArticles( + view, + list, + counts, + pagination, + } = useMyBookmarkContentData({ + activeBadge, + category, categoryId, - readStatus - ); - - const categoryList = - categoryId && categoryArticlesData?.pages - ? categoryArticlesData.pages.flatMap((page) => page.articles) - : []; - - const articlesToDisplay = category - ? categoryList - : (bookmarkArticlesData?.pages.flatMap((page) => page.articles) ?? []); - - const totalArticle = categoryId - ? categoryCountData?.totalArticleCount - : bookmarkCountData?.totalArticleCount; - const totalUnread = categoryId - ? categoryCountData?.unreadArticleCount - : bookmarkCountData?.unreadArticleCount; - const categoryNameFromResponse = categoryId - ? categoryArticlesData?.pages?.[0]?.categoryName || category || undefined - : undefined; - - const hasNextPage = category - ? hasNextCategoryArticles - : hasNextBookmarkArticles; - - const fetchNextPage = category - ? fetchNextCategoryArticles - : fetchNextBookmarkArticles; - - const observerRef = useInfiniteScroll({ - fetchNextPage, - hasNextPage, - root: scrollContainerRef, + scrollContainerRef, }); + const totalCount = counts.total ?? 0; /** Empty 상태 컴포넌트 */ const EmptyStateComponent = () => { - if (articlesToDisplay.length === 0) { - const totalCount = categoryId - ? categoryCountData?.totalArticleCount - : bookmarkCountData?.totalArticleCount; - if ((totalCount ?? 0) === 0) return ; + if (list.articles.length === 0) { + if (totalCount === 0) return ; return ; } return null; @@ -103,24 +52,24 @@ const MyBookmarkContent = ({
onBadgeChange('all')} isActive={activeBadge === 'all'} /> onBadgeChange('notRead')} isActive={activeBadge === 'notRead'} />
- {articlesToDisplay.length > 0 ? ( + {list.articles.length > 0 ? (
- {articlesToDisplay.map((article) => ( + {list.articles.map((article) => ( { @@ -166,7 +115,10 @@ const MyBookmarkContent = ({ /> ))} -
+
) : ( diff --git a/apps/client/src/pages/myBookmark/hooks/useMyBookmarkContentData.ts b/apps/client/src/pages/myBookmark/hooks/useMyBookmarkContentData.ts new file mode 100644 index 00000000..d81ecd28 --- /dev/null +++ b/apps/client/src/pages/myBookmark/hooks/useMyBookmarkContentData.ts @@ -0,0 +1,91 @@ +import { + useGetBookmarkArticles, + useGetBookmarkArticlesCount, + useGetCategoryBookmarkArticles, + useGetCategoryBookmarkArticlesCount, +} from '@pages/myBookmark/apis/queries'; +import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll'; +import { MutableRefObject } from 'react'; + +interface UseMyBookmarkContentDataParams { + activeBadge: 'all' | 'notRead'; + category: string | null; + categoryId: string | null; + scrollContainerRef: MutableRefObject; +} + +export const useMyBookmarkContentData = ({ + activeBadge, + category, + categoryId, + scrollContainerRef, +}: UseMyBookmarkContentDataParams) => { + const readStatus = activeBadge === 'notRead' ? false : null; + const isCategoryView = !!categoryId; + + const { + data: bookmarkArticlesData, + fetchNextPage: fetchNextBookmarkArticles, + hasNextPage: hasNextBookmarkArticles, + } = useGetBookmarkArticles(readStatus); + const { data: bookmarkCountData } = useGetBookmarkArticlesCount(); + const { data: categoryCountData } = useGetCategoryBookmarkArticlesCount( + categoryId + ); + + const { + data: categoryArticlesData, + fetchNextPage: fetchNextCategoryArticles, + hasNextPage: hasNextCategoryArticles, + } = useGetCategoryBookmarkArticles(categoryId, readStatus); + + const categoryList = + isCategoryView && categoryArticlesData?.pages + ? categoryArticlesData.pages.flatMap((page) => page.articles) + : []; + + const articlesToDisplay = isCategoryView + ? categoryList + : (bookmarkArticlesData?.pages.flatMap((page) => page.articles) ?? []); + + const totalArticle = isCategoryView + ? categoryCountData?.totalArticleCount + : bookmarkCountData?.totalArticleCount; + const totalUnread = isCategoryView + ? categoryCountData?.unreadArticleCount + : bookmarkCountData?.unreadArticleCount; + + const categoryNameFromResponse = isCategoryView + ? categoryArticlesData?.pages?.[0]?.categoryName || category || undefined + : undefined; + + const hasNextPage = isCategoryView + ? hasNextCategoryArticles + : hasNextBookmarkArticles; + const fetchNextPage = isCategoryView + ? fetchNextCategoryArticles + : fetchNextBookmarkArticles; + + const sentinelRef = useInfiniteScroll({ + fetchNextPage, + hasNextPage, + root: scrollContainerRef, + }); + + return { + view: { + isCategoryView, + categoryName: categoryNameFromResponse, + }, + list: { + articles: articlesToDisplay, + }, + counts: { + total: totalArticle, + unread: totalUnread, + }, + pagination: { + sentinelRef, + }, + }; +};