Skip to content

feat: 프롬프트 반응 기능 연결#46

Merged
ccconac merged 8 commits intodevfrom
feat/44-prompt-like
Feb 3, 2026
Merged

feat: 프롬프트 반응 기능 연결#46
ccconac merged 8 commits intodevfrom
feat/44-prompt-like

Conversation

@ccconac
Copy link
Copy Markdown
Member

@ccconac ccconac commented Feb 3, 2026

✅ 체크리스트

  • 프롬프트 반응 기능 연결
    • 사용자가 좋아요 표시한 프롬프트 목록 API 연결
    • 프롬프트 좋아요 기능 연결
    • 프롬프트 좋아요 삭제 기능 연결

📝 작업 상세 내용

프롬프트 반응(좋아요) 기능 연결이 완료되었습니다. 하지만, 현재 백엔드에서 좋아요 카운팅이 정상적으로 동작하지 않는 이슈가 있습니다. 백엔드에 이슈 및 일부 로직 수정을 변경 요청한 상태라 추가적인 작업이 필요합니다.

Feb-03-2026 16-42-46

🔎 후속 작업 (선택 사항)

  • 추후 로직이 변경될 수 있어 낙관적 업데이트를 적용하지 않았습니다.
  • 북마크 기능 추가가 필요합니다.

✅ 셀프 체크리스트

  • 브랜치 확인하기
  • 불필요한 코드가 들어가지 않았는지 재확인하기
  • issue 닫기
  • reiewers, assignees, Lables 등록 확인하기

이슈 번호: #44

Summary by CodeRabbit

출시 노트

  • 새로운 기능

    • 프롬프트 좋아요 및 싫어요 기능 추가
    • 사용자 좋아요 목록 조회 기능 추가
    • 미로그인 사용자 좋아요 시도 시 로그인 모달 자동 표시
    • 버튼 활성 상태 시각화 지원
  • 개선

    • 다이얼로그 컴포넌트 제어 상태 모드 추가

@ccconac ccconac self-assigned this Feb 3, 2026
@ccconac ccconac added the ✨ FEAT 기능 개발 label Feb 3, 2026
@ccconac ccconac linked an issue Feb 3, 2026 that may be closed by this pull request
1 task
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 3, 2026

📝 Walkthrough

요약

프롬프트 "좋아요" 기능을 구현하는 종합적인 변경 사항입니다. 새로운 API 엔드포인트, React Query 훅, UI 컴포넌트 개선, 그리고 PromptCard에 로그인 기반 좋아요 플로우를 추가합니다.

변경 사항

Cohort / File(s) 요약
API 함수 및 타입
src/apis/prompts/prompts.ts, src/apis/prompts/prompts.types.ts
getMyLikedPromptIds(), likePrompt(), unlikePrompt() 세 가지 새 API 함수 추가. PromptLikeResponse 타입 도입하여 좋아요 응답(성공, 좋아요 상태, 개수) 구조화.
Button 컴포넌트 개선
src/components/Button/Button.tsx, src/components/Button/Button.types.ts, src/components/Button/Button.styles.ts, src/components/Button/Button.stories.tsx
isActive prop 추가로 버튼의 활성 상태 표시. buttonActiveStyle 정의하여 활성 상태의 보라색 테마 스타일 적용. Icon에 fill 속성 동적 전달 구현.
Card 컴포넌트 개선
src/components/Card/Card.tsx, src/components/Card/Card.types.ts
active prop 추가로 isLiked, isBookmarked 상태를 Card 및 CardFooter에 전달. 하트와 북마크 버튼에 isActive 연결.
Dialog 컴포넌트 개선
src/components/Dialog/Dialog.tsx, src/components/Dialog/Dialog.types.ts, src/components/NavigationBar/_components/Dialog.tsx
trigger prop을 선택 사항으로 변경. open, onOpenChange 제어 상태 props 추가. LoginDialog 컴포넌트 제어 가능하게 확장.
좋아요 기능 훅
src/hooks/likes/useLikePrompt.ts, src/hooks/likes/useMyLikedPromptIds.ts
useLikePrompt: 좋아요/좋아요 취소 토글 처리 및 캐시 업데이트. useMyLikedPromptIds: 현재 사용자의 좋아요한 프롬프트 ID 목록 조회 (로그인 상태에서만).
PromptList 페이지 통합
src/pages/PromptList/PromptListPage.tsx, src/pages/PromptList/_components/PromptCard.tsx
PromptListPage에서 사용자 좋아요 ID 통합 및 isLiked 상태 병합. PromptCard에 로그인 확인 로직, useLikePrompt 훅 연결, 로그인 모달 추가. mergedPromptDTO 타입으로 좋아요 및 북마크 상태 포함.

시퀀스 다이어그램

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
Loading

예상 코드 리뷰 소요 시간

🎯 3 (Moderate) | ⏱️ ~35 minutes

연관 이슈

연관 PR

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 PR의 주요 변경사항인 프롬프트 좋아요(반응) 기능 연결을 명확하게 설명하고 있으며, PR 목표와 완벽하게 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/44-prompt-like

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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는 기본값을 []로 반환하는 편이 안전합니다.
비로그인 시 dataundefined일 수 있어 소비자 타입이 흔들립니다. 기본 배열을 반환하면 사용성이 좋아집니다.

♻️ 제안 수정안
-  return { likedIds: data };
+  return { likedIds: data ?? [] };

Comment on lines +22 to +47
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,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.ts" -o -name "*.tsx" | head -20

Repository: 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 20

Repository: promlog/promlog-client

Length of output: 93


🏁 Script executed:

rg "PromptListResponse" -t ts --max-count 20

Repository: promlog/promlog-client

Length of output: 487


🏁 Script executed:

cat -n src/hooks/likes/useLikePrompt.ts

Repository: promlog/promlog-client

Length of output: 2326


🏁 Script executed:

rg "type PromptListResponse" -A 10

Repository: promlog/promlog-client

Length of output: 48


🏁 Script executed:

cat -n src/apis/prompts/prompts.types.ts | head -50

Repository: 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.

Suggested change
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ FEAT 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 프롬프트 반응 기능 연결

1 participant