Glowb Dev Docs
SaaS API기업용 콘텐츠 검수

POST /ai/business/contents/review/{reviewId}/feedbacks/bulk

벌크 피드백 추가 / 검수 완료

벌크 피드백 추가 / 검수 완료

검수 라운드의 모든 아이템에 대해 피드백 추가 또는 검수 완료를 일괄 처리합니다.

기존 개별 API의 문제점 해결

기존 개별 API(POST /{itemId}/feedbacks)는 아이템마다 검수 횟수(count)가 증가하여, 여러 아이템을 순차적으로 처리할 때 "검수 횟수 초과" 오류가 발생할 수 있습니다.

벌크 API는 모든 아이템을 한 번에 처리하고, 검수 횟수는 라운드 단위로 1번만 증가합니다.

처리 규칙

  • 각 아이템별로 feedback이 null이고 파일이 없으면 -> 승인 (APPROVED)
  • feedback 내용이나 파일이 있으면 -> 재제출 요청 (REJECTED)
  • approveWithFeedback: true 시 -> 피드백을 생성하되 즉시 해결(resolved) 처리하고 승인 (APPROVED)
  • isDraft: true 시 -> 피드백 + 파일만 저장, 상태 변경/검수 횟수 증가/슬랙 알림 없음 (임시저장)
  • 검수 횟수(count)는 전체 처리 후 1번만 증가
  • 피드백 마감일은 가장 마지막 피드백 기준으로 4일 후 설정 (approveWithFeedback 시에는 설정하지 않음)

임시저장 (isDraft: true) 동작 방식

  • 피드백 내용과 첨부 파일(S3 업로드)만 저장하고, 제출물 상태 변경/검수 횟수 증가/슬랙 알림을 수행하지 않습니다.
  • 같은 검수 라운드에 대해 임시저장을 다시 호출하면 기존 임시저장 피드백이 삭제되고 새로 저장됩니다.
  • 정식 제출(isDraft: false/null)하면 기존 임시저장 피드백이 자동 삭제된 후 정식 처리됩니다.
  • guidelineFeedbackItems도 함께 임시저장됩니다.
  • 조회 시 isDraft: true인 피드백은 프론트에서 "임시저장된 피드백"으로 구분 표시할 수 있습니다.

HTTP 요청

POST /ai/business/contents/review/{reviewId}/feedbacks/bulk
Authorization: Bearer {access_token}
Content-Type: multipart/form-data

Path Parameters

파라미터타입필수설명
reviewIdlong검수 라운드 ID

Request Parts

파트타입필수설명
requestBulkFeedbackDto.Request벌크 피드백 요청 JSON
filesfile[]아니오첨부 파일 (모든 아이템 파일을 배열로 전달)

Request 필드 설명

필드타입필수설명
itemsItemFeedback[]아이템별 피드백 목록
approveWithFeedbackboolean아니오true: 피드백을 생성하되 즉시 해결 처리하고 승인. false/null: 기존 동작 (피드백 있으면 반려)
isDraftboolean아니오true: 임시저장 (피드백만 저장, 상태 변경/검수 횟수 증가/슬랙 알림 없음). false/null: 기존 동작
guidelineFeedbackItemsGuidelineFeedbackItem[]아니오가이드라인 피드백 목록 (아래 상세 참조)

GuidelineFeedbackItem 필드 설명

가이드라인 피드백은 3가지 종류(feedbackType)로 구분됩니다:

feedbackType설명category 예시item 예시
GUIDELINE_UNREFLECTED가이드라인에 있는 항목인데 반영 안 됨"필수 촬영 항목", "필수 소구 포인트""제형 표현 샷", "발색 샷"
GUIDELINE_EXTRA가이드라인 외 추가 피드백 항목"자막", "녹음", "편집효과"null (보통 사용 안 함)

자유 피드백(FREE)은 기존 items[]로 전달

guidelineFeedbackItems에는 GUIDELINE_UNREFLECTED 또는 GUIDELINE_EXTRA만 넣으세요. 자유 피드백은 기존처럼 items[]feedback 필드를 사용합니다.

필드타입필수설명
feedbackTypestring피드백 종류: GUIDELINE_UNREFLECTED 또는 GUIDELINE_EXTRA
categorystring아니오가이드라인 섹션명 또는 추가항목명. 프론트에서 가이드라인 데이터를 보고 동적으로 설정
itemstring아니오가이드라인 항목명 (GUIDELINE_UNREFLECTED일 때 사용)
commentstring아니오피드백 텍스트 설명

가이드라인 피드백 저장 방식

  • 가이드라인 피드백은 기존 ContentFeedback과 동일한 테이블에 저장됩니다.
  • VIDEO 타입 제출물에 자동 바인딩됩니다 (VIDEO가 없으면 첫 번째 제출물).
  • categoryitem 값은 프론트에서 가이드라인 데이터를 보고 동적으로 설정하면 됩니다. 백엔드는 String 그대로 저장만 합니다.
  • 가이드라인 버전이 바뀌어도 백엔드 수정이 필요 없습니다.

request JSON 구조

기본 사용 (기존 동작)

{
  "items": [
    {
      "itemId": 244,
      "feedback": null,
      "highlightedText": null,
      "selection": null,
      "fileIndices": [],
      "mediaList": []
    },
    {
      "itemId": 245,
      "feedback": null,
      "highlightedText": null,
      "selection": null,
      "fileIndices": [],
      "mediaList": []
    },
    {
      "itemId": 246,
      "feedback": "피드백 내용입니다. 이 부분을 수정해주세요.",
      "highlightedText": "수정 필요한 텍스트",
      "selection": "{\"anchor\":{},\"focus\":{}}",
      "fileIndices": [0, 1],
      "mediaList": [
        {
          "mediaType": "IMAGE",
          "startTime": null,
          "endTime": null,
          "comment": "이 부분 참고"
        },
        {
          "mediaType": "VIDEO",
          "startTime": 10.5,
          "endTime": 15.0,
          "comment": "이 구간 재촬영 필요"
        }
      ]
    }
  ]
}

가이드라인 피드백 포함

기존 아이템별 피드백(items)과 함께, 가이드라인 관련 피드백(guidelineFeedbackItems)을 같이 보낼 수 있습니다. 가이드라인 피드백은 VIDEO 제출물에 자동으로 바인딩됩니다.

{
  "items": [
    {
      "itemId": 244,
      "feedback": null,
      "fileIndices": [],
      "mediaList": []
    }
  ],
  "guidelineFeedbackItems": [
    {
      "feedbackType": "GUIDELINE_UNREFLECTED",
      "category": "필수 촬영 항목",
      "item": "제형 표현 샷",
      "comment": "제형 표현이 충분히 보이지 않습니다."
    },
    {
      "feedbackType": "GUIDELINE_UNREFLECTED",
      "category": "필수 소구 포인트",
      "item": "수분 공급 효과",
      "comment": null
    },
    {
      "feedbackType": "GUIDELINE_EXTRA",
      "category": "자막",
      "item": null,
      "comment": "자막 크기가 너무 작습니다."
    }
  ]
}

피드백 포함 승인 (approveWithFeedback: true)

피드백을 기록으로 남기되, 반려하지 않고 승인 처리합니다. 생성된 피드백은 즉시 resolved=true 상태로 저장됩니다.

{
  "approveWithFeedback": true,
  "items": [
    {
      "itemId": 244,
      "feedback": null,
      "highlightedText": null,
      "selection": null,
      "fileIndices": [],
      "mediaList": []
    },
    {
      "itemId": 245,
      "feedback": "경미한 수정사항 있으나 승인 처리합니다.",
      "highlightedText": null,
      "selection": null,
      "fileIndices": [],
      "mediaList": []
    }
  ]
}

임시저장 (isDraft: true)

피드백을 저장만 하고 상태 변경 없이 나중에 이어서 작업할 수 있습니다. 동일 검수 라운드에 다시 임시저장하면 이전 임시저장이 교체됩니다.

{
  "isDraft": true,
  "items": [
    {
      "itemId": 244,
      "feedback": null,
      "fileIndices": [],
      "mediaList": []
    },
    {
      "itemId": 245,
      "feedback": "작성 중인 피드백...",
      "highlightedText": "수정 필요한 부분",
      "selection": "{\"anchor\":{},\"focus\":{}}",
      "fileIndices": [0],
      "mediaList": [
        {
          "mediaType": "IMAGE",
          "comment": "참고 이미지"
        }
      ]
    }
  ],
  "guidelineFeedbackItems": [
    {
      "feedbackType": "GUIDELINE_UNREFLECTED",
      "category": "필수 촬영 항목",
      "item": "제형 표현 샷",
      "comment": "작성 중..."
    }
  ]
}

ItemFeedback 필드 설명

필드타입필수설명
itemIdlong제출물 아이템 ID
feedbackstring아니오피드백 내용 (null이면 승인 처리)
highlightedTextstring아니오하이라이트된 텍스트
selectionstring아니오선택 영역 정보 (JSON)
fileIndicesint[]아니오files 배열에서의 인덱스 목록
mediaListMediaMeta[]아니오각 파일에 대한 메타데이터

MediaMeta 필드 설명

필드타입필수설명
mediaTypestring아니오미디어 타입 (IMAGE, VIDEO)
startTimedouble아니오영상 시작 시간 (초)
endTimedouble아니오영상 끝 시간 (초)
commentstring아니오미디어에 대한 코멘트

응답

성공 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "3건의 제출물이 승인되었습니다.",
  "data": {
    "success": true,
    "approvedCount": 3,
    "rejectedCount": 0,
    "processedItemIds": [244, 245, 246],
    "message": "3건의 제출물이 승인되었습니다."
  }
}

피드백 포함 응답 (기존 동작 - 반려)

{
  "status": 200,
  "code": null,
  "message": "2건 승인, 1건 피드백 추가가 완료되었습니다.",
  "data": {
    "success": true,
    "approvedCount": 2,
    "rejectedCount": 1,
    "processedItemIds": [244, 245, 246],
    "message": "2건 승인, 1건 피드백 추가가 완료되었습니다."
  }
}

피드백 포함 승인 응답 (approveWithFeedback: true)

피드백이 있는 아이템도 승인으로 처리되므로 rejectedCount가 0이 됩니다.

{
  "status": 200,
  "code": null,
  "message": "2건의 제출물이 승인되었습니다.",
  "data": {
    "success": true,
    "approvedCount": 2,
    "rejectedCount": 0,
    "processedItemIds": [244, 245],
    "message": "2건의 제출물이 승인되었습니다."
  }
}

임시저장 응답 (isDraft: true)

임시저장 시에는 approvedCount, rejectedCount가 모두 0입니다.

{
  "status": 200,
  "code": null,
  "message": "2건 임시저장 완료",
  "data": {
    "success": true,
    "approvedCount": 0,
    "rejectedCount": 0,
    "processedItemIds": [244, 245],
    "message": "2건 임시저장 완료"
  }
}

에러 응답

에러 응답 (404) - 검수 횟수 초과

{
  "status": 404,
  "code": "INVALID_DATA",
  "message": "검수 횟수를 초과했습니다. (현재: 1/1) 검수 추가 요청이 필요합니다.",
  "data": null
}

API 테스트

On this page