Files
T

7.8 KiB

P1-T2 리뷰 보고서

리뷰 정보

항목 내용
리뷰 대상 Phase 1 / P1-T2, P1-GATE
기준 working tree P1-T2 구현 및 실제 Spring regression test 추가 상태
리뷰 일자 2026-09-10
리뷰어 automated review
기준 문서 prd.md, plan-task.md
리뷰 상태 추가 수정 및 회귀 검증 완료

범위

  • 코드: LanguageDetectEvent.kt, CreatorCommunityTranslationService.kt
  • 테스트: CreatorCommunityLanguageDetectTest.kt
  • 제외: 다른 리소스 감지, 공개 API, Docker·HTTP·실제 Papago·MySQL 수동 검증

확정 발견 사항

ID 심각도 상태 제목 후속 goal
REV-P1-T2-001 Blocker 수정 완료 커밋 후 번역 예약이 독립 트랜잭션으로 커밋되지 않는다 P1-R1
REV-P1-T2-002 High 수정 완료 잠금 조회가 stale managed 게시물 상태를 다시 읽지 않는다 P1-R1
REV-P1-T2-003 High 수정 완료 이미 언어가 있는 이벤트가 요청 target을 버린다 P1-R1
REV-P1-T2-004 High 수정 완료 이전 mock 검증은 실제 translation_job 저장을 증명하지 않는다 P1-R1
REV-P1-T2-005 High 수정 완료 동시 언어 미확정 감지의 두 번째 target이 lock 뒤 유실된다 P1-R2
REV-P1-T2-006 Blocker 수정 완료 동시 missing-memory 예약이 translation job unique key에 경합한다 P1-R3

근거와 판정

REV-P1-T2-001

  • 코드: CreatorCommunityTranslationService.requestTranslations는 기본 @Transactional이고, listener는 after-commit callback에서 이를 호출한다.
  • 재현: shouldExposeProxiedRequiresNewTranslationRequestEntryPoint는 Propagation.REQUIRES_NEW가 아닌 annotation으로 실패했다.
  • 영향: 감지 언어 저장 뒤에도 job 예약이 실제로 커밋되지 않아 번역 흐름이 진행되지 않는다.
  • 조치: 공유 서비스의 public 요청 진입점을 REQUIRES_NEW로 선언하고 실제 job 저장 test로 검증한다.

REV-P1-T2-002

  • 코드: listener는 findByIdAndIsActiveTrueForUpdate 뒤 persistence context의 entity를 refresh하지 않는다.
  • 재현: shouldRejectStalePostAfterBlockedDetection는 감지 동안 본문을 변경한 뒤에도 이전 언어가 저장되어 실패했다.
  • 영향: 오래된 감지 결과가 새 본문 언어로 저장될 수 있다.
  • 조치: 잠금 획득 뒤 최신 entity 상태를 refresh하고 revision/content를 재검사한다.

REV-P1-T2-003

  • 코드: listener는 이미 languageCode가 설정된 커뮤니티 이벤트를 감지 전 return한다.
  • 재현: shouldContinueBothRequestedTargetsWhenLanguageIsAlreadyKnown는 ja, en target job이 저장되지 않아 실패했다.
  • 영향: 상세 요청별 번역 예약 범위가 사라진다.
  • 조치: 현재 본문 검증 뒤 known-language 이벤트도 after-commit에서 원 target으로 shared service를 호출한다.

REV-P1-T2-004

  • 코드/테스트: 기존 테스트는 CreatorCommunityTranslationService mock의 호출만 검증했다.
  • 재현: 실제 Spring listener와 scheduler를 연결한 CreatorCommunityLanguageDetectTest에서 저장된 translation_job target을 확인하자 job이 없었다.
  • 영향: mock interaction만 통과해도 사용자 흐름의 persistence 실패를 놓친다.
  • 조치: 감지 provider만 제어하고 scheduler/repository는 실제 bean으로 사용한다.

REV-P1-T2-005

  • 코드: lock과 refresh 뒤 languageCode가 이미 있으면 revision/content 재검증 전에 return한다.
  • 재현: 같은 언어 미확정 본문으로 두 감지를 시작해 첫 감지를 먼저 커밋한 뒤 두 번째 감지를 진행하면 두 번째 target job이 없다.
  • 영향: 동시 상세 요청이 서로 다른 언어를 요청할 때 한 요청의 번역 예약이 유실된다.
  • 조치: 현재 revision/content가 유효하면 이미 저장된 언어를 보존하고, 두 번째 이벤트의 원 target을 after-commit에 등록한다.

REV-P1-T2-006

  • 코드: requestTranslations는 post lock 없이 materializer의 missing-memory 반환 뒤 scheduler의 job 존재 조회와 insert를 실행한다.
  • 재현: 같은 언어 확정 post와 target으로 두 request transaction을 동시에 시작하면 두 transaction이 missing job을 보고 insert를 시도할 수 있다.
  • 영향: job unique key 예외가 호출자 transaction까지 전파돼 상세 예약 흐름이 실패할 수 있다.
  • 조치: request transaction 시작에서 active post write lock과 refresh를 수행해 source extraction, memory lookup, job 존재 조회와 insert를 직렬화한다.

실행 증거

명령 결과 핵심 증거
./gradlew test --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' 실패 초기 test bean 보정 후 7개 중 5개 assertion 실패

결론

REV-P1-T2-001~006은 plan-task.md의 P1-R1~R3 회귀 수정 goal로 반영했고 모두 수정·검증을 완료했다.

수정 후 검증 기록

1차 수정 검증 — 2026-09-10

  • 무엇을: REQUIRES_NEW 번역 요청, stale entity refresh, known-language target continuation과 실제 translation_job 검증을 추가했다.
  • 어떻게:
    • ./gradlew test --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' — exit 0, BUILD SUCCESSFUL.
    • ./gradlew test --tests 'kr.co.vividnext.sodalive.v2.creator.channel.community.translation.*' --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.*' ktlintCheck bootJar — exit 0, BUILD SUCCESSFUL.
  • 남은 항목: 사용자 정책상 Docker, HTTP, 실제 Papago, MySQL 수동 동시성 검증은 실행하지 않았다.

2차 수정 검증 — 2026-09-10

  • 대상: REV-P1-T2-005.
  • RED: ./gradlew test --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' — exit 1, 8개 중 1개 실패. 두 감지가 언어 NULL에서 시작한 뒤 첫 감지의 ja job 저장 후 두 번째 en job이 누락됐다.
  • GREEN: lock과 refresh 뒤 revision/content가 유효하면 이미 저장된 languageCode를 유지하고, 원 target을 after-commit에 등록하도록 수정했다.
  • 검증:
    • ./gradlew test --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' — exit 0, BUILD SUCCESSFUL.
    • ./gradlew test --tests 'kr.co.vividnext.sodalive.v2.creator.channel.community.translation.*' --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.*' ktlintCheck — exit 0, BUILD SUCCESSFUL.
  • 남은 항목: Docker, HTTP, 실제 Papago, MySQL 수동 동시성 검증은 사용자 정책상 실행하지 않았다.

3차 수정 검증 — 2026-09-10

  • 대상: REV-P1-T2-006.
  • RED: ./gradlew test --tests 'kr.co.vividnext.sodalive.v2.creator.channel.community.translation.CreatorCommunityTranslationServiceTest' — exit 1. 실제 translation_job insert가 H2 SQLState 23505, uk_translation_job_resource_field_target_hash unique index 충돌로 실패했다.
  • GREEN: requestTranslations transaction 시작에서 active post write lock과 PESSIMISTIC_WRITE refresh를 수행하도록 수정했다.
  • 검증:
    • 같은 focused test — exit 0, BUILD SUCCESSFUL; 두 호출 정상 종료와 job 1개 저장을 확인했다.
    • P1 직접 영향 회귀와 P1 Gate — exit 0, BUILD SUCCESSFUL.
    • ./gradlew ktlintCheck — exit 0, BUILD SUCCESSFUL.
  • 남은 항목: Docker, HTTP, 실제 Papago, MySQL 수동 동시성 검증은 사용자 정책상 실행하지 않았다.