docs(i18n): 공급자 enum과 DB code 호환 계약을 기록한다
This commit is contained in:
@@ -3,19 +3,19 @@
|
||||
## 현재 실행 구조
|
||||
|
||||
운영 구현체는 `PapagoLanguageDetectionProvider`와 `PapagoTranslationService`뿐이다.
|
||||
AWS 감지·번역 adapter, 해당 SDK 및 전용 인증 설정은 추가하지 않았다. 지금 `aws`를 순서에 넣으면 시작에 실패한다.
|
||||
AWS 감지·번역 adapter, 해당 SDK 및 전용 인증 설정은 추가하지 않았다. 지금 `LanguageProviderId.AWS`를 순서에 넣으면 시작에 실패한다.
|
||||
|
||||
감지와 번역은 별도 capability다. 감지만 또는 번역만 제공하는 공급자도 등록할 수 있다.
|
||||
두 capability에서 같은 `providerName`을 쓰는 것은 가능하지만, 한 capability 안의 이름 중복은 시작 시 거부한다.
|
||||
두 capability에서 같은 `providerId`를 쓰는 것은 가능하지만, 한 capability 안의 ID 중복은 시작 시 거부한다.
|
||||
|
||||
| 경계 | 계약 |
|
||||
|---|---|
|
||||
| `LanguageDetectionProvider` | `providerName: String`, `detect(query: String): String?` |
|
||||
| `LanguageDetectionProvider` | `providerId: LanguageProviderId`, `detect(query: String): String?` |
|
||||
| `LanguageDetectionCacheService` | `detectWithCache(query: String): DetectedLanguage?` |
|
||||
| `DetectedLanguage` | `detectedLanguage: String`, `providerName: String` |
|
||||
| `TranslationProvider` | `providerName: String`, `providerVersion: String`, `translate(TranslateRequest): TranslateResult` |
|
||||
| `DetectedLanguage` | `detectedLanguage: String`, `providerId: LanguageProviderId` |
|
||||
| `TranslationProvider` | `providerId: LanguageProviderId`, `providerVersion: String`, `translate(TranslateRequest): TranslateResult` |
|
||||
| `TranslationCoordinator` | `findCached(sourceHash, sourceLanguage, targetLanguage): TranslationMemory?`, `translate(TranslateRequest): ProviderTranslationResult` |
|
||||
| `ProviderTranslationResult` | `result: TranslateResult`, `providerName: String`, `providerVersion: String` |
|
||||
| `ProviderTranslationResult` | `result: TranslateResult`, `providerId: LanguageProviderId`, `providerVersion: String` |
|
||||
|
||||
인터페이스와 DTO는 `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/`에 있다.
|
||||
감지 coordinator는 기존 `content/LanguageDetectionCacheService.kt`이며 별도 vendor가 아니다.
|
||||
@@ -23,12 +23,14 @@ AWS 감지·번역 adapter, 해당 SDK 및 전용 인증 설정은 추가하지
|
||||
|
||||
## 새 공급자 추가 순서
|
||||
|
||||
1. 필요한 capability 인터페이스를 구현하고 Spring `@Component`/`@Service` 또는 `@Bean`으로 등록한다.
|
||||
`providerName`은 capability 내 유일하고 재배포 후에도 안정적인 값이어야 한다.
|
||||
2. 공급자의 HTTP/SDK 및 인증은 adapter 안에 둔다. 외부 응답 형식과 빈 결과를 검증하고,
|
||||
1. `LanguageProviderId`에 공급자 enum 항목과 재배포 후에도 안정적인 DB `code`를 추가한다.
|
||||
현재 항목은 `PAPAGO("papago")`, `AWS("aws")`다. AWS는 이미 ID가 있으므로 실제 adapter만 별도로 구현한다.
|
||||
2. 필요한 capability 인터페이스를 구현하고 Spring `@Component`/`@Service` 또는 `@Bean`으로 등록한다.
|
||||
예를 들어 Papago adapter의 식별자는 `override val providerId = LanguageProviderId.PAPAGO`다.
|
||||
3. 공급자의 HTTP/SDK 및 인증은 adapter 안에 둔다. 외부 응답 형식과 빈 결과를 검증하고,
|
||||
원문·인증정보·응답 본문·예외 cause를 로그로 출력하지 않는다. 번역은 입력별 결과를 같은 순서로 반환한다.
|
||||
3. `LanguageProviderOrder.kt`에서 구현한 capability의 목록에만 이름을 넣고 빌드·배포한다. `@Primary`, selector 분기, enum 등록은 필요 없다.
|
||||
4. 아래 테스트 경계로 순서·실패 전환·실제 provider 저장·무호출 캐시 재사용·materializer 표시를 검증한다.
|
||||
4. `LanguageProviderOrder.kt`에서 구현한 capability의 목록에만 enum을 넣고 빌드·배포한다. `@Primary`나 selector 분기는 필요 없다.
|
||||
5. 아래 테스트 경계로 순서·실패 전환·실제 provider 저장·무호출 캐시 재사용·materializer 표시를 검증한다.
|
||||
|
||||
콘텐츠·댓글·커뮤니티 호출자, worker, materializer, scheduler에 공급자 분기를 추가하지 않는다.
|
||||
기존 두 coordinator는 주입된 구현체 목록과 설정만 사용한다. 공급자마다 다른 분할·요청 크기 제한은 adapter 책임이다.
|
||||
@@ -39,33 +41,42 @@ ko/en/ja 서비스 정책은 `TranslationLanguages`에 있으며 공급자를
|
||||
|
||||
production 순서의 유일한 편집 지점은
|
||||
`src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt`다.
|
||||
목록의 왼쪽부터 사용하며 기본값은 두 기능 모두 `listOf("papago")`다.
|
||||
목록의 왼쪽부터 사용하며 기본값은 두 기능 모두 `listOf(LanguageProviderId.PAPAGO)`다.
|
||||
|
||||
```kotlin
|
||||
object LanguageProviderOrder {
|
||||
val detectionOrder = listOf("papago")
|
||||
val translationOrder = listOf("papago")
|
||||
val detectionOrder = listOf(LanguageProviderId.PAPAGO)
|
||||
val translationOrder = listOf(LanguageProviderId.PAPAGO)
|
||||
}
|
||||
```
|
||||
|
||||
Kotlin 목록은 `const val`이 아닌 `val`이다. `LanguageProviderProperties`는 기존 주입 wrapper로 유지하며,
|
||||
Spring은 `@Autowired` 기본 생성자로 위 목록을 읽는다. 문자열 파싱은 하지 않는다.
|
||||
테스트에서는 전역 목록을 변경하지 않고 `LanguageProviderProperties(List<String>, List<String>)`로 만든 테스트 bean을 주입한다.
|
||||
테스트에서는 전역 목록을 변경하지 않고
|
||||
`LanguageProviderProperties(List<LanguageProviderId>, List<LanguageProviderId>)`로 만든 테스트 bean을 주입한다.
|
||||
|
||||
다음은 **secondary 구현체를 두 capability에 등록한 뒤에만** 적용할 수 있는 예시다.
|
||||
현재 `secondary` 구현체는 테스트에만 있다.
|
||||
다음은 **실제 AWS 구현체를 두 capability에 등록한 뒤에만** 적용할 수 있는 예시다.
|
||||
현재 AWS ID의 가짜 구현체는 테스트에만 있다.
|
||||
|
||||
```kotlin
|
||||
val detectionOrder = listOf("secondary", "papago")
|
||||
val translationOrder = listOf("papago", "secondary")
|
||||
val detectionOrder = listOf(LanguageProviderId.AWS, LanguageProviderId.PAPAGO)
|
||||
val translationOrder = listOf(LanguageProviderId.PAPAGO, LanguageProviderId.AWS)
|
||||
```
|
||||
|
||||
감지만 순서를 바꾸어도 번역 순서는 바뀌지 않는다. 반대로 번역만 바꿀 수도 있다.
|
||||
변경 후 빌드·배포해야 반영된다. 과거 우선순위 Spring property·환경변수·YAML 값이 남아 있어도 순서는 바뀌지 않는다.
|
||||
공급자 인증정보는 계속 환경변수로 주입하며 이 코드 목록에 넣지 않는다.
|
||||
빈 값·빈 항목·중복 이름·미등록 이름·capability 안의 중복 bean 이름표(`providerName`)는 초기화에서 거부한다.
|
||||
공급자가 번역에만 등록됐다면 그 이름을 감지 순서에 넣을 수 없다.
|
||||
실행 중 설정 변경은 지원하지 않는다. AWS 구현체 등록 전 `listOf("papago", "aws")`는 사용할 수 없다.
|
||||
빈 목록·중복 ID·해당 capability의 구현체 부재·capability 안의 중복 구현체 ID는 초기화에서 거부한다.
|
||||
빈 문자열 ID는 enum 타입으로 표현할 수 없다. 공급자가 번역에만 등록됐다면 그 ID를 감지 순서에 넣을 수 없다.
|
||||
실행 중 설정 변경은 지원하지 않는다. AWS 구현체 등록 전
|
||||
`listOf(LanguageProviderId.PAPAGO, LanguageProviderId.AWS)`는 사용할 수 없다.
|
||||
|
||||
## DB code 호환성
|
||||
|
||||
내부 순서·구현체 매칭·성공 결과는 `LanguageProviderId`를 사용한다. entity/repository의 `provider`는 String 그대로다.
|
||||
coordinator/cache/worker의 DB 조회·저장 접점에서만 `providerId.code`로 변환하므로 기존 `"papago"` 캐시를 재사용한다.
|
||||
가짜 AWS의 저장값은 `"aws"`다. enum `.name`/`.ordinal`, `@Enumerated`/`@Convert`, 역변환 helper는 사용하지 않는다.
|
||||
새 enum 항목을 추가할 때도 기존 code와 DB 스키마·캐시 키는 변경하지 않는다. `providerVersion`은 별도의 String 기록 필드다.
|
||||
|
||||
## 캐시와 오류 계약
|
||||
|
||||
@@ -94,10 +105,11 @@ adapter 예외·null 감지·빈 감지·번역 결과 개수 불일치·blank
|
||||
| 테스트 | 검증 내용 |
|
||||
|---|---|
|
||||
| `LanguageProviderSpringConfigurationTest` | 실제 production 기본 생성자, 과거 property/env 무시, typed-list 테스트 bean의 독립 순서와 잘못된 목록의 시작 실패 |
|
||||
| `LanguageProviderPropertiesTest` | 순서 검증 및 선택되지 않은 bean을 포함한 capability 내 중복 이름 거부 |
|
||||
| `LanguageProviderIdTest` | 런타임 enum getter 계약, 안정적인 papago/aws code, production 기본값과 실제 adapter ID |
|
||||
| `LanguageProviderPropertiesTest` | 순서 검증 및 선택되지 않은 bean을 포함한 capability 내 중복 ID 거부 |
|
||||
| `LanguageDetectionProviderTest` / `TranslationCoordinatorTest` | 전체 요청 폴백, 첫 성공 중단, cache-first, 안전한 전체 실패, DB 오류 전파 |
|
||||
| `PapagoLanguageDetectionProviderTest` / `PapagoTranslationServiceTest` | 제어된 HTTP form/header/response와 기존 17개 chunk 회귀 |
|
||||
| `LanguageProviderCacheEndToEndTest` | 실제 H2의 secondary 감지/번역 귀속, providerVersion, 두 번째 요청 무호출, worker와 materializer의 실제 표시 |
|
||||
| `LanguageProviderCacheEndToEndTest` | 실제 H2의 AWS 감지/번역 귀속과 문자열 aws, providerVersion, 두 번째 요청 무호출, worker와 materializer의 실제 표시 |
|
||||
|
||||
H2 버전은 MySQL `FOR UPDATE SKIP LOCKED`를 지원하지 않으므로 새 H2 테스트는 claim 조회 한 곳만
|
||||
test spy로 대체한다. 나머지 repository 저장·별도 트랜잭션 재조회·materialization은 실제 H2다.
|
||||
|
||||
Reference in New Issue
Block a user