Feat(Client): remind aticle list 수정#284
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough리마인드 페이지가 내부 FetchCard를 제거하고 디자인 시스템의 Card로 전환되었고, 리마인드 노출을 remindAt 기준으로 발생 후 24시간 이내로 필터링하는 로직이 추가되었습니다. API 엔드포인트(v3), 타입(articleId/thumbnailUrl/categoryColor 등), 쿼리 키( Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant Browser
participant RemindPage as Remind::CardList
participant QueryClient as ReactQuery
participant API as API:/api/v3
User->>Browser: 카드 클릭
Browser->>RemindPage: onCardClick(articleId, url)
RemindPage->>QueryClient: mutate putArticleReadStatus(articleId)
QueryClient->>API: PUT /api/.../read-status (articleId)
API-->>QueryClient: 200 OK
QueryClient->>QueryClient: invalidateQueries(['remindArticles'])
QueryClient->>QueryClient: invalidateQueries(['acorns'])
RemindPage->>Browser: window.open(url)
Browser->>User: 새 탭에 아티클 열림
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 4❌ Failed checks (1 warning, 3 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/client/src/pages/remind/Remind.tsx (1)
89-90:⚠️ Potential issue | 🟠 Major
REMIND_MOCK_DATA를 사용하여 제목을 조회하는 로직이 실제 데이터와 연결되지 않습니다.
getItemTitle함수가REMIND_MOCK_DATA에서 제목을 찾고 있지만, 실제 API에서 받아온articlesToDisplay의 article ID와 mock 데이터의 ID가 일치하지 않을 가능성이 높습니다. 이로 인해OptionsMenuPortal의getCategoryNameprop이 항상 빈 문자열을 반환할 수 있습니다.🐛 실제 아티클 데이터를 사용하도록 수정
- const getItemTitle = (id: number | null) => - id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? ''); + const getItemTitle = (id: number | null) => + id == null + ? '' + : (articlesToDisplay.find((a) => a.articleId === id)?.title || '제목 없음');또한
REMIND_MOCK_DATAimport가 더 이상 필요하지 않다면 제거하세요:-import { REMIND_MOCK_DATA } from './constants';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/client/src/pages/remind/Remind.tsx` around lines 89 - 90, getItemTitle currently looks up titles from REMIND_MOCK_DATA which can mismatch real articles; change getItemTitle to search the actual articlesToDisplay array (e.g., find an item where item.id === id and return its title or '') so OptionsMenuPortal's getCategoryName receives real data; after switching, remove the REMIND_MOCK_DATA import if it's no longer used.
🧹 Nitpick comments (2)
apps/client/src/pages/remind/Remind.tsx (1)
16-16:REMIND_MOCK_DATAimport는 실제 데이터 사용 시 제거 대상입니다.앞서 언급한
getItemTitle수정 후 이 import가 불필요해집니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/client/src/pages/remind/Remind.tsx` at line 16, The import REMIND_MOCK_DATA in Remind.tsx is now unused after the getItemTitle change; remove the import statement for REMIND_MOCK_DATA from the top of Remind.tsx to avoid an unnecessary dependency and unused symbol.apps/client/src/pages/remind/apis/axios.ts (1)
3-19: LGTM - API 버전 업그레이드 완료v2에서 v3로의 엔드포인트 변경이 적절합니다.
선택적 개선: 반환 타입을 명시하면 타입 안전성이 향상됩니다.
♻️ 타입 안전성을 위한 제안
+import { ArticleListResponse } from '../types/api'; + export const getRemindArticles = async ( nowDate: string, readStatus: boolean, page: number, size: number -) => { +): Promise<ArticleListResponse> => { const { data } = await apiRequest.get(`/api/v3/articles/remind`, {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/client/src/pages/remind/apis/axios.ts` around lines 3 - 19, The getRemindArticles function lacks an explicit return type which reduces type safety; update its signature to declare and return a proper typed promise (e.g., Promise<YourResponseType[]> or a paged response type) matching the shape returned by apiRequest.get, define or import the corresponding interface/type (e.g., RemindArticle, RemindArticlesResponse) and use it in the function signature and when extracting data so callers get full type information; refer to getRemindArticles and the response payload accessed via data.data to model the return type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@apps/client/src/pages/remind/Remind.tsx`:
- Around line 89-90: getItemTitle currently looks up titles from
REMIND_MOCK_DATA which can mismatch real articles; change getItemTitle to search
the actual articlesToDisplay array (e.g., find an item where item.id === id and
return its title or '') so OptionsMenuPortal's getCategoryName receives real
data; after switching, remove the REMIND_MOCK_DATA import if it's no longer
used.
---
Nitpick comments:
In `@apps/client/src/pages/remind/apis/axios.ts`:
- Around line 3-19: The getRemindArticles function lacks an explicit return type
which reduces type safety; update its signature to declare and return a proper
typed promise (e.g., Promise<YourResponseType[]> or a paged response type)
matching the shape returned by apiRequest.get, define or import the
corresponding interface/type (e.g., RemindArticle, RemindArticlesResponse) and
use it in the function signature and when extracting data so callers get full
type information; refer to getRemindArticles and the response payload accessed
via data.data to model the return type.
In `@apps/client/src/pages/remind/Remind.tsx`:
- Line 16: The import REMIND_MOCK_DATA in Remind.tsx is now unused after the
getItemTitle change; remove the import statement for REMIND_MOCK_DATA from the
top of Remind.tsx to avoid an unnecessary dependency and unused symbol.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/client/src/pages/remind/Remind.tsxapps/client/src/pages/remind/apis/axios.tsapps/client/src/pages/remind/components/fetchCard/FetchCard.tsxapps/client/src/pages/remind/types/api.ts
💤 Files with no reviewable changes (1)
- apps/client/src/pages/remind/components/fetchCard/FetchCard.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/client/src/shared/apis/axios.ts (1)
51-54: 개발 주석 제거 필요
// 이거 업데이트주석은 개발 중 메모로 보이며, 머지 전에 제거하는 것이 좋습니다.🧹 주석 제거 제안
-// 이거 업데이트 export const getArticleDetail = async (articleId: number) => { const { data } = await apiRequest.get(`/api/v3/articles/${articleId}`); return data.data; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/client/src/shared/apis/axios.ts` around lines 51 - 54, Remove the leftover development comment "// 이거 업데이트" above the getArticleDetail function; keep the function signature and implementation (export const getArticleDetail = async (articleId: number) => { ... }) unchanged and ensure no other stray dev comments remain in this file.apps/client/src/pages/remind/Remind.tsx (1)
183-191: 읽음 상태 업데이트 에러 핸들링 개선 고려현재
onError에서console.error만 호출하고 있습니다. 사용자에게 피드백을 주기 위한 토스트 메시지 추가를 고려해 보세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/client/src/pages/remind/Remind.tsx` around lines 183 - 191, The onError handler for the mutation that updates read status only logs to console; add user-visible feedback by invoking your app's toast/notification utility (e.g., useToast or showToast) inside the onError callback of the mutation that surrounds the queryClient.invalidateQueries calls, passing a short message like "읽음 상태 업데이트에 실패했습니다" and include the error message for debugging; ensure the toast hook/import is present in Remind.tsx and keep the existing queryClient.invalidateQueries usage in onSuccess unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/client/src/pages/remind/Remind.tsx`:
- Around line 95-101: The query key string 'arcons' is a typo and must be
corrected to 'acorns' to match the existing query definitions and types
(useGetArcons, AcornsResponse, getAcorns()); update every occurrence where the
key is used (e.g., the two queryClient.invalidateQueries calls in Remind.tsx and
the other listed places) so the queryKey arrays use ['acorns'] instead of
['arcons'], ensuring consistency with the useGetArcons/query definition and any
related cache invalidation logic.
---
Nitpick comments:
In `@apps/client/src/pages/remind/Remind.tsx`:
- Around line 183-191: The onError handler for the mutation that updates read
status only logs to console; add user-visible feedback by invoking your app's
toast/notification utility (e.g., useToast or showToast) inside the onError
callback of the mutation that surrounds the queryClient.invalidateQueries calls,
passing a short message like "읽음 상태 업데이트에 실패했습니다" and include the error message
for debugging; ensure the toast hook/import is present in Remind.tsx and keep
the existing queryClient.invalidateQueries usage in onSuccess unchanged.
In `@apps/client/src/shared/apis/axios.ts`:
- Around line 51-54: Remove the leftover development comment "// 이거 업데이트" above
the getArticleDetail function; keep the function signature and implementation
(export const getArticleDetail = async (articleId: number) => { ... }) unchanged
and ensure no other stray dev comments remain in this file.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/client/src/pages/myBookmark/MyBookmark.tsxapps/client/src/pages/remind/Remind.tsxapps/client/src/shared/apis/axios.tsapps/client/src/shared/components/cardEditModal/CardEditModal.tsxapps/client/src/shared/types/api.ts
| queryClient.invalidateQueries({ | ||
| queryKey: ['remindArticles'], | ||
| }); | ||
|
|
||
| queryClient.invalidateQueries({ | ||
| queryKey: ['arcons'], | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for all usages of 'arcons' and 'acorns' query keys
echo "=== All 'arcons' usages ==="
rg -n "arcons" --type ts --type tsx
echo ""
echo "=== All 'acorns' usages ==="
rg -n "acorns" --type ts --type tsx
echo ""
echo "=== Checking MyBookmark.tsx specifically ==="
rg -n "queryKey" apps/client/src/pages/MyBookmark.tsx 2>/dev/null || rg -n "queryKey" --type tsx | grep -i bookmarkRepository: Pinback-Team/pinback-client
Length of output: 274
🏁 Script executed:
#!/bin/bash
# Search without file type restrictions
echo "=== All 'arcons' usages ==="
rg "arcons" --line-number
echo ""
echo "=== All 'acorns' usages ==="
rg "acorns" --line-number
echo ""
echo "=== Check if files exist ==="
find apps/client/src/pages -name "Remind.tsx" -o -name "MyBookmark.tsx"
echo ""
echo "=== Content of Remind.tsx around lines 95-101 ==="
sed -n '90,105p' apps/client/src/pages/remind/Remind.tsx 2>/dev/null || echo "File not found at that path"
echo ""
echo "=== Check for queryKey 'arcons' or 'acorns' in MyBookmark ==="
find . -name "MyBookmark*" -type f | head -5Repository: Pinback-Team/pinback-client
Length of output: 2574
🏁 Script executed:
#!/bin/bash
# Check the query definition for 'arcons'
echo "=== Content of queries.ts around line 67 ==="
sed -n '60,75p' apps/client/src/shared/apis/queries.ts
echo ""
echo "=== Full context of 'arcons' usage in queries.ts ==="
rg -B5 -A5 "'arcons'" apps/client/src/shared/apis/queries.tsRepository: Pinback-Team/pinback-client
Length of output: 787
쿼리 키 'arcons' 오타 수정 필요
'arcons'은 'acorns'의 오타입니다. 쿼리 정의(useGetArcons), 반환 타입(AcornsResponse), 실제 함수 호출(getAcorns())은 모두 'acorns'를 사용하고 있어, 쿼리 키도 일관되게 'acorns'로 통일해야 합니다. 현재 다음 5개 위치에서 잘못된 쿼리 키를 사용하고 있습니다:
apps/client/src/shared/apis/queries.ts(쿼리 정의)apps/client/src/pages/remind/Remind.tsx(2개 위치)apps/client/src/pages/myBookmark/MyBookmark.tsxapps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx
✏️ 쿼리 키 수정 제안
queryClient.invalidateQueries({
- queryKey: ['arcons'],
+ queryKey: ['acorns'],
});📝 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.
| queryClient.invalidateQueries({ | |
| queryKey: ['remindArticles'], | |
| }); | |
| queryClient.invalidateQueries({ | |
| queryKey: ['arcons'], | |
| }); | |
| queryClient.invalidateQueries({ | |
| queryKey: ['acorns'], | |
| }); | |
| queryClient.invalidateQueries({ | |
| queryKey: ['acorns'], | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/client/src/pages/remind/Remind.tsx` around lines 95 - 101, The query key
string 'arcons' is a typo and must be corrected to 'acorns' to match the
existing query definitions and types (useGetArcons, AcornsResponse,
getAcorns()); update every occurrence where the key is used (e.g., the two
queryClient.invalidateQueries calls in Remind.tsx and the other listed places)
so the queryKey arrays use ['acorns'] instead of ['arcons'], ensuring
consistency with the useGetArcons/query definition and any related cache
invalidation logic.
There was a problem hiding this comment.
@constantly-dev 넵! 작은 오타지만 쿼리 캐싱에 영향을 줄 수 있어서 5개 위치 모두 수정해주시면 좋을 것 같아요 😊
수정이 필요한 위치들:
apps/client/src/shared/apis/queries.ts(쿼리 정의)apps/client/src/pages/remind/Remind.tsx(2곳)apps/client/src/pages/myBookmark/MyBookmark.tsxapps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx
🧠 Learnings used
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 5
File: apps/extension/src/index.css:1-1
Timestamp: 2025-08-19T17:18:57.678Z
Learning: constantly-dev는 디자인 시스템 설정 PR 머지 후 `import 'pinback/tailwind-config/shared-styles.css';`를 `app.css`나 `index.css`에서 사용하여 공유 스타일을 관리할 계획입니다.
constantly-dev
left a comment
There was a problem hiding this comment.
수고하셨어요! 간단한 코멘트만 확인해주세요 👍
| queryClient.invalidateQueries({ | ||
| queryKey: ['remindArticles'], | ||
| }); | ||
|
|
||
| queryClient.invalidateQueries({ | ||
| queryKey: ['arcons'], | ||
| }); |
| setIsDeleteOpen(false); | ||
|
|
||
| setDeleteTargetId(null); | ||
|
|
||
| closeMenu(); | ||
| close(); | ||
| }, | ||
|
|
| ? article.title | ||
| : '제목 없음'; | ||
|
|
||
| const displayImageUrl = article.thumbnailUrl || undefined; |
There was a problem hiding this comment.
이미지 없을때 fallback image 처리해야겠네요!
There was a problem hiding this comment.
card 컴포넌트에서 이미지 없을시 로고를 띄우고 있는걸로 아는데 따로 또 fallback 처리를 해야할까요?
| updateToReadStatus(article.articleId, { | ||
| onSuccess: () => { | ||
| queryClient.invalidateQueries({ | ||
| queryKey: ['remindArticles'], | ||
| }); | ||
|
|
||
| queryClient.invalidateQueries({ | ||
| queryKey: ['arcons'], | ||
| }); | ||
| }, | ||
|
|
||
| onError: (error) => { | ||
| console.error(error); | ||
| }, |
There was a problem hiding this comment.
onSuccess나 onError가 해당 컴포넌트 로직에서 섞이는 것이 가독성 측면에서 별로라면 updateToReadStatus 정의한 곳에서 추가해도 될 것 같아요! 판단에 맞게!
There was a problem hiding this comment.
확실히 onSuccess, onError를 옮기는게 가독성 측면에서 좋을것 같네요 👍
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/client/src/pages/remind/Remind.tsx (1)
82-83:⚠️ Potential issue | 🟠 Major
REMIND_MOCK_DATA사용은 프로덕션 코드에 적합하지 않습니다.
getItemTitle함수가 목 데이터(REMIND_MOCK_DATA)에서 제목을 조회하고 있습니다. 실제articlesToDisplay데이터에서 제목을 가져와야 합니다. 현재 로직은 삭제 확인 팝업에 잘못된 제목이 표시되거나 빈 문자열이 반환될 수 있습니다.🐛 실제 데이터에서 제목 조회하도록 수정
const getItemTitle = (id: number | null) => - id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? ''); + id == null ? '' : (articlesToDisplay.find((d) => d.articleId === id)?.title ?? '');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/client/src/pages/remind/Remind.tsx` around lines 82 - 83, The getItemTitle helper currently reads from REMIND_MOCK_DATA (production-inappropriate) so replace that lookup to use the real articles list (articlesToDisplay) instead: update getItemTitle (the function that takes id: number | null) to return '' for null/undefined ids and otherwise find the matching article via articlesToDisplay.find(a => a.id === id)?.title ?? '' so the delete-confirm popup shows the actual title from the live data; ensure articlesToDisplay is in scope (prop/state) or passed into getItemTitle if needed.
🧹 Nitpick comments (1)
apps/client/src/pages/remind/Remind.tsx (1)
72-80: 24시간 필터링 로직 최적화 제안
new Date().getTime()이filter콜백 내부에서 호출되어 매 아티클마다 새 Date 객체가 생성됩니다. 성능상 큰 영향은 없지만,now를 필터 외부에서 한 번만 계산하면 더 명확합니다.♻️ Date 객체 생성 최적화
+ const now = Date.now(); const articlesToDisplay = data?.pages .flatMap((page) => page.articles) .filter((article) => { - const now = new Date().getTime(); const remindTime = new Date(article.remindAt).getTime(); const expirationTime = remindTime + 24 * 60 * 60 * 1000; return now >= remindTime && now < expirationTime; }) ?? [];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/client/src/pages/remind/Remind.tsx` around lines 72 - 80, Compute "now" once before filtering instead of inside the filter callback: move the const now = new Date().getTime() out of the filter and reuse it when evaluating each article.remindAt in the articlesToDisplay calculation (the chain starting at data?.pages.flatMap and the filter that references article.remindAt and remindTime/expirationTime). This avoids creating a new Date per article and keeps the logic identical while improving clarity and minor performance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@apps/client/src/pages/remind/Remind.tsx`:
- Around line 82-83: The getItemTitle helper currently reads from
REMIND_MOCK_DATA (production-inappropriate) so replace that lookup to use the
real articles list (articlesToDisplay) instead: update getItemTitle (the
function that takes id: number | null) to return '' for null/undefined ids and
otherwise find the matching article via articlesToDisplay.find(a => a.id ===
id)?.title ?? '' so the delete-confirm popup shows the actual title from the
live data; ensure articlesToDisplay is in scope (prop/state) or passed into
getItemTitle if needed.
---
Nitpick comments:
In `@apps/client/src/pages/remind/Remind.tsx`:
- Around line 72-80: Compute "now" once before filtering instead of inside the
filter callback: move the const now = new Date().getTime() out of the filter and
reuse it when evaluating each article.remindAt in the articlesToDisplay
calculation (the chain starting at data?.pages.flatMap and the filter that
references article.remindAt and remindTime/expirationTime). This avoids creating
a new Date per article and keeps the logic identical while improving clarity and
minor performance.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/client/src/pages/remind/Remind.tsxapps/client/src/shared/apis/queries.ts
📌 Related Issues
📄 Tasks
⭐ PR Point (To Reviewer)
📷 Screenshot
Summary by CodeRabbit
새로운 기능
개선 사항
버그 픽스