Skip to content

[OT-125][FEAT]: 업로드 API 태그/카테고리 연동 및 숏폼 업로드 검증 보완#71

Merged
arlen02-01 merged 7 commits intodevelopfrom
OT-125-feature/upload-api-tag
Feb 27, 2026
Merged

[OT-125][FEAT]: 업로드 API 태그/카테고리 연동 및 숏폼 업로드 검증 보완#71
arlen02-01 merged 7 commits intodevelopfrom
OT-125-feature/upload-api-tag

Conversation

@arlen02-01
Copy link
Copy Markdown
Contributor

@arlen02-01 arlen02-01 commented Feb 26, 2026

📝 작업 내용

이번 PR에서 작업한 내용을 적어주세요

  • 업로드 API에 카테고리/태그 연동 로직 추가
    (시리즈/콘텐츠 업로드 시 categoryName, tagNameList 기반으로 media_tag 저장)
  • 공통 태그 연결 컴포넌트(MediaTagLinker) 도입 및 서비스 중복 로직 정리
  • 숏폼 업로드 검증 보강
    (seriesId, contentsId 상호배타 검증 + 시리즈 소속 콘텐츠로 숏폼 생성 차단)
  • 숏폼 태그 처리 정책 정리
    (숏폼은 요청 태그를 직접 받지 않고, 연결된 원본 미디어의 태그를 상속)

📷 스크린샷

  • 이전 upload api와 동일 합니다.

☑️ 체크 리스트

체크 리스트를 확인해주세요

  • 테스트는 잘 통과했나요?
  • 충돌을 해결했나요?
  • 이슈는 등록했나요?
  • 라벨은 등록했나요?

#️⃣ 연관된 이슈

ex) # 이슈번호
closed#70

💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 예외 처리를 이렇게 해도 괜찮을까요? / ~~부분 주의 깊게 봐주세요

  • 숏폼 : 시리즈에 속한 콘텐츠는 숏폼 생성 불가 코드 정말 단순하게 했는데 이정도면 되는지 봐주시면 감사하겠습니다~

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 콘텐츠·시리즈·숏폼 업로드 요청에 카테고리 및 태그 ID 입력 지원
    • 업로드 시 태그를 미디어에 연동하고, 숏폼 생성 시 원본 미디어 태그 자동 상속
  • 버그 픽스 / 개선

    • 태그 입력 검증 강화(빈값·중복·유효성 검사) 및 관련 오류 코드 추가
    • 업로드 파일 확장자/유효성 관련 에러 코드 및 숏폼 대상 검증 강화

@arlen02-01 arlen02-01 self-assigned this Feb 26, 2026
@arlen02-01 arlen02-01 added the feat 새로운 기능 구현 label Feb 26, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 26, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc0a015 and 75fbe5e.

📒 Files selected for processing (1)
  • modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java

Walkthrough

업로드 DTO에 categoryIdtagIdList가 추가되고, 새로운 MediaTagLinker 컴포넌트가 도입되어 업로드 흐름에서 미디어와 태그·카테고리를 연동합니다. 관련 리포지토리 메서드와 에러 코드, 숏폼 업로드 검증·태그 상속 로직도 확장되었습니다.

Changes

Cohort / File(s) Summary
업로드 요청 DTO 확장
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/series/dto/request/SeriesUploadRequest.java
categoryId(Long, @NotNull, @Positive) 및 tagIdList(List<@NotNull @positive Long>, @NotEmpty) 필드 추가, 관련 import·스웨거 어노테이션 추가.
콘텐츠/시리즈 서비스 통합
apps/api-admin/src/main/java/com/ott/api_admin/content/service/BackOfficeContentsService.java, apps/api-admin/src/main/java/com/ott/api_admin/series/service/BackOfficeSeriesService.java
MediaTagLinker 의존성 추가 및 업로드 생성 흐름에서 mediaTagLinker.linkTags(media, request.categoryId(), request.tagIdList()) 호출로 태그 연결 수행. 생성자(주입) 사양 변경.
태그 연결 컴포넌트 추가
apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java
새 컴포넌트 추가: 입력 검증(널/빈/중복) → 카테고리 조회 → 태그 조회 및 카테고 일치 검증 → MediaTag 엔티티 생성 → mediaTagRepository.saveAll 일괄 저장.비즈니스 예외는 BusinessException+ErrorCode.
쇼트폼 업로드 흐름 개선
apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java, apps/api-admin/src/main/java/com/ott/api_admin/shortform/dto/request/ShortFormUploadRequest.java
콘텐츠/타깃 검증 강화(세분화된 에러 코드), 원본 미디어 ID 해석 및 inheritOriginMediaTags로 원본 태그 복사 로직 추가.
저장소(Repository) 확장
modules/domain/src/main/java/com/ott/domain/category/repository/CategoryRepository.java, modules/domain/src/main/java/com/ott/domain/tag/repository/TagRepository.java
findByNameAndStatus(String, Status)findAllByCategoryIdAndNameInAndStatus(Long, Set<String>, Status) 등 조회 메서드 추가.
업로드 헬퍼/에러 코드 보완
apps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.java, modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java
파일 확장자 관련 예외 코드 구체화(UNSUPPORTED_*, INVALID_FILE_EXTENSION) 및 태그/숏폼 관련 새 ErrorCode 상수 추가.
사소한 형식/EOF 변경
apps/api-admin/src/main/java/com/ott/api_admin/shortform/dto/request/ShortFormUploadRequest.java
파일 말미 개행/포맷 소소 변경만 존재.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • marulog
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 업로드 API의 태그/카테고리 연동 및 숏폼 검증 추가라는 핵심 변경사항을 명확하게 요약하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch OT-125-feature/upload-api-tag

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.

Copy link
Copy Markdown

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

📥 Commits

Reviewing files that changed from the base of the PR and between c466756 and ab43183.

📒 Files selected for processing (9)
  • 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/content/service/BackOfficeContentsService.java
  • apps/api-admin/src/main/java/com/ott/api_admin/series/dto/request/SeriesUploadRequest.java
  • apps/api-admin/src/main/java/com/ott/api_admin/series/service/BackOfficeSeriesService.java
  • apps/api-admin/src/main/java/com/ott/api_admin/shortform/dto/request/ShortFormUploadRequest.java
  • apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java
  • apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java
  • modules/domain/src/main/java/com/ott/domain/category/repository/CategoryRepository.java
  • modules/domain/src/main/java/com/ott/domain/tag/repository/TagRepository.java

Comment thread apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java Outdated
@phonil
Copy link
Copy Markdown
Contributor

phonil commented Feb 26, 2026

@coderabbitai

이번 PR 리뷰에서 coderabbit/coderabbit-guidelines.md 읽었는지 알려주고, 안 읽었으면 읽고 리뷰해주는데, 리뷰 하나당 댓글 하나씩 달아줘.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 26, 2026

[P2] 주석 포맷 개선 필요

파일: apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java:198

문제: 주석이 코드와 붙어있어 가독성이 떨어집니다.

// 현재
if(contents.getSeries()!=null){
    throw new BusinessException(ErrorCode.INVALID_INPUT);
}
//시리즈에 속한 콘텐츠 제외

제안: 주석을 코드 위에 배치하고 공백을 추가하세요.

// 시리즈에 속한 콘텐츠는 숏폼 생성 대상에서 제외
if (contents.getSeries() != null) {
    throw new BusinessException(ErrorCode.INVALID_INPUT);
}

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 26, 2026

[P1] 에러 메시지 구체화 필요

파일: apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java:186,198,210

문제: 여러 검증 실패 케이스에서 동일한 ErrorCode.INVALID_INPUT을 사용하여 클라이언트가 정확한 실패 원인을 파악하기 어렵습니다.

  • Line 186: seriesId와 contentsId 상호 배타성 위반
  • Line 198: 시리즈에 속한 콘텐츠로 숏폼 생성 시도
  • Line 210: series와 contents 모두 null

제안: 각 케이스에 맞는 구체적인 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, "시리즈에 속한 콘텐츠는 숏폼 생성 대상이 아닙니다.");
}

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 26, 2026

[P2] MediaTagLinker 클래스 및 메서드 문서화 필요

파일: apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java

문제: 새로 추가된 컴포넌트이지만 클래스 레벨 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) {
        // ...
    }
}

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 26, 2026

[P2] 중복 태그 검증 시 명확한 에러 메시지 필요

파일: apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java:62

문제: 태그명 중복 시 일반적인 INVALID_INPUT 에러만 발생하여 사용자가 어떤 태그가 중복되었는지 알 수 없습니다.

if (!normalizedTagNameSet.add(normalizedTagName)) {
    throw new BusinessException(ErrorCode.INVALID_INPUT);
}

제안: 중복된 태그명을 포함한 명확한 에러 메시지를 제공하세요.

if (!normalizedTagNameSet.add(normalizedTagName)) {
    throw new BusinessException(
        ErrorCode.INVALID_INPUT, 
        "중복된 태그명이 포함되어 있습니다: " + normalizedTagName
    );
}

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 26, 2026

[P2] inheritOriginMediaTags 메서드 문서화 필요

파일: apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java:213

문제: 숏폼의 핵심 비즈니스 로직인 태그 상속 메서드에 설명이 없습니다.

제안: 메서드 위에 주석을 추가하여 동작을 명확히 하세요.

/**
 * 원본 미디어(시리즈 또는 콘텐츠)의 태그를 숏폼에 상속합니다.
 * 숏폼은 자체 태그를 갖지 않고 원본의 태그를 그대로 복사합니다.
 * 
 * @param targetMedia 태그를 상속받을 숏폼의 Media
 * @param originMediaId 태그를 복사할 원본 Media ID
 */
private void inheritOriginMediaTags(Media targetMedia, Long originMediaId) {
    // ...
}

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 26, 2026

[P2] 코드 스타일: if문 괄호 공백 누락

파일: apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java:197

문제: if문 괄호 앞뒤로 공백이 없습니다.

if(contents.getSeries()!=null){

제안: 가독성을 위해 공백을 추가하세요.

if (contents.getSeries() != null) {

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 26, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@phonil

먼저 coderabbit/coderabbit-guidelines.md 파일을 찾아서 읽어보겠습니다.


🏁 Script executed:

#!/bin/bash
# Check if the guidelines file exists and read its content
if [ -f "coderabbit/coderabbit-guidelines.md" ]; then
    echo "=== Guidelines file found ==="
    cat coderabbit/coderabbit-guidelines.md
else
    echo "=== Guidelines file not found ==="
    fd -t f "coderabbit-guidelines.md"
fi

Length of output: 12283


@phonil

네, coderabbit/coderabbit-guidelines.md 파일을 읽었습니다!

이제 가이드라인을 기반으로 PR #71의 모든 변경사항을 상세히 리뷰하겠습니다. 각 리뷰 포인트마다 별도의 댓글로 남기겠습니다.

먼저 전체 파일 내용을 확인하고 리뷰를 시작하겠습니다.


🏁 Script executed:

#!/bin/bash

# Read all changed files for detailed review
echo "=== ContentsUploadRequest.java ==="
cat apps/api-admin/src/main/java/com/ott/api_admin/content/dto/request/ContentsUploadRequest.java

echo -e "\n=== BackOfficeContentsService.java ==="
cat apps/api-admin/src/main/java/com/ott/api_admin/content/service/BackOfficeContentsService.java

echo -e "\n=== SeriesUploadRequest.java ==="
cat apps/api-admin/src/main/java/com/ott/api_admin/series/dto/request/SeriesUploadRequest.java

echo -e "\n=== BackOfficeSeriesService.java ==="
cat apps/api-admin/src/main/java/com/ott/api_admin/series/service/BackOfficeSeriesService.java

echo -e "\n=== ShortFormUploadRequest.java ==="
cat apps/api-admin/src/main/java/com/ott/api_admin/shortform/dto/request/ShortFormUploadRequest.java

echo -e "\n=== BackOfficeShortFormService.java ==="
cat apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java

echo -e "\n=== MediaTagLinker.java ==="
cat apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java

echo -e "\n=== CategoryRepository.java ==="
cat modules/domain/src/main/java/com/ott/domain/category/repository/CategoryRepository.java

echo -e "\n=== TagRepository.java ==="
cat modules/domain/src/main/java/com/ott/domain/tag/repository/TagRepository.java

Length of output: 37010


@phonil

네, 가이드라인을 읽었습니다! 이제 각 리뷰 항목을 별도의 댓글로 PR에 작성하겠습니다.


🏁 Script executed:

#!/bin/bash

PR_NUMBER=71

# Review 1: P2 - BackOfficeShortFormService 주석 포맷
gh pr comment $PR_NUMBER --body "**[P2] 주석 포맷 개선 필요**

**파일**: \`apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java:198\`

**문제**: 주석이 코드와 붙어있어 가독성이 떨어집니다.

\`\`\`java
// 현재
if(contents.getSeries()!=null){
    throw new BusinessException(ErrorCode.INVALID_INPUT);
}
//시리즈에 속한 콘텐츠 제외
\`\`\`

**제안**: 주석을 코드 위에 배치하고 공백을 추가하세요.

\`\`\`java
// 시리즈에 속한 콘텐츠는 숏폼 생성 대상에서 제외
if (contents.getSeries() != null) {
    throw new BusinessException(ErrorCode.INVALID_INPUT);
}
\`\`\`"

# Review 2: P1 - 에러 메시지 구체화
gh pr comment $PR_NUMBER --body "**[P1] 에러 메시지 구체화 필요**

**파일**: \`apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java:186,198,210\`

**문제**: 여러 검증 실패 케이스에서 동일한 \`ErrorCode.INVALID_INPUT\`을 사용하여 클라이언트가 정확한 실패 원인을 파악하기 어렵습니다.

- Line 186: seriesId와 contentsId 상호 배타성 위반
- Line 198: 시리즈에 속한 콘텐츠로 숏폼 생성 시도
- Line 210: series와 contents 모두 null

**제안**: 각 케이스에 맞는 구체적인 ErrorCode를 추가하거나, 최소한 로그에 상세 정보를 남기세요.

\`\`\`java
// 예시
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, \"시리즈에 속한 콘텐츠는 숏폼 생성 대상이 아닙니다.\");
}
\`\`\`"

# Review 3: P2 - MediaTagLinker 클래스/메서드 문서화
gh pr comment $PR_NUMBER --body "**[P2] MediaTagLinker 클래스 및 메서드 문서화 필요**

**파일**: \`apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java\`

**문제**: 새로 추가된 컴포넌트이지만 클래스 레벨 Javadoc과 주요 메서드 설명이 없습니다.

**제안**: 클래스와 public 메서드에 Javadoc을 추가하세요.

\`\`\`java
/**
 * 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) {
        // ...
    }
}
\`\`\`"

# Review 4: P2 - normalizeTagNames 중복 검증 에러 메시지
gh pr comment $PR_NUMBER --body "**[P2] 중복 태그 검증 시 명확한 에러 메시지 필요**

**파일**: \`apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java:62\`

**문제**: 태그명 중복 시 일반적인 \`INVALID_INPUT\` 에러만 발생하여 사용자가 어떤 태그가 중복되었는지 알 수 없습니다.

\`\`\`java
if (!normalizedTagNameSet.add(normalizedTagName)) {
    throw new BusinessException(ErrorCode.INVALID_INPUT);
}
\`\`\`

**제안**: 중복된 태그명을 포함한 명확한 에러 메시지를 제공하세요.

\`\`\`java
if (!normalizedTagNameSet.add(normalizedTagName)) {
    throw new BusinessException(
        ErrorCode.INVALID_INPUT, 
        \"중복된 태그명이 포함되어 있습니다: \" + normalizedTagName
    );
}
\`\`\`"

# Review 5: P2 - inheritOriginMediaTags 메서드 문서화
gh pr comment $PR_NUMBER --body "**[P2] inheritOriginMediaTags 메서드 문서화 필요**

**파일**: \`apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java:213\`

**문제**: 숏폼의 핵심 비즈니스 로직인 태그 상속 메서드에 설명이 없습니다.

**제안**: 메서드 위에 주석을 추가하여 동작을 명확히 하세요.

\`\`\`java
/**
 * 원본 미디어(시리즈 또는 콘텐츠)의 태그를 숏폼에 상속합니다.
 * 숏폼은 자체 태그를 갖지 않고 원본의 태그를 그대로 복사합니다.
 * 
 * `@param` targetMedia 태그를 상속받을 숏폼의 Media
 * `@param` originMediaId 태그를 복사할 원본 Media ID
 */
private void inheritOriginMediaTags(Media targetMedia, Long originMediaId) {
    // ...
}
\`\`\`"

# Review 6: P2 - BackOfficeShortFormService if문 공백
gh pr comment $PR_NUMBER --body "**[P2] 코드 스타일: if문 괄호 공백 누락**

**파일**: \`apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java:197\`

**문제**: if문 괄호 앞뒤로 공백이 없습니다.

\`\`\`java
if(contents.getSeries()!=null){
\`\`\`

**제안**: 가독성을 위해 공백을 추가하세요.

\`\`\`java
if (contents.getSeries() != null) {
\`\`\`"

echo "✅ 모든 리뷰 댓글이 PR `#71에` 작성되었습니다."

Length of output: 460


@phonil

리뷰 완료! coderabbit/coderabbit-guidelines.md를 기반으로 PR #71을 상세히 리뷰하고, 각 항목을 별도의 댓글로 작성했습니다.

📋 리뷰 요약

6개의 리뷰 댓글을 작성했습니다:

P1 (머지 전 보완 권장) - 1건

  • 에러 메시지 구체화: BackOfficeShortFormService에서 여러 검증 실패 케이스가 동일한 INVALID_INPUT을 사용하여 클라이언트가 정확한 원인 파악이 어려움

P2 (품질 개선) - 5건

  1. 주석 포맷: 주석이 코드와 붙어있어 가독성 저하
  2. MediaTagLinker 문서화: 새로 추가된 컴포넌트에 Javadoc 누락
  3. 중복 태그 에러 메시지: 어떤 태그가 중복인지 명시 필요
  4. inheritOriginMediaTags 문서화: 핵심 비즈니스 로직 설명 누락
  5. if문 공백 스타일: 괄호 앞뒤 공백 누락

✨ 긍정적인 점

  • 태그/카테고리 연동 로직이 MediaTagLinker로 잘 분리됨
  • 숏폼 검증 로직(상호 배타성, 시리즈 소속 콘텐츠 제외)이 명확히 구현됨
  • 가이드라인에 명시된 Repository 네이밍, 트랜잭션 어노테이션, DTO record 사용 등이 잘 준수됨

P0 이슈는 없어서 머지 차단 요소는 없습니다! 🎉

 />  />
(O.o)
( > <)

Copy link
Copy Markdown

@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.

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 | 🔴 Critical

back-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

기대 결과: findWithTagAndCategoryByMediaIdmediaTag/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

📥 Commits

Reviewing files that changed from the base of the PR and between ab43183 and 2e14e39.

📒 Files selected for processing (3)
  • apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java
  • apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java
  • apps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.java

Copy link
Copy Markdown
Contributor

@phonil phonil left a comment

Choose a reason for hiding this comment

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

확인해주세요!

Comment thread apps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.java Outdated
Comment thread apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java Outdated
Copy link
Copy Markdown

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2e14e39 and 96ea7af.

📒 Files selected for processing (8)
  • 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/content/service/BackOfficeContentsService.java
  • apps/api-admin/src/main/java/com/ott/api_admin/series/dto/request/SeriesUploadRequest.java
  • apps/api-admin/src/main/java/com/ott/api_admin/series/service/BackOfficeSeriesService.java
  • apps/api-admin/src/main/java/com/ott/api_admin/shortform/service/BackOfficeShortFormService.java
  • apps/api-admin/src/main/java/com/ott/api_admin/upload/support/MediaTagLinker.java
  • apps/api-admin/src/main/java/com/ott/api_admin/upload/support/UploadHelper.java
  • modules/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

Copy link
Copy Markdown

@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: 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).

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96ea7af and dc0a015.

📒 Files selected for processing (1)
  • modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java

Comment thread modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java Outdated
Comment thread modules/common-web/src/main/java/com/ott/common/web/exception/ErrorCode.java Outdated
@arlen02-01 arlen02-01 merged commit 0ac4404 into develop Feb 27, 2026
1 check passed
@coderabbitai coderabbitai Bot mentioned this pull request Feb 27, 2026
8 tasks
@phonil phonil deleted the OT-125-feature/upload-api-tag branch April 4, 2026 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants