Skip to content

[fix] 채팅 총 갯수, 좋아요 API 연결 및 UI 수정사항 반영#114

Merged
maylh merged 16 commits intodevelopfrom
fix/#113/cd-api-ui
Oct 24, 2025
Merged

[fix] 채팅 총 갯수, 좋아요 API 연결 및 UI 수정사항 반영#114
maylh merged 16 commits intodevelopfrom
fix/#113/cd-api-ui

Conversation

@maylh
Copy link
Collaborator

@maylh maylh commented Oct 23, 2025

🛰️ 관련 이슈


✨ 주요 변경 사항

▪️ 기능

  • 채팅 총 갯수 반환 API 연결
  • 좋아요 API 연결 (좋아요 상태를 실제 서버 데이터와 일치하도록 수정)

▪️ UI

  • 홈화면 캐러셀 수정사항 반영
  • 나의CD 존재하지 않을 때 화면 수정사항 반영
  • 토스트 메시지 수정사항 반영
  • 둘러보기, 나의CD 상세페이지 /tracklist로 변경

🔍 테스트 방법 / 체크리스트

  • 없음

🗯️ PR 포인트

  • CD response 타입 수정한 건 stash 해두고 따로 커밋 안 했는데, 제 부분은 제가 수정하는 게 나을 것 같으면 말씀해주세요 !!!! 반영하겠습니다 ~

🚀 알게된 점


📖 참고 자료 (선택)

Summary by CodeRabbit

  • New Features

    • 채팅 버튼에 실시간 메시지 카운트 표시 추가
    • 홈에 새 캐러셀(플레이리스트/캐러셀) 및 첫 섹션 컴포넌트 추가
  • Improvements

    • 좋아요 인터랙션 간소화 및 상태 동기화 개선
    • 액션 바에 공유 버튼 통합
    • 알림 토스트 비주얼 개선(배경/블러/그림자)
  • UI Updates

    • 경로/레이블 변경: "플레이리스트" → "트랙리스트"
    • CD 없는 경우 생성 유도 화면 추가

@maylh maylh self-assigned this Oct 23, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

홈 페이지 레이아웃과 캐러셀을 재구성하고, 좋아요/채팅 관련 API·훅·컴포넌트를 쿼리 기반으로 리팩토링하며 여러 UI/스타일·라우트·아이콘 공개 내보내기 변경을 적용합니다.

Changes

응집 / 파일(s) 변경 요약
아이콘 바렐 정리
src/assets/icons/index.ts
CtaArrow 공개 내보내기 제거
채팅 API/타입/훅
src/features/chat/api/chat.ts, src/features/chat/model/useChat.ts, src/features/chat/types/chat.ts
GET /chat/rooms/{roomId}/count/chat용 getChatCount, ChatCountResponse, useChatCount 추가
좋아요 API/타입/훅/버튼
src/features/like/api/like.ts, src/features/like/type/like.ts, src/features/like/model/useLike.ts, src/features/like/ui/LikeButton.tsx
LikeStatusResponse 타입 추가, getLikeStatus에 제네릭 적용, useLikeStatus 추가, useLike 시그니처·반환 변경(초기값 제거), LikeButton에서 isLiked prop 제거
홈 페이지 구조 변경
src/pages/home/index.tsx, src/pages/home/ui/FirstSection.tsx, src/pages/home/ui/HomeCarousel.tsx, src/pages/home/ui/index.ts, src/pages/home/config/messages.ts
기존 상단 히어로 제거, FirstSection·HomeCarousel 추가, 메시지 키/문구 업데이트, LoopCarousel 교체(삭제)
공유 LoopCarousel 추가 / 스토리 업데이트
src/shared/ui/LoopCarousel.tsx, src/shared/ui/index.ts, src/stories/LoopCarousel.stories.tsx
Embla 래퍼 LoopCarousel 신규 도입 및 재내보내기, 스토리 경로/렌더 방식 수정
My CD 페이지 캐러셀 교체
src/pages/mycd/index.tsx, src/pages/mycd/ui/PlaylistCarousel.tsx, src/pages/mycd/ui/index.ts
PlaylistCarousel 추가, 데이터 소스 cdResponse 사용으로 변경, 빈 상태 UI 추가, LoopCarousel 대체
라우트 경로 변경
src/shared/config/routesConfig.ts
경로 및 lazy import 변경: /playlist/tracklist (discover, mycd)
UI·스타일·위젯 변경
src/widgets/chat/ChatButton.tsx, src/widgets/playlist/ActionBar.tsx, src/widgets/playlist/Playlist.tsx, src/widgets/playlist/PlaylistInfo.tsx, src/pages/mypage/ui/setting/index.tsx, src/shared/styles/mixins.ts, src/shared/ui/Cd.tsx, src/shared/ui/Toast.tsx
ChatButton에 useChatCount 적용 및 레이아웃 조정, ActionBar에 selectedTab 추가·경로 변경·버튼 렌더 조건 조정, Playlist에서 isLiked 제거, PlaylistInfo 헤더 텍스트 변경, Setting 아이콘 stroke 조정, mixin·myCdButton 스타일 보강, Cd'home' variant 추가, Toast 스타일 교체

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Home
    participant FirstSection
    participant Query as ReactQuery
    participant API

    User->>Home: 접속
    Home->>FirstSection: 렌더
    FirstSection->>Query: playlists( limit 4 )
    FirstSection->>API: recent CD API
    Query-->>FirstSection: playlists 데이터
    API-->>FirstSection: CD 목록
    FirstSection->>User: Carousel 또는 CTA 표시

    par Like flow
        User->>Playlist: 좋아요 UI 표시 요청
        Playlist->>Query: useLikeStatus(playlistId)
        Query->>API: GET /main/likes/{playlistId}
        API-->>Query: { isLiked }
        Query-->>Playlist: 상태 전달
    end

    par Chat count flow
        User->>ChatButton: 버튼 렌더
        ChatButton->>Query: useChatCount(roomId)
        Query->>API: GET /chat/rooms/{roomId}/count/chat
        API-->>Query: { totalCount }
        Query-->>ChatButton: count 전달
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested labels

HIGH

Suggested reviewers

  • hansololiviakim

안내 요약

백엔드 CD 응답 변경사항을 반영하고 좋아요, 채팅, 캐러셀, 토스트 UI를 수정하는 대규모 리팩토링입니다. 홈페이지 구조를 재설계하고 캐러셀 컴포넌트를 공유 라이브러리로 이동하며 좋아요 기능을 쿼리 기반 상태 관리로 전환합니다.

🐰 캐러셀 위를 살랑 달려가,
좋아요는 쿼리로 톡톡,
채팅은 숫자로 속삭이고,
홈엔 새 섹션이 피어나네,
당근 토스트는 반짝이며 축하한다 🎉

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed PR 제목 "[fix] 채팅 총 갯수, 좋아요 API 연결 및 UI 수정사항 반영"은 변경사항의 핵심을 명확하게 요약하고 있습니다. 제목은 채팅 API, 좋아요 API, UI 수정이라는 세 가지 주요 변경 영역을 구체적으로 언급하고 있어서, 팀원이 커밋 이력을 훑을 때 이 PR의 목적을 쉽게 파악할 수 있습니다. 제목의 길이와 가독성도 적절하며, 불필요한 이모지나 파일 목록 없이 깔끔합니다.
Linked Issues Check ✅ Passed 연결된 이슈 #113의 주요 목표들이 코드 변경사항을 통해 충족되고 있습니다. (1) 플레이리스트의 좋아요 여부를 실제 API 데이터로 반영 - useLikeStatus hook과 LikeStatusResponse 타입이 추가되어 useLike hook이 리팩토링되었습니다. (2) 채팅 총 갯수 표시 수정 - getChatCount API, useChatCount hook, ChatCountResponse 타입이 추가되고 ChatButton에서 동적으로 표시됩니다. (3) 홈 상단 캐러셀 업데이트 반영 - FirstSection, HomeCarousel 컴포넌트가 새로 추가되고 LoopCarousel이 재구현되었습니다. (4) 토스트 알림 UI 수정 - Toast.tsx의 스타일이 업데이트되었습니다.
Out of Scope Changes Check ✅ Passed 변경사항 대부분이 이슈 #113의 범위 내에 있으며, 외부 범위 변경은 최소화되어 있습니다. 경로 변경(/playlist → /tracklist), 메시지 텍스트 업데이트, 여러 UI 컴포넌트 조정들은 모두 홈화면, 나의CD 페이지, 토스트, 좋아요/채팅 기능과 관련된 것으로 이슈 목표의 일부입니다. CtaArrow 제거는 직접적으로 이슈 목표와 연결되지 않지만, home/index.tsx 리팩토링 과정에서 사용되지 않게 된 컴포넌트를 정리한 것으로 보이므로 자연스러운 정리 과정입니다.
Description Check ✅ Passed PR 설명은 저장소의 템플릿 구조를 따르고 있으며 필수 섹션이 모두 작성되어 있습니다. 관련 이슈(#113)가 명시되어 있고, 주요 변경사항이 기능(채팅 API, 좋아요 API)과 UI(캐러셀, 나의CD 화면, 토스트, 경로 변경)로 잘 구분되어 설명되어 있습니다. 테스트 방법은 "없음"으로 명확히 표시되었고, PR 포인트에서 CD response 타입 수정에 대한 참고사항도 포함되어 있습니다. 선택 사항인 알게된 점과 참고 자료는 비워져 있지만, 이는 템플릿의 선택 섹션이므로 문제가 되지 않습니다.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ 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 fix/#113/cd-api-ui

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.

@gemini-code-assist
Copy link

Summary of Changes

Hello @maylh, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 채팅 메시지 총 갯수 API 연동 및 좋아요 기능의 서버 동기화 로직 개선을 통해 사용자 경험을 향상시킵니다. 또한, 홈 화면 캐러셀과 나의 CD가 없을 때의 UI를 전면 개편하고, 토스트 메시지 디자인을 업데이트하여 전반적인 시각적 일관성과 사용성을 개선했습니다. 마지막으로, 페이지 경로명을 /playlist에서 /tracklist로 변경하여 서비스 내 용어의 통일성을 확보했습니다.

Highlights

  • 채팅 총 갯수 API 연동: 채팅방의 총 메시지 갯수를 가져오는 API를 연결하고, 이를 UI에 표시하도록 구현했습니다.
  • 좋아요 API 개선: 좋아요 상태를 서버 데이터와 실시간으로 동기화하도록 좋아요 API 로직을 수정했습니다. useLike 훅이 초기 isLiked 상태를 직접 받지 않고, useLikeStatus 훅을 통해 서버에서 가져오도록 변경되었습니다.
  • 홈 화면 캐러셀 UI 개편: 홈 화면의 캐러셀 UI를 전면 개편하여, 로그인 상태 및 사용자 CD 유무에 따라 다른 콘텐츠를 표시하도록 FirstSection 컴포넌트를 도입했습니다. 또한, 새로운 HomeCarousel 컴포넌트를 사용하여 캐러셀 기능을 구현했습니다.
  • 나의 CD가 없을 때 UI 개선: 사용자의 CD가 존재하지 않을 경우, CD를 만들도록 유도하는 UI를 개선하고 관련 메시지를 업데이트했습니다.
  • 토스트 메시지 UI 변경: 토스트 메시지의 디자인을 업데이트하여 배경, 테두리, 그림자, 블러 효과 및 모서리 반경을 조정했습니다.
  • 경로명 변경: 둘러보기 및 나의 CD 상세 페이지의 URL 경로를 /playlist에서 /tracklist로 변경하여 일관성을 높였습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

github-actions bot commented Oct 23, 2025

🎵 Storybook Link 🎵
🔗 https://689dbb45f8d09aea7832eeb1-zfvpqhowjd.chromatic.com/

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

전반적으로 채팅 수 및 좋아요 API 연동과 UI 수정이 잘 이루어진 것 같습니다. 특히 useLike 훅을 Tanstack Query를 사용해 서버 상태를 관리하도록 리팩토링하고, 홈 페이지의 컴포넌트를 분리한 점은 코드 품질을 크게 향상시켰습니다. 몇 가지 개선점을 제안드립니다. 첫째, 여러 파일에서 하드코딩된 색상값이 사용되고 있어 이를 테마 시스템을 사용하도록 수정하는 것을 권장합니다. 둘째, playlistIdnull 대신 0을 기본값으로 사용하는 부분에서 발생할 수 있는 잠재적 버그를 지적했습니다. 마지막으로, useLike 훅을 Tanstack Query의 낙관적 업데이트 패턴을 사용해 개선하면 더 나은 사용자 경험과 깔끔한 코드를 만들 수 있을 것입니다. 추가적으로, PR에 포함되지 않은 코드 변경사항이지만 API 응답 필드명이 cdResponseonlyCdResponse로 일관되지 않게 사용되고 있는 부분이 있어 확인이 필요해 보입니다.

Comment on lines +18 to +28
const useLike = (playlistId: number) => {
const queryClient = useQueryClient()
const [isLiked, setIsLiked] = useState(initialIsLiked)
const navigate = useNavigate()
const { isLogin } = useAuthStore()
const navigate = useNavigate()

const [isLiked, setIsLiked] = useState<boolean>(false)
const { data: statusData, isLoading } = useLikeStatus(playlistId, { enabled: isLogin })

useEffect(() => {
setIsLiked(statusData?.isLiked ?? false)
}, [statusData])

Choose a reason for hiding this comment

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

medium

현재 useLike 훅에서 useStateuseEffect를 사용하여 isLiked 상태를 관리하고 있는데, 이는 Tanstack Query의 데이터와 UI 상태를 동기화하기 위한 보일러플레이트 코드를 추가합니다. Tanstack Query를 사용하고 있으므로, 서버 상태는 useQuery에서 직접 파생하여 사용하는 것이 더 간결하고 권장되는 방식입니다.1

useStateuseEffect를 제거하고 useLikeStatus에서 반환된 데이터를 직접 사용하도록 리팩토링하는 것을 제안합니다. 이렇게 하면 likeMutationunlikeMutationonSuccess 콜백에서 setIsLiked를 호출할 필요가 없어집니다.

더 나은 사용자 경험을 위해, 서버 응답을 기다리지 않고 UI를 즉시 업데이트하는 낙관적 업데이트(Optimistic Update)를 적용하는 것을 고려해보세요. onMutate 콜백을 사용하면 이를 구현할 수 있습니다.

Style Guide References

Footnotes

  1. 서버 상태 관리에 Tanstack Query를 적극 활용하여 데이터 fetching, 캐싱, 동기화, 에러 처리 등을 효율적으로 관리하도록 권장합니다. useQuery, useMutation 등의 훅을 적절히 사용합니다.

Copy link
Collaborator Author

@maylh maylh Oct 23, 2025

Choose a reason for hiding this comment

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

Copy link

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/features/like/ui/LikeButton.tsx (1)

31-33: 초기 로딩 시 ‘찜 안됨’처럼 보이는 깜빡임(flicker) 완화

isLoading 동안 HOME 타입 불투명도를 별도 처리하면 시각적 혼선을 줄일 수 있습니다.

-  const opacity = type === 'HOME' ? (liked ? 1 : 0.2) : 1
+  const opacity = type === 'HOME' ? (isLoading ? 0.5 : liked ? 1 : 0.2) : 1
🧹 Nitpick comments (13)
src/pages/myPage/ui/setting/index.tsx (1)

49-49: 테마 시스템을 사용하여 색상 값을 관리하세요.

하드코딩된 색상 값 "#BDBDBD" 대신 테마 시스템을 활용하는 것이 좋습니다. 파일의 다른 스타일 컴포넌트들(lines 98, 103, 108)은 일관되게 theme.COLOR를 사용하고 있습니다. 하드코딩된 색상은 유지보수를 어렵게 하며, 테마 변경 시 일관성을 해칠 수 있습니다.

다음 diff를 적용하여 테마 색상을 사용하도록 수정하세요:

           <SvgButton
             icon={RightArrow}
             width={16}
             height={20}
-            stroke="#BDBDBD"
+            stroke={theme => theme.COLOR['gray-400']}
             onClick={() => navigate('/mypage/terms')}
           />
           <SvgButton
             icon={RightArrow}
             width={16}
             height={20}
-            stroke="#BDBDBD"
+            stroke={theme => theme.COLOR['gray-400']}
             onClick={() => navigate('/mypage/privacy')}
           />

참고: #BDBDBD 색상이 프로젝트의 테마에서 어떤 색상 키에 해당하는지 확인한 후, 적절한 테마 색상 키를 사용하세요. gray-400은 예시이며, 실제 테마 정의를 확인해야 합니다.

Based on coding guidelines

Also applies to: 59-59

src/shared/ui/Toast.tsx (1)

20-35: 토스트 UI 개선이 잘 반영되었습니다.

세련된 glassmorphism 효과가 적용되어 시각적으로 개선되었습니다. 다만 유지보수성 향상을 위해 하드코딩된 색상 값과 숫자들을 테마 시스템으로 추출하는 것을 고려해보세요.

선택적으로 다음과 같이 테마 상수를 활용할 수 있습니다:

 const ToastContainer = styled.div`
   width: 100%;
   height: 48px;
   display: flex;
   align-items: center;
   gap: 8px;
   padding: 12px 16px;
   ${({ theme }) => theme.FONT['body1-normal']};
   color: ${({ theme }) => theme.COLOR['common-white']};
 
-  background: rgba(93, 100, 111, 0.2);
-  border: 0.5px solid rgba(255, 255, 255, 0.1);
-  box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.5);
+  background: ${({ theme }) => theme.COLOR['toast-background'] || 'rgba(93, 100, 111, 0.2)'};
+  border: ${({ theme }) => theme.BORDER?.['toast-border'] || '0.5px solid rgba(255, 255, 255, 0.1)'};
+  box-shadow: ${({ theme }) => theme.SHADOW?.['toast'] || '0px 5px 20px rgba(0, 0, 0, 0.5)'};
   backdrop-filter: blur(25px);
-  border-radius: 31px;
+  border-radius: ${({ theme }) => theme.RADIUS?.['toast'] || '31px'};
 `

참고: backdrop-filter는 모던 브라우저에서 잘 지원되므로 호환성 문제는 없습니다.

src/features/chat/model/useChat.ts (1)

41-47: 쿼리 키 네이밍 컨벤션을 통일해주세요.

같은 파일 내에서 쿼리 키 네이밍이 일관되지 않습니다:

  • Line 20: ['chat-history', roomId] (kebab-case)
  • Line 43: ['chatCount', roomId] (camelCase)

일관성을 위해 하나의 네이밍 컨벤션으로 통일하는 것을 권장합니다.

-    queryKey: ['chatCount', roomId],
+    queryKey: ['chat-count', roomId],
src/widgets/chat/ChatButton.tsx (1)

48-56: 조건부 스타일링을 css 헬퍼로 통일하는 것을 권장합니다.

현재 myCdButton은 css 객체이지만, 비교 조건은 템플릿 리터럴 문자열을 사용하고 있습니다. 일관성과 타입 안정성을 위해 둘 다 css 헬퍼를 사용하는 것이 좋습니다.

+import styled, { css } from 'styled-components'

 const ButtonWrapper = styled.div<{ $isMy: boolean }>`
   ${({ $isMy }) =>
     $isMy
       ? myCdButton
-      : `
+      : css`
         ${flexColCenter};
         gap: 2px;
-      `}
+      `}
 `
src/pages/mycd/ui/PlaylistCarousel.tsx (1)

40-41: key prop에 배열 인덱스 대신 고유 ID 사용 권장

배열 인덱스를 key로 사용하면 항목 순서가 변경될 때 React가 올바르게 재조정하지 못할 수 있습니다. 또한 indexnumber 타입인데 Key 타입으로 선언되어 타입 불일치가 있습니다.

다음과 같이 수정하세요:

-{data.map((slide, index: Key) => (
-  <EmblaSlide key={index}>
+{data.map((slide, index) => (
+  <EmblaSlide key={slide.playlistId}>
src/pages/home/ui/HomeCarousel.tsx (1)

60-60: 옵셔널 체이닝 추가 권장

PlaylistInfo 타입에 따르면 cdItems는 옵셔널일 수 있습니다. 안전한 접근을 위해 옵셔널 체이닝 또는 기본값 처리가 필요합니다.

다음과 같이 수정하세요:

-<Cd variant="home" bgColor="none" stickers={slide.cdItems} />
+<Cd variant="home" bgColor="none" stickers={slide.cdItems || slide.onlyCdResponse?.cdItems} />
src/pages/home/ui/FirstSection.tsx (1)

32-32: 불필요한 isLogin 체크 제거 가능

Line 25에서 isEmpty는 이미 isLogin && MyCdData && MyCdData.length === 0로 정의되어 있어, isEmpty가 true일 때는 항상 isLogin이 true입니다. 따라서 조건부 검사가 중복됩니다.

다음과 같이 간소화할 수 있습니다:

-<CtaButton onClick={() => (isLogin ? navigate('/mypage/customize') : navigate('/login'))}>
+<CtaButton onClick={() => navigate('/mypage/customize')}>
src/features/like/ui/LikeButton.tsx (2)

21-24: 토글 버튼 로딩 상태 노출 및 접근성 보강 제안

useLike에서 isLoading도 구조분해하여 UI 제어에 활용하세요.

-const LikeButton = ({ playlistId, type = 'HOME' }: LikeButtonProps) => {
+const LikeButton = ({ playlistId, type = 'HOME' }: LikeButtonProps) => {
   const theme = useTheme()
-  const { liked, toggleLike } = useLike(playlistId)
+  const { liked, toggleLike, isLoading } = useLike(playlistId)

35-50: SvgButton에 토글 의미를 전달(aria-pressed)하고 로딩 중 비활성화

보조기기 인식 및 중복 클릭 방지를 위해 아래 속성 추가를 권장합니다. SvgButton이 native button props를 전달한다는 전제입니다.

       <SvgButton
         icon={Icon}
         onClick={handleClick}
         width={size}
         height={size}
+        type="button"
+        disabled={isLoading}
+        aria-pressed={liked}
+        aria-label={liked ? '좋아요 취소' : '좋아요'}
         fill={

SvgButton이 disabled/aria-*를 지원하는지 확인 부탁드립니다.

src/features/like/model/useLike.ts (2)

46-59: 로딩 중 토글 차단으로 비정합 방지

초기 상태 로딩 중 토글 시 실제 상태와 반대 호출 가능성이 있습니다(isLiked 초기값 false). 로딩/진행 중엔 조작을 막아주세요.

   const toggleLike = () => {
     if (!isLogin) {
       navigate('/login')
       return
     }
 
-    if (likeMutation.isPending || unlikeMutation.isPending) return
+    if (isLoading || likeMutation.isPending || unlikeMutation.isPending) return

30-44: 오류 처리 경로 부재(토스트/로그)와 호출자용 진행 상태 노출

실패 시 사용자 피드백이 없습니다. 공용 토스트 유틸이 있다면 onError에서 안내를 추가하고, 진행 상태를 호출자에게 노출하면 버튼 비활성화에 유용합니다.

   const likeMutation = useMutation({
     mutationFn: () => postLike(playlistId),
     onSuccess: () => {
       setIsLiked(true)
       queryClient.invalidateQueries({ queryKey: ['likeStatus', playlistId] })
     },
+    // onError: () => toast.error('좋아요 처리에 실패했어요. 잠시 후 다시 시도해 주세요.'),
   })
@@
   const unlikeMutation = useMutation({
     mutationFn: () => deleteLike(playlistId),
     onSuccess: () => {
       setIsLiked(false)
       queryClient.invalidateQueries({ queryKey: ['likeStatus', playlistId] })
     },
+    // onError: () => toast.error('좋아요 취소에 실패했어요. 잠시 후 다시 시도해 주세요.'),
   })
@@
-  return { liked: isLiked, toggleLike, isLoading }
+  const isMutating = likeMutation.isPending || unlikeMutation.isPending
+  return { liked: isLiked, toggleLike, isLoading, isMutating }
src/stories/LoopCarousel.stories.tsx (1)

24-35: 테마 토큰 일관 사용

Box의 글자색은 theme 토큰을 사용해 주세요.

   background-color: ${({ theme }) => theme.COLOR['gray-800']};
-  color: white;
+  color: ${({ theme }) => theme.COLOR['common-white']};
src/pages/home/config/messages.ts (1)

8-11: 문구 내 줄바꿈 뒤 공백 제거 권장

'\n ' 형태의 선행 공백이 실제 렌더링에 반영되어 들여쓰기처럼 보일 수 있습니다. 의도된 게 아니라면 제거해 주세요.

-  MEMBER: (name: string) => `${name}님! 오늘의 첫 곡,\n 여기서 시작하세요`,
+  MEMBER: (name: string) => `${name}님! 오늘의 첫 곡,\n여기서 시작하세요`,
   MEMBER_NO_CD: `아직 나만의 CD가 없어요\n오늘의 첫 곡을 담아볼까요?`,
-  GUEST: `오늘의 무드에 어울리는\n 트랙리스트를 들어 보세요`,
+  GUEST: `오늘의 무드에 어울리는\n트랙리스트를 들어 보세요`,
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ab2a4e and 1f6d482.

⛔ Files ignored due to path filters (3)
  • src/assets/icons/icn_cta_arrow.svg is excluded by !**/*.svg
  • src/assets/icons/icn_right_arrow.svg is excluded by !**/*.svg
  • src/assets/images/img_character_home.png is excluded by !**/*.png
📒 Files selected for processing (30)
  • src/assets/icons/index.ts (0 hunks)
  • src/features/chat/api/chat.ts (2 hunks)
  • src/features/chat/model/useChat.ts (2 hunks)
  • src/features/chat/types/chat.ts (1 hunks)
  • src/features/like/api/like.ts (2 hunks)
  • src/features/like/model/useLike.ts (2 hunks)
  • src/features/like/type/like.ts (1 hunks)
  • src/features/like/ui/LikeButton.tsx (2 hunks)
  • src/features/share/ui/ShareButton.tsx (0 hunks)
  • src/pages/home/config/messages.ts (1 hunks)
  • src/pages/home/index.tsx (2 hunks)
  • src/pages/home/ui/FirstSection.tsx (1 hunks)
  • src/pages/home/ui/HomeCarousel.tsx (1 hunks)
  • src/pages/home/ui/LoopCarousel.tsx (0 hunks)
  • src/pages/home/ui/index.ts (1 hunks)
  • src/pages/myPage/ui/setting/index.tsx (2 hunks)
  • src/pages/mycd/index.tsx (5 hunks)
  • src/pages/mycd/ui/PlaylistCarousel.tsx (1 hunks)
  • src/pages/mycd/ui/index.ts (1 hunks)
  • src/shared/config/routesConfig.ts (3 hunks)
  • src/shared/styles/mixins.ts (1 hunks)
  • src/shared/ui/Cd.tsx (2 hunks)
  • src/shared/ui/LoopCarousel.tsx (1 hunks)
  • src/shared/ui/Toast.tsx (1 hunks)
  • src/shared/ui/index.ts (1 hunks)
  • src/stories/LoopCarousel.stories.tsx (1 hunks)
  • src/widgets/chat/ChatButton.tsx (4 hunks)
  • src/widgets/playlist/ActionBar.tsx (1 hunks)
  • src/widgets/playlist/Playlist.tsx (2 hunks)
  • src/widgets/playlist/PlaylistInfo.tsx (1 hunks)
💤 Files with no reviewable changes (3)
  • src/assets/icons/index.ts
  • src/features/share/ui/ShareButton.tsx
  • src/pages/home/ui/LoopCarousel.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: ## 1. 일반적인 코딩 컨벤션

포맷팅

  • .prettierrc 설정에 따라 포맷팅 확인
  • 들여쓰기: 2칸 스페이스
  • 최대 줄 길이: 100자
  • 세미콜론 사용 안함
  • 따옴표: 작은따옴표 사용
  • 괄호 안 공백: 있음
  • 화살표 함수 괄호: 항상 사용
  • 줄바꿈: LF 사용

네이밍 컨벤션

  • 컴포넌트: PascalCase (예: UserProfile)
  • 유틸리티/훅/변수: camelCase (예: getUserData, useUserInfo)
  • 상수: UPPER_SNAKE_CASE (예: API_BASE_URL)
  • 이미지 파일: kebab-case (예: user-profile-icon.png)

주석 사용

  • 복잡한 로직에만 주석 추가
  • 불필요한 주석 지양 (코드로 설명 가능한 것)
  • TODO/FIXME 형식: // TODO: 설명 - 작성자

가독성

  • 매직 넘버 지양, 의미있는 상수 사용
  • 함수는 하나의 책임만 가지도록 작성 (최대 20줄 권장)
  • 중첩 깊이 최소화 (3단계 이하 권장)

2. React 모범 사례

컴포넌트 작성

  • 최신 React hooks 사용 권장
  • 컴포넌트는 단일 책임 원칙 준수
  • Presentational/Container 컴포넌트 분리
  • 성능 최적화: memo, useCallback, useMemo 적절히 사용
  • 대용량 리스트는 가상화 라이브러리 사용 고려

상태 관리

  • Zustand와 Tanstack Query를 일관되게 사용
  • 상태 구조는 정규화된 형태로 관리
  • 에러 처리: Error Boundary와 try-catch 또는 onError 콜백 활용

3. 스타일링

Styled Components

  • Styled Components 일관되게 사용
  • 스타일드 컴포넌트명은 의미있게 작성
  • 동적 스타일링은 props나 CSS 변수 활용
  • 테마 시스템 활용하여 글로벌 스타일 관리
  • 재사용 가능한 스타일은 mixin이나 확장으로 관리
  • CSS 포맷팅 가독성 유지
  • 사용하지 않는 스타일이나 중복 스타일 제거

4. Vite 및 빌드 최적화

  • 모듈 import 최적화 (tree-shaking 고려)
  • 환경변수는 .env 파일로 관리
  • vite.config.ts에서 빌드 성능 튜닝 (sourcemap 설정, 플러그인 최적화 등)

5. 아키텍처 및 개발 환경

폴더 구조

  • Feature-Sliced Design (FSD) 구조 준수
  • 레이어별 참조 규칙 엄격히 적용

타입스크립트

  • strict 모드 사용
  • 타입 명시적으로 작성 (any 사용 지양)
  • path alias (@/_) 절대 경로 import 사용

Git 훅

  • Husky 설정으로 pre-commit, commit-msg 린팅 확인

6. 기타 가이드라인

  • 충분한 근거와 함께 리뷰 제공
  • 정보 검증 후 답변
  • 간결하고 명확한 응답
  • 필요시 추가 컨텍스트 요청
  • 검증되지 않은 주장 지양
  • 가능한 경우 출처 명시
  • 별도 언급 없으면 JavaScript 기준
  • 한국어로 응답
  • 대부분 브라우저에서 지원하는 ES6+ 기능 활용
  • 코드 리뷰를 통한 유지보수성 향상에 적극 활용

Files:

  • src/features/like/type/like.ts
  • src/features/chat/types/chat.ts
  • src/features/chat/model/useChat.ts
  • src/shared/ui/Toast.tsx
  • src/features/like/api/like.ts
  • src/pages/home/ui/FirstSection.tsx
  • src/pages/myPage/ui/setting/index.tsx
  • src/widgets/playlist/Playlist.tsx
  • src/shared/config/routesConfig.ts
  • src/widgets/playlist/PlaylistInfo.tsx
  • src/shared/ui/LoopCarousel.tsx
  • src/shared/styles/mixins.ts
  • src/shared/ui/index.ts
  • src/features/like/ui/LikeButton.tsx
  • src/shared/ui/Cd.tsx
  • src/pages/mycd/ui/PlaylistCarousel.tsx
  • src/pages/mycd/ui/index.ts
  • src/pages/mycd/index.tsx
  • src/pages/home/config/messages.ts
  • src/features/chat/api/chat.ts
  • src/pages/home/index.tsx
  • src/widgets/chat/ChatButton.tsx
  • src/pages/home/ui/index.ts
  • src/features/like/model/useLike.ts
  • src/pages/home/ui/HomeCarousel.tsx
  • src/widgets/playlist/ActionBar.tsx
  • src/stories/LoopCarousel.stories.tsx
🧬 Code graph analysis (11)
src/features/chat/model/useChat.ts (1)
src/features/chat/api/chat.ts (1)
  • getChatCount (18-20)
src/features/like/api/like.ts (2)
src/shared/api/httpClient.ts (1)
  • api (5-19)
src/features/like/type/like.ts (1)
  • LikeStatusResponse (1-3)
src/pages/home/ui/FirstSection.tsx (6)
src/features/auth/store/authStore.ts (1)
  • useAuthStore (7-52)
src/entities/playlist/model/usePlaylists.ts (1)
  • useShufflePlaylists (19-43)
src/entities/playlist/model/useMyPlaylist.ts (1)
  • useMyCdList (14-20)
src/pages/home/config/messages.ts (2)
  • TITLE_TEXT (7-11)
  • BUTTON_TEXT (1-5)
src/shared/styles/mixins.ts (1)
  • flexRowCenter (3-8)
src/shared/styles/theme.ts (1)
  • theme (3-122)
src/widgets/playlist/Playlist.tsx (2)
src/features/recommend/types/recommend.ts (1)
  • Playlist (3-15)
src/features/search/types/search.ts (1)
  • Playlist (32-45)
src/pages/mycd/ui/PlaylistCarousel.tsx (2)
src/entities/playlist/types/playlist.ts (1)
  • CdCustomData (23-34)
src/shared/styles/mixins.ts (1)
  • flexRowCenter (3-8)
src/pages/mycd/index.tsx (2)
src/shared/styles/mixins.ts (2)
  • flexColCenter (10-15)
  • flexRowCenter (3-8)
src/shared/styles/theme.ts (1)
  • theme (3-122)
src/features/chat/api/chat.ts (2)
src/shared/api/httpClient.ts (1)
  • api (5-19)
src/features/chat/types/chat.ts (1)
  • ChatCountResponse (22-24)
src/widgets/chat/ChatButton.tsx (3)
src/features/chat/model/useChat.ts (1)
  • useChatCount (41-47)
src/shared/styles/mixins.ts (2)
  • myCdButton (17-26)
  • flexColCenter (10-15)
src/shared/styles/theme.ts (1)
  • theme (3-122)
src/features/like/model/useLike.ts (2)
src/features/like/api/like.ts (3)
  • getLikeStatus (12-14)
  • postLike (4-6)
  • deleteLike (8-10)
src/features/auth/store/authStore.ts (1)
  • useAuthStore (7-52)
src/pages/home/ui/HomeCarousel.tsx (5)
src/entities/playlist/types/playlist.ts (1)
  • PlaylistInfo (82-94)
src/pages/home/ui/DotButton.tsx (1)
  • useDotButton (15-49)
src/pages/home/config/messages.ts (1)
  • BUTTON_TEXT (1-5)
src/shared/styles/mixins.ts (2)
  • flexRowCenter (3-8)
  • flexColCenter (10-15)
src/shared/styles/theme.ts (1)
  • theme (3-122)
src/stories/LoopCarousel.stories.tsx (1)
src/shared/styles/theme.ts (1)
  • theme (3-122)
🔇 Additional comments (35)
src/shared/styles/mixins.ts (1)

25-25: myCdButton 믹스인 통합은 올바르게 적용되었으며, 영향 범위는 제한적입니다.

검증 결과, myCdButton 믹스인은 4개 파일에서 사용 중이며 모두 조건부($isMy)로 적용됩니다:

  • ActionBar, ChatButton, ShareButton, LikeButton

flexRowCenter 추가로 인해 $isMy=true일 때만 flex row center 레이아웃이 적용되므로 의도된 동작입니다. flexRowCenter는 여전히 다른 컴포넌트에서 독립적으로 사용 중(ButtonBar 등)이므로 breaking change 없습니다.

src/features/chat/types/chat.ts (1)

21-24: 타입 정의가 명확하고 적절합니다.

채팅 개수를 반환하는 API 응답 타입이 간결하게 정의되어 있습니다.

src/features/chat/model/useChat.ts (1)

4-4: 필요한 import가 올바르게 추가되었습니다.

새로운 useChatCount 훅 구현을 위한 import가 적절하게 추가되었습니다.

Also applies to: 9-9

src/features/chat/api/chat.ts (2)

1-5: 타입 import가 올바르게 추가되었습니다.

새로운 API 함수를 위한 ChatCountResponse 타입이 적절하게 import되었습니다.


18-20: API 함수가 기존 패턴을 잘 따르고 있습니다.

getChatCount 함수가 기존 API 함수들과 일관된 구조로 구현되어 있으며, 타입 안정성도 잘 유지되고 있습니다.

src/widgets/chat/ChatButton.tsx (3)

6-7: 필요한 import가 적절하게 추가되었습니다.

채팅 개수 조회를 위한 useChatCount 훅과 새로운 레이아웃을 위한 flexColCenter 믹스인이 올바르게 import되었습니다.


21-21: 데이터 페칭 로직이 올바르게 구현되었습니다.

useChatCount 훅을 사용하여 채팅 개수를 조회하고 있으며, roomId 타입 변환도 적절하게 처리되었습니다.


31-31: Count 컴포넌트와 렌더링 로직이 잘 구현되었습니다.

로딩/에러 상태 처리가 적절하며, 스타일 컴포넌트가 테마 시스템을 올바르게 활용하고 있습니다. 옵셔널 체이닝과 nullish coalescing을 사용하여 안전하게 기본값을 처리하고 있습니다.

Also applies to: 58-61

src/pages/home/ui/index.ts (1)

2-3: 컴포넌트 export 변경사항 확인 완료

홈 UI의 public API 변경사항이 명확합니다. LoopCarousel에서 HomeCarousel과 FirstSection으로 분리된 구조가 잘 반영되어 있습니다.

src/shared/ui/LoopCarousel.tsx (1)

47-50: 스타일 컴포넌트 구현 확인 완료

Embla Carousel의 표준 레이아웃 패턴을 잘 따르고 있으며, touch-action 속성으로 터치 제스처를 적절히 처리합니다.

src/widgets/playlist/PlaylistInfo.tsx (1)

48-48: UI 텍스트 업데이트 확인 완료

"플레이리스트"에서 "트랙리스트"로의 명명 변경이 일관되게 적용되었습니다. 라우트 설정 변경사항과도 일치합니다.

src/features/like/type/like.ts (1)

1-3: 타입 정의 확인 완료

좋아요 상태 응답 타입이 명확하게 정의되어 있습니다. API 레이어의 타입 안정성을 향상시킵니다.

src/features/like/api/like.ts (1)

1-1: 타입 안정성 개선 확인 완료

type-only import와 제네릭 타입 적용으로 TypeScript의 타입 안정성이 향상되었습니다. strict 모드 가이드라인을 잘 따르고 있습니다.

As per coding guidelines

Also applies to: 13-13

src/shared/config/routesConfig.ts (1)

21-21: 라우트 설정 업데이트 확인 완료

"playlist"에서 "tracklist"로의 경로 및 임포트 변경이 일관되게 적용되었습니다. 둘러보기와 나의 CD 섹션 모두 새로운 명명 규칙을 따르고 있습니다.

Also applies to: 26-26, 55-55, 67-67

src/shared/ui/index.ts (1)

21-21: Shared UI export 추가 확인 완료

LoopCarousel이 공유 UI로 올바르게 노출되었습니다. 여러 feature에서 재사용 가능한 구조입니다.

src/pages/mycd/ui/index.ts (1)

2-2: MyCD UI export 추가 확인 완료

PlaylistCarousel이 나의 CD UI의 public API로 적절히 노출되었습니다. 컴포넌트 리팩토링과 일치합니다.

src/shared/ui/Cd.tsx (1)

10-10: LGTM! 'home' variant 추가가 적절합니다.

홈 캐러셀에서 사용할 새로운 'home' variant가 기존 패턴과 일관되게 추가되었습니다. 크기와 스타일 설정이 적절합니다.

Also applies to: 101-101

src/widgets/playlist/Playlist.tsx (1)

9-14: LGTM! 좋아요 상태 관리 개선이 잘 반영되었습니다.

isLiked prop을 제거하고 LikeButton이 내부적으로 상태를 관리하도록 변경되어 컴포넌트가 단순해지고 책임이 명확해졌습니다. PR 목표인 좋아요 API 연결과 일치합니다.

Also applies to: 16-16, 28-28

src/pages/mycd/index.tsx (4)

95-95: LGTM! CD 아이템 데이터 경로 통일

onlyCdResponse?.cdItems에서 cdResponse?.cdItems로 변경하여 백엔드 응답 구조 변경사항이 올바르게 반영되었습니다.

Also applies to: 173-173


132-143: LGTM! 빈 상태 UI 처리가 잘 구현되었습니다.

사용자에게 나의 CD가 없을 때 적절한 안내와 CTA를 제공합니다. PR 목표인 "나의 CD가 존재하지 않을 때의 화면 수정사항 반영"이 구현되었습니다.


155-165: LGTM! selectedTab 기반 조건부 렌더링

selectedTab에 따라 편집 버튼과 ActionBar 동작을 제어하는 로직이 적절합니다.

Also applies to: 172-176


168-168: LGTM! PlaylistCarousel 컴포넌트 전환

LoopCarousel에서 새로운 PlaylistCarousel 컴포넌트로 변경되어 캐러셀 로직이 더 명확하게 분리되었습니다.

src/pages/mycd/ui/PlaylistCarousel.tsx (1)

21-53: LGTM! PlaylistCarousel 구현이 깔끔합니다.

활성 슬라이드만 스티커를 렌더링하여 성능을 최적화하고, onCenterChange 콜백을 통해 부모 컴포넌트와 적절히 통신합니다.

src/pages/home/ui/HomeCarousel.tsx (1)

21-81: LGTM! HomeCarousel 구현이 잘 되어있습니다.

로그인 상태에 따른 첫 슬라이드 분기 처리, 장르 배지와 제목 오버레이, 닷 네비게이션 등이 적절히 구현되었습니다. PR 목표인 홈 화면 캐러셀 수정사항이 반영되었습니다.

src/pages/home/ui/FirstSection.tsx (1)

16-46: LGTM! FirstSection 구현이 적절합니다.

로그인 여부와 CD 보유 여부에 따라 적절한 UI를 렌더링하며, 홈 화면의 첫 섹션으로서 역할이 명확합니다.

src/widgets/playlist/ActionBar.tsx (4)

18-18: LGTM! selectedTab 기반 조건부 렌더링 추가

selectedTab prop 추가로 탭 상태에 따라 LikeButton을 제어할 수 있게 되어 유연성이 향상되었습니다.

Also applies to: 21-27, 40-42


32-32: LGTM! 경로를 /tracklist로 변경

PR 목표인 "둘러보기, 나의CD 상세페이지 경로를 /tracklist로 변경"이 올바르게 반영되었습니다.

Also applies to: 34-34


44-44: LGTM! ShareButton 추가

ShareButtonActionBar에 추가되어 공유 기능이 통합되었습니다.


51-51: LGTM! UI 텍스트 개선

"목록"에서 "트랙리스트"로 변경하여 사용자에게 더 명확한 의미를 전달합니다.

src/pages/home/index.tsx (2)

37-47: LGTM! 홈 페이지 구조 개선

HeaderFirstSection을 명확히 분리하여 컴포넌트 책임이 단순해지고 코드 가독성이 향상되었습니다.


52-61: LGTM! isLiked prop 제거

다른 파일들과 일관되게 isLiked prop이 제거되어 좋아요 상태 관리가 통일되었습니다.

src/stories/LoopCarousel.stories.tsx (1)

4-12: Story 구조/경로 정리 LGTM

공용 UI로의 import와 layout 옵션 정리가 일관성 있습니다.

src/pages/home/config/messages.ts (1)

2-5: Based on the initial script output you provided, I can now verify the key usage and generate the rewritten review comment.

메시지 키 정의 확인 완료

제공하신 스크립트 출력 결과를 검토한 결과:

  1. BUTTON_TEXT 사용처

    • BUTTON_TEXT.MEMBER (HomeCarousel.tsx:48)
    • BUTTON_TEXT.MEMBER_NO_CD (FirstSection.tsx:33)
    • BUTTON_TEXT.GUEST (HomeCarousel.tsx:48)
  2. TITLE_TEXT 사용처

    • TITLE_TEXT.MEMBER_NO_CD (FirstSection.tsx:31)
    • TITLE_TEXT.MEMBER (FirstSection.tsx:40)
    • TITLE_TEXT.GUEST (FirstSection.tsx:40)

검증 결과, 리뷰 코멘트에서 제시한 새 키들(MEMBER_NO_CD, MEMBER, GUEST)이 실제로 여러 컴포넌트에서 일관되게 사용되고 있습니다. BUTTON_TEXT와 TITLE_TEXT 모두에서 동일한 키 이름이 사용되므로 메시지 파일에서도 두 객체 모두에 해당 키들이 정의되어 있어야 하며, 현재 코드의 사용처들이 정상적으로 동작할 것으로 보입니다.

src/features/like/ui/LikeButton.tsx (1)

10-13: Breaking change 검증 완료: 기존 isLiked prop 사용처 없음

LikeButton 컴포넌트 호출처 2곳을 확인했으며, 모두 isLiked prop을 전달하지 않고 있습니다.

  • src/widgets/playlist/ActionBar.tsx:41: <LikeButton playlistId={playlistId} type={type} />
  • src/widgets/playlist/Playlist.tsx:28: <LikeButton playlistId={id} type="HOME" />

코드베이스가 이미 isLiked prop 제거에 맞춰 업데이트되어 있으며, breaking change가 없습니다.

src/features/like/model/useLike.ts (1)

9-16: 리뷰 코멘트의 전제가 잘못되었습니다

src/shared/api/instance.ts의 응답 인터셉터(라인 29)에서 response.data를 직접 반환하므로, api.get<T>()AxiosResponse<T>가 아닌 Promise<T>를 반환합니다. 따라서 현재 코드의 statusData?.isLiked 접근은 정확하며, select 옵션을 추가할 필요가 없습니다.

Likely an incorrect or invalid review comment.

Comment on lines +21 to +36
useEffect(() => {
if (!emblaApi) return
onReady?.(emblaApi) // 상위에 전달

const onSelect = () => {
const index = emblaApi.selectedScrollSnap()
onSelectIndex?.(index)
}

emblaApi.on('select', onSelect)
onSelect()

return () => {
emblaApi.off('select', onSelect)
}
}, [emblaApi, onSelectIndex, onReady])
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

useEffect 의존성 배열의 콜백 안정성 확인 필요

onSelectIndexonReady 콜백이 의존성 배열에 포함되어 있습니다. 부모 컴포넌트에서 이 콜백들을 useCallback으로 메모이제이션하지 않으면 매 렌더링마다 useEffect가 재실행될 수 있습니다.

다음 스크립트로 이 컴포넌트를 사용하는 부모 컴포넌트들에서 콜백 메모이제이션 여부를 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: LoopCarousel 사용처에서 콜백 메모이제이션 확인

# LoopCarousel을 import하는 파일들 찾기
echo "=== LoopCarousel 사용처 찾기 ==="
rg -n "LoopCarousel" --type=tsx --type=ts -A 10 -B 2

# useCallback 사용 패턴 확인
echo -e "\n=== useCallback 사용 패턴 확인 ==="
rg -nP "useCallback.*onSelectIndex|useCallback.*onReady" --type=tsx --type=ts -C 3

Length of output: 113


🏁 Script executed:

#!/bin/bash
# Description: LoopCarousel 사용처 및 콜백 메모이제이션 확인 (수정된 스크립트)

# LoopCarousel을 import하는 파일들 찾기
echo "=== LoopCarousel 사용처 찾기 ==="
rg -n "LoopCarousel" -g "*.ts" -g "*.tsx" -A 15 -B 2

echo -e "\n=== LoopCarousel 컴포넌트 자체 확인 ==="
cat src/shared/ui/LoopCarousel.tsx

echo -e "\n=== useCallback 사용 현황 ==="
rg -n "useCallback" -g "*.tsx" -g "*.ts" -C 2 | head -50

Length of output: 16569


부모 컴포넌트에서 콜백에 useCallback 적용 필요

LoopCarousel에 전달되는 onSelectIndexonReady 콜백이 의존성 배열에 포함되어 있어, 메모이제이션되지 않은 콜백은 부모 컴포넌트 렌더링 시마다 새 참조를 생성하고 useEffect가 불필요하게 재실행됩니다.

수정 필요 위치:

  • src/pages/mycd/ui/PlaylistCarousel.tsx: handleSelectIndexuseCallback으로 감싸기
  • src/pages/home/ui/HomeCarousel.tsx: 인라인 콜백들을 useCallback으로 변환하기
🤖 Prompt for AI Agents
In src/shared/ui/LoopCarousel.tsx around lines 21 to 36, the review warns that
parent callbacks passed as props (onSelectIndex, onReady) should be memoized to
avoid re-running this useEffect on every parent render; update the parent files
listed: in src/pages/mycd/ui/PlaylistCarousel.tsx wrap handleSelectIndex with
React.useCallback (include the correct dependency array for values it uses) and
in src/pages/home/ui/HomeCarousel.tsx convert the inline callbacks passed to
LoopCarousel into useCallback hooks (again supplying the proper dependencies) so
the prop references remain stable and the effect in LoopCarousel only re-runs
when intended.

Copy link
Member

@hansololiviakim hansololiviakim left a comment

Choose a reason for hiding this comment

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

끝날듯 끝나지 않는 1차..! 수고하셨습니다👏👏
가현님 혹시 마이페이지 트랙리스트에서 재생버튼 클릭해서 /mycd 넘어가는 경우는
어떻게 데이터 넘기면 될까요?

@maylh
Copy link
Collaborator Author

maylh commented Oct 23, 2025

끝날듯 끝나지 않는 1차..! 수고하셨습니다👏👏 가현님 혹시 마이페이지 트랙리스트에서 재생버튼 클릭해서 /mycd 넘어가는 경우는 어떻게 데이터 넘기면 될까요?

오 ...! 이거 생각을 못 했는데 이제 보니까 모두 재생이 있네요 😶
/mycd/:playlistId 로 넘겨주시면 될 것 같아요 !
지금은 /mycd에서 인덱스로 관리 중인데, /mycd/:playlistId에 대응 가능하도록 수정하겠습니다

Copy link

@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: 0

🧹 Nitpick comments (1)
src/stories/Playlist.stories.tsx (1)

6-7: Storybook 환경에 맞게 QueryClient를 구성하세요.

설정 없이 생성된 QueryClient 인스턴스가 모든 스토리에서 공유되면 다음과 같은 문제가 발생할 수 있습니다:

  • 스토리 간 캐시 오염 (한 스토리의 데이터가 다른 스토리에 영향)
  • 기본 재시도 동작(3회)으로 인한 느린 스토리 로딩
  • 스토리 간 상태 공유로 인한 불안정한 동작

Storybook 환경에 적합하도록 QueryClient를 구성하는 것을 권장합니다.

다음 diff를 적용하여 Storybook에 적합한 설정을 추가하세요:

-const queryClient = new QueryClient()
+const queryClient = new QueryClient({
+  defaultOptions: {
+    queries: {
+      retry: false,
+      cacheTime: 0,
+      staleTime: 0,
+    },
+  },
+})

또는 각 스토리마다 격리된 QueryClient를 제공하려면 decorator를 다음과 같이 수정할 수 있습니다:

   decorators: [
     (Story) => (
-      <QueryClientProvider client={queryClient}>
+      <QueryClientProvider client={new QueryClient({ defaultOptions: { queries: { retry: false } } })}>
         <Story />
       </QueryClientProvider>
     ),
   ],
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f6d482 and 961b659.

📒 Files selected for processing (1)
  • src/stories/Playlist.stories.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: ## 1. 일반적인 코딩 컨벤션

포맷팅

  • .prettierrc 설정에 따라 포맷팅 확인
  • 들여쓰기: 2칸 스페이스
  • 최대 줄 길이: 100자
  • 세미콜론 사용 안함
  • 따옴표: 작은따옴표 사용
  • 괄호 안 공백: 있음
  • 화살표 함수 괄호: 항상 사용
  • 줄바꿈: LF 사용

네이밍 컨벤션

  • 컴포넌트: PascalCase (예: UserProfile)
  • 유틸리티/훅/변수: camelCase (예: getUserData, useUserInfo)
  • 상수: UPPER_SNAKE_CASE (예: API_BASE_URL)
  • 이미지 파일: kebab-case (예: user-profile-icon.png)

주석 사용

  • 복잡한 로직에만 주석 추가
  • 불필요한 주석 지양 (코드로 설명 가능한 것)
  • TODO/FIXME 형식: // TODO: 설명 - 작성자

가독성

  • 매직 넘버 지양, 의미있는 상수 사용
  • 함수는 하나의 책임만 가지도록 작성 (최대 20줄 권장)
  • 중첩 깊이 최소화 (3단계 이하 권장)

2. React 모범 사례

컴포넌트 작성

  • 최신 React hooks 사용 권장
  • 컴포넌트는 단일 책임 원칙 준수
  • Presentational/Container 컴포넌트 분리
  • 성능 최적화: memo, useCallback, useMemo 적절히 사용
  • 대용량 리스트는 가상화 라이브러리 사용 고려

상태 관리

  • Zustand와 Tanstack Query를 일관되게 사용
  • 상태 구조는 정규화된 형태로 관리
  • 에러 처리: Error Boundary와 try-catch 또는 onError 콜백 활용

3. 스타일링

Styled Components

  • Styled Components 일관되게 사용
  • 스타일드 컴포넌트명은 의미있게 작성
  • 동적 스타일링은 props나 CSS 변수 활용
  • 테마 시스템 활용하여 글로벌 스타일 관리
  • 재사용 가능한 스타일은 mixin이나 확장으로 관리
  • CSS 포맷팅 가독성 유지
  • 사용하지 않는 스타일이나 중복 스타일 제거

4. Vite 및 빌드 최적화

  • 모듈 import 최적화 (tree-shaking 고려)
  • 환경변수는 .env 파일로 관리
  • vite.config.ts에서 빌드 성능 튜닝 (sourcemap 설정, 플러그인 최적화 등)

5. 아키텍처 및 개발 환경

폴더 구조

  • Feature-Sliced Design (FSD) 구조 준수
  • 레이어별 참조 규칙 엄격히 적용

타입스크립트

  • strict 모드 사용
  • 타입 명시적으로 작성 (any 사용 지양)
  • path alias (@/_) 절대 경로 import 사용

Git 훅

  • Husky 설정으로 pre-commit, commit-msg 린팅 확인

6. 기타 가이드라인

  • 충분한 근거와 함께 리뷰 제공
  • 정보 검증 후 답변
  • 간결하고 명확한 응답
  • 필요시 추가 컨텍스트 요청
  • 검증되지 않은 주장 지양
  • 가능한 경우 출처 명시
  • 별도 언급 없으면 JavaScript 기준
  • 한국어로 응답
  • 대부분 브라우저에서 지원하는 ES6+ 기능 활용
  • 코드 리뷰를 통한 유지보수성 향상에 적극 활용

Files:

  • src/stories/Playlist.stories.tsx
🔇 Additional comments (2)
src/stories/Playlist.stories.tsx (2)

2-2: React Query import가 올바르게 추가되었습니다.

Playlist 컴포넌트가 이제 React Query 훅을 사용하는 것으로 보이며, 스토리북에서 이를 지원하기 위한 적절한 import입니다.


12-18: 데코레이터 구조가 올바릅니다.

QueryClientProvider로 스토리를 감싸는 방식은 React Query context를 제공하는 표준적인 Storybook 패턴입니다.

@hansololiviakim
Copy link
Member

끝날듯 끝나지 않는 1차..! 수고하셨습니다👏👏 가현님 혹시 마이페이지 트랙리스트에서 재생버튼 클릭해서 /mycd 넘어가는 경우는 어떻게 데이터 넘기면 될까요?

오 ...! 이거 생각을 못 했는데 이제 보니까 모두 재생이 있네요 😶 /mycd/:playlistId 로 넘겨주시면 될 것 같아요 ! 지금은 /mycd에서 인덱스로 관리 중인데, /mycd/:playlistId에 대응 가능하도록 수정하겠습니다

감사합니다~! 넘길 때 /mycd 위에 탭 부분 '나의 CD' / '좋아요한 CD' 까지 선택되게도 가능할까요?!
나의 CD 트랙리스트에서 넘어가는 케이스가 있고 좋아요한 CD 트랙리스트에서 넘어가는 케이스가 있는데
이전에 회의 때 그렇게 켜졌으면 좋겠다고 다른 분들이 말씀하셨던 것 같아요~!

@maylh
Copy link
Collaborator Author

maylh commented Oct 23, 2025

끝날듯 끝나지 않는 1차..! 수고하셨습니다👏👏 가현님 혹시 마이페이지 트랙리스트에서 재생버튼 클릭해서 /mycd 넘어가는 경우는 어떻게 데이터 넘기면 될까요?

오 ...! 이거 생각을 못 했는데 이제 보니까 모두 재생이 있네요 😶 /mycd/:playlistId 로 넘겨주시면 될 것 같아요 ! 지금은 /mycd에서 인덱스로 관리 중인데, /mycd/:playlistId에 대응 가능하도록 수정하겠습니다

감사합니다~! 넘길 때 /mycd 위에 탭 부분 '나의 CD' / '좋아요한 CD' 까지 선택되게도 가능할까요?! 나의 CD 트랙리스트에서 넘어가는 케이스가 있고 좋아요한 CD 트랙리스트에서 넘어가는 케이스가 있는데 이전에 회의 때 그렇게 켜졌으면 좋겠다고 다른 분들이 말씀하셨던 것 같아요~!

앗 그러면 /mycd/:playlistId?type='my' 이런 식으로 라우팅 처리 부탁드릴게요 !!
나의 cd에서 넘어오는 경우에는 type='my', 좋아요에서 넘어오는 경우에는 type='like'로 구분되도록 해주시면 될 것 같습니다 ~~!

요거까지 반영하고 머지할게요 ! -> 새 이슈에서 진행

@maylh maylh merged commit f5e7937 into develop Oct 24, 2025
2 of 3 checks passed
@maylh maylh deleted the fix/#113/cd-api-ui branch October 24, 2025 02:16
@maylh maylh added the HIGH 빠르게 처리해야 하는 높은 우선순위 label Oct 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

HIGH 빠르게 처리해야 하는 높은 우선순위

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[fix] CD response 백엔드 변경사항 및 UI 수정사항 반영

2 participants