Files
sodalive-backend-spring-boot/docs/20261028_최근공지사항_v2/reviews/implementation-review.md
T

7.9 KiB

최근 공지사항 v2 구현 리뷰

1. 리뷰 정보와 범위

항목 내용
대상 2026-10-28 작업 트리의 P1-T1 구현 및 P1-GATE 독립 리뷰 부분
리뷰어 / 상태 독립 리뷰 에이전트 / 판정 완료
기준 PRD의 NOTICE-001~005, 계획, API 계약, 클라이언트 전달문, 리뷰 샘플
방법 신규 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 보류는 그대로 유지한다. 이 기록은 리뷰어의 재실행 주장이 아니라 실행자의 추가 증거다.