[OT-125][FEAT]: 업로드 API 태그/카테고리 연동 및 숏폼 업로드 검증 보완#71
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review infoConfiguration used: Repository UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
Walkthrough업로드 DTO에 Changes
Sequence Diagram(s)sequenceDiagram
participant BackOfficeService as BackOfficeService
participant MediaTagLinker as MediaTagLinker
participant CategoryRepo as CategoryRepository
participant TagRepo as TagRepository
participant MediaTagRepo as MediaTagRepository
BackOfficeService->>MediaTagLinker: linkTags(media, categoryId, tagIdList)
MediaTagLinker->>CategoryRepo: findByIdAndStatus(categoryId, ACTIVE)
CategoryRepo-->>MediaTagLinker: Category or empty
alt category not found or inactive
MediaTagLinker-->>BackOfficeService: throw INVALID_TAG_CATEGORY
else
MediaTagLinker->>TagRepo: findAllByIdInAndStatus(tagIdSet, ACTIVE)
TagRepo-->>MediaTagLinker: List<Tag>
alt size mismatch or tag category mismatch
MediaTagLinker-->>BackOfficeService: throw INVALID_TAG_SELECTION
else
MediaTagLinker->>MediaTagRepo: saveAll(MediaTag entities)
MediaTagRepo-->>MediaTagLinker: saved entities
MediaTagLinker-->>BackOfficeService: return (void)
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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.
Actionable comments posted: 2
🤖 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/shortform/service/BackOfficeShortFormService.java`:
- Around line 256-269: The inheritOriginMediaTags flow copies tags from
originMedia via mediaTagRepository.findWithTagAndCategoryByMediaId which
currently returns inactive entries; update the repository query (or add a new
repository method used here) to restrict results to active records by adding
where conditions mediaTag.status.eq(Status.ACTIVE),
tag.status.eq(Status.ACTIVE), and category.status.eq(Status.ACTIVE) so only
active MediaTag/Tag/Category are returned to inheritOriginMediaTags.
In
`@apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java`:
- Around line 28-54: In linkTags (MediaTagLinker) add a null check for the media
parameter before normalizing inputs and creating MediaTag entities; if media is
null throw the same BusinessException(ErrorCode.INVALID_INPUT) used elsewhere
(or a more specific error if preferred) so MediaTag.builder(...).media(media)
and mediaTagRepository.saveAll(...) cannot cause an NPE.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/api-admin/src/main/java/com/ott/api_admin/content/dto/request/ContentsUploadRequest.javaapps/api-admin/src/main/java/com/ott/api_admin/content/service/BackOfficeContentsService.javaapps/api-admin/src/main/java/com/ott/api_admin/series/dto/request/SeriesUploadRequest.javaapps/api-admin/src/main/java/com/ott/api_admin/series/service/BackOfficeSeriesService.javaapps/api-admin/src/main/java/com/ott/api_admin/shortform/dto/request/ShortFormUploadRequest.javaapps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.javaapps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.javamodules/domain/src/main/java/com/ott/domain/category/repository/CategoryRepository.javamodules/domain/src/main/java/com/ott/domain/tag/repository/TagRepository.java
|
이번 PR 리뷰에서 coderabbit/coderabbit-guidelines.md 읽었는지 알려주고, 안 읽었으면 읽고 리뷰해주는데, 리뷰 하나당 댓글 하나씩 달아줘. |
|
[P2] 주석 포맷 개선 필요 파일: 문제: 주석이 코드와 붙어있어 가독성이 떨어집니다. // 현재
if(contents.getSeries()!=null){
throw new BusinessException(ErrorCode.INVALID_INPUT);
}
//시리즈에 속한 콘텐츠 제외제안: 주석을 코드 위에 배치하고 공백을 추가하세요. // 시리즈에 속한 콘텐츠는 숏폼 생성 대상에서 제외
if (contents.getSeries() != null) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
} |
|
[P1] 에러 메시지 구체화 필요 파일: 문제: 여러 검증 실패 케이스에서 동일한
제안: 각 케이스에 맞는 구체적인 ErrorCode를 추가하거나, 최소한 로그에 상세 정보를 남기세요. // 예시
if ((seriesId == null && contentsId == null) || (seriesId != null && contentsId != null)) {
throw new BusinessException(ErrorCode.INVALID_INPUT, "seriesId와 contentsId 중 하나만 제공해야 합니다.");
}
if (contents.getSeries() != null) {
throw new BusinessException(ErrorCode.INVALID_INPUT, "시리즈에 속한 콘텐츠는 숏폼 생성 대상이 아닙니다.");
} |
|
[P2] MediaTagLinker 클래스 및 메서드 문서화 필요 파일: 문제: 새로 추가된 컴포넌트이지만 클래스 레벨 Javadoc과 주요 메서드 설명이 없습니다. 제안: 클래스와 public 메서드에 Javadoc을 추가하세요. /**
* Media와 Tag를 연결하는 헬퍼 컴포넌트.
* 카테고리명과 태그명 리스트를 검증하고 MediaTag 레코드를 생성합니다.
*/
@Component
@RequiredArgsConstructor
public class MediaTagLinker {
/**
* 지정된 Media에 카테고리와 태그를 연결합니다.
*
* @param media 태그를 연결할 Media 엔티티
* @param categoryName 카테고리명 (trim 및 존재 여부 검증)
* @param tagNameList 태그명 목록 (중복 및 존재 여부 검증)
* @throws BusinessException 카테고리나 태그가 존재하지 않거나, 태그명에 중복이 있을 경우
*/
public void linkTags(Media media, String categoryName, List<String> tagNameList) {
// ...
}
} |
|
[P2] 중복 태그 검증 시 명확한 에러 메시지 필요 파일: 문제: 태그명 중복 시 일반적인 if (!normalizedTagNameSet.add(normalizedTagName)) {
throw new BusinessException(ErrorCode.INVALID_INPUT);
}제안: 중복된 태그명을 포함한 명확한 에러 메시지를 제공하세요. if (!normalizedTagNameSet.add(normalizedTagName)) {
throw new BusinessException(
ErrorCode.INVALID_INPUT,
"중복된 태그명이 포함되어 있습니다: " + normalizedTagName
);
} |
|
[P2] inheritOriginMediaTags 메서드 문서화 필요 파일: 문제: 숏폼의 핵심 비즈니스 로직인 태그 상속 메서드에 설명이 없습니다. 제안: 메서드 위에 주석을 추가하여 동작을 명확히 하세요. /**
* 원본 미디어(시리즈 또는 콘텐츠)의 태그를 숏폼에 상속합니다.
* 숏폼은 자체 태그를 갖지 않고 원본의 태그를 그대로 복사합니다.
*
* @param targetMedia 태그를 상속받을 숏폼의 Media
* @param originMediaId 태그를 복사할 원본 Media ID
*/
private void inheritOriginMediaTags(Media targetMedia, Long originMediaId) {
// ...
} |
|
[P2] 코드 스타일: if문 괄호 공백 누락 파일: 문제: if문 괄호 앞뒤로 공백이 없습니다. if(contents.getSeries()!=null){제안: 가독성을 위해 공백을 추가하세요. if (contents.getSeries() != null) { |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain
먼저 🏁 Script executed: Length of output: 12283
네, 이제 가이드라인을 기반으로 PR 먼저 전체 파일 내용을 확인하고 리뷰를 시작하겠습니다. 🏁 Script executed: Length of output: 37010
네, 가이드라인을 읽었습니다! 이제 각 리뷰 항목을 별도의 댓글로 PR에 작성하겠습니다. 🏁 Script executed: Length of output: 460
✅ 리뷰 완료! 📋 리뷰 요약총 6개의 리뷰 댓글을 작성했습니다: P1 (머지 전 보완 권장) - 1건
P2 (품질 개선) - 5건
✨ 긍정적인 점
P0 이슈는 없어서 머지 차단 요소는 없습니다! 🎉 |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.java (1)
59-79:⚠️ Potential issue | 🔴 Criticalback-office 모듈의 업로드 엔드포인트 권한 불일치 문제
resolveUploader()의 역할 검증은 SecurityConfig의 route-level 보호로 처리되고 있습니다. 하지만 업로드 기능의 권한 설정에 불일치가 있습니다:
- Series/Contents 업로드:
/back-office/admin/**경로로 ADMIN만 접근 가능- ShortForm 업로드:
/back-office/short-forms경로로 ADMIN/EDITOR 모두 접근 가능back-office 모듈에서 EDITOR 권한의 범위를 명확히 하고, ShortForm 업로드 엔드포인트 경로를
/back-office/admin/short-forms로 변경하거나@PreAuthorize("hasRole('ADMIN')")추가를 검토하세요.🤖 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/upload/support/UploadHelper.java` around lines 59 - 79, resolveUploader() is fine but your route-level security is inconsistent: ShortForm upload is currently reachable by ADMIN and EDITOR via /back-office/short-forms while Series/Contents are restricted to /back-office/admin/** (ADMIN only); fix by either moving the ShortForm endpoint under the admin path or locking the controller method with an ADMIN-only annotation. Update the ShortForm controller route to /back-office/admin/short-forms or add `@PreAuthorize`("hasRole('ADMIN')") to the ShortForm upload handler (referencing the controller class/method that calls resolveUploader()), and ensure SecurityConfig route mappings reflect the chosen change so resolveUploader() only receives authenticated ADMIN principals.
♻️ Duplicate comments (1)
apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java (1)
256-269:⚠️ Potential issue | 🟠 Major비활성 태그 상속 가능성은 다시 확인이 필요합니다.
현재 로직은
findWithTagAndCategoryByMediaId결과를 그대로 복사합니다. 이 조회가 ACTIVE 상태만 보장하지 않으면 소프트 삭제된 태그까지 상속될 수 있습니다. 이전 리뷰에서 같은 리스크가 제기된 포인트라, 최종 머지 전 repository 쿼리 조건을 재확인해 주세요.아래 스크립트로 실제 쿼리/구현에 ACTIVE 필터가 있는지 바로 검증할 수 있습니다.
#!/bin/bash set -euo pipefail echo "[1] MediaTagRepository 관련 파일 찾기" fd -t f 'MediaTagRepository.*\.java$' echo echo "[2] findWithTagAndCategoryByMediaId 선언/사용 위치 확인" rg -n -C4 'findWithTagAndCategoryByMediaId\s*\(' $(fd -t f 'MediaTagRepository.*\.java$') echo echo "[3] 구현체에서 ACTIVE 상태 필터 확인" impl_file="$(fd -t f 'MediaTagRepositoryImpl.java' | head -n1 || true)" if [ -n "${impl_file:-}" ]; then echo "Inspect: ${impl_file}" rg -n -C8 'findWithTagAndCategoryByMediaId|Status\.ACTIVE|status\.eq' "$impl_file" else echo "MediaTagRepositoryImpl.java not found" fi기대 결과:
findWithTagAndCategoryByMediaId에mediaTag/tag/category의 ACTIVE 조건(또는 동등한 소프트딜리트 필터)이 포함되어야 합니다.🤖 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/shortform/service/BackOfficeShortFormService.java` around lines 256 - 269, The inheritOriginMediaTags method copies results from MediaTagRepository.findWithTagAndCategoryByMediaId into new MediaTag entities without ensuring the source Tag/Category/MediaTag are ACTIVE; confirm and if missing add the ACTIVE (soft-delete) filters in the repository query (MediaTagRepository / MediaTagRepositoryImpl) for mediaTag, tag and category, or alternatively filter originMediaTagList in inheritOriginMediaTags to only include entries whose getStatus()/getTag().getStatus()/getTag().getCategory().getStatus() == Status.ACTIVE before mapping and saving; update either the repository signature/implementation or the stream filter accordingly and re-run tests/script in the review comment to verify the ACTIVE guard is present.
🧹 Nitpick comments (2)
apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java (2)
20-27: 클래스 및 public 메서드에 Javadoc 문서화 추가 권장PR 리뷰 코멘트에서 언급된 바와 같이,
MediaTagLinker컴포넌트의 목적과linkTags메서드의 파라미터/동작에 대한 Javadoc을 추가하면 유지보수성이 향상됩니다.📝 Javadoc 추가 제안
+/** + * 미디어와 태그 연결을 담당하는 컴포넌트. + * 업로드 시 카테고리 이름과 태그 목록을 기반으로 미디어-태그 관계를 생성합니다. + */ `@Component` `@RequiredArgsConstructor` public class MediaTagLinker {🤖 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/upload/support/MediaTagLinker.java` around lines 20 - 27, Add Javadoc to the MediaTagLinker class and its public method(s) (notably linkTags) describing the component's responsibility, expected inputs, side effects, return behavior, and any exceptions thrown; for linkTags document parameters (e.g., mediaId, tagNames), what the method does (resolves/creates Category/Tag entities and saves MediaTag links via mediaTagRepository), expected preconditions, and error handling/transactional behavior so future maintainers can understand usage and guarantees.
74-76: 중복 태그 에러 메시지에 구체적인 태그 이름 포함 권장PR 리뷰 코멘트에서 언급된 대로, 어떤 태그가 중복되었는지 명시하면 사용자가 문제를 빠르게 파악할 수 있습니다.
💡 중복 태그 이름 포함 제안
if (!normalizedTagNameSet.add(normalizedTagName)) { - throw new BusinessException(ErrorCode.INVALID_INPUT, "태그 목록에 중복된 값이 있습니다."); + throw new BusinessException(ErrorCode.INVALID_INPUT, + String.format("태그 목록에 중복된 값이 있습니다: '%s'", normalizedTagName)); }🤖 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/upload/support/MediaTagLinker.java` around lines 74 - 76, In MediaTagLinker, when checking normalizedTagNameSet.add(normalizedTagName) currently throwing BusinessException with a generic message, change the exception to include the actual duplicated tag name (normalizedTagName) in the message so the user sees which tag is duplicated; update the throw site that uses ErrorCode.INVALID_INPUT to build a message like "태그 목록에 중복된 값이 있습니다: <tagName>" (include normalizedTagName) so the duplicate is explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@apps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.java`:
- Around line 59-79: resolveUploader() is fine but your route-level security is
inconsistent: ShortForm upload is currently reachable by ADMIN and EDITOR via
/back-office/short-forms while Series/Contents are restricted to
/back-office/admin/** (ADMIN only); fix by either moving the ShortForm endpoint
under the admin path or locking the controller method with an ADMIN-only
annotation. Update the ShortForm controller route to
/back-office/admin/short-forms or add `@PreAuthorize`("hasRole('ADMIN')") to the
ShortForm upload handler (referencing the controller class/method that calls
resolveUploader()), and ensure SecurityConfig route mappings reflect the chosen
change so resolveUploader() only receives authenticated ADMIN principals.
---
Duplicate comments:
In
`@apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java`:
- Around line 256-269: The inheritOriginMediaTags method copies results from
MediaTagRepository.findWithTagAndCategoryByMediaId into new MediaTag entities
without ensuring the source Tag/Category/MediaTag are ACTIVE; confirm and if
missing add the ACTIVE (soft-delete) filters in the repository query
(MediaTagRepository / MediaTagRepositoryImpl) for mediaTag, tag and category, or
alternatively filter originMediaTagList in inheritOriginMediaTags to only
include entries whose
getStatus()/getTag().getStatus()/getTag().getCategory().getStatus() ==
Status.ACTIVE before mapping and saving; update either the repository
signature/implementation or the stream filter accordingly and re-run
tests/script in the review comment to verify the ACTIVE guard is present.
---
Nitpick comments:
In
`@apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java`:
- Around line 20-27: Add Javadoc to the MediaTagLinker class and its public
method(s) (notably linkTags) describing the component's responsibility, expected
inputs, side effects, return behavior, and any exceptions thrown; for linkTags
document parameters (e.g., mediaId, tagNames), what the method does
(resolves/creates Category/Tag entities and saves MediaTag links via
mediaTagRepository), expected preconditions, and error handling/transactional
behavior so future maintainers can understand usage and guarantees.
- Around line 74-76: In MediaTagLinker, when checking
normalizedTagNameSet.add(normalizedTagName) currently throwing BusinessException
with a generic message, change the exception to include the actual duplicated
tag name (normalizedTagName) in the message so the user sees which tag is
duplicated; update the throw site that uses ErrorCode.INVALID_INPUT to build a
message like "태그 목록에 중복된 값이 있습니다: <tagName>" (include normalizedTagName) so the
duplicate is explicit.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.javaapps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.javaapps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.java
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/upload/support/MediaTagLinker.java`:
- Around line 29-39: Add defensive null checks at the start of linkTags: verify
media, categoryId, and tagIdList are not null (and that tagIdList contains no
null entries and is not empty) before calling
categoryRepository.findByIdAndStatus or iterating tagIdList; if a validation
fails, throw a BusinessException with an appropriate ErrorCode (e.g.,
INVALID_INPUT or a specific existing error code) so NPEs are avoided and
failures are explicit. Ensure these checks are in the linkTags method and run
prior to using categoryId, media, or tagIdList.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/api-admin/src/main/java/com/ott/api_admin/content/dto/request/ContentsUploadRequest.javaapps/api-admin/src/main/java/com/ott/api_admin/content/service/BackOfficeContentsService.javaapps/api-admin/src/main/java/com/ott/api_admin/series/dto/request/SeriesUploadRequest.javaapps/api-admin/src/main/java/com/ott/api_admin/series/service/BackOfficeSeriesService.javaapps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.javaapps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.javaapps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.javamodules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/api-admin/src/main/java/com/ott/api_admin/content/dto/request/ContentsUploadRequest.java
- apps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.java
- apps/api-admin/src/main/java/com/ott/api_admin/series/service/BackOfficeSeriesService.java
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java`:
- Around line 55-56: The error codes for UNSUPPORTED_VIDEO_EXTENSION and
INVALID_FILE_EXTENSION in ErrorCode are using B0010 and B0011 which do not match
the project's B0XX pattern; update those enum entries to use B010 and B011
respectively (modify the enum constants UNSUPPORTED_VIDEO_EXTENSION and
INVALID_FILE_EXTENSION in ErrorCode to change their code strings) so they follow
the documented B0XX format and remain consistent with other codes.
- Around line 52-54: The enum ErrorCode has a missing comma after the
BOOKMARK_NOT_FOUND constant causing a Java compile error; open the ErrorCode
enum and add a comma immediately after the BOOKMARK_NOT_FOUND(...) entry so enum
constants are properly comma-separated (affects the BOOKMARK_NOT_FOUND enum
constant and the following UNSUPPORTED_IMAGE_EXTENSION constant).
📝 작업 내용
(시리즈/콘텐츠 업로드 시 categoryName, tagNameList 기반으로 media_tag 저장)
(seriesId, contentsId 상호배타 검증 + 시리즈 소속 콘텐츠로 숏폼 생성 차단)
(숏폼은 요청 태그를 직접 받지 않고, 연결된 원본 미디어의 태그를 상속)
📷 스크린샷
☑️ 체크 리스트
#️⃣ 연관된 이슈
💬 리뷰 요구사항
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 픽스 / 개선