diff --git a/docs/20260910_크리에이터커뮤니티게시물본문번역/reviews/phase-3-review.md b/docs/20260910_크리에이터커뮤니티게시물본문번역/reviews/phase-3-review.md new file mode 100644 index 00000000..8314a476 --- /dev/null +++ b/docs/20260910_크리에이터커뮤니티게시물본문번역/reviews/phase-3-review.md @@ -0,0 +1,84 @@ +# Phase 3 리뷰 보고서 + +## 1. 리뷰 정보 + +| 항목 | 내용 | +|---|---| +| 대상 | Phase 3 `P3-T1`, `P3-T2`, `P3-R1`, `P3-GATE` | +| 기준 | HEAD `50409e41c0c469529572f5d77033c3cb23d67d22` + 현재 작업 트리 | +| 작업 트리 지문 | `3bbb1d90869484073c3af681118a13f5f03c3ccecbe84de16f1568338acd66b7` | +| 일자 / 리뷰어 | 2026-09-10 / Codex 및 문맥·목표·보안·QA 증거 리뷰어 | +| 기준 문서 | [PRD](../prd.md), [구현 계획](../plan-task.md), `docs/sample/sample-review.md` | +| 상태 | 판정 완료 | + +## 2. 목적과 범위 + +`CCT-006~010`의 목록·미리보기·홈 표면, 저장 번역만 읽는 경계, 권한·페이지·공개 계약 유지를 확인한다. +기존 최종 Gate의 실제 실행 미완료 범위도 대조한다. 사용자 지시에 따라 테스트·컴파일·서버·HTTP는 실행하지 않는다. + +## 3. 판정 기준과 코드 대조 + +아래 경로는 `src/main/kotlin/kr/co/vividnext/sodalive/` 기준이다. + +| 표면 / 기준 | 코드 근거 | 판정 | +|---|---|---| +| 레거시 일반·최신 목록 | `explorer/profile/creatorCommunity/CreatorCommunityService.kt:305`, `:359`, `:641`, `:679` | 충족. 표시할 ID를 모아 번역 조회 후 본문만 복사 | +| v2 탭 | `v2/creator/channel/community/application/CreatorChannelCommunityQueryService.kt:74` | 충족. 반환 페이지 확정 후 번역 조회, 다음 페이지 확인용 추가 행 제외 | +| 채널 홈 고정·일반 | `v2/creator/channel/home/application/CreatorChannelHomeQueryService.kt:104`, `:133` → 공용 `findHomeCommunityPosts` | 충족. 두 그룹 모두 같은 읽기 연결 | +| 홈 추천 | `v2/recommendation/application/HomeRecommendationQueryService.kt:158` | 충족. 후보 정렬·중복 제거·limit 이후 최종 ID만 처리 | +| 팔로잉 소식 | `v2/home/following/application/HomeFollowingQueryService.kt:32`, `:60` | 충족. 커뮤니티 유형만 변경하고 다른 소식 유지 | +| 목록 계열 예약 금지 | `v2/creator/channel/community/translation/application/CreatorCommunityTranslationService.kt:33` | 충족. `findDisplayContents`는 읽기 전용이며 감지·예약·materialize 호출 없음 | +| 공개 스키마 | DTO/도메인 `copy(content = ...)` | 충족. 내부 `CreatorCommunityDisplayContent`를 공개 응답으로 추가하지 않음 | +| DDL 타입 정합성 | `schema.sql:11`, `CreatorCommunityTranslation.kt`의 sourceHash 매핑 | 기존 `P3-R1`의 VARCHAR(64) 수정 반영 확인 | + +`LiveApiService`와 `ExplorerService`의 간접 목록 호출도 기존 서비스에 연결되어 저장 번역을 사용한다. +팔로잉 본문은 `DefaultHomeFollowingQueryRepository.kt:160`, `:251`에서 현재 게시물 내용을 읽으므로 +소식 발행 당시 저장한 미리보기를 번역 원문으로 사용하지 않는다. + +## 4. 테스트·기록 대조 + +test 경로는 `src/test/kotlin/kr/co/vividnext/sodalive/` 기준이다. + +| 검증 대상 | 읽은 기존 테스트 | +|---|---| +| 배치 크기 1/4에서 SELECT 2회 유지 | `explorer/profile/creatorCommunity/CreatorCommunityTranslationEndToEndTest.kt:66`, `shouldReadTranslationsWithConstantSelectCountAcrossBatchSizes` | +| 목록 누락 번역의 작업/이벤트 미생성 | 같은 파일 `:98`, `shouldNotPublishOrScheduleWhenLegacyListReadsMissingTranslation` | +| 레거시 필터·마스킹·일괄 조회 | `explorer/profile/creatorCommunity/CreatorCommunityServiceTest.kt:579` 이후 관련 테스트 | +| v2 페이지 추가 행 제외·홈 두 그룹 | `v2/creator/channel/community/application/CreatorChannelCommunityQueryServiceTest.kt:180` 이후 관련 테스트 | +| 채널 홈 저장 번역 | `v2/api/creator/channel/home/CreatorChannelHomeEndToEndTest.kt:89`, `shouldUseStoredTranslationsForPinnedAndNormalHomeCommunityPosts` | +| 추천 최종 배치·팔로잉 커뮤니티 유형만 처리 | `v2/recommendation/application/HomeRecommendationQueryServiceTest.kt:705`, `v2/home/following/application/HomeFollowingQueryServiceTest.kt:123` | + +`git diff`, `rg`, `sed`로 호출부와 테스트를 대조했다. 기존 XML과 레인 결과는 [증거 기록](review-evidence.md)에 있다. +이 표는 테스트를 새로 실행했다는 의미가 아니다. 기존 계획의 로컬 성공 기록과 실제 MySQL/Papago/HTTP 미완료 표기는 +서로 구분되어 있으며, 후자를 완료한 것으로 평가하지 않는다. + +## 5. 발견 사항 요약 + +**확정 발견 사항 없음.** 목록·채널 홈·추천·팔로잉의 저장 번역 적용은 인터뷰에서 확정한 범위에 부합한다. + +## 6. 후보 판정 + +| 후보 | 판정 / 근거 | +|---|---| +| 추천·팔로잉 본문 복사가 유료 전체 본문을 노출 | 오탐. `DefaultHomeRecommendationQueryRepository.kt:893`, `DefaultHomeFollowingQueryRepository.kt:461`의 기존 무료 조건 유지 | +| 레거시/v2 마스킹 전에 원문 길이를 사용 | 오탐. 번역 본문을 복사한 뒤 `SelectCommunityPostResponse.kt:44`, `CreatorChannelCommunityQueryService.kt:253`의 기존 마스킹 실행 | +| 번역 배치 조회가 작성자별 N+1을 추가 | 오탐. `member` LAZY이며 읽기에서 접근하지 않는다. 서로 다른 작성자의 배치 크기 테스트가 기존에 존재 | +| 목록 번역 누락을 처리하기 위해 자동 작업 예약 | 오탐. 공용 읽기 메서드만 사용하고 예약 진입점을 호출하지 않음 | + +## 7. 계획 전환 + +**전환 항목 없음.** 추가 수정 Task는 만들지 않는다. Phase 1 결함으로 번역이 아직 생성되지 않은 경우에는 +이 Phase의 요구대로 원문을 표시한다. 예약 결함은 `P1-R4`에서 처리한다. + +## 8. 리뷰 종료 판정 + +| 항목 | 결과 | +|---|---| +| 모든 합의된 목록·미리보기 표면 확인 | 충족 | +| 확정 결함 | 없음 | +| 후보 판정 완료 | 충족, 위 후보는 오탐 | +| 확정 항목 계획 반영 | 해당 없음 | +| 실행 범위·기존 미완료 검증 구분 | 충족 | + +**최종 결론: Phase 3 기준 충족, 확정 발견 사항 없음.** +실제 DDL·MySQL 동시성·HTTP·Papago 확인은 기존 수동 체크리스트대로 남긴다. 이번 리뷰에서 제품 코드를 수정하지 않았다. diff --git a/docs/20260910_크리에이터커뮤니티게시물본문번역/reviews/review-evidence.md b/docs/20260910_크리에이터커뮤니티게시물본문번역/reviews/review-evidence.md new file mode 100644 index 00000000..141fa23a --- /dev/null +++ b/docs/20260910_크리에이터커뮤니티게시물본문번역/reviews/review-evidence.md @@ -0,0 +1,121 @@ +# 2026-09-10 Phase별 정적 리뷰 증거 + +## 대상과 검증 범위 + +아래 지문부터 리뷰 산출물 검증까지는 최초 정적 리뷰 당시 기록이다. `P1-R4` 이후 변경에는 이 지문과 PASS를 +소급 적용하지 않는다. 현재 최종 판정과 후속 자동 증거는 문서 끝에 별도로 누적한다. + +- 기준 HEAD: `50409e41c0c469529572f5d77033c3cb23d67d22`. +- 리뷰 대상은 HEAD 자체가 아닌 현재 수정/미추적 source·test와 `schema.sql` 32개 파일이다. +- 위 파일의 경로순 `경로 + NUL + SHA256 + LF`를 결합한 SHA256: + `3bbb1d90869484073c3af681118a13f5f03c3ccecbe84de16f1568338acd66b7`. +- 사용자 지시: 컴파일·테스트는 이미 통과했으므로 테스트를 직접 실행하지 않는다. +- 이번 판정은 소스·테스트·기존 기록의 정적 대조다. 테스트/컴파일/서버/HTTP/Papago/MySQL 실행 판정이 아니다. +- 이전 리뷰의 PASS를 재사용하지 않고 현재 작업 트리를 다시 읽었다. 아래 PASS는 정적 리뷰 범위에만 적용한다. + +## 레인 기록 + +모든 기록의 기준은 위 전체 HEAD와 작업 트리 지문이다. 소스가 바뀌면 이 결과를 그대로 재사용하지 않는다. + +| 레인 | 판정 | 근거 / 결과 출처 | +|---|---|---| +| 보안·권한 `security_review` | PASS | 현재 본문 정합성, 번역 선택 후 유료 마스킹, 추천/팔로잉 무료 필터, 관리자 원문 보존 확인. `/root/security_review` 최종 보고, 2026-09-10. | +| 목표·제약 `goal_review` | 수정 필요 | Phase 1 캐시 경합으로 요청 target 유실을 독립 확인. Phase 2·3 추가 결함 없음. `/root/goal_review` 최종 보고, 2026-09-10. | +| 코드 품질·동시성 `code_review` | 수정 필요 | 감지 캐시 최초 INSERT 경합으로 후발 target 예약 유실. `/root/code_review` 최종 보고, 2026-09-10. Phase 1 보고서에 상세 근거를 기록한다. | +| 기존 QA 증거 `qa_evidence_review` | 정적 증거 확인 완료 | 기존 테스트 소스/XML 성공 기록을 확인했다. 실제 캐시 INSERT 경합과 한·영·일 전체 파이프라인의 직접 통합 증거 한계는 별도로 남긴다. `/root/qa_evidence_review` 최종 보고, 2026-09-10. | +| 문맥·통합 `context_review` | PASS — Phase 3 범위 | 목록/홈 및 간접 소비자, 공개 스키마·무료 필터·읽기 전용 경계 확인. `/root/context_review` 최종 보고, 2026-09-10. Phase 1·2 전체 판정으로 확대하지 않는다. | + +동적 QA/debugging 감사는 사용자 지시에 따라 재실행하지 않았다. 이를 PASS나 신규 실패로 분류하지 않는다. + +## 기존 실행 증거 표본 + +아래는 `build/test-results/test/`의 기존 XML을 읽은 결과이며 현재 턴에서 실행한 테스트가 아니다. + +| XML 파일명 | 테스트 수 | 실패 / 오류 / skip | 기록 timestamp | +|---|---:|---|---| +| `TEST-kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest.xml` | 12 | 0 / 0 / 0 | 2026-09-10T02:43:16 | +| `TEST-kr.co.vividnext.sodalive.v2.creator.channel.community.translation.CreatorCommunityTranslationServiceTest.xml` | 16 | 0 / 0 / 0 | 2026-09-10T02:50:05 | +| `TEST-kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityTranslationEndToEndTest.xml` | 4 | 0 / 0 / 0 | 2026-09-10T02:44:03 | +| `TEST-kr.co.vividnext.sodalive.i18n.translation.TranslationJobWorkerTest.xml` | 7 | 0 / 0 / 0 | 2026-09-10T02:44:03 | +| `TEST-kr.co.vividnext.sodalive.v2.api.creator.channel.community.adapter.in.web.CreatorChannelCommunityEndToEndTest.xml` | 15 | 0 / 0 / 0 | 2026-09-10T02:48:44 | +| `TEST-kr.co.vividnext.sodalive.v2.api.creator.channel.home.CreatorChannelHomeEndToEndTest.xml` | 2 | 0 / 0 / 0 | 2026-09-10T02:49:09 | + +XML에 source 지문이 포함되어 있다고 가정하지 않는다. 사용자 보고와 계획의 과거 성공 기록을 보조하는 자료로만 사용한다. + +## 최종 정적 판정 + +- [Phase 1](phase-1-review.md): 확정 수정 사항 1건 → `P1-R4`. +- [Phase 2](phase-2-review.md): 자체 구현 범위 충족, 추가 확정 발견 사항 없음. Phase 1 결함의 상세 예약 영향은 남는다. +- [Phase 3](phase-3-review.md): 저장 번역 표시·목록 비예약 범위 충족, 추가 확정 발견 사항 없음. +- 이번 리뷰는 테스트·제품 코드·DDL을 변경하지 않았다. Phase 1 수정과 실제 실행 검증은 후속 작업이다. + +## 리뷰 산출물 검증 + +- `./gradlew tasks --all` — exit 0, `BUILD SUCCESSFUL`. 저장소 문서 가이드에 따른 task 목록 확인만 수행했다. + 이 명령으로 컴파일이나 테스트를 실행하지 않았다. +- `git diff --check` — exit 0. 새 미추적 리뷰 문서는 별도 공백/개행 검사로 확인했다. +- 문서 링크·공백·템플릿 잔여값 검사 — 5개 문서 PASS. `P1-R4`는 Phase 1 안에 한 번만 미완료 Goal로 등록됐다. +- 리뷰 전후 source/test/DDL 32개 파일 SHA256 대조 — 동일 지문으로 PASS. 제품 코드·테스트·DDL 수정 없음. + +## P1-R4 수정 후 증거 · 2026-09-10 + +이 절은 완료된 구현의 RED/GREEN·자동 회귀·리뷰 결과를 전달받아 동기화한 기록이다. +이번 문서 작업에서 제품 테스트를 다시 실행한 결과가 아니며, 위 최초 정적 리뷰의 지문을 새 코드의 지문으로 재사용하지 않는다. + +### 구현과 RED/GREEN + +- RED: 아래 focused 명령은 두 provider 진입 이후 exit 1로 실패했다. + `ExecutionException → DataIntegrityViolationException → H2 23505`로 감지 캐시 유일 키 충돌을 확인했다. +- GREEN: native MySQL `ON DUPLICATE KEY UPDATE id=id`와 scalar `SELECT ... FOR UPDATE`로 승자 행을 + 현재 읽기한다. native INSERT에 audit 시각을 명시하며 DB 예외를 잡고 후속 예약을 계속하지 않는다. +- 실제 캐시 service/repository를 통한 같은 게시물 `ja`/`en` 이중 miss와 다른 게시물의 동일 본문 경합을 검증했다. + 캐시 1행으로 수렴하고 각 요청의 목표 언어 작업을 보존한다. + +### 자동 검증 명령과 결과 + +RED focused 명령 (exit 1, 위 유일 키 충돌): + +```bash +./gradlew test --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectionCacheConcurrencyTest' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest' +``` + +GREEN focused fresh 재실행, `BUILD SUCCESSFUL` (5분 19초): + +```bash +./gradlew test --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectionCacheConcurrencyTest' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest' --rerun-tasks --no-parallel +``` + +P1 listener/scheduler/materializer 영향 회귀, `BUILD SUCCESSFUL` (1분 09초): + +```bash +./gradlew test --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' --tests 'kr.co.vividnext.sodalive.v2.creator.channel.community.translation.*' --tests 'kr.co.vividnext.sodalive.i18n.translation.*' +``` + +P2 상세/API 소비자 회귀, `BUILD SUCCESSFUL` (1분 42초): + +```bash +./gradlew test --tests 'kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.*' --tests 'kr.co.vividnext.sodalive.v2.api.creator.channel.community.*' +``` + +형식 검사, `BUILD SUCCESSFUL` (44초): + +```bash +./gradlew ktlintCheck +``` + +전체 `./gradlew test`는 생략했다. 공통 캐시 계약, 실제 repository 경합, 모든 직접 listener/scheduler/materializer +테스트와 P2 상세/API 소비자가 위 명령으로 통과했고 별도로 미해결된 공통 경계가 없었다. +과거 전체 빌드 성공을 이번 변경의 전체 테스트 실행으로 간주하지 않는다. + +### 수정 후 리뷰와 현재 최종 판정 + +| 리뷰 | 판정 | 출처 | +|---|---|---| +| spec | PASS | `ses_f7657d350ffe3YUYhEHvHPuikK` | +| quality | APPROVED | `ses_f7656994dffeR2odTLswaw6RTo` | + +**현재 최종 판정: 기존 Phase 1 결함 `REV-P1-007`은 `P1-R4` 로컬 자동 검증 범위에서 수정 완료다.** +H2 `MODE=MySQL` 결과는 실제 MySQL 8/InnoDB의 유일 키·격리 수준·audit 컬럼·잠금 동작 검증이 아니다. +테스트 서버의 순서 제어 이중 miss, 다른 게시물의 공유 캐시 수렴, provider 차단 중 본문 수정과 stale 결과 차단은 +[계획의 수동 체크리스트](../plan-task.md)에 미완료로 남긴다. 실제 MySQL/Papago/live HTTP, Docker, 배포는 +검증하지 않았으며 기존 정적 리뷰의 실환경 검증 한계도 유지한다.