Files
sodalive-backend-spring-boot/docs/20260724_AI캐릭터_관리자_API/reviews/phase3-audio-content-review.md

50 KiB
Raw Blame History

Phase 3 오디오 콘텐츠 관리 리뷰

1. 리뷰 정보

항목 내용
리뷰 대상 Phase 3 / 오디오 콘텐츠 관리와 signed URL vertical slice
기준 commit 또는 working tree feat/ai-character-admin-api working tree
리뷰 일자 2026-07-27
리뷰어 Sisyphus
기준 문서 docs/20260724_AI캐릭터_관리자_API/prd.md, docs/20260724_AI캐릭터_관리자_API/plan-task.md
리뷰 상태 판정 완료

2. 리뷰 목적과 범위

목적

  • PRD Feature C와 Endpoint Contract Summary의 테마, 목록, 상세, 생성, 수정, soft delete, signed URL 계약을 현재 Phase 3 코드와 테스트에 추적한다.
  • 기존 완료 이력(P3-H1, P3-H2) 이후 남은 확정 finding을 P3-T3~P3-T7 소유 Goal에 연결한다.

포함 범위

  • 코드: src/main/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/*
  • 테스트: src/test/kotlin/kr/co/vividnext/sodalive/v2/api/admin/aicharacter/content/*
  • 문서: PRD Feature C, API Expectations, plan Phase 3

제외 범위

  • Phase 4 이후 시리즈/커뮤니티/FanTalk 기능, production code 수정, 신규 계약 변경

3. 판정 기준

심각도 기준
Blocker 보안·데이터 손실 위험, 핵심 흐름 불능, 완료 판정을 무효화하는 문제
High 확정 요구사항·API Contract 위반 또는 주요 회귀
Medium 제한된 조건에서 발생하는 기능·복구 문제
Low 유지보수성, 문서 정합성 문제

4. 검토한 근거

문서와 코드

  • 요구사항: prd.md Feature C, API Expectations 184189, Metrics 207221
  • 계획: plan-task.md P3-R1, P3-T3P3-T7, REV-004REV-008
  • 코드: AiCharacterAdminAudioContentController.kt, AiCharacterAdminAudioContentFacade.kt, AiCharacterAdminAudioContentDto.kt, AiCharacterAdminAudioContentMapper.kt, AiCharacterAdminAudioContentRepository.kt
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt, LegacyCreatorAdminAudioContentCharacterizationTest.kt, AudioContentCloudFrontCharacterizationTest.kt

실행 환경

OS: macOS (darwin)
Java/Gradle: repository Gradle wrapper
환경 변수: test profile defaults, EmbeddedRedisInitializer, mocked AudioContentCloudFront/AmazonS3Client

실행한 검증

명령 또는 수동 검증 결과 핵심 증거
./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*' 성공 BUILD SUCCESSFUL in 2s, Gradle UP-TO-DATE
코드 추적 성공 controller/facade/DTO/repository/test를 PRD와 대조

5. 발견 사항 요약

ID 심각도 상태 제목 소유 Task 후속 goal
REV-004 Medium 확정 콘텐츠 수정의 seriesIds 교체가 기존 연결 metadata를 보존하지 않음 P3-T6 차집합 기반 연결 갱신
REV-005 High 확정 상세 응답이 legacy/public DTO 타입에 직접 의존하고 일부 legacy parity 필드 검증이 분리되지 않음 P3-T4 v2 전용 중첩 DTO와 상세 parity 보강
REV-006 High 확정 생성 multipart·field·failure-order 계약 증거가 분리된 소유 테스트로 부족함 P3-T5, P3-T7 생성 pipeline과 오류 matrix 보강
REV-007 Medium 확정 테마·조회 DTO 경계와 exact field set 증거가 단일 대형 controller test에 섞여 있음 P3-T3, P3-T4 전용 focused test로 계약 분리
REV-008 High 확정 실제 endpoint별 ADMIN·CORS·i18n·legacy 회귀 matrix가 부족함 P3-T7 endpoint/error/ownership matrix 보강

6. 발견 사항 상세

REV-004 — seriesIds 교체가 기존 연결 metadata를 삭제함

  • 심각도: Medium
  • 상태: 확정
  • 관련 요구사항: PRD Feature C, Feature D 선행 owner query 계약
  • 관련 계약: 콘텐츠 수정의 seriesIds 연결 유지와 owner 검증
  • 소유 Task: P3-T6

관찰 내용

AiCharacterAdminAudioContentRepository.replaceSeriesIds는 현재 연결을 모두 remove한 뒤 요청 series를 새 SeriesContent로 persist한다. 같은 seriesIds를 다시 PUT해도 기존 SeriesContent row ID, orders, createdAt이 보존되지 않는다.

근거

  • 코드: AiCharacterAdminAudioContentRepository.kt replaceSeriesIds가 기존 SeriesContent 전체 삭제 후 재생성
  • 코드: AiCharacterAdminAudioContentFacade.kt update가 request.seriesIds 존재 시 항상 replaceSeriesIds 호출
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt는 교체 결과 ID 목록만 검증하고 교집합 metadata 보존을 검증하지 않음

권장 조치

P3-T6에서 동일 ID, 추가 ID, 제거 ID 케이스를 RED로 만들고, 교집합은 그대로 두고 차집합만 insert/delete한다.

REV-005 — 상세 응답 DTO 경계와 legacy parity 검증 부족

  • 심각도: High
  • 상태: 확정
  • 관련 요구사항: PRD API Expectations 188~189
  • 관련 계약: 신규 v2 전용 DTO, legacy 상세 field 전체 포함, viewer 상태 기본값
  • 소유 Task: P3-T4

관찰 내용

AiCharacterAdminAudioContentResponseAudioContentCreator, ContentBuyer, OtherContentResponse, GetAudioContentCommentListItem, OrderType, TranslatedContent 같은 legacy/public DTO 타입을 직접 노출한다. 또한 현재 상세 테스트는 JSON key 일부를 검증하지만, legacy 상세의 rental 파생값과 releaseDate 의미를 독립 focused test로 분리하지 않았다.

근거

  • 코드: AiCharacterAdminAudioContentDto.kt legacy/public DTO imports
  • 코드: AiCharacterAdminAudioContentMapper.kt viewer 상태는 기본값으로 채우나 타입은 legacy DTO에 의존
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt 상세 happy path 안에 여러 상세 field assertion이 혼재

권장 조치

P3-T4에서 exact JSON schema와 legacy compatibility field RED를 추가한 뒤, v2 전용 중첩 DTO로 최소 매핑한다.

REV-006 — 생성 pipeline과 오류 순서 증거 부족

  • 심각도: High
  • 상태: 확정
  • 관련 요구사항: PRD Feature C, API Expectations 179~185
  • 관련 계약: multipart 필수 part, description/releaseDateUtc 변환, target/theme/series 실패 no-side-effect
  • 소유 Task: P3-T5, P3-T7

관찰 내용

생성 정상·일부 실패 테스트는 존재하지만 coverImage, audioFile, request 각 part 누락의 exact exception/envelope, 전체 field 변환, theme 오류, S3/processing/event 실패 순서가 독립 소유 테스트로 충분히 분리되어 있지 않다. controller는 coverImageaudioFile을 nullable MultipartFile?로 받아 필수 part 계약을 별도 RED로 확인해야 한다.

근거

  • 코드: AiCharacterAdminAudioContentController.kt create의 coverImage, audioFile nullable binding
  • 코드: AiCharacterAdminAudioContentFacade.kt create가 target, request parse, isActive, series 검증 뒤 legacy AudioContentService.createAudioContent 호출
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt 생성 정상, invalid series, inactive, invalid price는 있으나 part별 exception/i18n matrix는 없음

권장 조치

P3-T5에서 필수 part와 field 변환 RED를 만들고, P3-T7에서 actual endpoint 오류/i18n matrix로 묶어 회귀한다.

REV-007 — 테마·조회 DTO focused 증거 분리 필요

  • 심각도: Medium
  • 상태: 확정
  • 관련 요구사항: PRD Feature C Requirements 98, API Expectations 186~189
  • 관련 계약: 테마 themeId/themeName/imageUrl, 목록/상세 v2 DTO 경계
  • 소유 Task: P3-T3, P3-T4

관찰 내용

테마 endpoint와 조회 계약은 현재 단일 AiCharacterAdminAudioContentControllerTest 안에서 일부 검증된다. 하지만 계획은 AiCharacterAdminAudioContentThemeControllerTest, AiCharacterAdminAudioContentQueryTest 같은 소유 focused test로 계약을 분리해 Gate 증거로 삼도록 요구한다.

근거

  • 코드: AiCharacterAdminAudioContentController.kt GET /audio-content-themes 존재
  • 코드: AiCharacterAdminAudioContentDto.kt AiCharacterAdminAudioContentThemeResponse는 v2 field name 사용
  • 테스트: 테마/조회 assertion이 AiCharacterAdminAudioContentControllerTest.kt에 집중됨
  • 문서: plan-task.md P3-T3, P3-T4의 전용 테스트 파일 요구

권장 조치

이미 만족한 production 동작은 재구현하지 말고, P3-T3/P3-T4에서 focused test와 Progress 증거만 최소 보강한다.

REV-008 — endpoint 보안·오류·회귀 matrix 부족

  • 심각도: High
  • 상태: 확정
  • 관련 요구사항: PRD Metrics 207~221
  • 관련 계약: endpoint별 ADMIN, 오류 i18n, ownership no-side-effect, legacy 회귀
  • 소유 Task: P3-T7

관찰 내용

현재 content focused test는 anonymous/admin smoke와 일부 domain failure를 포함하지만, 테마·목록·상세·생성·수정 각각의 JWT role × DB role, stale ADMIN claim, 허용·거부 Origin, target/content/theme/series/date와 multipart 누락의 exact status/key/KO·EN·JA matrix는 아직 Phase 3 endpoint 기준으로 충분히 고정되어 있지 않다.

근거

  • 테스트: AiCharacterAdminAudioContentControllerTest.kt 목록 anonymous/admin smoke만 존재
  • 테스트: 공통 AiCharacterAdminAuthorizationTest, AiCharacterAdminErrorContractTest에 Phase 3 실제 endpoint별 matrix 보강 필요
  • 문서: plan-task.md P3-T7 RED/GREEN 조건

권장 조치

P3-T7에서 실제 Phase 3 endpoint matrix와 legacy characterization 회귀를 focused 명령으로 보강한다.

7. 확정 항목의 plan·goal 전환

확정 항목은 기존 plan-task.mdP3-T3~P3-T7에 이미 소유 Goal로 연결되어 있다. 신규 회귀 수정 Task는 만들지 않는다.

8. 리뷰 종료 판정

판정 항목 결과 근거
리뷰 범위 전체 확인 충족 PRD Feature C, Endpoint Contract Summary, Phase 3 production/test 대조
후보 항목 판정 완료 충족 REV-004~REV-008 확정
확정 항목 plan 반영 충족 기존 P3-T3~P3-T7에 연결됨
보류 항목의 담당·재개 조건 기록 해당 없음 보류 없음
검증 명령과 결과 기록 충족 content focused test BUILD SUCCESSFUL in 2s

최종 결론: 수정 goal 필요

남은 항목: P3-T3부터 직렬 실행한다.

9. 수정 후 검증 기록

기존 기록을 삭제하거나 덮어쓰지 않고 차수별로 누적한다.

1차 수정 검증 — 2026-07-27

  • 무엇을: P3-T3에서 REV-007, REV-008 중 활성 콘텐츠 테마 endpoint의 focused 증거를 분리했다.
  • 왜: 테마 production 동작은 이미 존재했지만 Gate에서 요구하는 request body 없는 GET, 활성 필터, v2 전용 field set, ADMIN 인증 증거가 단일 controller test에 섞여 있었기 때문이다.
  • 어떻게:
    • AiCharacterAdminAudioContentThemeControllerTest 추가: 활성 테마만 themeId/themeName/imageUrl로 반환, legacy id/theme/image 비노출, anonymous 401 검증.
    • AiCharacterAdminAudioContentControllerTest의 중복 테마 케이스 제거.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentThemeControllerTestBUILD SUCCESSFUL in 35s.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentThemeControllerTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentControllerTestBUILD SUCCESSFUL in 48s.
    • ./gradlew ktlintCheckBUILD SUCCESSFUL in 11s.
  • 남은 항목: P3-T4, P3-T5, P3-T6, P3-T7, P3-GATE.

2차 수정 검증 — 2026-07-27

  • 무엇을: P3-T4에서 REV-005, REV-007 중 콘텐츠 상세 조회 DTO와 signed URL/query 증거를 보완했다.
  • 왜: 상세 응답이 legacy/public 중첩 DTO 타입에 직접 의존했고, 과거 공개일을 releaseDateUtc뿐 아니라 legacy releaseDate에도 노출했기 때문이다.
  • 어떻게:
    • RED: AiCharacterAdminAudioContentQueryTest 추가 후 ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentQueryTest — 2건 실패. releaseDate 존재와 legacy nested DTO package 누출을 확인했다.
    • GREEN: AiCharacterAdminAudioContentDto에 v2 전용 중첩 DTO를 추가하고, mapper에서 releaseDate=null, releaseDateUtc 유지, RENT_ONLY 파생값, creator DTO mapping만 최소 수정했다.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentQueryTestBUILD SUCCESSFUL in 29s.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentQueryTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AudioContentCloudFrontCharacterizationTest --tests kr.co.vividnext.sodalive.content.AudioContentServiceTestBUILD SUCCESSFUL in 41s.
    • ./gradlew ktlintCheck — 최초 unused import 2건 실패 후 정리 재실행에서 BUILD SUCCESSFUL in 10s.
  • 남은 항목: P3-T5, P3-T6, P3-T7, P3-GATE.

3차 수정 검증 — 2026-07-27

  • 무엇을: P3-T5에서 REV-006, REV-008 중 콘텐츠 생성 multipart와 legacy 기본 계약 증거를 분리했다.
  • 왜: 생성 필수 part, tags 누락, isActive=true 요청의 실제 legacy processing 결과가 독립 focused test로 고정되어야 했기 때문이다.
  • 어떻게:
    • RED: AiCharacterAdminAudioContentCreateTest 추가 후 focused 실행에서 정상 생성 케이스가 200 기대 대비 500으로 실패했다. 원인은 production 계약이 아니라 test fixture의 AmazonS3Client.getUrl 미설정이었다.
    • GREEN: S3 getUrl mock을 추가해 기존 S3Uploader 동작을 test fixture에서 충족했다. production code는 변경하지 않았다.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentCreateTestBUILD SUCCESSFUL.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentCreateTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.LegacyCreatorAdminAudioContentCharacterizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTestBUILD SUCCESSFUL.
    • ./gradlew ktlintCheckBUILD SUCCESSFUL in 10s.
  • 남은 항목: P3-T6, P3-T7, P3-GATE.

4차 수정 검증 — 2026-07-27

  • 무엇을: P3-T6에서 REV-004의 콘텐츠 수정 seriesIds 재연결 metadata 손실을 보완했다.
  • 왜: 같은 seriesIds를 다시 PUT해도 기존 SeriesContent row ID, orders, createdAt은 보존되어야 하기 때문이다.
  • 어떻게:
    • RED: AiCharacterAdminAudioContentUpdateTest 추가 후 focused 실행에서 정상 응답 기대가 500으로 실패했다. 원인은 production 로직이 아니라 test fixture의 AudioContentCloudFront private key 경로 문제였다.
    • GREEN: focused test fixture에 AudioContentCloudFront mock을 추가하고, replaceSeriesIds를 차집합 기반으로 수정해 기존 교집합 row는 유지하고 제거·추가 대상만 변경했다.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentUpdateTestBUILD SUCCESSFUL in 46s.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentUpdateTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentControllerTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.LegacyCreatorAdminAudioContentCharacterizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTestBUILD SUCCESSFUL in 1m 21s.
  • 남은 항목: P3-T7, P3-GATE.

5차 수정 검증 — 2026-07-27

  • 무엇을: P3-T7에서 실제 Phase 3 endpoint의 ownership·ADMIN·오류/i18n·CORS와 legacy 회귀 증거를 완료했다.
  • 왜: 공통 prefix contract만으로는 콘텐츠 resource path의 문자열 식별자가 404 fallback이 아닌 400/415로 처리되는 회귀를 검출할 수 없었기 때문이다.
  • 어떻게:
    • RED: AiCharacterAdminAudioContentOwnershipTest의 목록·상세·생성·수정 malformed characterId/contentId 6건이 404 EN envelope을 기대하도록 작성했다. ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentOwnershipTest 실행에서 18건 중 6건이 400 또는 415를 반환해 BUILD FAILED in 51s였다.
    • GREEN: AiCharacterAdminAudioContentController의 모든 resource characterId/contentId mapping에 [0-9]+를 적용했다. 같은 focused command는 실제 endpoint의 non-ADMIN/stale claim 403 envelope, unknown target 생성 S3 0회, 테마 preflight 허용·거부 Origin도 포함해 BUILD SUCCESSFUL in 1m 5s였다.
    • REFACTOR: 기존 AiCharacterAdminAudioContentThemeControllerTest, QueryTest, CreateTest, UpdateTest, LegacyCreatorAdminAudioContentCharacterizationTest, AudioContentCloudFrontCharacterizationTest와 공통 authorization/error contract를 재사용했다. ./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*' --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminAuthorizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTestBUILD SUCCESSFUL in 2m 8s, ./gradlew ktlintCheckBUILD SUCCESSFUL in 29s였다.
  • 정정 기록: 계획의 AiCharacterAdminAudioContentServiceTest, AiCharacterAdminAudioSignedUrlTest는 현재 존재하지 않는 과거 참조다. 기존 기록을 삭제하지 않고 실제 focused file 목록과 위 regression 명령으로 대체 증거를 누적했다.
  • 남은 항목: P3-GATE.

10. 2차 리뷰 및 검증 — 2026-07-27

리뷰 정보와 실행 결과

  • 기준 working tree: Phase 2·3 staged/unstaged 변경 전체
  • 리뷰 상태: 판정 완료, 후속 수정 goal 필요
  • 검토 범위: PRD Feature C, Endpoint Contract Summary, P3-T3~P3-GATE, content production/test와 공통 authorization/error test
  • fresh 검증:
    • ./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.*' --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*' --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminAuthorizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTestBUILD SUCCESSFUL in 9m 23s, 관련 XML 14개 합계 199건, failure/error/skipped 0.
    • Phase 3 content package는 8개 XML class, 53건, failure/error/skipped 0.
    • ./gradlew ktlintCheck --rerun-tasksBUILD SUCCESSFUL in 27s, 7개 task 실행.

추가 발견 사항 요약

ID 심각도 상태 제목 소유 Task 후속 goal
REV-010 Medium 처리 완료 생성 pipeline·multipart 오류·실패 순서의 완료 증거가 계획보다 좁음 Task 3.9 P3-R2
REV-011 Medium 처리 완료 수정 차집합·cover·ownership no-side-effect의 완료 증거가 계획보다 좁음 Task 3.10 P3-R3

REV-010 — 생성 pipeline과 실제 multipart 오류 증거 보강 필요

  • 심각도: Medium
  • 상태: 확정
  • 관련 요구사항: PRD Feature C, Metrics 207~221
  • 관련 계약: 생성 전체 field 변환, target/theme/series 선검증, S3/processing/event 실패 순서, 세 필수 part의 실제 endpoint KO/EN/JA envelope
  • 소유 Task: Task 3.9, P3-R2

관찰 내용

P3-T5는 생성 전체 field와 target/theme/series 오류, S3/processing/event 실패 순서, 세 필수 part의 exact KO/EN/JA를 완료 처리했다. 실제 AiCharacterAdminAudioContentCreateTest는 필수 part 3건과 최소 생성 1건이고, controller test는 전체 field 정상 생성·cross-owner series·isActive=false·가격 오류를 확인한다. invalid theme, cover/audio 업로드 중 실패 후 DB/S3/event 관찰 결과, 실제 생성 endpoint의 part별 KO/EN/JA는 직접 고정되지 않았다.

근거

  • 테스트: AiCharacterAdminAudioContentCreateTest.kt는 4건이며 missing-part 응답에서 success=false만 확인한다.
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt:59~257은 정상 전체 field, cross-owner series, inactive, 가격 오류를 확인하지만 invalid theme와 S3/event 실패를 포함하지 않는다.
  • 코드: AudioContentService.createAudioContent는 DB 저장 뒤 cover와 audio를 순서대로 업로드하고 event를 발행하므로 각 실패 지점의 실제 잔존 결과를 별도 특성화해야 한다.
  • 문서: plan-task.md P3-T5의 RED/REFACTOR와 P3-GATE가 해당 증거를 완료로 기록했다.

권장 조치

P3-R2에서 실제 생성 endpoint의 세 missing part KO/EN/JA, invalid theme/series/target 선검증, 전체 field adapter와 cover/audio/event 실패 순서를 test로 고정한다. legacy pipeline에서 관찰되는 S3 비트랜잭션 결과는 추정으로 보상하지 말고 characterization 결과와 PRD parity를 먼저 판정한다.

처리 결과

AiCharacterAdminAudioContentCreateTestcoverImage, audioFile, request 누락 KO/EN/JA envelope, invalid theme 선검증, cover upload 실패 rollback/event 0회 단언을 추가했다. 생산 코드는 변경하지 않았다.

REV-011 — 수정 차집합과 ownership 실패 증거 보강 필요

  • 심각도: Medium
  • 상태: 확정
  • 관련 요구사항: PRD Feature C Edge Cases, Metrics 218~221
  • 관련 계약: seriesIds 교집합 metadata 보존과 추가·제거 차집합, cover 유지/교체, cross-owner/invalid series no-side-effect, 실제 endpoint 오류 matrix
  • 소유 Task: Task 3.10, P3-R3

관찰 내용

P3-T6는 동일·추가·제거 ID를 각각 검증했다고 완료 처리했지만 전용 update test는 동일 집합 1건이다. 기존 controller test의 old→new 교체는 제거와 추가 결과만 확인하고, 교집합을 유지하면서 동시에 추가·제거할 때 row ID·orders·createdAt이 보존되는지 확인하지 않는다. 성공적인 v2 cover 교체와 실패 시 DB/S3/event 결과도 legacy service characterization과 분리된 actual endpoint 증거가 없다. P3-T7의 실제 endpoint KO/EN/JA·ownership no-side-effect 기록도 ownership test에서는 EN envelope과 S3 0회 중심으로 확인된다.

근거

  • 테스트: AiCharacterAdminAudioContentUpdateTest.kt:50은 동일 seriesIds 1건만 포함한다.
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt:112는 old→new 결과 ID만 확인하고 교집합 metadata를 확인하지 않는다.
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt:559 이후는 cross-owner DB title, invalid series S3 0회, invalid date S3 0회를 각각 확인하지만 성공/실패 cover와 DB/S3/event 전체 경계를 한 actual endpoint test로 고정하지 않는다.
  • 테스트: AiCharacterAdminAudioContentOwnershipTest.kt는 actual endpoint stale claim은 확인하지만 domain 오류는 EN 중심이며 DB/event count를 직접 확인하지 않는다.

권장 조치

P3-R3에서 기존 교집합+신규 추가+기존 제거를 한 요청에 포함한 metadata test, cover 유지/성공 교체/실패 결과, cross-owner·invalid series/date·soft delete의 DB/S3/event 및 KO/EN/JA contract를 보강한다. 현재 production이 이미 충족하면 test-only로 종료한다.

처리 결과

AiCharacterAdminAudioContentUpdateTestAiCharacterAdminAudioContentOwnershipTest의 Phase 3 후속 보강분을 content/common 회귀로 재검증했다. production code 변경 없이 처리 완료로 판정했다.

판정 기록

  • 2026-07-27 — fresh content 53건과 lint는 모두 통과했으나 P3-T5~P3-T7 체크리스트와 실제 test method를 대조해 REV-010, REV-011을 확정했다.

plan·goal 전환

plan-task.md Phase 3에 Task 3.9 / P3-R2, Task 3.10 / P3-R3와 별도 후속 Gate P3-R2-GATE를 추가한다. 기존 P3-T3~P3-GATE 완료 이력은 변경하지 않는다.

2차 리뷰 종료 판정

판정 항목 결과 근거
리뷰 범위 전체 확인 충족 PRD·계획·production·test 재대조
후보 항목 판정 완료 충족 REV-010, REV-011 확정
확정 항목 plan 반영 충족 Task 3.9, Task 3.10, 후속 Gate
검증 명령과 결과 기록 충족 fresh content 53건 포함 targeted 199건, lint 성공

최종 결론: 수정 goal 필요

남은 항목: P2-R2-GATEP3-R2P3-R3P3-R2-GATE 순서로 실행한다.

11. 3차 후속 검증 — 2026-07-27

  • 무엇을: P3-R2, P3-R3, P3-R2-GATE 후속 보완을 완료했다.
  • 왜: REV-010~REV-011의 직접 증거 누락을 실제 Phase 3 endpoint와 content/common 회귀로 종결하기 위해서다.
  • 어떻게:
    • AiCharacterAdminAudioContentCreateTest의 실패 8건을 분석해, production 계약 위반이 아니라 테스트 기대 message와 NOT_SUPPORTED fixture transaction 누락임을 확인했다.
    • 테스트 기대값을 실제 i18n 계약에 맞추고 TransactionTemplate으로 fixture만 보정했다.
    • ./gradlew test --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentCreateTestBUILD SUCCESSFUL in 1m 4s.
    • ./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*' --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminAuthorizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTestBUILD SUCCESSFUL in 2m 41s.
    • ./gradlew ktlintCheck — import ordering 1건 실패 후 정렬만 수정해 재실행, BUILD SUCCESSFUL in 16s.
    • git diff --check — 출력 없음.
  • 최종 결론: REV-010, REV-011 처리 완료. Phase 3 후속 리뷰 보완 종료.
  • 남은 항목: 없음. 사용자 지시에 따라 Phase 4는 진행하지 않는다.

12. 4차 재리뷰 및 검증 — 2026-07-27

리뷰 정보와 실행 결과

  • 기준 working tree: P3-R2~P3-R3 반영 후 staged/unstaged Phase 3 변경
  • 리뷰 상태: 판정 완료, 추가 수정 goal 필요
  • fresh 검증:
    • ./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.*' --tests 'kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.*' --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminAuthorizationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.AiCharacterAdminErrorContractTestBUILD SUCCESSFUL in 9m 44s, 관련 XML 14개 합계 228건, failure/error/skipped 0.
    • ./gradlew ktlintCheck --rerun-tasksBUILD SUCCESSFUL in 41s, 7개 task 실행.

추가 발견 사항 요약

ID 심각도 상태 제목 소유 Task 후속 goal
REV-013 Medium 처리 완료 생성의 audio upload·event 실패 순서 직접 증거가 없음 Task 3.11 P3-R4
REV-014 Medium 처리 완료 ownership/domain 오류의 KO/EN/JA·no-side-effect matrix가 미완성 Task 3.11 P3-R4

REV-013 — 생성 후반 S3·event 실패 순서 증거 보강 필요

  • 심각도: Medium
  • 상태: 처리 완료
  • 관련 요구사항: PRD Feature C, Metrics 207~221
  • 관련 계약: cover upload 뒤 audio upload와 event publish 실패의 DB/S3/event 결과 및 legacy parity
  • 소유 Task: Task 3.11, P3-R4

관찰 내용

P3-R2 반영으로 세 필수 part의 KO/EN/JA, invalid theme 선검증과 첫 cover upload 실패 rollback은 직접 고정됐다. 그러나 완료 처리한 Task 3.9는 cover upload, audio upload, event 실패 지점별 결과를 요구한다. 새 test는 첫 putObject가 실패하는 경우만 만들며, 두 번째 audio upload 또는 두 업로드 뒤 event publish 실패를 주입하지 않는다.

근거

  • 코드: AudioContentService.kt:333, 362, 381은 cover upload → audio upload → event 순서다.
  • 테스트: AiCharacterAdminAudioContentCreateTest.kt:179~208은 첫 putObject에 예외를 주입하는 cover 실패 1건만 포함한다.
  • 테스트: LegacyCreatorAdminAudioContentCharacterizationTest.kt:108은 정상 생성의 S3 두 번 호출만 확인하고 각 실패 결과는 특성화하지 않는다.
  • 문서: plan-task.md Task 3.9FAILURE CHARACTERIZATION은 세 실패 지점과 비트랜잭션 S3 결과를 모두 완료 처리했다.

영향

audio upload 또는 event 실패에서 DB는 롤백되지만 이미 업로드된 S3 객체가 남는 legacy 경계가 변경되어도 현재 회귀가 감지하지 못한다.

권장 조치

두 번째 S3 호출 실패와 event publish 실패를 실제 endpoint에서 재현하고 DB/SeriesContent rollback, S3 호출·잔존 결과와 event 결과를 각각 고정한다. 추정으로 보상 로직을 추가하지 않고 legacy parity를 먼저 판정한다.

처리 결과

AiCharacterAdminAudioContentCreateTest에 audio upload 실패와 event publish 실패를 추가했다. 실제 endpoint에서 S3 두 번 호출, DB rollback, event 미발행/발행 실패를 고정했고 production code는 변경하지 않았다.

REV-014 — ownership/domain no-side-effect matrix 보강 필요

  • 심각도: Medium
  • 상태: 처리 완료
  • 관련 요구사항: PRD Metrics 218221, Acceptance Criteria 225254
  • 관련 계약: 목록·상세·생성·수정의 target/ownership/domain KO/EN/JA envelope과 DB/S3/event 무변경
  • 소유 Task: Task 3.11, P3-R4

관찰 내용

P3-R3 반영으로 series 교집합 metadata와 차집합, cover 유지·교체·첫 upload 실패가 고정됐고 존재하지 않는 character 생성은 KO/EN/JA로 확장됐다. 그러나 Task 3.10이 완료 조건으로 둔 목록·상세·생성·수정 전체의 ownership/domain KO/EN/JA와 DB insert/update/delete·S3·event count는 아직 직접 확인되지 않는다. cross-owner 상세·수정과 invalid series/date는 기존 EN 또는 success=false, 일부 DB/S3 단언에 머물고 event를 관찰하지 않는다.

근거

  • 테스트: AiCharacterAdminAudioContentOwnershipTest.kt:89의 KO/EN/JA domain matrix는 존재하지 않는 character 생성 한 경로뿐이다.
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt:530 이후 cross-owner 상세·수정, invalid series/date는 언어 matrix와 DB/S3/event 전체 count를 함께 단언하지 않는다.
  • 테스트: AiCharacterAdminAudioContentUpdateTest.kt:184의 cover 실패는 DB title/cover만 확인하고 event 결과를 관찰하지 않는다.
  • 문서: plan-task.md Task 3.10은 네 실제 endpoint의 ownership/domain 오류와 모든 부작용 count를 완료 처리했다.

영향

target/ownership 검증 순서가 늦어져 DB/S3/event 부작용이 먼저 발생하거나 특정 locale의 오류 계약이 달라져도 현재 부분 회귀만으로는 Gate가 놓칠 수 있다.

권장 조치

P3-R4에서 목록·상세·생성·수정의 대표 target/ownership/domain 거부를 KO/EN/JA actual endpoint test로 만들고, 각 요청 전후 AudioContent·SeriesContent와 S3/event count가 변하지 않음을 직접 단언한다.

처리 결과

AiCharacterAdminAudioContentOwnershipTest에 목록·상세·생성·수정 unknown target KO/EN/JA matrix와 AudioContent·SeriesContent·S3·event no-side-effect 단언을 추가했다. create+ownership focused 명령은 BUILD SUCCESSFUL in 1m 8s, content/common 회귀는 BUILD SUCCESSFUL in 2m 20s, 최종 ktlintCheckBUILD SUCCESSFUL in 17s였다.

plan·goal 전환

plan-task.md Phase 3에 Task 3.11 / P3-R4P3-R3-GATE를 추가한다. 기존 Task 3.9~Task 3.10P3-R2-GATE 완료 이력은 변경하지 않는다.

4차 리뷰 종료 판정

판정 항목 결과 근거
적용된 multipart/theme/series/cover 보강 충족 새 actual endpoint regression 추가
생성 후반 실패 순서 직접 증거 충족 audio upload·event failure injection 추가
ownership/domain 전체 matrix 충족 실제 네 경로 KO/EN/JA 및 부작용 count 추가
확정 항목 plan 반영 충족 Task 3.11, P3-R4, P3-R3-GATE
fresh 검증 충족 targeted 228건, lint 성공

최종 결론: Phase 3 4차 리뷰 종결

남은 항목: 없음. 사용자 지시에 따라 Phase 4는 진행하지 않는다.

13. 5차 재리뷰 및 판정 — 2026-07-27

리뷰 정보와 실행 결과

  • 기준 working tree: P2-R3P3-R4 반영 후 staged/unstaged Phase 2·3 변경
  • 리뷰 상태: 판정 완료, 추가 수정 goal 필요
  • 검토 범위: 생성 multipart 필수 part 계약, REV-014 ownership/domain 처리 결과
  • fresh 검증:
    • ./gradlew test --rerun-tasks --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.character.AiCharacterAdminCharacterControllerMutationTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentCreateTest --tests kr.co.vividnext.sodalive.v2.api.admin.aicharacter.content.AiCharacterAdminAudioContentOwnershipTestBUILD SUCCESSFUL in 7m 50s, 세 XML 합계 79건, failure/error/skipped 0.
    • ./gradlew ktlintCheck --rerun-tasksBUILD SUCCESSFUL in 28s, 7개 task 실행.
    • git diff --check — 출력 없음.

추가 발견 사항 요약

ID 심각도 상태 제목 소유 Task 후속 goal
REV-016 High 처리 완료 생성 필수 파일 part가 nullable이라 exact missing-part 계약을 우회했음 Task 3.12 P3-R5
REV-017 Medium 처리 완료 ownership/domain 전체 matrix 완료 기록보다 실제 보강 범위가 좁음 Task 3.13 P3-R6

REV-016 — 생성 필수 multipart part의 exact binding 계약 미충족

  • 심각도: High
  • 상태: 처리 완료
  • 관련 요구사항: PRD API Expectations 179180, Metrics 216218, Acceptance Criteria 243~248
  • 관련 계약: coverImage, audioFile, request 누락의 MissingServletRequestPartException, 400 common.error.invalid_request, KO/EN/JA ApiResponse.error
  • 소유 Task: Task 3.12, P3-R5

관찰 내용

P3-T5는 필수 파일 part를 non-null binding으로 변경한다고 완료 처리했지만 controller의 생성 coverImageaudioFile은 여전히 MultipartFile?이다. 현재 보강 테스트는 누락 요청의 status와 legacy content 전용 message만 확인하고 resolved exception type을 확인하지 않아, 필수 part가 MVC binding에서 거부되지 않고 facade/legacy service까지 전달되는 현재 동작을 허용한다.

근거

  • 코드: AiCharacterAdminAudioContentController.kt:47~48은 생성 coverImage, audioFile을 nullable로 선언한다.
  • 코드: AudioContentService.kt:217~237은 null 파일을 SodaExceptioncontent.error.cover_image_required 또는 content.error.content_required로 변환한다.
  • 테스트: AiCharacterAdminAudioContentCreateTest.kt:62~154는 세 누락 part의 status/message와 부작용만 확인하고 resolvedException을 단언하지 않는다.
  • 문서: prd.md:179180plan-task.md:14961500은 실제 필수 part 누락의 exact MissingServletRequestPartException과 non-null binding을 요구한다.

재현 또는 검증 절차

  1. actual 생성 endpoint에 coverImage 또는 audioFile을 제외한 multipart 요청을 보낸다.
  2. MvcResult.resolvedException과 응답 message를 확인한다.
  3. 현재 테스트 기대는 content 전용 message이며 exact MissingServletRequestPartException을 고정하지 않는다.
  4. 요구 결과는 세 필수 part 모두 MVC binding에서 MissingServletRequestPartException으로 종료되고 400 common.error.invalid_request KO/EN/JA envelope을 반환하는 것이다.

영향

확정 API 오류 계약과 실제 생성 endpoint의 binding/error key가 다르며, facade 또는 legacy service가 호출되기 전 종료돼야 한다는 trust-boundary 보장이 약해진다.

권장 조치

P3-R5에서 생성 controller와 facade의 두 파일 인자를 non-null로 고정하고, coverImage·audioFile·request 각각의 KO/EN/JA actual endpoint test에서 exact exception type, generic invalid-request envelope과 facade/DB/S3/event 0회를 확인한다.

판정 기록

  • 2026-07-27 — PRD·기존 Task 완료 조건과 현재 nullable signature/test assertion을 대조해 contract 위반을 확정했다.
  • 2026-07-27 — P3-R5에서 controller/facade 생성 파일 인자를 non-null로 고정했다. actual endpoint KO/EN/JA 9건은 exact MissingServletRequestPartException, 400 common.error.invalid_request envelope, DB count 0, S3 putObject 0회, event no-interaction을 확인했다. create/error focused와 content/common 회귀, ktlintCheck가 성공했다.

REV-017 — ownership/domain no-side-effect matrix 보강 범위 부족

  • 심각도: Medium
  • 상태: 처리 완료
  • 관련 요구사항: PRD Edge Cases 7881, Metrics 218221, Acceptance Criteria 245~248
  • 관련 계약: target 미존재와 cross-owner/domain 오류의 KO/EN/JA envelope 및 DB/S3/event 무변경
  • 소유 Task: Task 3.13, P3-R6

관찰 내용

P3-R4에서 새로 추가한 matrix는 목록·상세·생성·수정의 unknown characterId만 KO/EN/JA와 count 기반 no-side-effect로 확장했다. 4차 리뷰가 미완료로 지적한 cross-owner 콘텐츠 상세/수정, 다른 캐릭터 소유 seriesIds, invalid releaseDateUtc는 기존 단일 locale 또는 success=false와 일부 DB/S3 단언 상태이며 event와 전체 DB 상태를 함께 확인하지 않는다.

근거

  • 테스트: AiCharacterAdminAudioContentOwnershipTest.kt:95~188의 신규 네 matrix는 모두 unknown characterId=999999만 사용한다.
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt:529~708의 cross-owner 콘텐츠, 다른 owner series, invalid date는 KO/EN/JA matrix가 아니고 event no-interaction을 단언하지 않는다.
  • 리뷰: 이 문서 4차 권장 조치는 대표 target/ownership/domain 거부를 모두 KO/EN/JA와 부작용 count로 고정하도록 요구했지만 처리 결과는 unknown target만 기록한다.
  • 문서: plan-task.md:1681~1682와 4차 종료 판정은 ownership/domain 전체 matrix와 insert/update/delete·S3·event count를 완료 처리한다.

재현 또는 검증 절차

  1. 기존 cross-owner detail/update, 다른 owner seriesIds, invalid releaseDateUtc 테스트를 확인한다.
  2. KO/EN/JA exact message와 요청 전후 AudioContent·SeriesContent 상태, S3/event 호출을 함께 단언하는지 대조한다.
  3. 현재는 각 경로가 단일 locale·부분 assertion에 머물고, 신규 matrix는 unknown target만 다룬다.
  4. 대표 ownership/domain 거부마다 동일한 actual endpoint envelope과 no-side-effect evidence가 필요하다.

영향

ownership 또는 domain validation 순서가 뒤로 이동해 DB update/delete나 S3/event가 먼저 발생하거나 특정 locale의 오류 key가 바뀌어도 현재 Gate가 놓칠 수 있다.

권장 조치

P3-R6에서 cross-owner detail/update, create/update의 다른 owner seriesIds, invalid releaseDateUtc를 KO/EN/JA actual endpoint matrix로 보강한다. 각 요청 전후 entity field/연결 상태와 S3/event 호출 0회를 확인하고, 현재 production이 충족하면 test-only로 종료한다.

판정 기록

  • 2026-07-27 — fresh 테스트 성공과 별개로 4차 완료 조건과 실제 test method 범위를 대조해 증거 누락을 확정했다.
  • 2026-07-27 — P3-R6에서 cross-owner detail/update, create/update 다른 owner seriesIds, invalid releaseDateUtc를 KO/EN/JA actual endpoint 15건으로 보강했다. exact 400 ApiResponse.error, 전후 AudioContent·SeriesContent count와 field/연결 row, S3 putObject 0회와 event no-interaction을 확인했다. focused characterization, content/common 회귀, ktlintCheck가 모두 성공해 production 변경 없이 처리 완료로 판정했다.

plan·goal 전환

plan-task.md Phase 3에 Task 3.12 / P3-R5, Task 3.13 / P3-R6P3-R4-GATE를 추가한다. 기존 Task 3.11P3-R3-GATE 완료 이력은 변경하지 않는다.

5차 리뷰 종료 판정

판정 항목 결과 근거
리뷰 범위 전체 확인 충족 PRD multipart·ownership 계약과 production/test 대조
후보 항목 판정 완료 충족 REV-016, REV-017 처리 완료
확정 항목 plan 반영 충족 Task 3.12, Task 3.13, P3-R4-GATE
fresh 검증 충족 관련 79건과 lint 성공, diff check 이상 없음

최종 결론: REV-016, REV-017 처리 완료, Phase 3 Gate 완료

남은 항목: 없음. Phase 4는 사용자 진행 지시 전까지 시작하지 않는다.

P3-R4-GATE 종료 판정

  • 무엇을: REV-016 exact multipart와 REV-017 ownership/domain matrix 보강을 최종 판정했다.
  • 왜: Phase 3 5차 리뷰를 종결하고 Phase 4를 대기 상태로 넘기기 위해서다.
  • 어떻게: character mutation/error와 content create/controller/ownership focused 명령 및 git diff --check를 fresh 실행했다.
  • 결과: focused 명령은 BUILD SUCCESSFUL in 52s, git diff --check는 출력이 없었다.
판정 항목 결과 근거
REV-016 처리 충족 non-null multipart binding과 exact MissingServletRequestPartException KO/EN/JA test 추가
REV-017 처리 충족 cross-owner/domain actual endpoint KO/EN/JA no-side-effect matrix 추가
focused 회귀 충족 5차 Gate focused 명령 성공
범위 준수 충족 Phase 4 기능 구현 없음

최종 결론: Phase 3 5차 리뷰 종결

남은 항목: 없음. 다음은 사용자 지시 후 P4-T1이다.

14. 6차 재리뷰 및 판정 — 2026-07-28

리뷰 정보와 실행 결과

  • 기준 working tree: P2-R4-GATE, P3-R4-GATE 반영 후 staged/unstaged Phase 2·3 변경
  • 리뷰 상태: 판정 완료, 추가 수정 goal 필요
  • 검토 범위: 생성·수정 multipart empty-file 경계와 REV-017의 event no-interaction 실제 관찰 대상
  • fresh 검증:
    • Phase 2·3 5차 Gate focused 5개 test class를 --rerun-tasks로 실행해 XML 합계 216건, failure/error/skipped 0과 BUILD SUCCESSFUL in 4m 16s를 확인했다.
    • ./gradlew ktlintCheck --rerun-tasksBUILD SUCCESSFUL in 17s, 7개 task 실행.
    • staged/unstaged git diff --check — 출력 없음.

추가 발견 사항 요약

ID 심각도 상태 제목 소유 Task 후속 goal
REV-019 Medium 확정 빈 multipart 파일이 생성·수정의 파일 계약을 우회함 Task 3.14 P3-R7
REV-020 Low 확정 ownership/domain event 0회 assertion이 실제 service publisher를 관찰하지 않음 Task 3.15 P3-R8

REV-019 — 빈 multipart 파일의 생성·수정 계약 우회

  • 심각도: Medium
  • 상태: 확정
  • 관련 요구사항: PRD Feature C, Endpoint Contract Summary의 생성 필수 파일·수정 audioFile 미지원 계약
  • 관련 계약: 생성의 coverImage·audioFile은 비어 있지 않은 필수 파일이며, 수정의 빈 coverImage는 생략으로 처리하고 audioFile part는 크기와 관계없이 거부한다.
  • 소유 Task: Task 3.14, P3-R7

관찰 내용

REV-016은 part 누락을 non-null MVC binding으로 막았지만 MultipartFile.isEmpty는 검증하지 않는다. 따라서 생성의 빈 cover/audio part는 legacy null 검사 뒤 0-byte S3 업로드를 시도할 수 있다. 수정은 빈 coverImage도 non-null인 경우 legacy service가 업로드하고, 빈 audioFileaudioFile?.isEmpty == false 조건을 우회해 미지원 요청이 성공할 수 있다.

근거

  • 코드: AiCharacterAdminAudioContentController.kt:47~48은 생성 파일의 존재만 non-null type으로 강제한다.
  • 코드: AudioContentService.kt:218~237은 두 생성 파일을 null로만 검사하고 :333, :362에서 S3 upload에 전달한다.
  • 코드: AiCharacterAdminAudioContentFacade.kt:140은 non-empty audioFile만 거부하고 :148에서 coverImage를 그대로 legacy update service에 전달한다.
  • 코드: CreatorAdminContentService.kt:118coverImage != null이면 크기와 관계없이 upload와 cover path 교체를 수행한다.
  • 테스트: AiCharacterAdminAudioContentUpdateTest.kt:145 이후는 cover 생략과 non-empty 교체만, AiCharacterAdminAudioContentControllerTest.kt:691 이후는 non-empty audioFile 거부만 확인한다.

영향

빈 파일 part를 생성하는 multipart client에서 생성 콘텐츠가 0-byte S3 객체를 참조하거나 수정 요청이 기존 cover를 빈 객체로 교체할 수 있다. 수정 audioFile 미지원 계약도 part 존재 여부가 아니라 파일 크기에 따라 달라진다.

권장 조치

P3-R7에서 v2 facade 경계만 수정한다. 생성의 빈 cover/audio는 400 common.error.invalid_request로 거부하고, 수정의 빈 cover는 null로 정규화해 기존 경로와 S3 상태를 유지한다. 수정 audioFile은 part가 존재하면 빈 파일도 400으로 거부한다. legacy service의 공용 동작은 변경하지 않는다.

REV-020 — event no-interaction의 실제 publisher 미관찰

  • 심각도: Low
  • 상태: 확정
  • 관련 요구사항: PRD Metrics 218~221, P3-R6 NON-VACUOUS 완료 조건
  • 관련 계약: ownership/domain validation 실패는 실제 생성·수정 service의 event publisher를 호출하지 않는다.
  • 소유 Task: Task 3.15, P3-R8

관찰 내용

ownership/domain 테스트는 @MockBean ApplicationEventPublisherverifyNoInteractions를 수행하지만, Phase 2에서 같은 @MockBean이 이미 생성된 service/facade field를 대체하지 못한다는 사실을 확인했다. Phase 3 event 실패 테스트는 AudioContentService proxy target의 publisher를 명시적으로 교체하지만 P3-R6 테스트에는 같은 연결이 없다.

근거

  • 문서: plan-task.md:1319@MockBean이 이미 생성된 facade field를 대체하지 못해 실제 listener가 실행됐음을 기록한다.
  • 테스트: AiCharacterAdminAudioContentControllerTest.kt:66, AiCharacterAdminAudioContentOwnershipTest.kt:53ApplicationEventPublisher@MockBean으로 선언한다.
  • 테스트: 두 파일의 verifyNoInteractions는 각각 AiCharacterAdminAudioContentControllerTest.kt:944, AiCharacterAdminAudioContentOwnershipTest.kt:194에서 위 mock만 관찰한다.
  • 대조: AiCharacterAdminAudioContentCreateTest.kt:247, :355 이후 event 실패 테스트는 실제 AudioContentService proxy target의 publisher를 교체한다.

영향

현재 production이 validation 실패에서 event를 발행한다는 증거는 없다. 다만 REV-017 처리 결과의 event 0회 assertion은 실제 생성·수정 service publisher를 측정하지 않아 P3-R6의 NON-VACUOUS 완료 증거로 사용할 수 없다.

권장 조치

P3-R8에서 Phase 2와 기존 create failure test의 proxy-target 교체 방식을 재사용해 AudioContentServiceCreatorAdminContentService의 실제 publisher field를 같은 mock으로 교체하고 테스트 뒤 복원한다. production code는 변경하지 않는다.

plan·goal 전환

plan-task.md Phase 3에 Task 3.14 / P3-R7, Task 3.15 / P3-R8P3-R5-GATE를 추가한다. 기존 Task 3.12~Task 3.13P3-R4-GATE 완료 이력은 변경하지 않는다.

6차 리뷰 종료 판정

판정 항목 결과 근거
리뷰 범위 전체 확인 충족 multipart boundary·production service·actual endpoint test 대조
후보 항목 판정 완료 충족 REV-019, REV-020 확정
확정 항목 plan 반영 충족 Task 3.14, Task 3.15, P3-R5-GATE
fresh 검증 충족 관련 216건과 lint 성공, staged/unstaged diff check 이상 없음

최종 결론: 수정 goal 필요

남은 항목: P2-R5-GATEP3-R7P3-R8P3-R5-GATE 순서로 실행한다. Phase 4는 진행하지 않는다.

P3-R5-GATE 종료 판정 — 2026-07-28

  • 무엇을: REV-019 empty-file 계약과 REV-020 actual publisher 증거를 최종 판정했다.
  • 왜: Phase 3 6차 리뷰를 종결하고 Phase 4를 대기 상태로 유지하기 위해서다.
  • 어떻게: empty-file RED/GREEN focused, publisher focused, content/common 회귀, ktlintCheck, git diff --check를 실행했다.
  • 결과: empty-file RED는 신규 8건 실패로 재현됐고, reviewer gate 보완 뒤 empty create/update focused는 BUILD SUCCESSFUL in 43s, non-empty audio update 보완 focused는 BUILD SUCCESSFUL in 44s였다. publisher focused는 BUILD SUCCESSFUL in 39s, 최종 content/common 회귀는 BUILD SUCCESSFUL in 2m 21s, ktlintCheckBUILD SUCCESSFUL in 44s, git diff --check는 출력이 없었다.
판정 항목 결과 근거
REV-019 처리 충족 생성 empty cover/audio 400, 수정 empty cover 유지, audio part 존재 거부 actual endpoint test 추가
REV-020 처리 충족 AudioContentService, CreatorAdminContentService proxy target publisher identity/no-interaction 단언
focused/영향 회귀 충족 content/common 회귀와 lint 성공
범위 준수 충족 legacy service 공용 계약과 Phase 4 기능 변경 없음

최종 결론: Phase 3 6차 리뷰 종결

남은 항목: 없음. 다음은 사용자 진행 지시 후 P4-T1이다.