Conversation
📝 Walkthrough요약프롬프트 "좋아요" 기능을 구현하는 종합적인 변경 사항입니다. 새로운 API 엔드포인트, React Query 훅, UI 컴포넌트 개선, 그리고 PromptCard에 로그인 기반 좋아요 플로우를 추가합니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
actor User
participant PromptCard
participant useAuth
participant useLikePrompt
participant API
participant QueryClient
User->>PromptCard: 좋아요 버튼 클릭
PromptCard->>useAuth: 로그인 상태 확인
alt 미로그인
useAuth-->>PromptCard: isLoggedIn = false
PromptCard->>PromptCard: 로그인 모달 열기
else 로그인됨
useAuth-->>PromptCard: isLoggedIn = true
PromptCard->>useLikePrompt: toggleLike(promptId, isLiked)
useLikePrompt->>API: likePrompt() 또는 unlikePrompt()
API-->>useLikePrompt: PromptLikeResponse
useLikePrompt->>QueryClient: prompts 캐시 업데이트
useLikePrompt->>QueryClient: prompts/me/likes 캐시 업데이트
QueryClient-->>PromptCard: UI 상태 동기화
PromptCard->>PromptCard: Card 컴포넌트 isLiked 반영
end
예상 코드 리뷰 소요 시간🎯 3 (Moderate) | ⏱️ ~35 minutes 연관 이슈
연관 PR
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/NavigationBar/_components/Dialog.tsx (1)
56-64:⚠️ Potential issue | 🟠 Major조건 분기의 양쪽에서 동일한 동작(
logout())을 수행합니다.
result값과 관계없이 양쪽 분기 모두logout()을 호출하므로 조건문이 의미가 없습니다. 의도에 따라 수정이 필요합니다:
- 실패 시에도 로그아웃해야 한다면 조건문을 제거하세요.
- 성공 시에만 로그아웃해야 한다면
else분기를 제거하거나 에러 처리 로직으로 변경하세요.🐛 의도에 따른 수정 제안
옵션 1: 항상 로그아웃 (조건문 불필요)
try { const result = await deleteAccount(); - - if (result) logout(); - else logout(); + logout(); } catch (error) {옵션 2: 성공 시에만 로그아웃
try { const result = await deleteAccount(); - - if (result) logout(); - else logout(); + if (result) { + logout(); + } else { + // TODO: 사용자에게 실패 알림 + console.error('회원 탈퇴 요청이 실패했습니다.'); + } } catch (error) {
🤖 Fix all issues with AI agents
In `@src/hooks/likes/useLikePrompt.ts`:
- Around line 22-47: The cache update in queryClient.setQueriesData is mutating
the wrong shape: PromptListResponse nests items under data.items, but the code
returns a top-level items field so data.items is unchanged; in the updater for
['prompts','list'] (inside useLikePrompt) replace the returned object to update
oldData.data by spreading oldData and setting data: {...oldData.data, items:
newItems} (using promptId, liked, likeCount and PromptListItemResponse mapping
as already implemented) so the nested data.items is actually replaced in the
cache.
🧹 Nitpick comments (3)
src/components/NavigationBar/_components/Dialog.tsx (2)
80-90: 주석 처리된 코드를 제거하세요.기존 버튼 구현이 주석으로 남아 있습니다. 버전 관리를 사용하고 있으므로 불필요한 주석 코드는 제거하는 것이 좋습니다.
♻️ 주석 코드 제거 제안
secondaryAction={ - // <button - // type="button" - // onClick={handleWithdraw} - // className="flex-1 px-4 py-3 bg-red-600 rounded-lg hover:bg-red-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"> - // <span className="text-white">탈퇴하기</span> - // </button> <Button onClick={handleWithdraw} variant="destructive" className="flex-1" size="lg"> 탈퇴하기 </Button> }
60-62: 에러 발생 시 사용자에게 피드백이 제공되지 않습니다.현재 에러가 콘솔에만 기록되고 사용자는 탈퇴 실패 여부를 알 수 없습니다. 토스트 알림이나 에러 메시지 표시를 고려해 주세요.
src/hooks/likes/useMyLikedPromptIds.ts (1)
5-16:likedIds는 기본값을[]로 반환하는 편이 안전합니다.
비로그인 시data가undefined일 수 있어 소비자 타입이 흔들립니다. 기본 배열을 반환하면 사용성이 좋아집니다.♻️ 제안 수정안
- return { likedIds: data }; + return { likedIds: data ?? [] };
| queryClient.setQueriesData( | ||
| { queryKey: ['prompts', 'list'] }, | ||
| (oldData: PromptListResponse) => { | ||
| if (!oldData) return oldData; | ||
|
|
||
| const items = oldData.data?.items || []; | ||
|
|
||
| const newItems = items.map((item: PromptListItemResponse) => { | ||
| if (item.id === promptId) { | ||
| return { | ||
| ...item, | ||
| isLiked: liked, | ||
| stats: { | ||
| ...item.stats, | ||
| likeCount: likeCount, | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| return item; | ||
| }); | ||
|
|
||
| return { | ||
| ...oldData, | ||
| items: newItems, | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "*.ts" -o -name "*.tsx" | head -20Repository: promlog/promlog-client
Length of output: 705
🏁 Script executed:
fd -e ts -e tsx "useLikePrompt" .Repository: promlog/promlog-client
Length of output: 100
🏁 Script executed:
rg "PromptListResponse" -t ts -t tsx --max-count 20Repository: promlog/promlog-client
Length of output: 93
🏁 Script executed:
rg "PromptListResponse" -t ts --max-count 20Repository: promlog/promlog-client
Length of output: 487
🏁 Script executed:
cat -n src/hooks/likes/useLikePrompt.tsRepository: promlog/promlog-client
Length of output: 2326
🏁 Script executed:
rg "type PromptListResponse" -A 10Repository: promlog/promlog-client
Length of output: 48
🏁 Script executed:
cat -n src/apis/prompts/prompts.types.ts | head -50Repository: promlog/promlog-client
Length of output: 1237
🏁 Script executed:
cat -n src/apis/prompts/prompts.types.ts | grep -A 10 "export interface PromptListResponse"Repository: promlog/promlog-client
Length of output: 367
캐시 업데이트에서 data.items이 갱신되지 않습니다.
PromptListResponse는 items이 data.items에 중첩되어 있으므로, 현재 반환 구조에서는 기존 data.items이 그대로 유지됩니다. 수정 제안과 같이 data 객체 내부를 업데이트하도록 변경하세요.
✅ 수정 제안
- return {
- ...oldData,
- items: newItems,
- };
+ return {
+ ...oldData,
+ data: {
+ ...oldData.data,
+ items: newItems,
+ },
+ };📝 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.setQueriesData( | |
| { queryKey: ['prompts', 'list'] }, | |
| (oldData: PromptListResponse) => { | |
| if (!oldData) return oldData; | |
| const items = oldData.data?.items || []; | |
| const newItems = items.map((item: PromptListItemResponse) => { | |
| if (item.id === promptId) { | |
| return { | |
| ...item, | |
| isLiked: liked, | |
| stats: { | |
| ...item.stats, | |
| likeCount: likeCount, | |
| }, | |
| }; | |
| } | |
| return item; | |
| }); | |
| return { | |
| ...oldData, | |
| items: newItems, | |
| }; | |
| queryClient.setQueriesData( | |
| { queryKey: ['prompts', 'list'] }, | |
| (oldData: PromptListResponse) => { | |
| if (!oldData) return oldData; | |
| const items = oldData.data?.items || []; | |
| const newItems = items.map((item: PromptListItemResponse) => { | |
| if (item.id === promptId) { | |
| return { | |
| ...item, | |
| isLiked: liked, | |
| stats: { | |
| ...item.stats, | |
| likeCount: likeCount, | |
| }, | |
| }; | |
| } | |
| return item; | |
| }); | |
| return { | |
| ...oldData, | |
| data: { | |
| ...oldData.data, | |
| items: newItems, | |
| }, | |
| }; |
🤖 Prompt for AI Agents
In `@src/hooks/likes/useLikePrompt.ts` around lines 22 - 47, The cache update in
queryClient.setQueriesData is mutating the wrong shape: PromptListResponse nests
items under data.items, but the code returns a top-level items field so
data.items is unchanged; in the updater for ['prompts','list'] (inside
useLikePrompt) replace the returned object to update oldData.data by spreading
oldData and setting data: {...oldData.data, items: newItems} (using promptId,
liked, likeCount and PromptListItemResponse mapping as already implemented) so
the nested data.items is actually replaced in the cache.
✅ 체크리스트
📝 작업 상세 내용
🔎 후속 작업 (선택 사항)
✅ 셀프 체크리스트
이슈 번호: #44
Summary by CodeRabbit
출시 노트
새로운 기능
개선