Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. Walkthrough클라이언트의 북마크·리마인드 페이지와 카드 컴포넌트에서 로딩/모달 가드, 카테고리 기반 데이터 선택, 옵션 클릭 전파 차단, 페이지네이션 시작 인자(1→0) 및 CardEditModal의 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as 사용자
participant MB as MyBookmark 페이지
participant RM as Remind 페이지
participant API as Query 훅들
participant CARD as Card 컴포넌트
participant MOD as CardEditModal
rect rgba(200,230,255,0.25)
U->>MB: 페이지 진입
MB->>API: useGetBookmarkArticles(), useGetCategoryBookmarkArticles(..., offset=0)
API-->>MB: { data, isPending }
alt isPending == true
MB-->>U: Loading...
else
MB-->>U: articlesToDisplay 렌더 (카테고리 우선)
end
end
rect rgba(255,240,200,0.25)
U->>RM: 페이지 진입
RM->>API: useGetRemindArticles(..., offset=0)
API-->>RM: { data }
RM-->>U: 리스트 렌더
end
rect rgba(220,255,220,0.25)
U->>CARD: 옵션 버튼 클릭
CARD-->>CARD: e.stopPropagation()
CARD->>MB: onOptionsClick(event, categoryId)
MB->>MOD: isEditOpen && articleDetail → 모달 오픈
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/client/src/pages/remind/Remind.tsx (1)
51-60: 메뉴 닫기 호출 오타: close() → closeMenu()성공 후 윈도우 close가 시도될 수 있습니다. 실제 닫아야 하는 것은 메뉴입니다.
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['remindArticles'] }); - close(); + closeMenu(); },apps/client/src/pages/myBookmark/MyBookmark.tsx (1)
61-79: 메뉴 닫기 호출 오타: close() → closeMenu()삭제 성공 후 메뉴만 닫아야 합니다.
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['bookmarkReadArticles'], }); queryClient.invalidateQueries({ queryKey: ['bookmarkUnreadArticles'], }); queryClient.invalidateQueries({ queryKey: ['categoryBookmarkArticles'], }); - close(); + closeMenu(); },apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (1)
79-93: 저장 전 리마인드 날짜/시간 검증 추가 및 불필요 옵셔널 체이닝 제거파일: apps/client/src/shared/components/cardEditModal/CardEditModal.tsx — saveData (Lines 79–121)
- isRemindOn일 때 date/time 존재 및 dateError/timeError 검증 추가 — 실패 시 토스트 열고 저장 중단.
- prevData는 Props에서 non-null이므로 prevData?.id → prevData.id로 단순화.
- remindTime은 초(HH:mm:ss)까지 포함하도록 포맷 통일(예: "HH:mm"이면 ":00" 추가).
const saveData = () => { - if (!prevData?.id) { + if (!prevData.id) { console.error('Article ID is missing, cannot save.'); setToastIsOpen(true); return; } + // 리마인드 사용 시 입력 검증 + if (isRemindOn) { + if (dateError || timeError || !date || !time) { + setToastIsOpen(true); + return; + } + } + const editArticleData: EditArticleRequest = { memo, categoryId: category?.categories.find((cat) => cat.name === selectedCategory)?.id || -1, now: new Date().toISOString(), - remindTime: isRemindOn ? `${date}T${time}` : null, + remindTime: isRemindOn + ? `${date}T${time.length === 5 ? `${time}:00` : time}` + : null, };참고: categoryId에 -1을 전송하는 것이 서버 계약과 맞는지 확인하세요. 불일치하면 저장을 차단하고 안내 토스트를 띄우는 것이 안전합니다.
🧹 Nitpick comments (9)
packages/design-system/src/components/card/MyBookmarkCard.tsx (1)
45-48: 카드 클릭 전파 중단 LGTM + 메뉴 a11y 속성 추가 제안현재 동작은 올바릅니다. 메뉴 트리거임을 명시하려면 aria-haspopup="menu"를 추가하세요. 선택적으로 onMouseDown에서도 전파 중단을 고려할 수 있습니다.
적용 예시:
<button type="button" aria-label="카테고리 상세" + aria-haspopup="menu" className="cursor-pointer self-start" onClick={(e) => { e.stopPropagation(); onOptionsClick?.(e); }} >packages/design-system/src/components/card/RemindCard.tsx (3)
20-21: timeRemaining 타입/렌더 불일치 정리 필요프롭은 필수(timeRemaining: string)인데 UI는 ‘-’만 표기합니다. 프롭을 optional로 바꾸고 fallback 렌더링을 하거나, 값을 그대로 표시하세요.
-interface RemindCardProps { +interface RemindCardProps { title: string; content?: string; category?: string; imageUrl?: string; - timeRemaining: string; + timeRemaining?: string; onClick?: () => void; onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void; } @@ - <span className="body2-m text-main400 mr-[0.2rem]"> - {/* {timeRemaining || '-'} */}- - </span> + <span className="body2-m text-main400 mr-[0.2rem]"> + {timeRemaining ?? '-'} + </span>Also applies to: 29-29
53-56: 옵션 버튼 전파 중단 LGTM + a11y 속성 추가동작은 적절합니다. 메뉴 트리거를 명시하려면 aria-haspopup="menu"를 추가하세요.
<button type="button" aria-label="카테고리 상세" + aria-haspopup="menu" className="cursor-pointer self-start" onClick={(e) => { e.stopPropagation(); onOptionsClick?.(e); }}
65-67: category 미지정 시 'undefined' 노출 가능category가 optional인데 무조건 렌더링합니다. 조건부 렌더로 안전하게 처리하세요.
- <span className="bg-category-red-bg caption2-sb text-category-red-text h-[2.2rem] w-[6.2rem] rounded-[0.4rem] px-[0.8rem] py-[0.2rem]"> - {category} - </span> + {category && ( + <span className="bg-category-red-bg caption2-sb text-category-red-text h-[2.2rem] w-[6.2rem] rounded-[0.4rem] px-[0.8rem] py-[0.2rem]"> + {category} + </span> + )}apps/client/src/pages/remind/Remind.tsx (2)
23-25: nowDate 메모이제이션 의존성 재검토activeBadge 변경과 무관하게 고정 now가 필요하면 의존성을 제거하세요. 현재 패턴은 배지 전환 때만 now가 갱신됩니다.
- const formattedDate = useMemo(() => { - return formatLocalDateTime(); - }, [activeBadge]); + const formattedDate = useMemo(() => formatLocalDateTime(), []);
75-78: 로딩 UI 제거됨 — 깜빡임 방지용 스켈레톤/플레이스홀더 권장데이터 없을 때 빈 상태 컴포넌트가 먼저 노출될 수 있습니다.
apps/client/src/pages/myBookmark/MyBookmark.tsx (3)
95-99: 로딩 UI 추가 LGTM — Remind 페이지와 일관성 유지 권장두 페이지 모두 스켈레톤으로 통일하면 좋습니다.
121-125: 배지 카운트 기본값 누락(전체)로딩 시 undefined가 찍힐 수 있습니다. null 병합으로 0 디폴트를 주세요.
<Badge text="전체보기" - countNum={ - category - ? categoryArticles?.totalArticle - : articles?.totalArticle || 0 - } + countNum={ + (category ? categoryArticles?.totalArticle : articles?.totalArticle) ?? 0 + }
131-135: 배지 카운트 기본값 누락(안 읽음)동일하게 null 병합 처리 권장.
<Badge text="안 읽음" - countNum={ - category - ? categoryArticles?.totalUnreadArticle - : articles?.totalUnreadArticle || 0 - } + countNum={ + (category ? categoryArticles?.totalUnreadArticle : articles?.totalUnreadArticle) ?? 0 + }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/client/src/pages/myBookmark/MyBookmark.tsx(5 hunks)apps/client/src/pages/remind/Remind.tsx(5 hunks)apps/client/src/shared/components/cardEditModal/CardEditModal.tsx(1 hunks)packages/design-system/src/components/card/MyBookmarkCard.tsx(1 hunks)packages/design-system/src/components/card/RemindCard.tsx(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🧬 Code graph analysis (3)
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (2)
apps/client/src/shared/types/api.ts (1)
ArticleDetailResponse(34-41)apps/client/src/shared/hooks/usePageMeta.ts (1)
usePageMeta(11-55)
apps/client/src/pages/remind/Remind.tsx (2)
apps/client/src/shared/utils/formatDateTime.ts (1)
formatLocalDateTime(1-9)apps/client/src/pages/remind/apis/queries.ts (1)
useGetRemindArticles(6-16)
apps/client/src/pages/myBookmark/MyBookmark.tsx (1)
apps/client/src/pages/myBookmark/apis/queries.ts (3)
useGetBookmarkArticles(14-22)useGetBookmarkUnreadArticles(24-32)useGetCategoryBookmarkArticles(34-46)
🔇 Additional comments (7)
apps/client/src/pages/remind/Remind.tsx (2)
31-36: 페이지 인덱스 0 기반 변경 LGTMAPI 파라미터와 일치합니다.
150-163: 모달 게이트 강화 LGTMisEditOpen && articleDetail 조건으로 prevData 필수화와 합치됩니다.
apps/client/src/pages/myBookmark/MyBookmark.tsx (4)
35-35: 로딩 상태 플래그 도입 LGTM
37-42: 카테고리 API 페이지 0 기반 변경 LGTM
55-60: 표시 데이터 소스 분기 로직 LGTM카테고리 선택 시 categoryArticles 우선 사용으로 UX 개선되었습니다.
200-213: 모달 게이트 강화 LGTMprevData 필수화와 합치됩니다.
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (1)
28-35: prevData 필수화 방향 LGTM — 호출부 점검 완료(누락 없음)apps/client/src/pages/remind/Remind.tsx:157, apps/client/src/pages/myBookmark/MyBookmark.tsx:207에서 prevData 전달 확인 (총 2곳, 누락 0).
| const { data: categoryArticles } = useGetCategoryBookmarkArticles( | ||
| categoryId, | ||
| activeBadge === 'all', | ||
| 1, | ||
| 0, | ||
| 10 | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
카테고리별 읽음 필터 캐시 키 누락 가능성 안내(별도 파일 패치)
useGetCategoryBookmarkArticles는 readStatus로 결과가 달라지지만 queryKey에 포함되어 있지 않아 배지 전환 시 캐시 재조회가 되지 않을 수 있습니다.
아래 패치를 별도 파일에 적용해 주세요.
파일: apps/client/src/pages/myBookmark/apis/queries.ts
export const useGetCategoryBookmarkArticles = (
categoryId: string | null,
readStatus: boolean,
page: number,
size: number
): UseQueryResult<CategoryBookmarkArticleResponse, AxiosError> => {
return useQuery({
- queryKey: ['categoryBookmarkArticles', categoryId, page, size],
+ queryKey: ['categoryBookmarkArticles', categoryId, readStatus, page, size],
queryFn: () =>
getCategoryBookmarkArticles(categoryId, readStatus, page, size),
enabled: !!categoryId,
});
};검증 스크립트(키에 readStatus 누락된 다른 훅도 있는지 확인):
🏁 Script executed:
#!/bin/bash
# categoryBookmarkArticles 관련 queryKey에서 readStatus 포함 여부 점검
rg -nP "queryKey:\s*\[\s*'categoryBookmarkArticles'[^]]*\]" apps/client -C2Length of output: 1836
쿼리 키에 readStatus 누락 — 수정 필요
useGetCategoryBookmarkArticles의 queryKey에 readStatus가 빠져 있어 배지(읽음/전체) 전환 시 캐시가 재조회되지 않습니다. 아래 패치를 apps/client/src/pages/myBookmark/apis/queries.ts에 적용하세요.
파일: apps/client/src/pages/myBookmark/apis/queries.ts
export const useGetCategoryBookmarkArticles = (
categoryId: string | null,
readStatus: boolean,
page: number,
size: number
): UseQueryResult<CategoryBookmarkArticleResponse, AxiosError> => {
return useQuery({
- queryKey: ['categoryBookmarkArticles', categoryId, page, size],
+ queryKey: ['categoryBookmarkArticles', categoryId, readStatus, page, size],
queryFn: () =>
getCategoryBookmarkArticles(categoryId, readStatus, page, size),
enabled: !!categoryId,
});
};검증 결과:
- 누락 위치: apps/client/src/pages/myBookmark/apis/queries.ts (queryKey — 줄 41).
- invalidateQueries 호출 위치(참고): apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (줄 112), apps/client/src/pages/myBookmark/MyBookmark.tsx (줄 71, 164). 이 호출들은 상위 키로 광범위 무효화하므로 우선 queries.ts 수정만으로 문제 해결됨. 필요하면 invalidateQueries에 readStatus를 포함해 더 좁게 무효화하세요.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { data: categoryArticles } = useGetCategoryBookmarkArticles( | |
| categoryId, | |
| activeBadge === 'all', | |
| 1, | |
| 0, | |
| 10 | |
| ); | |
| export const useGetCategoryBookmarkArticles = ( | |
| categoryId: string | null, | |
| readStatus: boolean, | |
| page: number, | |
| size: number | |
| ): UseQueryResult<CategoryBookmarkArticleResponse, AxiosError> => { | |
| return useQuery({ | |
| queryKey: ['categoryBookmarkArticles', categoryId, readStatus, page, size], | |
| queryFn: () => | |
| getCategoryBookmarkArticles(categoryId, readStatus, page, size), | |
| enabled: !!categoryId, | |
| }); | |
| }; |
🤖 Prompt for AI Agents
In apps/client/src/pages/myBookmark/apis/queries.ts around line 41, the queryKey
for useGetCategoryBookmarkArticles is missing the readStatus value so toggling
the read/all badge doesn't change the cache key; include the readStatus
parameter as an element in the queryKey array (alongside categoryId, offset,
limit) so the key differs when readStatus changes, leaving the rest of the hook
logic intact and then verify badge switches trigger a refetch.
| onOptionsClick={(e) => { | ||
| e.stopPropagation(); | ||
| openMenu(article.category.categoryId, e.currentTarget); | ||
| }} |
There was a problem hiding this comment.
🧩 Analysis chain
잘못된 ID 전달: categoryId를 articleId로 교체
openMenu에 categoryId를 넘기고 이후 onEdit/onDelete에서 같은 값을 articleId로 취급합니다. 삭제/수정이 오동작합니다.
- onOptionsClick={(e) => {
- e.stopPropagation();
- openMenu(article.category.categoryId, e.currentTarget);
- }}
+ onOptionsClick={(e) => {
+ e.stopPropagation();
+ openMenu(article.articleId, e.currentTarget);
+ }}후속으로 OptionsMenuPortal의 prop 명(categoryId)을 의미에 맞게 articleId(또는 entityId)로 리네이밍하는 것을 권장합니다.
🏁 Script executed:
#!/bin/bash
# openMenu 호출 인자 일관성 점검(가능한 articleId 사용 여부 확인)
rg -nP 'openMenu\(([^,]+),' -C2Length of output: 1302
openMenu에 articleId 전달로 수정 필요 (apps/client/src/pages/remind/Remind.tsx:124)
Remind.tsx에서 openMenu에 article.category.categoryId를 전달하고 있어 onEdit/onDelete가 articleId로 처리될 때 오동작합니다. MyBookmark.tsx와 일관되게 article.articleId를 전달하도록 변경하세요.
- onOptionsClick={(e) => {
- e.stopPropagation();
- openMenu(article.category.categoryId, e.currentTarget);
- }}
+ onOptionsClick={(e) => {
+ e.stopPropagation();
+ openMenu(article.articleId, e.currentTarget);
+ }}OptionsMenuPortal의 prop명(categoryId)을 의미에 맞게 articleId 또는 entityId로 리네이밍 권장.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onOptionsClick={(e) => { | |
| e.stopPropagation(); | |
| openMenu(article.category.categoryId, e.currentTarget); | |
| }} | |
| onOptionsClick={(e) => { | |
| e.stopPropagation(); | |
| openMenu(article.articleId, e.currentTarget); | |
| }} |
🤖 Prompt for AI Agents
In apps/client/src/pages/remind/Remind.tsx around lines 122 to 125, the
onOptionsClick currently passes article.category.categoryId to openMenu which
causes onEdit/onDelete to receive the wrong id; change it to pass
article.articleId (matching MyBookmark.tsx) so handlers operate on the article
id. Also update the OptionsMenuPortal prop name (currently categoryId) to a
clearer name such as articleId or entityId and propagate that rename through
openMenu and any downstream handlers to keep semantics consistent.
📌 Related Issues
📄 Tasks
sp1 1차 QA 반영 (대시보드-기능 관련)
Summary by CodeRabbit
New Features
Bug Fixes
Refactor