Conversation
📝 WalkthroughWalkthrough백오피스용 콘텐츠 목록 조회, 상세 조회 및 시리즈 제목 검색 API가 추가되고 이를 지원하는 DTO/매퍼/서비스와 QueryDSL 기반 커스텀 리포지토리 구현이 도입되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as BackOfficeContentsController
participant Service as BackOfficeContentsService
participant MediaRepo as MediaRepository
participant Mapper as BackOfficeContentsMapper
Client->>Controller: GET /back-office/admin/contents?page=&size=&searchWord=&publicStatus=
Controller->>Service: getContents(page, size, searchWord, publicStatus)
Service->>MediaRepo: findMediaListByMediaTypeAndSearchWordAndPublicStatus(pageable, CONTENTS, searchWord, publicStatus)
MediaRepo-->>Service: Page<Media>
Service->>Mapper: toContentsListResponse(media) [for each]
Mapper-->>Service: ContentsListResponse
Service-->>Controller: PageResponse<ContentsListResponse>
Controller-->>Client: 200 OK (SuccessResponse)
sequenceDiagram
participant Client
participant Controller as BackOfficeContentsController
participant Service as BackOfficeContentsService
participant ContentsRepo as ContentsRepository
participant TagRepo as MediaTagRepository
participant Mapper as BackOfficeContentsMapper
Client->>Controller: GET /back-office/admin/contents/{mediaId}
Controller->>Service: getContentsDetail(mediaId)
Service->>ContentsRepo: findWithMediaAndUploaderByMediaId(mediaId)
ContentsRepo-->>Service: Optional<Contents> (with Media, Uploader, Series)
Service->>TagRepo: findByMediaId(mediaId)
TagRepo-->>Service: List<MediaTag>
Service->>Mapper: toContentsDetailResponse(contents, media, uploaderNickname, seriesTitle, mediaTagList)
Mapper-->>Service: ContentsDetailResponse
Service-->>Controller: ContentsDetailResponse
Controller-->>Client: 200 OK (SuccessResponse)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/api-admin/src/main/java/com/ott/api_admin/content/controller/BackOfficeContentsApi.java`:
- Around line 50-67: The 400-response description in the Swagger annotations for
BackOfficeContentsApi.getContentsDetail is incorrect ("시리즈 상세 조회 실패"); update
the ApiResponse description for responseCode = "400" under the getContentsDetail
method to a correct phrase like "콘텐츠 상세 조회 실패" so the error message matches the
endpoint semantics.
In
`@apps/api-admin/src/main/java/com/ott/api_admin/content/dto/response/ContentsListResponse.java`:
- Around line 12-16: ContentsListResponse currently declares a field poster_url
that may not match the API spec (PR mentions poster_uri); update the DTO so the
JSON property matches the contract: either rename the Java field to
posterUri/poster_uri to match the spec or add an explicit mapping annotation
(e.g., `@JsonProperty`("poster_uri")) on the poster_url field in
ContentsListResponse to guarantee the serialized/deserialized name matches the
client expectation.
In
`@modules/domain/src/main/java/com/ott/domain/contents/repository/ContentsRepositoryImpl.java`:
- Around line 23-26: The query in ContentsRepositoryImpl reuses the same entity
type alias for two joins which causes JPQL alias collisions; update the
leftJoin(series.media).fetchJoin() to supply an explicit, unique alias (e.g.,
seriesMedia) and declare/use a corresponding Q-type variable (e.g., QMedia
seriesMedia) when building the QueryDSL query so the join becomes
leftJoin(series.media, seriesMedia).fetchJoin(); ensure the new alias name does
not conflict with the existing media variable and is referenced consistently in
the method.
apps/api-admin/src/main/java/com/ott/api_admin/content/controller/BackOfficeContentsApi.java
Show resolved
Hide resolved
apps/api-admin/src/main/java/com/ott/api_admin/content/dto/response/ContentsListResponse.java
Show resolved
Hide resolved
modules/domain/src/main/java/com/ott/domain/contents/repository/ContentsRepositoryImpl.java
Outdated
Show resolved
Hide resolved
|
코드래빗 리뷰 수정 후 반영했어요~ |
| @Schema(type = "Integer", description = "영상 길이(초)", example = "7200") | ||
| Integer duration, | ||
|
|
||
| @Schema(type = "Integer", description = "영상 크기(바이트)", example = "1048576") |
There was a problem hiding this comment.
30분짜리 영상이 2에서3GB가 넘는걸로 알고 있고
추후 러닝타임이 긴 영상 업로드 시 문제가 발생할 것 같습니다.
Integer 대신 Long을 써보시는건 어떠신가요?
There was a problem hiding this comment.
오 생각하지 못한 부분이네요!!
해당 DTO에서 Integer로 한 이유는
- Contents Entity에 Integer로 선언되어 있음 (db도 INT 타입)
- Integer: ~21억 => 1억 초 == 3년
입니다.
그래서 안 바꿔도 괜찮을 것 같은데 어떤가요??
아랫줄 영상 크기 문구는 스웨거 문서에 바이트가 아니라 KB로 바꿔야겠네요
apps/api-admin/src/main/java/com/ott/api_admin/content/service/BackOfficeContentsService.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/api-admin/src/main/java/com/ott/api_admin/content/dto/response/ContentsDetailResponse.java`:
- Around line 12-13: The response field name in ContentsDetailResponse (the
field contentsId) doesn't match the request path parameter name mediaId and
differs from ContentsListResponse which uses mediaId; update
ContentsDetailResponse to either rename contentsId to mediaId or add an
additional mediaId field while keeping contentsId (to preserve compatibility),
and update related serializers/constructors/factory methods that populate
ContentsDetailResponse (look for usages of the ContentsDetailResponse class and
its constructor/mapper) so the returned JSON contains mediaId consistently with
the endpoint and ContentsListResponse.
| @Schema(type = "Long", description = "콘텐츠 ID", example = "1") | ||
| Long contentsId, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's explore the directory structure to understand the codebase layout
find apps/api-admin/src -type f -name "*.java" | grep -E "(content|Controller)" | head -20Repository: OpenTheTaste/backend
Length of output: 914
🏁 Script executed:
# Let's read the ContentsDetailResponse.java file to see its full content
cat -n apps/api-admin/src/main/java/com/ott/api_admin/content/dto/response/ContentsDetailResponse.javaRepository: OpenTheTaste/backend
Length of output: 2421
🏁 Script executed:
# Search for API endpoints related to content detail or media
rg -n "contentsId|mediaId" apps/api-admin/src --type java -B 2 -A 2 | head -50Repository: OpenTheTaste/backend
Length of output: 6192
🏁 Script executed:
# Look for the Controller that handles content/media detail requests
find apps/api-admin/src -type f -name "*Controller.java" | xargs grep -l "mediaId\|contentsId" 2>/dev/nullRepository: OpenTheTaste/backend
Length of output: 260
🏁 Script executed:
# Read the BackOfficeContentsController to see the endpoint definition
cat -n apps/api-admin/src/main/java/com/ott/api_admin/content/controller/BackOfficeContentsController.javaRepository: OpenTheTaste/backend
Length of output: 2360
🏁 Script executed:
# Read the BackOfficeContentsMapper to understand how data is mapped
cat -n apps/api-admin/src/main/java/com/ott/api_admin/content/mapper/BackOfficeContentsMapper.javaRepository: OpenTheTaste/backend
Length of output: 2617
🏁 Script executed:
# Check the Contents entity and Media entity structure
find apps -type f -name "Contents.java" -o -name "Media.java" | head -10Repository: OpenTheTaste/backend
Length of output: 46
🏁 Script executed:
# Look at the method that retrieves contents detail
grep -n "getContentsDetail\|toContentsDetailResponse" apps/api-admin/src/main/java/com/ott/api_admin/content/service/BackOfficeContentsService.java -A 10Repository: OpenTheTaste/backend
Length of output: 783
🏁 Script executed:
# Let's check the ContentsListResponse to compare ID naming
cat -n apps/api-admin/src/main/java/com/ott/api_admin/content/dto/response/ContentsListResponse.javaRepository: OpenTheTaste/backend
Length of output: 1090
🏁 Script executed:
# Check what the Contents and Media entities look like to understand their relationship
find apps/domain -type f \( -name "Contents.java" -o -name "Media.java" \) | head -5Repository: OpenTheTaste/backend
Length of output: 110
🏁 Script executed:
# Let's verify the API contract one more time by looking at how mediaId is used
rg -n "mediaId" apps/api-admin/src/main/java/com/ott/api_admin/content -A 2 -B 2 | grep -E "(getContentsDetail|ContentsDetailResponse|@Schema|Long)"Repository: OpenTheTaste/backend
Length of output: 1758
요청 경로의 mediaId와 응답 필드의 contentsId 명칭 불일치 해결 필요
엔드포인트는 /{mediaId}로 호출하는데 응답은 contentsId를 반환합니다. 같은 콘텐츠 도메인의 목록 조회(ContentsListResponse)에서는 mediaId를 사용하므로, 일관성을 위해 상세 조회도 mediaId로 통일하거나 mediaId 필드를 추가로 제공하는 것을 권장합니다.
수정 예시
- `@Schema`(type = "Long", description = "콘텐츠 ID", example = "1")
- Long contentsId,
+ `@Schema`(type = "Long", description = "미디어 ID", example = "1")
+ Long mediaId,📝 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.
| @Schema(type = "Long", description = "콘텐츠 ID", example = "1") | |
| Long contentsId, | |
| `@Schema`(type = "Long", description = "미디어 ID", example = "1") | |
| Long mediaId, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/api-admin/src/main/java/com/ott/api_admin/content/dto/response/ContentsDetailResponse.java`
around lines 12 - 13, The response field name in ContentsDetailResponse (the
field contentsId) doesn't match the request path parameter name mediaId and
differs from ContentsListResponse which uses mediaId; update
ContentsDetailResponse to either rename contentsId to mediaId or add an
additional mediaId field while keeping contentsId (to preserve compatibility),
and update related serializers/constructors/factory methods that populate
ContentsDetailResponse (look for usages of the ContentsDetailResponse class and
its constructor/mapper) so the returned JSON contains mediaId consistently with
the endpoint and ContentsListResponse.
📝 작업 내용
📷 스크린샷
[콘텐츠 목록 조회]

[콘텐츠 상세 조회]

[시리즈 검색(텍스트) (콘텐츠 업로드 모달에서 사용)]

☑️ 체크 리스트
#️⃣ 연관된 이슈
closes #38
💬 리뷰 요구사항
다른 방식으로 프로젝션 사용해도 괜찮은데, 우선 DTO 방식에 대해서 생각해봤습니다. (인터페이스 사용 방식도 마찬가지)
Summary by CodeRabbit
새로운 기능
개선