Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit 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 모듈에서 백엔드 엔드포인트 경로를 신규 기준으로 일괄 수정하고, 플레이리스트 셔플 조회용 공개 함수 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant UI as UI
participant API as Frontend API Client
participant BE as Backend (/main)
rect rgba(198,235,247,0.3)
note over UI,BE: 신규 셔플 플레이리스트 조회 흐름
UI->>API: getShufflePlaylists(params)
API->>BE: GET /main/playlist/browse?{params}
BE-->>API: 200 PlaylistResponse
API-->>UI: PlaylistResponse
end
sequenceDiagram
autonumber
participant UI as UI
participant API as Frontend API Client
participant BE as Backend (/main)
rect rgba(224,255,224,0.35)
note over UI,BE: 팔로우 생성/삭제/조회 흐름 (경로 통합)
UI->>API: postFollow(playlistId)
API->>BE: POST /main/follow/{playlistId}
BE-->>API: 200 OK
API-->>UI: Result
UI->>API: getFollowStatus(playlistId)
API->>BE: GET /main/follow/{playlistId}
BE-->>API: 200 Status
API-->>UI: Status
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks (4 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. 📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ Finishing touches
🧪 Generate unit tests
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.
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 엔드포인트 경로를 전반적으로 수정하여 백엔드와의 통신 일관성을 확보하고, 기존에 발생할 수 있었던 API 호출 관련 문제를 해결하는 데 중점을 둡니다. 변경된 경로는 보다 직관적이고 표준화된 구조를 따르며, 이는 향후 유지보수성을 높이고 API 연동의 안정성을 개선할 것입니다.
Highlights
- API 엔드포인트 경로 일관성 개선: 여러 API 호출 경로에서
/main/mypage/playlists/,/main/browse/playlists/,/main/playlists/등의 패턴을/main/playlist/mypage/,/main/playlist/browse/,/main/playlist/등으로 변경하여 엔드포인트 구조를 표준화했습니다. - 프로필 관련 API 경로 업데이트: 사용자 프로필 업데이트 API (
patchProfile)의 엔드포인트가/main/mypage/playlists/me에서/main/user/profile로 수정되었습니다. - 팔로우 및 추천 관련 API 경로 간소화: 팔로우 및 추천 기능 관련 API 엔드포인트가
/main/browse/playlists/{playlistId}/follow에서/main/follow/{playlistId}로,/main/playlists/recommendations/에서/main/recommendation/으로 간소화되었습니다.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| 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 issue 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
-
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. ↩
|
🎵 Storybook Link 🎵 |
| // 나의 CD 리스트 조회 | ||
| export const getMyCdList = (sort: string) => { | ||
| return api.get<MyCdListResponse>(`/main/mypage/playlists/me?sort=${sort}`) | ||
| return api.get<MyCdListResponse>(`/main/playlist/mypage/me?sort=${sort}`) |
There was a problem hiding this comment.
이 파일 전체에 걸쳐 API 엔드포인트 경로가 하드코딩된 문자열('매직 스트링')로 사용되고 있습니다. 이는 스타일 가이드에 위배되며1, 향후 경로 변경 시 여러 곳을 수정해야 하는 유지보수 문제를 야기합니다.
API 경로들을 중앙에서 관리하는 상수 객체나 헬퍼 함수로 추출하는 것을 강력히 권장합니다. 예를 들어, src/shared/api/paths.ts 같은 파일을 만들어 경로를 관리할 수 있습니다.
중복 예시:
deleteMyPagePlaylist와getPlaylistDetail함수에서'/main/playlist/${playlistId}'경로가 중복됩니다.
리팩토링 제안:
// src/shared/api/paths.ts (예시)
const API_BASE = '/main';
export const PLAYLIST_PATHS = {
MY_PAGE: `${API_BASE}/playlist/mypage/me`,
BY_ID: (id: number) => `${API_BASE}/playlist/${id}`,
BROWSE: `${API_BASE}/playlist/browse`,
// ... 기타 경로
};
// 사용 예시 (playlist.ts)
import { PLAYLIST_PATHS } from '@/shared/api/paths';
export const getMyCdList = (sort: string) => {
return api.get<MyCdListResponse>(`${PLAYLIST_PATHS.MY_PAGE}?sort=${sort}`)
}
export const deleteMyPagePlaylist = (playlistId: number) => {
return api.delete<string | null>(PLAYLIST_PATHS.BY_ID(playlistId))
}이러한 접근 방식은 코드의 가독성, 일관성 및 유지보수성을 크게 향상시킬 것입니다. 이 PR에서 수정된 모든 API 파일에 이 패턴을 적용하는 것을 고려해보세요.
Style Guide References
Footnotes
| return api.post<FinalPlaylistResponse>('/main/playlist/final', payload) | ||
| } | ||
|
|
||
| // 플레이리스트 수정: 세션 임시본 사용 + cd 수정 | ||
| export const patchEditPlaylist = (payload: EditPlaylistPayload) => { | ||
| return api.patch<FinalPlaylistResponse>('/main/mypage/playlists/final', payload) | ||
| return api.patch<FinalPlaylistResponse>('/main/playlist/final', payload) |
There was a problem hiding this comment.
postFinalPlaylist와 patchEditPlaylist 함수에서 동일한 API 엔드포인트 '/main/playlist/final'가 중복으로 사용되고 있습니다. 이는 '매직 스트링' 사용에 해당하며, 코드 중복을 야기합니다.1
이 경로를 파일 상단에 상수로 추출하여 중복을 제거하고 코드의 명확성을 높이는 것을 권장합니다.
const FINAL_PLAYLIST_PATH = '/main/playlist/final';
// ...
// 플레이리스트 생성: 세션 임시본 사용 + cd 요청
export const postFinalPlaylist = (payload: FinalPlaylistPayload) => {
return api.post<FinalPlaylistResponse>(FINAL_PLAYLIST_PATH, payload)
}
// 플레이리스트 수정: 세션 임시본 사용 + cd 수정
export const patchEditPlaylist = (payload: EditPlaylistPayload) => {
return api.patch<FinalPlaylistResponse>(FINAL_PLAYLIST_PATH, payload)
}Style Guide References
Footnotes
| export const postFollow = (playlistId: number) => { | ||
| return api.post(`/main/browse/playlists/${playlistId}/follow`) | ||
| return api.post(`/main/follow/${playlistId}`) | ||
| } | ||
|
|
||
| export const deleteFollow = (playlistId: number) => { | ||
| return api.delete(`/main/browse/playlists/${playlistId}/follow`) | ||
| return api.delete(`/main/follow/${playlistId}`) | ||
| } | ||
|
|
||
| export const getFollowStatus = (playlistId: number) => { | ||
| return api.get(`/main/browse/playlists/${playlistId}/follow`) | ||
| return api.get(`/main/follow/${playlistId}`) | ||
| } |
There was a problem hiding this comment.
세 함수(postFollow, deleteFollow, getFollowStatus)에서 동일한 API 엔드포인트 경로를 생성하는 로직이 반복되고 있습니다. 이는 '매직 스트링' 사용 및 코드 중복에 해당합니다.1
경로를 생성하는 헬퍼 함수를 만들어 중복을 제거하고, 코드의 일관성과 유지보수성을 높이는 것을 제안합니다.
| export const postFollow = (playlistId: number) => { | |
| return api.post(`/main/browse/playlists/${playlistId}/follow`) | |
| return api.post(`/main/follow/${playlistId}`) | |
| } | |
| export const deleteFollow = (playlistId: number) => { | |
| return api.delete(`/main/browse/playlists/${playlistId}/follow`) | |
| return api.delete(`/main/follow/${playlistId}`) | |
| } | |
| export const getFollowStatus = (playlistId: number) => { | |
| return api.get(`/main/browse/playlists/${playlistId}/follow`) | |
| return api.get(`/main/follow/${playlistId}`) | |
| } | |
| const followPath = (playlistId: number) => `/main/follow/${playlistId}` | |
| export const postFollow = (playlistId: number) => { | |
| return api.post(followPath(playlistId)) | |
| } | |
| export const deleteFollow = (playlistId: number) => { | |
| return api.delete(followPath(playlistId)) | |
| } | |
| export const getFollowStatus = (playlistId: number) => { | |
| return api.get(followPath(playlistId)) | |
| } |
Style Guide References
Footnotes
| export const getRecommendationsByRecent = () => { | ||
| return api.get<RecommendationsResponse>('/main/playlists/recommendations/playlist') | ||
| return api.get<RecommendationsResponse>('/main/recommendation/playlist') | ||
| } | ||
|
|
||
| export const getRecommendationsByFollow = () => { | ||
| return api.get<RecommendationsResponse>('/main/playlists/recommendations/follow') | ||
| return api.get<RecommendationsResponse>('/main/recommendation/follow') | ||
| } | ||
|
|
||
| export const getRecommendedGenres = () => { | ||
| return api.get<RecommendedGenresResponse>('/main/playlists/recommendations/genres') | ||
| return api.get<RecommendedGenresResponse>('/main/recommendation/genres') | ||
| } |
There was a problem hiding this comment.
API 엔드포인트들이 하드코딩된 문자열로 작성되어 있습니다. 이는 '매직 스트링' 사용에 해당하며, 유지보수를 어렵게 만듭니다.1
공통 경로인 /main/recommendation을 기반으로 상수 객체를 만들어 관리하면 가독성과 유지보수성이 향상됩니다.
| export const getRecommendationsByRecent = () => { | |
| return api.get<RecommendationsResponse>('/main/playlists/recommendations/playlist') | |
| return api.get<RecommendationsResponse>('/main/recommendation/playlist') | |
| } | |
| export const getRecommendationsByFollow = () => { | |
| return api.get<RecommendationsResponse>('/main/playlists/recommendations/follow') | |
| return api.get<RecommendationsResponse>('/main/recommendation/follow') | |
| } | |
| export const getRecommendedGenres = () => { | |
| return api.get<RecommendedGenresResponse>('/main/playlists/recommendations/genres') | |
| return api.get<RecommendedGenresResponse>('/main/recommendation/genres') | |
| } | |
| const RECOMMENDATION_PATH = '/main/recommendation' | |
| export const getRecommendationsByRecent = () => { | |
| return api.get<RecommendationsResponse>(`${RECOMMENDATION_PATH}/playlist`) | |
| } | |
| export const getRecommendationsByFollow = () => { | |
| return api.get<RecommendationsResponse>(`${RECOMMENDATION_PATH}/follow`) | |
| } | |
| export const getRecommendedGenres = () => { | |
| return api.get<RecommendedGenresResponse>(`${RECOMMENDATION_PATH}/genres`) | |
| } |
Style Guide References
Footnotes
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/features/recommend/api/recommend.ts (1)
8-9: 엔드포인트 정리 잘 됐습니다 + 베이스 경로 상수화 제안
/main/recommendation/*로 일관화된 점 좋습니다. 추후 변경 용이성을 위해 베이스 경로를 상수로 두는 것을 고려해주세요(예:const RECO_BASE = '/main/recommendation').Also applies to: 12-13, 16-17
src/features/profile/api/profile.ts (1)
13-17: multipart 업로드 시 Content-Type 수동 지정 제거 권장브라우저가
FormData에 맞춰 boundary 포함 헤더를 자동 설정합니다. 수동 지정 시 boundary 누락으로 서버 파싱 이슈가 날 수 있습니다. 아래처럼 헤더 제거를 권장합니다.- return api.patch<ProfileResponse>('/main/user/profile', formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }) + return api.patch<ProfileResponse>('/main/user/profile', formData)src/features/follow/api/follow.ts (1)
4-5: 엔드포인트 변경 OK + 응답 타입 명시 확인경로 이전은 적절합니다. 다만 제네릭을 생략해 응답 타입이 암시적
any에 가까워질 수 있습니다. BE 스펙에 맞춰 반환 타입(예:void | null | { followed: boolean })을 명시해 strict 모드에서 안전성을 높여주세요.Also applies to: 8-9, 12-13
src/entities/playlist/api/playlist.ts (2)
15-16: 쿼리스트링 직접 문자열화 대신 Axios params 사용 제안
sort는 객체 파라미터로 넘기는 편이 안전하고 일관적입니다(인코딩/캐시 키 관리 용이).-export const getMyCdList = (sort: string) => { - return api.get<MyCdListResponse>(`/main/playlist/mypage/me?sort=${sort}`) -} +export const getMyCdList = (sort: string) => { + return api.get<MyCdListResponse>('/main/playlist/mypage/me', { params: { sort } }) +} -export const getMyFollowingList = (sort: string) => { - return api.get<MyFollowingListResponse>(`/main/playlist/mypage/me/follows?sort=${sort}`) -} +export const getMyFollowingList = (sort: string) => { + return api.get<MyFollowingListResponse>('/main/playlist/mypage/me/follows', { params: { sort } }) +}Also applies to: 20-21
55-56: 정적 문자열에 백틱 사용 — 컨벤션에 맞춰 작은따옴표로 교체 권장템플릿 리터럴이 불필요한 구간은 작은따옴표가 가이드에 부합합니다.
-export const postPlaylistStart = (playlistId: number) => { - return api.post(`/main/playlist/browse/start`, null, { params: { playlistId } }) -} +export const postPlaylistStart = (playlistId: number) => { + return api.post('/main/playlist/browse/start', null, { params: { playlistId } }) +} -export const postPlaylistConfirm = (playlistId: number) => { - return api.post(`/main/playlist/browse/confirm`, null, { params: { playlistId } }) -} +export const postPlaylistConfirm = (playlistId: number) => { + return api.post('/main/playlist/browse/confirm', null, { params: { playlistId } }) +}Also applies to: 60-61
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/entities/playlist/api/playlist.ts(2 hunks)src/features/customize/api/customize.ts(2 hunks)src/features/follow/api/follow.ts(1 hunks)src/features/profile/api/profile.ts(1 hunks)src/features/recommend/api/recommend.ts(1 hunks)src/features/share/api/share.ts(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/features/recommend/api/recommend.tssrc/features/customize/api/customize.tssrc/features/share/api/share.tssrc/features/follow/api/follow.tssrc/features/profile/api/profile.tssrc/entities/playlist/api/playlist.ts
🧬 Code graph analysis (6)
src/features/recommend/api/recommend.ts (2)
src/shared/api/httpClient.ts (1)
api(5-19)src/features/recommend/types/recommend.ts (2)
RecommendationsResponse(17-17)RecommendedGenresResponse(24-24)
src/features/customize/api/customize.ts (2)
src/shared/api/httpClient.ts (1)
api(5-19)src/features/customize/types/customize.ts (2)
FinalPlaylistResponse(72-75)EditPlaylistPayload(58-61)
src/features/share/api/share.ts (1)
src/shared/api/httpClient.ts (1)
api(5-19)
src/features/follow/api/follow.ts (1)
src/shared/api/httpClient.ts (1)
api(5-19)
src/features/profile/api/profile.ts (2)
src/shared/api/httpClient.ts (1)
api(5-19)src/features/profile/types/profile.ts (1)
ProfileResponse(7-11)
src/entities/playlist/api/playlist.ts (2)
src/shared/api/httpClient.ts (1)
api(5-19)src/entities/playlist/types/playlist.ts (7)
MyCdListResponse(9-9)MyFollowingListResponse(18-21)MyPlaylistResponse(46-49)PlaylistParams(109-113)PlaylistResponse(102-107)PlaylistDetailResponse(72-80)MyRepresentResponse(115-124)
🔇 Additional comments (5)
src/features/customize/api/customize.ts (2)
20-21: 엔드포인트 교체 LGTM
/main/playlist/temp로의 이전 적절합니다. 호환성 문제 없을 듯합니다.
42-48: POST와 PATCH가 동일 경로(/main/playlist/final)를 사용 — 백엔드 계약 확인 필요생성(POST)과 수정(PATCH)이 동일 경로를 사용합니다. BE가 body 내
playlistId로 라우팅한다면 OK지만, 일반적으로는/main/playlist/{id}or/main/playlist/{id}/final처럼 id path param을 기대하는 경우가 많습니다. 현재 500 에러 원인 후보이므로 계약 상세를 한 번 더 확인해주세요.src/features/share/api/share.ts (1)
5-6: 응답 타입 확인 요청 (string 가정)
postShare가string(공유 코드) 응답을 가정합니다. BE가 JSON 래핑 없이 순수 문자열을 반환하는지(or{ code: string }) 확인 부탁드립니다. 타입 불일치가 있으면 호출부 파싱 로직 영향 가능성이 있습니다.src/entities/playlist/api/playlist.ts (2)
30-31: 주요 경로 이전 LGTM마이페이지 조회/삭제/대표 설정, 상세/조회수 조회 경로 모두 새 기준과 일치합니다.
Also applies to: 35-36, 40-41, 50-51, 65-66
45-46: 셔플 리스트 API 추가 LGTM
/main/playlist/browse+params패턴 일관성 좋습니다.
hansololiviakim
left a comment
There was a problem hiding this comment.
오 전체적으로 다 바꿔주셨군요 감사합니다! 저도 내일 타입 흩어져있는거랑 하드코딩한 부분들 전반적으로 확인해보겠습니다👀
🛰️ 관련 이슈
✨ 주요 변경 사항
1️⃣ api 엔드포인트 경로 수정사항 반영했습니다.
🔍 테스트 방법 / 체크리스트
🗯️ PR 포인트
(로그인이랑 플리 생성이 안되고 있어서 테스트를 못해보는 중 ..)체크 완료++)
작업 내용은 아니고, 마일스톤에
1차 스프린트를 만들어 두었습니다.이번 스프린트에서 진행하는 task들은 별도로 카테고리화 시키지 않고 (api 연동, 퍼블리싱 ..) 모두 해당 스프린트 안에 넣어두면 될 것 같습니다 !
🚀 알게된 점
📖 참고 자료 (선택)
Summary by CodeRabbit
New Features
Refactor