docs(banner): 예약 기간 계약과 검증 결과를 기록한다

This commit is contained in:
2026-10-27 20:49:42 +09:00
parent 69e6b7d24c
commit 70eaf39475
4 changed files with 472 additions and 0 deletions
@@ -0,0 +1,45 @@
# 관리자 콘텐츠 배너 예약 리뷰
## 리뷰 정보
| 항목 | 내용 |
|---|---|
| 대상 | P1-T1, P1-T1-R1의 working tree 변경 |
| 일자 | 2026-10-27 |
| 리뷰어 | 독립 scoped reviewer, parent 실행 검증 |
| 기준 | `../prd.md` SCH-001~005, `../plan-task.md` |
| 상태 | 수정 검증 완료 |
## 범위와 근거
- 관리자 create/update/list, nullable entity 시각, KST/UTC 변환, MySQL DDL 및 실제 HTTP/DB 테스트를 검토했다.
- `AdminContentBannerService.kt`, `GetAdminContentBannerResponse.kt`, `AdminContentBannerRepository.kt` 및 요청 DTO와 entity의 변경을 확인했다.
- `AdminContentBannerControllerIntegrationTest.kt`의 multipart POST/PUT/GET, H2 `TIMESTAMP(0)` 별도 트랜잭션 재조회 및 S3 미호출 assertion을 확인했다.
- 공개 기간 필터는 다음 P1-T2 범위이므로 이 리뷰에서 판정하지 않았다. 운영 MySQL과 JDBC/session timezone은 검증하지 않았다.
## 발견 사항
| ID | 심각도 | 상태 | 내용 | 후속 Goal |
|---|---|---|---|---|
| REV-ADMIN-001 | Medium | 수정 완료 | 예약 필드에 JSON 객체/배열이 오면 DTO 매핑 단계에서 실패해 `common.error.unknown`으로 반환됨 | P1-T1-R1 |
### REV-ADMIN-001
- 관찰: `startDate: {}` 또는 `endDate: []`에서 Jackson 매핑 예외가 날짜 검증 전에 발생했고, 일반 예외 처리기가 예약 오류 계약과 다른 결과를 반환했다.
- 재현: create/update × start/end × 객체/배열의 HTTP 사례 8개를 추가했다. 실제 RED에서 기존 64건은 통과하고 신규 8건만 실패했다.
- 조치: 같은 JSON 트리에서 두 예약 필드의 타입을 DTO 변환 전에 검사한다. 문자열/null/생략만 허용하며 다른 필드의 기존 매핑 계약이나 전역 예외 처리는 바꾸지 않았다.
- 검증: 오류 키 `common.error.invalid_request`, S3 미호출, 생성 미저장 및 수정 전후 DB row 일치를 확인했다. 기존 생략/null·시간대·관리자 목록 테스트도 유지했다.
## 실행 증거
```bash
./gradlew test --rerun --tests '*AdminContentBannerControllerIntegrationTest' --tests '*AdminContentBannerServiceTest' --tests '*AudioContentBannerRepositoryTest' --console=plain
```
parent가 위 명령을 실행해 exit 0, `BUILD SUCCESSFUL in 36s`를 확인했다. XML 집계는 통합 72 + 기존 서비스 1 + 공개 repository 1 = 74건이며 failures/errors/skipped는 모두 0이다. 구현자의 `ktlintCheck tasks --all`도 exit 0이다. LSP 도구가 없어 Kotlin 컴파일/ktlint를 사용했다.
## 종료 판정
- 최초 발견 사항은 기존 완료 이력을 되돌리지 않고 P1-T1-R1로 누적했다.
- scoped 재검토 결과 REV-ADMIN-001은 ADDRESSED, 명세 준수와 코드 품질 모두 APPROVE다. 수정 범위의 새 actionable finding은 없다.
- 관리자 기능의 미해결 finding은 없다. P1-T2와 최종 Gate는 별도로 수행한다.
@@ -0,0 +1,46 @@
# 콘텐츠 배너 예약 최종 리뷰
## 리뷰 정보
| 항목 | 내용 |
|---|---|
| 대상 | P1-T2와 전체 working tree 변경, P1-GATE |
| 일자 | 2026-10-27 |
| 리뷰어 | Oracle 독립 통합 리뷰, parent 실행 검증 |
| 기준 | `../prd.md`, `../plan-task.md`, `admin-reservation-review.md` |
| 상태 | 판정 완료 |
## 목적과 범위
관리자 예약 설정에서 공개 배너 노출 판정까지 승인된 계약이 연결되는지 확인했다. 관리자 DTO/service/repository/응답·entity, 네 공개 query와 공유 predicate, 관련 테스트·DDL을 검토했다. 새로운 기능 설계나 다른 배너 도메인, 전역 보안·시간대 설정 변경은 범위 밖이다.
## 검토 근거
- 공개 query: `AudioContentQueryRepositoryImpl.getAudioContentMainBannerList`, `AudioContentBannerQueryRepositoryImpl.getAudioContentMainBannerList`, `DefaultHomeRecommendationQueryRepository.findHomeBanners`, `DefaultAudioRecommendationQueryRepository.findBanners` 모두 기존 조건에 공유 `isVisibleAt(now)`를 AND 적용한다. v2는 limit 전에 예약을 제외한다.
- 경계: NULL 제한 없음과 양 끝 포함, 초 단위 UTC now, 종료 분 마지막 초의 나노초·자정 경계를 확인했다. 네 생성자의 optional nowProvider는 기존 호출과 Spring 주입을 유지한다.
- 관리자: 엄격한 문자열/JSON 타입 및 effective 기간 검증이 S3·entity 변경보다 앞선다. update 생략 유지/명시 null 해제, KST 입력/응답·UTC 저장, 관리자 날짜 무관 조회와 기존 active/tab 조건을 확인했다.
- 응답: QueryProjection 보조 생성자와 생성 Q 타입의 `LocalDateTime` 인자 일치 및 UTC→KST 문자열 변환을 확인했다. 공개 서비스의 기존 DTO 매핑은 예약 필드를 공개 응답에 추가하지 않는다.
- 테스트: 공유 16-case matrix × 4 query, 실제 H2 `TIMESTAMP(0)` 저장/flush/clear/재조회, v2 limit 충족 및 관리자 multipart HTTP 계약을 확인했다.
## 실행 증거
parent가 다음 명령을 실행했다. Oracle은 같은 테스트를 중복 실행하지 않고 정적 검토를 수행했다.
```bash
./gradlew test --rerun --tests '*AdminContentBannerControllerIntegrationTest' --tests '*AdminContentBannerServiceTest' --tests '*AudioContentBannerRepositoryTest' --tests '*AudioContentRepositoryBannerTest' --tests '*DefaultHomeRecommendationQueryRepositoryTest' --tests '*DefaultAudioRecommendationQueryRepositoryTest' ktlintCheck bootJar tasks --all --console=plain
```
- exit 0, `BUILD SUCCESSFUL in 46s`; test 강제 재실행, ktlint 통과와 실행 JAR 패키징 성공.
- XML 직접 집계: 72 + 1 + 19 + 18 + 98 + 29 = 237건, failures/errors/skipped 모두 0.
- `git diff --check` exit 0. LSP 도구가 없어 main/test Kotlin 컴파일과 ktlint로 정적 검증을 대체했다.
- 관리자 surface는 MockMvc POST/PUT/GET, 공개 persistence surface는 실제 QueryDSL/JPA/H2로 실행했다. 별도 공개 live HTTP/curl과 운영 DB는 검증하지 않았다.
## 발견 사항과 종료 판정
**확정 발견 사항 없음.** 명세 준수와 코드 품질 모두 APPROVE다. P1-T2/전체 변경을 함께 검토했으며 누락된 조회 경로나 Gate를 막는 테스트 공백을 발견하지 못했다. 신규 회귀 Task 전환 항목은 없다.
앞선 관리자 JSON 객체/배열 오류는 P1-T1-R1로 수정·검증됐으며 이번 전체 검토에서도 유지됨을 확인했다. parent의 fresh 실행 결과와 함께 P1-GATE 완료 근거로 사용한다.
## 잔여 검증 경계
전체 test/check/build는 실행하지 않았고, 직접 영향받는 여섯 클래스 회귀와 bootJar 패키징만 실행했다. 운영 MySQL의 TIMESTAMP 허용 범위, JDBC/session timezone 및 실제 DDL 왕복은 미검증이며 입증된 코드 결함으로 판정하지 않았다. nullable 컬럼 추가 DDL은 backfill 없이 기존 NULL 노출을 유지하며, 앱 rollout 전에 별도 승인 절차로 적용해야 한다.