From 6093b37747f106a81433164b4ae35820f3a04057 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 9 Nov 2026 18:24:24 +0900 Subject: [PATCH] =?UTF-8?q?docs(i18n):=20=EC=BD=94=EB=93=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EA=B3=B5=EA=B8=89=EC=9E=90=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=EA=B3=BC=20=EA=B2=80=EC=A6=9D=EC=9D=84=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan-task.md | 161 +++++++++++++++++- docs/20261109_감지번역공급자공통화/prd.md | 19 ++- .../provider-guide.md | 38 +++-- .../reviews/phase-2-review.md | 31 ++++ 4 files changed, 229 insertions(+), 20 deletions(-) create mode 100644 docs/20261109_감지번역공급자공통화/reviews/phase-2-review.md diff --git a/docs/20261109_감지번역공급자공통화/plan-task.md b/docs/20261109_감지번역공급자공통화/plan-task.md index dcfbb5de..9811c43e 100644 --- a/docs/20261109_감지번역공급자공통화/plan-task.md +++ b/docs/20261109_감지번역공급자공통화/plan-task.md @@ -3,8 +3,8 @@ | 항목 | 내용 | |---|---| | 작성일 | 2026-11-09 | -| 상태 | 구현·검증·독립 리뷰·리소스 정리 완료 | -| 요구사항 | [prd.md](prd.md), LP-001~010 및 확정 구현 계약 | +| 상태 | Phase 1·2 구현·검증·리뷰·리소스 정리 완료 | +| 요구사항 | [prd.md](prd.md), LP-001~012 및 확정 구현 계약 | | 활성 Goal | 없음 | ## 목표와 제약 @@ -339,3 +339,160 @@ M v2/creator/channel/community/translation/CreatorCommunityTranslationServiceTes 출력: `CLEANUP PASS: owned language-providers.06uAbV removed; shared caches untouched`. 임시 Java/class/추출 라이브러리/로그가 함께 제거됐으며 필요한 증거는 이 문서에 보존했다. 작업 임시 노트도 문서에 이관 후 삭제한다. 다른 작업의 파일·프로세스·캐시에는 조치하지 않았다. + +### Phase 2: 코드 목록으로 공급자 순서 지정 + +- [x] **Task 2.1: 우선순위 환경 설정 제거 (P2-T1)** + +**Objective:** LP-011/012에 따라 코드의 독립 목록만 production 순서를 결정한다. +**시작 조건:** 사용자의 상수 목록 방식 승인, 기존 Phase 1 완료. +**완료 증거:** 과거 property/env 무시 회귀 RED/GREEN, typed-list 순서/검증, Spring/H2 wiring 회귀. +**범위 밖:** 공급자 선택/캐시 로직 변경, 인증 키 환경변수 제거, AWS 구현, 전역 mutable 테스트 설정. + +**Files:** +- Add: `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt`. +- Modify: 같은 패키지 `LanguageProviderProperties.kt`, `src/main/resources/application.yml`의 우선순위 항목만. +- Update tests: `LanguageProviderPropertiesTest.kt`, `LanguageProviderSpringConfigurationTest.kt`, + `LanguageProviderCacheEndToEndTest.kt`, `LanguageDetectionProviderTest.kt`, `TranslationCoordinatorTest.kt`. +- Update docs: 이 PRD/plan과 `provider-guide.md`. 이전 완료 이력·리뷰는 삭제하지 않는다. + +**계약:** `LanguageProviderOrder` object에 두 `val` 목록. `LanguageProviderProperties`의 주 생성자는 +두 `List`을 받고 `@Autowired`가 붙은 noarg 보조 생성자가 production object 목록으로 위임한다. +기존 `orderedProviders` 검증은 그대로 유지한다. 기본 생성자를 사용하는 호출자와 coordinator는 변경하지 않는다. + +- [x] **RED:** 실제 Spring 기본 생성에서 과거 priority property/env override를 무시해야 한다는 테스트를 추가하고 기존 binding 동작으로 실패함을 확인한다. +- [x] **GREEN:** 코드 목록·typed 생성자·Spring 기본 생성자를 적용하고 우선순위 YAML/env 연결을 삭제한다. +- [x] **GREEN 확인:** default/legacy override 무시/테스트별 독립 순서/잘못된 목록·공급자 등록 검증이 통과한다. +- [x] **REFACTOR:** 실제 H2 fallback 캐시 테스트를 property override 대신 테스트 bean으로 연결하고 기존 assertion을 유지한다. + +**명령:** +```bash +./gradlew --no-daemon --offline test --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest' +./gradlew --no-daemon --offline test --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectionCacheConcurrencyTest' --tests 'kr.co.vividnext.sodalive.v2.creator.channel.community.translation.CreatorCommunityTranslationServiceTest' +./gradlew --no-daemon --offline ktlintCheck bootJar +``` + +- [x] **Phase 2 Gate (P2-GATE): 코드 순서 실행·리뷰·정리** + +**Objective:** production bean이 코드 목록으로 동작하고 환경값으로 순서가 바뀌지 않음을 직접 실행 검증한다. +**시작 조건:** P2-T1 통과. +**대체 검증:** 부모 Spring driver에 충돌 property/env를 넣고 코드 목록 유지, 테스트 bean으로 목록 교체·validation을 확인한다. +**완료 증거:** focused 통과, build/lint, 독립 scoped 리뷰 승인, 소유 임시 경로 정리. +**범위 밖:** 전체 테스트·유료 API·운영 환경 조작. 전체 테스트는 사용자 `./gradlew test`. + +Phase 2 실행 증거는 아래에 누적한다. 코드 변경 전 현재 git status는 깨끗했다. + +### P2-T1 실행 증거 (2026-11-09) + +승인된 Phase 2 계약과 기존 Phase 1 결과를 재사용했다. baseline 85건을 변경 전에 다시 실행하지 않고 +실제 Spring의 과거 override 무시 회귀부터 작성했다. PRD의 승인 정정과 Phase 1 이력·리뷰는 수정하지 않았다. + +**실행 환경:** parent 존재를 `ls -ld`로 확인하고 아래 명령으로 이 구현자만 소유하는 child를 생성했다. +이후 모든 shell 명령에서 아래 세 환경변수를 이 child로 지정했다. Gradle tool timeout은 모두 `600000ms`다. + +```bash +mktemp -d /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/provider-order-code.RRCyJ1/phase2.XXXXXX +export TMPDIR=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/provider-order-code.RRCyJ1/phase2.QPrb3i +export NODE_COMPILE_CACHE="$TMPDIR" BUN_RUNTIME_TRANSPILER_CACHE_PATH="$TMPDIR" +``` + +**RED (2건, 2실패, errors/skipped 0, exit 1, 20초):** production 코드를 바꾸기 전에 다음 명령을 실행했다. +두 테스트 모두 context는 정상 시작했지만 기대 `[papago]`, 실제 `[secondary, papago]`로 assertion 실패했다. +property 직접 지정과 `SystemEnvironmentPropertySource`의 과거 환경변수 → property placeholder 연결을 각각 검증했다. +환경변수 테스트는 실제 property가 `secondary,papago`로 해석됐다는 assertion도 통과한 뒤 실패했다. +컴파일·unresolved symbol·context 오류를 행동 RED로 계산하지 않았다. + +```bash +./gradlew --no-daemon --offline test --tests 'kr.co.vividnext.sodalive.i18n.translation.LanguageProviderSpringConfigurationTest.shouldIgnoreLegacy*Overrides' +``` + +**GREEN:** 하나의 구현 변경으로 코드 목록·생성자·다섯 테스트의 typed-list fixture·현재 가이드를 전환했다. +`LanguageProviderOrder`에는 독립 `val` 두 개만 있으며 각각 `listOf("papago")`다. 전역 mutable 상태는 추가하지 않았다. +`LanguageProviderProperties`의 `orderedProviders`는 그대로다. production 기본/negative 테스트는 +`withUserConfiguration(LanguageProviderProperties::class.java)`로 실제 component를 Spring이 생성하게 한다. +독립 순서·잘못된 목록은 테스트 bean으로 주입하고 H2는 테스트 `@Bean`으로 두 목록을 주입한다. +기존 캐시·폴백·귀속·materializer·동시성·revision assertion은 삭제하거나 완화하지 않았다. + +**결합 focused 회귀 + lint/산출물 (exit 0, 3분 14초):** 아래 명령 한 번으로 모두 실행했다. +`:test`, `:ktlintCheck`, `:bootJar`의 실제 실행과 `BUILD SUCCESSFUL`을 확인했다. + +```bash +./gradlew --no-daemon --offline test \ + --tests 'kr.co.vividnext.sodalive.i18n.translation.*' \ + --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest' \ + --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectTest' \ + --tests 'kr.co.vividnext.sodalive.content.CreatorCommunityLanguageDetectionCacheConcurrencyTest' \ + --tests 'kr.co.vividnext.sodalive.v2.creator.channel.community.translation.CreatorCommunityTranslationServiceTest' \ + --tests 'kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityTranslationEndToEndTest' \ + ktlintCheck bootJar +``` + +| XML로 확인한 범위 | 결과 | +|---|---| +| 번역 package + 감지 cache | 54/54 | +| 커뮤니티 감지 + cache 경합 | 14/14 (12 + 2) | +| 커뮤니티 번역 service + 기존 end-to-end | 20/20 (16 + 4) | +| 합계 | 15개 XML, 88/88, failures/errors/skipped 모두 0 | + +새 override 무시 회귀 2/2가 GREEN이며 Spring 클래스 전체는 5/5다. +85건 대비 증가는 회귀 2건과 기존 default/invalid 검증을 서로 다른 테스트로 분리한 1건이다. +기존 Papago chunk 17건과 H2 secondary 귀속·재사용 2건도 통과했다. +변경하지 않은 테스트의 Kotlin 경고 7개(불필요한 `!!` 4개, unchecked cast 3개)와 Gradle 9 deprecation 경고는 남아 있다. + +**부모 QA용 실제 생성자:** 아래 `javap` 명령은 exit 0이었다. 문자열 생성자와 setter는 없다. + +```bash +javap -classpath build/classes/kotlin/main kr.co.vividnext.sodalive.i18n.translation.LanguageProviderProperties kr.co.vividnext.sodalive.i18n.translation.LanguageProviderOrder +``` + +```text +LanguageProviderProperties(java.util.List, java.util.List) +LanguageProviderProperties() // @Autowired, LanguageProviderOrder의 두 목록으로 위임 +getDetectionOrder(): List +getTranslationOrder(): List +LanguageProviderOrder.INSTANCE.getDetectionOrder(): List +LanguageProviderOrder.INSTANCE.getTranslationOrder(): List +``` + +**이 구현자의 변경 파일:** production은 `i18n/translation/LanguageProviderOrder.kt` 추가, +`LanguageProviderProperties.kt` 수정과 `application.yml` 우선순위 블록 5줄 삭제뿐이다. +테스트는 같은 패키지의 `LanguageProviderPropertiesTest.kt`, `LanguageProviderSpringConfigurationTest.kt`, +`LanguageProviderCacheEndToEndTest.kt`, `LanguageDetectionProviderTest.kt`, `TranslationCoordinatorTest.kt`만 수정했다. +문서는 `provider-guide.md`와 이 `plan-task.md`를 갱신했다. 합계 10개다. +기본 생성자 기존 호출자·인증 환경변수·기타 설정·AWS/SDK·DB/schema·domain·캐시·폴백 코드는 변경하지 않았다. + +**한계/소유권:** LSP/diagnostics 도구가 제공되지 않아 Kotlin 컴파일과 ktlint로 보완했다. +H2의 기존 claim SQL test spy 한계는 유지된다. 실제 API·운영 DB·전체 테스트는 실행하지 않았다. +전체 테스트는 사용자 명령 `./gradlew test`이며 무필터 test/check/build는 실행하지 않았다. +별도 수동 Spring driver와 ONE reviewer 및 P2-GATE 완료 판정은 부모가 맡는다. +commit/staging/push/worktree/하위 에이전트 작업은 하지 않았다. + +**소유 임시 자원 정리 영수증 (2026-11-09 18:13:46 KST):** 모든 Gradle/test 실행이 종료된 뒤 +`lsof -nP +D "$TMPDIR"` 출력이 없고 Java process 목록에 이 child를 사용하는 프로세스가 없음을 확인했다. +정확한 child 경로와 존재를 `test`로 대조한 뒤 이 구현자 소유 `phase2.QPrb3i`만 `rm -rf -- "$TMPDIR"`로 삭제했다. +`test ! -e "$TMPDIR"`와 parent `ls -ld`가 포함된 정리 명령은 exit 0이었다. +parent `provider-order-code.RRCyJ1`, 부모 QA의 형제 경로, 기존 공유 daemon/cache는 건드리지 않았다. +마지막 `git diff --check`는 exit 0이었다. P2-T1만 완료 처리하고 P2-GATE는 부모 검증을 위해 미완료로 남겼다. + +### P2-GATE 부모 검증 + +- 부모가 최종 production/test/guide diff와 신규 `LanguageProviderOrder.kt`를 직접 읽었다. + 구현자 결과 XML 15개를 대조해 88건, failures/errors/skipped 0을 확인했다. +- 별도 Java 17 `CodeOrderQa`로 실제 Spring component를 생성했다. 충돌하는 과거 Spring property와 + `SystemEnvironmentPropertySource`를 주입해도 두 기본 순서는 코드의 `papago` 목록을 유지했다. + typed-list 생성자로 감지 B/A/C, 번역 C/B/A를 독립 지정했고 빈 목록·빈 이름·중복·미등록 순서 및 + 중복 공급자 이름 거부를 확인했다. 전역 목록은 변경하지 않았다. + 출력: `QA PASS: production code defaults, ignored legacy configuration, independent lists and validation; context closed`. + driver exit 0, Spring context 종료, 외부 API 요청 없음. +- 부모 fresh 명령: + `./gradlew --no-daemon --offline test --rerun --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest' ktlintCheck bootJar` + → exit 0, `BUILD SUCCESSFUL in 36s`, `:test` 실제 실행. lint/build는 검증된 현재 산출물과 일치해 UP-TO-DATE였다. +- 이후 사용자가 enum의 적합성을 물어 검토 의견만 제공했다. enum 변경은 별도 구현 승인 전이므로 Phase 2에 포함하지 않는다. +- 부모 fresh XML 11개 합계 54건, failures/errors/skipped 0을 확인했다. +- [독립 Phase 2 리뷰](reviews/phase-2-review.md): **APPROVE**, 차단/수정 요구 사항 없음. + 이전 Phase 1 리뷰는 그대로 보존한다. 코드·테스트와 문서는 각각 원자적으로 커밋한다. +- 코드·테스트 커밋: `9136b0e3`. pre-commit ktlint와 커밋 메시지 사전/사후 검증을 통과했다. +- 부모 리소스 정리: `lsof -nP +D` 출력 없음으로 열린 파일이 없음을 확인하고, 소유 root + `provider-order-code.RRCyJ1`(driver/class/라이브러리/로그)만 제거했다. `test ! -e` exit 0. + 출력: `CLEANUP PASS: owned provider-order-code.RRCyJ1 removed; shared caches untouched`. + 필요한 증거는 이 문서에 이관했으며 임시 노트도 삭제한다. 다른 작업의 리소스는 건드리지 않았다. diff --git a/docs/20261109_감지번역공급자공통화/prd.md b/docs/20261109_감지번역공급자공통화/prd.md index b153495a..3eb02044 100644 --- a/docs/20261109_감지번역공급자공통화/prd.md +++ b/docs/20261109_감지번역공급자공통화/prd.md @@ -3,7 +3,7 @@ | 항목 | 내용 | |---|---| | 작성일 | 2026-11-09 | -| 상태 | 구현·기능 범위 검증·독립 리뷰 완료 | +| 상태 | Phase 1·2 구현 및 기능 범위 검증·독립 리뷰 완료 | | 승인 범위 | 감지·번역 별도 인터페이스, 공통 공급자 선택·실패 전환, 기능별 순서 설정 | | 계획 | [plan-task.md](plan-task.md) | @@ -35,6 +35,8 @@ | LP-008 | 빈/중복/알 수 없는 공급자 순서 설정은 초기화 단계에서 명확히 거부한다. 새 구현체 추가 시 기존 선택 코드나 공급자 enum 수정이 필요 없다. | | LP-009 | 기존 파파고 캐시·번역 데이터 및 분할 번역 동작과 감지 캐시 동시성 승자 반환을 보존한다. | | LP-010 | 추가 방법과 기능별 순서 설정 예시, 실제 AWS 미구현 상태를 문서화한다. | +| LP-011 | 공급자 순서는 `LanguageProviderOrder`의 독립 `val List` 두 개로 지정한다. 기본은 각각 `listOf("papago")`이다. | +| LP-012 | 우선순위 환경변수·YAML·문자열 파싱을 제거한다. 기존 우선순위 property/env 값이 있어도 production 순서를 바꾸지 않는다. 인증정보 환경변수는 유지한다. | ## 보안·오류 처리 @@ -58,6 +60,8 @@ LSP 도구가 제공되지 않으면 Kotlin 컴파일과 ktlint로 보완하고 ## 확정 구현 계약 +아래는 Phase 1 구현 계약이며, 순서 설정 방식은 하단의 Phase 2 정정 계약이 우선한다. 기존 실행 기록은 보존한다. + - 기존 `TranslationProvider`와 raw `TranslateResult`는 유지한다. - `LanguageDetectionProvider`: `providerName: String`, `detect(query: String): String?`. - `DetectedLanguage`: `detectedLanguage: String`, `providerName: String`. @@ -79,3 +83,16 @@ LSP 도구가 제공되지 않으면 Kotlin 컴파일과 ktlint로 보완하고 - 공통 `TranslationLanguages`로 서비스 언어 정책만 옮긴다. Papago 분할은 Papago adapter에 유지한다. - 현재 entity에는 공급자별 cache key가 있으므로 DDL 변경 없이 실제 공급자를 기록한다. 캐시 두 개가 있으면 설정상 앞선 결과를 선택하며 특정 job의 공급자를 영구 고정하는 새 모델은 만들지 않는다. + +## Phase 2 결정 정정 — 2026-11-09 + +사용자는 YAML·환경변수 대신 코드의 상수 목록으로 감지·번역 순서를 지정하도록 승인했다. + +- 유일한 production 순서 편집 지점은 `i18n/translation/LanguageProviderOrder.kt`의 + `detectionOrder = listOf("papago")`, `translationOrder = listOf("papago")`이다. Kotlin 목록은 `const val`이 아닌 `val`이다. +- 목록의 왼쪽부터 사용한다. 감지·번역은 각각 수정할 수 있으며 변경 후 빌드·배포한다. +- 기존 `LanguageProviderProperties`는 typed-list 생성자와 명시적 Spring 기본 생성자로 코드 목록을 읽는다. + 선택 코드·호출자의 이름 변경을 확산시키지 않는다. 문자열 생성자·`@Value`·split/trim은 제거한다. +- 테스트는 전역 목록을 바꾸지 않고 테스트 전용 bean에 typed list를 주입하여 독립 순서와 invalid order를 검증한다. +- 과거 우선순위 environment/property override는 무시한다. 다른 환경변수나 Spring 설정 체계는 변경하지 않는다. +- 실패 전환·공급자 검증·캐시·DB·인증·AWS 미구현 정책은 Phase 1을 그대로 유지한다. diff --git a/docs/20261109_감지번역공급자공통화/provider-guide.md b/docs/20261109_감지번역공급자공통화/provider-guide.md index 4acfe4b0..7370202d 100644 --- a/docs/20261109_감지번역공급자공통화/provider-guide.md +++ b/docs/20261109_감지번역공급자공통화/provider-guide.md @@ -27,7 +27,7 @@ AWS 감지·번역 adapter, 해당 SDK 및 전용 인증 설정은 추가하지 `providerName`은 capability 내 유일하고 재배포 후에도 안정적인 값이어야 한다. 2. 공급자의 HTTP/SDK 및 인증은 adapter 안에 둔다. 외부 응답 형식과 빈 결과를 검증하고, 원문·인증정보·응답 본문·예외 cause를 로그로 출력하지 않는다. 번역은 입력별 결과를 같은 순서로 반환한다. -3. 구현한 capability의 설정 순서에만 이름을 넣고 재시작/재배포한다. `@Primary`, selector 분기, enum 등록은 필요 없다. +3. `LanguageProviderOrder.kt`에서 구현한 capability의 목록에만 이름을 넣고 빌드·배포한다. `@Primary`, selector 분기, enum 등록은 필요 없다. 4. 아래 테스트 경계로 순서·실패 전환·실제 provider 저장·무호출 캐시 재사용·materializer 표시를 검증한다. 콘텐츠·댓글·커뮤니티 호출자, worker, materializer, scheduler에 공급자 분기를 추가하지 않는다. @@ -35,33 +35,37 @@ AWS 감지·번역 adapter, 해당 SDK 및 전용 인증 설정은 추가하지 Papago의 5,000 UTF-16 단위 분할과 서로게이트 쌍 보존은 `PapagoTranslationService`에 남아 있다. ko/en/ja 서비스 정책은 `TranslationLanguages`에 있으며 공급자를 추가해도 자동 확장하지 않는다. -## 기능별 독립 설정 +## 기능별 독립 코드 목록 -`LanguageProviderProperties`는 component scan으로 등록되며 생성자 `@Value`로 문자열을 주입받는다. -별도 `@EnableConfigurationProperties` 등록은 필요 없다. 쉼표로 나누고 각 이름의 앞뒤 공백을 제거한다. -기본값은 두 기능 모두 `papago`다. +production 순서의 유일한 편집 지점은 +`src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt`다. +목록의 왼쪽부터 사용하며 기본값은 두 기능 모두 `listOf("papago")`다. -```yaml -sodalive: - language-providers: - detection-order: ${LANGUAGE_PROVIDERS_DETECTION_ORDER:papago} - translation-order: ${LANGUAGE_PROVIDERS_TRANSLATION_ORDER:papago} +```kotlin +object LanguageProviderOrder { + val detectionOrder = listOf("papago") + val translationOrder = listOf("papago") +} ``` +Kotlin 목록은 `const val`이 아닌 `val`이다. `LanguageProviderProperties`는 기존 주입 wrapper로 유지하며, +Spring은 `@Autowired` 기본 생성자로 위 목록을 읽는다. 문자열 파싱은 하지 않는다. +테스트에서는 전역 목록을 변경하지 않고 `LanguageProviderProperties(List, List)`로 만든 테스트 bean을 주입한다. + 다음은 **secondary 구현체를 두 capability에 등록한 뒤에만** 적용할 수 있는 예시다. 현재 `secondary` 구현체는 테스트에만 있다. -```bash -export LANGUAGE_PROVIDERS_DETECTION_ORDER=secondary,papago -export LANGUAGE_PROVIDERS_TRANSLATION_ORDER=papago,secondary +```kotlin +val detectionOrder = listOf("secondary", "papago") +val translationOrder = listOf("papago", "secondary") ``` 감지만 순서를 바꾸어도 번역 순서는 바뀌지 않는다. 반대로 번역만 바꿀 수도 있다. -Spring property를 직접 지정할 때는 `sodalive.language-providers.detection-order`와 -`sodalive.language-providers.translation-order`를 각각 설정한다. +변경 후 빌드·배포해야 반영된다. 과거 우선순위 Spring property·환경변수·YAML 값이 남아 있어도 순서는 바뀌지 않는다. +공급자 인증정보는 계속 환경변수로 주입하며 이 코드 목록에 넣지 않는다. 빈 값·빈 항목·중복 이름·미등록 이름·capability 안의 중복 bean 이름표(`providerName`)는 초기화에서 거부한다. 공급자가 번역에만 등록됐다면 그 이름을 감지 순서에 넣을 수 없다. -실행 중 설정 변경은 지원하지 않는다. AWS 구현체 등록 전 `papago,aws`는 사용할 수 없다. +실행 중 설정 변경은 지원하지 않는다. AWS 구현체 등록 전 `listOf("papago", "aws")`는 사용할 수 없다. ## 캐시와 오류 계약 @@ -89,7 +93,7 @@ adapter 예외·null 감지·빈 감지·번역 결과 개수 불일치·blank | 테스트 | 검증 내용 | |---|---| -| `LanguageProviderSpringConfigurationTest` | 실제 `@Value` binding, 두 test bean, 기능별 독립 순서, 기본값과 잘못된 설정의 시작 실패 | +| `LanguageProviderSpringConfigurationTest` | 실제 production 기본 생성자, 과거 property/env 무시, typed-list 테스트 bean의 독립 순서와 잘못된 목록의 시작 실패 | | `LanguageProviderPropertiesTest` | 순서 검증 및 선택되지 않은 bean을 포함한 capability 내 중복 이름 거부 | | `LanguageDetectionProviderTest` / `TranslationCoordinatorTest` | 전체 요청 폴백, 첫 성공 중단, cache-first, 안전한 전체 실패, DB 오류 전파 | | `PapagoLanguageDetectionProviderTest` / `PapagoTranslationServiceTest` | 제어된 HTTP form/header/response와 기존 17개 chunk 회귀 | diff --git a/docs/20261109_감지번역공급자공통화/reviews/phase-2-review.md b/docs/20261109_감지번역공급자공통화/reviews/phase-2-review.md new file mode 100644 index 00000000..753ea00a --- /dev/null +++ b/docs/20261109_감지번역공급자공통화/reviews/phase-2-review.md @@ -0,0 +1,31 @@ +# 코드 기반 공급자 순서 Phase 2 리뷰 + +| 항목 | 내용 | +|---|---| +| 기준 | [PRD](../prd.md) LP-011/012, [계획](../plan-task.md) Phase 2 | +| 일자 | 2026-11-09 | +| 리뷰어 | 독립 읽기 전용 gate reviewer | +| 판정 | **APPROVE** | + +## 판정 근거 + +- `LanguageProviderOrder`의 두 독립 `val List`이 production 순서의 기준이며 기본값은 각각 papago다. +- `LanguageProviderProperties`는 typed-list 생성자와 `@Autowired` 기본 생성자만 제공한다. + 우선순위용 String overload, `@Value`, 문자열 파싱이나 mutable 전역 설정이 없다. +- YAML 삭제는 우선순위 블록 5줄뿐이다. 인증 환경변수는 유지한다. +- 실제 Spring 기본 경로에서 과거 property/env 값은 순서에 영향을 주지 않는다. +- 독립 순서·invalid 목록·중복 공급자·H2 귀속/재사용의 기존 검증을 유지한다. +- 현재 가이드가 코드 편집 지점·빌드/배포·기존 override 무시 동작과 일치한다. +- enum 변경은 사용자가 적합성만 질문한 별도 미승인 범위로, 이번 구현 누락이 아니다. + +**확정 발견 사항 없음. 수정 Task 전환 항목 없음.** + +## 검증 근거와 한계 + +변경 전 의미 있는 RED 2건, 구현 후 88/88 통과와 실제 lint/bootJar 성공 기록을 검토했다. +부모가 XML 15개를 직접 대조했고, focused 54건을 `--rerun`하여 exit 0(36초)을 확인했다. +부모의 별도 Java/Spring 실행은 충돌 property/env 무시, 감지 B/A/C·번역 C/B/A, invalid 목록/공급자 거부를 통과했다. +Spring context는 종료됐으며 외부 API 요청은 없었다. reviewer `git diff --check`는 exit 0이다. + +리뷰는 코드·테스트·설정을 변경하거나 테스트/빌드를 재실행하지 않았다. +전체 테스트·LSP·유료 API·운영 MySQL은 미검증이며 기존 H2 claim SQL 대역 한계를 그대로 명시한다.