docs(i18n): 코드 기반 공급자 순서 설정과 검증을 기록한다
This commit is contained in:
@@ -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<String>`을 받고 `@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.lang.String>, java.util.List<java.lang.String>)
|
||||
LanguageProviderProperties() // @Autowired, LanguageProviderOrder의 두 목록으로 위임
|
||||
getDetectionOrder(): List<String>
|
||||
getTranslationOrder(): List<String>
|
||||
LanguageProviderOrder.INSTANCE.getDetectionOrder(): List<String>
|
||||
LanguageProviderOrder.INSTANCE.getTranslationOrder(): List<String>
|
||||
```
|
||||
|
||||
**이 구현자의 변경 파일:** 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`.
|
||||
필요한 증거는 이 문서에 이관했으며 임시 노트도 삭제한다. 다른 작업의 리소스는 건드리지 않았다.
|
||||
|
||||
@@ -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<String>` 두 개로 지정한다. 기본은 각각 `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을 그대로 유지한다.
|
||||
|
||||
@@ -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<String>, List<String>)`로 만든 테스트 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 회귀 |
|
||||
|
||||
@@ -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<String>`이 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 대역 한계를 그대로 명시한다.
|
||||
Reference in New Issue
Block a user