docs(notice): v2 최근 공지 구현 검증과 리뷰를 기록한다
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# 최근 공지사항 v2 구현 리뷰
|
||||
|
||||
## 1. 리뷰 정보와 범위
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 대상 | 2026-10-28 작업 트리의 P1-T1 구현 및 P1-GATE 독립 리뷰 부분 |
|
||||
| 리뷰어 / 상태 | 독립 리뷰 에이전트 / 판정 완료 |
|
||||
| 기준 | [PRD](../prd.md)의 NOTICE-001~005, [계획](../plan-task.md), [API 계약](../api-contract.md), [클라이언트 전달문](../client-prompt.md), [리뷰 샘플](../../sample/sample-review.md) |
|
||||
| 방법 | 신규 controller/service, SecurityConfig, 통합 테스트, 기존 repository/DTO/v1 및 홈 호출 경로를 읽기 전용 대조 |
|
||||
| 제외 | 코드·테스트 수정, 명령·테스트·실서버 재실행, 운영 MySQL·배포 검증 |
|
||||
|
||||
이전 리뷰 결과는 회수되지 않아 재사용하지 않았다. 이번 판정은 현재 파일을 직접 읽은 정적 검토와 부모 실행자가 전달한 실행 증거를 구분한다. Git SHA/diff는 이번 리뷰에서 독립 확인하지 않았으며, 기존 파일의 변경 없음은 부모의 diff 관찰을 함께 근거로 삼았다.
|
||||
|
||||
## 2. 판정 기준과 계약 대조
|
||||
|
||||
확정 계약 위반·보안 노출·주요 회귀는 수정 필요 항목으로 판정한다. 재현 조건과 코드·테스트 근거가 없는 추측이나 공개된 환경 한계는 코드 결함으로 올리지 않는다. 아래 경로는 저장소 루트 기준이며 숫자는 줄 번호다.
|
||||
|
||||
| 기준 | 판정 및 근거 |
|
||||
|---|---|
|
||||
| NOTICE-001: 익명 GET 및 배열 envelope | 적합. `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/notice/adapter/in/web/LatestNoticeController.kt:10`의 경로와 `:13`의 `ApiResponse.ok`가 목록을 직접 반환한다. `src/main/kotlin/kr/co/vividnext/sodalive/common/ApiResponse.kt:3`의 기존 네 필드를 재사용한다. |
|
||||
| NOTICE-002: 활성·id DESC·DB 최대 3개 | 적합. `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/notice/application/LatestNoticeService.kt:12`가 `getNoticeList(PageRequest.of(0, 3))`를 호출한다. `src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceServiceNoticeRepository.kt:44`의 활성 필터, `:45`의 offset, `:46`의 DB limit, `:47`의 id 역순을 그대로 사용한다. 전체 조회 후 메모리 절단이나 totalCount 조회가 없다. |
|
||||
| NOTICE-003: 필드·전체 본문·KST | 적합. `src/main/kotlin/kr/co/vividnext/sodalive/notice/GetNoticeResponse.kt:10`의 `NoticeItem(id: Long, title: String, content: String, date: String)`을 재사용한다. `ServiceServiceNoticeRepository.kt:36`은 원래 제목·본문을 projection하고 `:67`은 UTC→Asia/Seoul 및 `%Y-%m-%d`를 유지한다. 신규 변환·축약이 없다. |
|
||||
| NOTICE-004: 0/1/2/3+ 및 전부 비활성 | 적합. service의 non-null 목록을 그대로 반환하므로 빈 목록도 `[]`이고 패딩·반복이 없다. 통합 테스트 `:59`, `:101`, `:110`, `:125`, `:142`가 활성 4개와 더 큰 비활성 id, 0/1/2개, 전부 비활성을 검증한다. |
|
||||
| NOTICE-005: v1·홈·권한 보존 | 적합. `src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceNoticeController.kt:39` → `ServiceNoticeService.kt:51` → `ServiceServiceNoticeRepository.kt:51`은 단일/null 경로다. `src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeService.kt:52`와 `GetContentMainTabHomeResponse.kt:13`도 기존 `NoticeItem?`를 유지한다. `src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt:194`는 정확한 GET 한 경로만 허용하고 `:214`의 나머지 인증 규칙을 유지한다. |
|
||||
|
||||
## 3. 코드·테스트 품질
|
||||
|
||||
- **품질 판정: 적합.** 읽기 전용 트랜잭션 service와 얇은 controller만 추가하며 기존 repository·DTO·envelope를 재사용한다. 신규 port·facade·캐시·페이지네이션·의존성·오류 fallback 등 불필요한 추상화가 없다.
|
||||
- 테스트 기준 파일은 `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/notice/LatestNoticeControllerIntegrationTest.kt`다. `:26`의 RANDOM_PORT, `:33`의 embedded Redis, `:35`의 실제 repository/JDBC와 TestRestTemplate을 사용하며 controller/service를 mock하지 않는다. 파라미터 확장 포함 11건이다.
|
||||
- `:193`은 실제 저장·flush 후 JDBC로 고정 시각을 저장한다. 이는 `src/main/kotlin/kr/co/vividnext/sodalive/common/BaseEntity.kt:29`의 PrePersist 덮어쓰기를 피하고 별도 HTTP 요청이 DB를 읽게 한다. `:185`는 상태와 전체 JSON tree를 비교하므로 null 필드 누락·추가 필드·본문 변형·배열 순서도 검출한다.
|
||||
- `:59`의 id와 createdAt 역순 fixture, `:84`의 UTC 14:59:59/15:00:00 KST 경계, `:154`/`:171`의 v1 객체/null, `:176`의 POST·인접 GET 거부가 핵심 회귀를 덮는다. H2 날짜 alias는 기존 `src/test/kotlin/kr/co/vividnext/sodalive/support/H2MysqlDateFunctions.kt:10`을 사용하므로 운영 MySQL 검증과는 구분한다.
|
||||
- 클라이언트 전달문도 배열·서버 순서·날짜 재변환 금지·미배포 시 명시적 mock·자동 fallback 금지를 계약과 일치하게 안내한다.
|
||||
|
||||
## 4. 실행 증거와 검증 한계
|
||||
|
||||
아래 실행 결과는 **부모 실행자가 실제 관찰하여 전달한 증거**다. 리뷰어는 명령, XML 또는 HTTP 호출을 재검증하지 않았다.
|
||||
|
||||
| 전달된 검증 | 결과와 한계 |
|
||||
|---|---|
|
||||
| focused RED | 11 tests / 7 failures. 미구현·미허용 v2 GET이 HTTP 401로 실패했으며 컴파일·인프라 실패를 RED로 세지 않았다. |
|
||||
| focused GREEN XML | timestamp `2026-10-28T06:28:11`, 11 tests / 0 failures / 0 errors / 0 skipped. 전체 테스트 성공의 증거는 아니다. |
|
||||
| 실제 HTTP 관찰 | v2 HTTP 200의 id 12/11/10 배열과 `[]`, v1 null/객체, 익명 POST 및 인접 GET의 401을 확인했다. 전체 필드·KST 경계는 위 focused 테스트와 계획의 응답 기록으로 연결된다. |
|
||||
| `./gradlew --no-daemon bootJar ktlintCheck` | exit 0, 59초. 패키징·lint 성공이며 전체 `build` 성공으로 확대하지 않는다. |
|
||||
| `./gradlew --no-daemon build --continue` | `:test`에서 1,200,000ms 후 타임아웃. **전체 테스트 및 전체 build 검증은 pending/blocked이며 P1-GATE 완료 불가.** 타임아웃 후 focused HTTP 재실행은 부모 담당 예정으로, 이 리뷰 시점에는 결과 미확인이다. |
|
||||
|
||||
LSP 도구는 미제공이다. 운영 MySQL의 실제 timezone 처리·배포 및 홈 전체 HTTP 응답 회귀는 미검증이다. 이 한계만을 이유로 무관한 코드 수정을 요구하지 않는다. 이번 리뷰는 shell·런타임 자원·임시 root를 생성하지 않았다.
|
||||
|
||||
## 5. 발견 사항과 후속 전환
|
||||
|
||||
**확정 발견 사항 없음.** 승인 계약과 검토 코드·테스트에서 수정이 필요한 결함을 확인하지 못했다. 신규 회귀 수정 Task/goal 전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
**규격 판정: 적합. 코드·테스트 품질 판정: 승인(검토 범위 내). 독립 리뷰는 완료하되 최종 P1-GATE는 전체 회귀·전체 build 미확인으로 보류한다. 배포 승인을 뜻하지 않는다.**
|
||||
|
||||
남은 담당은 부모 실행자다. 타임아웃 후 focused HTTP 재실행 결과와 전체 회귀·전체 build의 종료 결과를 별도로 기록하고, 기존 문서의 Gate 상태·역할 표현을 최종 증거에 맞춰 정리한다. 이 리뷰에서는 다른 파일이나 완료 체크박스를 변경하지 않는다.
|
||||
|
||||
## 7. 실행자 후속 증거
|
||||
|
||||
- 2026-10-28: 리뷰와 독립적으로 공지·기존 배포 엔드포인트 HTTP 테스트를 함께 재실행했다. `./gradlew --no-daemon test --tests kr.co.vividnext.sodalive.v2.api.notice.LatestNoticeControllerIntegrationTest --tests kr.co.vividnext.sodalive.deployment.DeploymentEndpointIntegrationTest`가 exit 0, 3m29s로 끝났으며 XML은 11+4=15 tests, failures/errors/skipped 모두 0이다.
|
||||
- production/test 수정 없이 문서의 현재 상태를 갱신했다. 전체 회귀의 시간 초과와 P1-GATE 보류는 그대로 유지한다. 이 기록은 리뷰어의 재실행 주장이 아니라 실행자의 추가 증거다.
|
||||
Reference in New Issue
Block a user