-
Notifications
You must be signed in to change notification settings - Fork 4
Fix: 라우트 순서 충돌로 컬렉션 순서 변경 API 동작 문제 해결 #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* feat: Youtube 영상 검색 api 구현 * feat: YouTube 영상 상세 조회 API 구현 및 DB 저장 기능 추가 * Fix: Add authentication middleware (requireAuth) to search route * Chore: remove unnecessary folder * Chore: format code using Prettier * Update src/controllers/youtubeDetailController.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Chore: format code using Prettier * fix: add .js extensions to import paths in controller and route for deployment * Fix: resolved merge conflict after pulling from upstream main * feat: Youtube 영상 검색 api 구현 * feat: YouTube 영상 상세 조회 API 구현 및 DB 저장 기능 추가 * Fix: Add authentication middleware (requireAuth) to search route * Chore: remove unnecessary folder * Chore: format code using Prettier * Update src/controllers/youtubeDetailController.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Chore: format code using Prettier * fix: add .js extensions to import paths in controller and route for deployment * Fix: resolved merge conflict after pulling from upstream main * Feat: bookmark API 개발 및 schema 수정 * Feat: Bookmark message, timeline parsing을 위한 util 함수 추가 * Fix: 북마크 생성 시 parsing 함수 적용 및 timeline 분리 처리 * Update src/dtos/bookmarkDto.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update src/controllers/bookmarkController.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Refactor: 북마크 기반 방 생성 로직 정리 및 유효성 검사 강화 * feat: Add validation for maxParticipants in createRoomFromBookmark * Add /api prefix to bookmark-related endpoints * Refactor: Move Prisma client import to lib/prisma.js * Feat: 사용자 추천 관련 api 구현 * Feat: 일일 추천 시 인기도(popularity) 증가 로직 추가 * chore: upstream main 병합 --------- Co-authored-by: DongilMin <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* feat: Youtube 영상 검색 api 구현 * feat: YouTube 영상 상세 조회 API 구현 및 DB 저장 기능 추가 * Fix: Add authentication middleware (requireAuth) to search route * Chore: remove unnecessary folder * Chore: format code using Prettier * Update src/controllers/youtubeDetailController.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Chore: format code using Prettier * fix: add .js extensions to import paths in controller and route for deployment * Fix: resolved merge conflict after pulling from upstream main * feat: Youtube 영상 검색 api 구현 * feat: YouTube 영상 상세 조회 API 구현 및 DB 저장 기능 추가 * Fix: Add authentication middleware (requireAuth) to search route * Chore: remove unnecessary folder * Chore: format code using Prettier * Update src/controllers/youtubeDetailController.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Chore: format code using Prettier * fix: add .js extensions to import paths in controller and route for deployment * Fix: resolved merge conflict after pulling from upstream main * Feat: bookmark API 개발 및 schema 수정 * Feat: Bookmark message, timeline parsing을 위한 util 함수 추가 * Fix: 북마크 생성 시 parsing 함수 적용 및 timeline 분리 처리 * Update src/dtos/bookmarkDto.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update src/controllers/bookmarkController.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Refactor: 북마크 기반 방 생성 로직 정리 및 유효성 검사 강화 * feat: Add validation for maxParticipants in createRoomFromBookmark * Add /api prefix to bookmark-related endpoints * Refactor: Move Prisma client import to lib/prisma.js * Feat: 사용자 추천 관련 api 구현 * Chore: Remove unintended sharedCollection-related files * Feat: 공유받은 컬렉션 목록 조회 API 구현 및 upstream main 병합 - upstream main 브랜치 최신 내용 pull 및 병합 완료 - 공유받은 컬렉션 목록 조회 기능 추가 * Fix: Remove unrelated code for collection acceptance/rejection, keep shared collection list API --------- Co-authored-by: DongilMin <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…ature/collection-api
…ature/collection-api
…ature/collection-api
…ature/collection-api
…ature/collection-api
Walkthrough
Sequence Diagram(s)sequenceDiagram
autonumber
actor C as Client
participant R as Router (/api/collections)
participant A as requireAuth
participant Ctrl as collectionController
participant DB as Prisma/DB
C->>R: PUT /order { collectionOrders[] }
R->>A: 인증 검증
A-->>R: 인증 통과 / 실패
alt 인증 실패
R-->>C: 401 Unauthorized
else 인증 성공
R->>Ctrl: updateCollectionOrder(dto)
Ctrl->>DB: 트랜잭션으로 순서 업데이트
alt 성공
DB-->>Ctrl: 업데이트 결과
Ctrl-->>C: 200 OK (결과)
else 오류
DB-->>Ctrl: 에러
Ctrl-->>C: 4xx/5xx (에러)
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/services/collectionService.ts (2)
109-113:room.video.*접근은 relation 제거 시 즉시 실패
videoTitle: room.video.title,videoThumbnail: room.video.thumbnail접근은 relation이 없으면 타입/런타임 오류입니다.옵션 B를 택하는 경우의 개략적 접근:
- 1차 쿼리에서는
room: true까지만 includeconst videoIds = uniq(collection.bookmarks.map(b => b.room.videoId).filter(Boolean))const videos = await prisma.youtubeVideo.findMany({ where: { videoId: { in: videoIds }}})const videoMap = new Map(videos.map(v => [v.videoId, v]))- 매핑 시
videoMap.get(room.videoId)?.title사용원하시면 이 경로로 수정된 코드 패치를 제안드리겠습니다.
61-68: 심각: Prisma 스키마와 코드의 Room–YoutubeVideo 관계 불일치
Prisma schema에 Room.video / YoutubeVideo.rooms 관계가 정의되어 있지 않으므로,include: { video: true }구문이 컴파일/런타임 오류를 유발합니다.수정이 필요한 위치:
- src/services/collectionService.ts
include: { bookmarks: { include: { room: { include: { video: true } } } } }(약 61–68행)room.video.title,room.video.thumbnail접근부 (약 110–112행)다음 중 하나로 정합성을 맞춰주세요:
- 옵션 A (권장): Prisma schema에 Room.video ↔ YoutubeVideo.rooms relation을 복원
- 옵션 B: relation 없이
room.videoId로 YoutubeVideo를 별도 조회한 뒤 매핑
🧹 Nitpick comments (5)
src/services/collectionService.ts (3)
36-40: 정렬 기준 미지정: 재정렬 결과가 목록 조회에 반영되지 않을 수 있음컬렉션 순서 변경 API의 의도와 일관되게 목록 조회에도 정렬을 적용하는 것을 권장합니다.
다음과 같이 orderBy를 추가해 주세요:
const collections = await prisma.collection.findMany({ where: { userId: userId, }, + orderBy: { order: 'asc' }, });
203-211: 업데이트 검증 부재: 일부 ID가 유효하지 않아도 조용히 성공 처리될 수 있음
updateMany는 일치 항목이 없으면 0건 업데이트하고 에러가 나지 않습니다. 전체 일괄 변경의 신뢰성을 위해 각 항목이 실제로 1건 업데이트되었는지 검증하는 것이 좋습니다.다음과 같이 트랜잭션 내에서 count를 검증하세요:
-export const updateCollectionOrder = async ( - userId: number, - collectionOrders: { collectionId: number; order: number }[], -) => { - await prisma.$transaction( - collectionOrders.map(({ collectionId, order }) => - prisma.collection.updateMany({ - where: { collectionId, userId }, - data: { order }, - }), - ), - ); -}; +export const updateCollectionOrder = async ( + userId: number, + collectionOrders: { collectionId: number; order: number }[], +) => { + await prisma.$transaction(async tx => { + for (const { collectionId, order } of collectionOrders) { + const result = await tx.collection.updateMany({ + where: { collectionId, userId }, + data: { order }, + }); + if (result.count !== 1) { + throw new AppError('COLLECTION_001', `컬렉션(${collectionId})을 찾을 수 없거나 권한이 없습니다.`); + } + } + }); +};
206-208:order필드 명명 주의SQL 예약어와 충돌 가능성이 있어 혼동을 줄 수 있습니다. 스키마 변경 여지가 생길 때
sortOrder등의 명칭으로 마이그레이션을 고려해 주세요.src/routes/collectionRoutes.ts (2)
107-149: Swagger 문서 내 components 정의의 위치
components.schemas.ReorderCollectionsDto를 해당 경로 블록 안에서 정의하고 있습니다. swagger-jsdoc 병합으로 동작하긴 하나, 유지보수/중복 방지를 위해 공통 components 영역(공용 파일/상단 블록)으로 이동하는 것을 권장합니다.이점:
- 스키마 재사용 용이
- 중복/충돌 감소
- 리뷰 및 변경 추적 용이
116-124: 요청 본문 유효성 검증 미들웨어 권장스키마는 문서화되어 있으나 런타임 검증이 보이지 않습니다. 재정렬 API 특성상 잘못된 ID/중복 order 값이 들어오면 데이터 정합성 문제가 생길 수 있습니다.
제안:
express-validator또는zod기반 미들웨어 추가- 규칙 예: collectionOrders 필수, 각 요소 {collectionId:number>0, order: 정수, unique}, 최소 1개 이상
원하시면 DTO 기반 검증 미들웨어 초안을 제공하겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
prisma/schema.prisma(0 hunks)src/routes/collectionRoutes.ts(1 hunks)src/services/collectionService.ts(1 hunks)
💤 Files with no reviewable changes (1)
- prisma/schema.prisma
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/routes/collectionRoutes.ts (1)
src/middleware/authMiddleware.ts (1)
requireAuth(20-75)
🔇 Additional comments (2)
src/services/collectionService.ts (1)
211-211: 변경 없음(LGTM): EOF 포맷만 수정파일 내용상의 동작 변화는 없습니다.
src/routes/collectionRoutes.ts (1)
150-150: PUT /order 라우트 우선순위 충돌 해결 확인 (LGTM)
- 코드에서
/order가 동적 라우트(/:collectionId)보다 150라인에 먼저 선언되어 충돌이 해소되었습니다.- 전체 레포에 중복된
/order경로 정의가 없습니다.- Swagger/OpenAPI 스펙에도
/order와/:collectionId가 각각 한 번씩만 정의되어 있고, 문서와 실제 라우트가 일치하는지 최종 확인 부탁드립니다.
…ature/collection-api
…-AIR-mate-backend into feature/collection-api
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
prisma/schema.prisma(2 hunks)
🔇 Additional comments (1)
prisma/schema.prisma (1)
309-309: YoutubeVideo.rooms 역참조 정의는 현재 상태로 충분합니다 (단일 관계 시 자동 추론 가능)두 모델 간 단일 관계만 존재하므로 @relation 이름을 생략해도 Prisma가 올바르게 추론합니다. 현 상태 유지에 동의합니다.
| youtube_videos YoutubeVideo @relation(fields: [videoId], references: [videoId]) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
관계 필드 네이밍 불일치(youtube_videos → youtubeVideo) 및 onDelete 명시 권장
- 단수(1) → 단수(1) 관계 필드를 복수형(youtube_videos)으로 네이밍한 점이 혼동을 유발합니다. 프로젝트 전반(CamelCase + @Map으로 스네이크케이스 매핑)과도 일관되지 않습니다. 이 필드는 DB 컬럼이 아닌 Prisma 관계 필드이므로 안전하게 리네이밍 가능합니다(클라이언트 타입만 변경).
- videoId가 NOT NULL이므로 참조 대상(YoutubeVideo) 삭제 시 의도된 동작을 명확히 하려면 onDelete 정책을 명시해 주세요. 보통 제한(RESTRICT)이 자연스럽습니다. 만약 SET NULL을 원한다면 videoId를 옵셔널로 바꾸는 스키마 변경이 선행되어야 합니다.
제안 diff:
- youtube_videos YoutubeVideo @relation(fields: [videoId], references: [videoId])
+ youtubeVideo YoutubeVideo @relation(fields: [videoId], references: [videoId], onDelete: Restrict)검증 스크립트(레거시 참조 정리: Room.video, youtube_videos, VideoOnRoom 등):
🏁 Script executed:
#!/bin/bash
# 레거시 필드/관계명 사용 여부 확인
# 참고: node_modules 등은 기본적으로 미존재 환경이므로 별도 제외 옵션은 생략
# Room.video 또는 새/구 관계 필드 사용 흔적
rg -n -C2 -S '\broom\.(video|youtube_videos|youtubeVideo)\b' --type=ts
# Prisma select/include 등에서의 필드명 사용 흔적
rg -n -C3 -S '\b(video|youtube_videos|youtubeVideo)\s*:' --type=ts
# 기존 명시적 관계명 사용 흔적
rg -n -C2 -S '\bVideoOnRoom\b'Length of output: 11187
스키마 파일에서 실제 youtube_videos 필드 존재 여부를 확인하기 위해 아래 스크립트를 실행해주세요:
🏁 Script executed:
#!/bin/bash
rg -n 'youtube_videos' -C2 prisma/schema.prismaLength of output: 442
Prisma 스키마: 관계 필드 youtube_videos → youtubeVideo로 변경 및 onDelete 정책 명시
Prisma 스키마에서 관계 필드명이 복수형·스네이크케이스(youtube_videos)로 정의되어 있어, 프로젝트의 CamelCase 규칙과 불일치합니다. 또한 onDelete 행동이 명시되어 있지 않아 암묵적 DB 기본값(NO ACTION/RESTRICT)이 적용되는데, 추후 혼동을 방지하려면 명시하는 것이 좋습니다.
- prisma/schema.prisma
Room모델의 관계 필드명을youtube_videos→youtubeVideo(singular CamelCase)로 변경@relation에onDelete: Restrict추가- (DB 컬럼명(videoId), 테이블 매핑(@@Map) 등은 그대로 유지됩니다)
제안 diff:
model Room {
participants RoomParticipant[]
host User @relation("HostUser", fields: [hostId], references: [userId], onDelete: Cascade)
- youtube_videos YoutubeVideo @relation(fields: [videoId], references: [videoId])
+ youtubeVideo YoutubeVideo @relation(fields: [videoId], references: [videoId], onDelete: Restrict)
@@index([hostId], map: "rooms_host_id_fkey")
}- TS 서비스 코드
위 스키마 변경에 따라 Prisma 클라이언트 타입이 바뀌므로, 다음 패턴을 전역 검색·교체해야 합니다.- include/ select 구문에서
•{ video: true }→{ youtubeVideo: true } - 조회·처리 로직에서
•room.video→room.youtubeVideo - 관련 타입 정의(예:
BookmarkWithRelations,CollectionWithRelations, 메시지 페이로드 등)도 동일하게 업데이트
- include/ select 구문에서
이 변경은 Prisma 클라이언트 API가 깨지는 브레이킹 체인지이므로, 코드 전반에 걸쳐 반드시 리팩터링을 적용해 주세요.
📝 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.
| youtube_videos YoutubeVideo @relation(fields: [videoId], references: [videoId]) | |
| model Room { | |
| participants RoomParticipant[] | |
| host User @relation("HostUser", fields: [hostId], references: [userId], onDelete: Cascade) | |
| youtubeVideo YoutubeVideo @relation(fields: [videoId], references: [videoId], onDelete: Restrict) | |
| @@index([hostId], map: "rooms_host_id_fkey") | |
| } |
🤖 Prompt for AI Agents
In prisma/schema.prisma around lines 125-126, rename the Room model relation
field from youtube_videos to youtubeVideo and add an explicit relation policy by
updating the @relation to include onDelete: Restrict while keeping the
underlying DB column names and @@map unchanged; after applying the schema
change, run prisma generate and update all TypeScript usages: replace
include/select { video: true } with { youtubeVideo: true }, replace room.video
references with room.youtubeVideo, and update related type names (e.g.,
BookmarkWithRelations, CollectionWithRelations, message payloads) to match the
new Prisma client types across the codebase to avoid breaking changes.
There was a problem hiding this 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 (5)
src/services/collectionService.ts (2)
66-67: include 범위를 축소해 불필요한 컬럼 로드를 줄이세요.youtube_videos 전 컬럼 로드는 과합니다. 제목/썸네일만 필요하므로 select로 최소화하는 편이 효율적입니다.
- youtube_videos: true, + youtube_videos: { select: { title: true, thumbnail: true } },
110-112: youtube_videos가 null일 가능성에 대비해 안전하게 접근하세요.다른 파일(bookmarkService)에서는 optional chaining을 사용하고 있는데, 여기서는 단정 접근으로 NPE가 날 수 있습니다(참조 무결성이 항상 보장되지 않거나, 마이그레이션/데이터 이상 시).
- videoTitle: room.youtube_videos.title, - videoThumbnail: room.youtube_videos.thumbnail || '', + videoTitle: room.youtube_videos?.title ?? '', + videoThumbnail: room.youtube_videos?.thumbnail ?? '',검증 제안:
- prisma/schema에서 Room.videoId가 non-null FK로 설정되어 있고 연관 YoutubeVideo가 항상 존재하는지 확인 바랍니다.
src/services/bookmarkService.ts (3)
46-47: 타입 정의의 include 범위를 최소화하세요.BookmarkWithRelations에서 youtube_videos 전체를 포함하면 타입과 실제 쿼리 모두 과도합니다. 필요한 필드만 select 하도록 축소하면 좋습니다.
- youtube_videos: true; // Room.video (YoutubeVideo) + youtube_videos: { select: { title: true, thumbnail: true } }; // Room.video (YoutubeVideo)
74-76: findMany include도 동일하게 최소 필드만 select 하세요.실 데이터 페치에서도 전 컬럼 로딩 대신 필요한 title/thumbnail만 선택하세요.
- room: { include: { youtube_videos: true } }, + room: { include: { youtube_videos: { select: { title: true, thumbnail: true } } } },
249-251: 여기서는 thumbnail만 필요하므로 select로 축소하세요.createRoomFromBookmark 컨텍스트에서는 썸네일만 사용합니다. 전체 컬럼 로딩 대신 select: { thumbnail: true }가 적절합니다.
- youtube_videos: true, + youtube_videos: { select: { thumbnail: true } },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/services/bookmarkService.ts(5 hunks)src/services/collectionService.ts(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-30T15:59:43.603Z
Learnt from: ekdbss
PR: ON-AIR-mate/Node.js#66
File: src/controllers/bookmarkController.ts:117-124
Timestamp: 2025-07-30T15:59:43.603Z
Learning: In the createRoomFromBookmark function in src/controllers/bookmarkController.ts, the service function expects parameters in this order: userId, bookmarkId, roomTitle, maxParticipants, isPublic (not isPrivate), startFrom. The videoId parameter is not needed because the service function retrieves it internally from bookmark.room.videoId.
Applied to files:
src/services/collectionService.tssrc/services/bookmarkService.ts
🔇 Additional comments (2)
src/services/bookmarkService.ts (2)
111-113: Option chaining 적용이 적절합니다.유실/누락된 연관 데이터에 안전하게 대응하고 있어 좋습니다.
259-261: 썸네일 파생 로직이 명확하고 안전합니다.null 병합으로 기본값 처리하는 접근이 깔끔합니다.
name: pull_request_template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
🚩 관련 이슈
📋 작업 내용
PUT /api/collections/orderAPI가 정상 동작하지 않던 문제 수정✨ 주요 변경 사항
PUT /api/collections/order) 정상적으로 라우트 매칭됨✅ 테스트 방법
PUT /api/collections/order호출 시 200 응답 및 컬렉션 순서 정상 변경 확인💬 기타 사항
Summary by CodeRabbit