82 lines
7.2 KiB
Markdown
82 lines
7.2 KiB
Markdown
# 언어 감지·번역 공급자 공통화 PRD
|
|
|
|
| 항목 | 내용 |
|
|
|---|---|
|
|
| 작성일 | 2026-11-09 |
|
|
| 상태 | 구현·기능 범위 검증·독립 리뷰 완료 |
|
|
| 승인 범위 | 감지·번역 별도 인터페이스, 공통 공급자 선택·실패 전환, 기능별 순서 설정 |
|
|
| 계획 | [plan-task.md](plan-task.md) |
|
|
|
|
## 목적
|
|
|
|
파파고에 결합된 언어 감지·번역 호출을 분리한다. 이후 공급자는 해당 인터페이스 구현체와 빈·인증·순서 설정을
|
|
추가하면 사용할 수 있어야 하며, 콘텐츠·댓글·커뮤니티 등 도메인 코드에 공급자 분기를 추가하지 않는다.
|
|
|
|
## 범위와 제외
|
|
|
|
- 현재 실행되는 실제 구현체는 파파고뿐이다. AWS 실제 API·SDK 구현은 다음 작업이며 이번에는 추가하지 않는다.
|
|
- AWS 구현체가 등록된 뒤 기본 운영 의도는 파파고 → AWS이다. 감지와 번역 순서는 각각 독립 설정으로 바꾼다.
|
|
- 기본 실행 설정은 존재하는 파파고만 사용한다. 미구현 AWS를 등록한 것처럼 처리하거나 네트워크 호출하지 않는다.
|
|
- 기존 파파고 5,000 UTF-16 단위 분할, 지원 번역 언어 ko/en/ja, 공개 API, DB 스키마, job 재시도 정책은 유지한다.
|
|
- 감지 결과 저장 대상별 차이, 커뮤니티 원문 revision 검증, 이벤트 after-commit 처리 및 DB 경합 규칙을 유지한다.
|
|
- 실행 중 설정 변경, 관리 화면, 임의 플러그인 로딩, 새 번역 품질 정책, 기존 FAILED 작업 재처리는 제외한다.
|
|
|
|
## 요구사항
|
|
|
|
| ID | 수용 기준 |
|
|
|---|---|
|
|
| LP-001 | 감지와 번역은 독립된 공급자 인터페이스이며, 둘 중 하나만 구현한 공급자도 해당 기능에 등록 가능하다. |
|
|
| LP-002 | 감지·번역의 공급자 순서를 서로 다른 설정으로 지정한다. 테스트 공급자 2개로 한쪽만 순서를 바꿔도 다른 쪽에 영향이 없음을 증명한다. |
|
|
| LP-003 | 공급자별 예외 또는 유효 결과 없음이면 다음 공급자를 순서대로 한 번 시도한다. 첫 성공 뒤 후속 공급자를 호출하지 않는다. |
|
|
| LP-004 | 모든 공급자 실패 시 감지는 기존처럼 결과 없음으로 처리하고 번역은 기존 워커 실패·재시도 경로를 따른다. |
|
|
| LP-005 | 캐시는 실제 성공 공급자 이름으로 저장한다. AWS 역할의 가짜 공급자 성공을 papago 이름으로 저장하지 않는다. |
|
|
| LP-006 | 설정된 공급자들의 유효 캐시를 우선 재사용한다. 새 외부 호출의 순서는 별도 설정 순서를 따른다. 워커와 결과 조립의 캐시 선택 정책이 같다. |
|
|
| LP-007 | 빈 원문·동일 언어·서비스 미지원 번역 언어는 불필요한 공급자 전환을 일으키지 않는다. 유효한 미지원 언어 감지 결과는 감지 장애와 구분한다. |
|
|
| LP-008 | 빈/중복/알 수 없는 공급자 순서 설정은 초기화 단계에서 명확히 거부한다. 새 구현체 추가 시 기존 선택 코드나 공급자 enum 수정이 필요 없다. |
|
|
| LP-009 | 기존 파파고 캐시·번역 데이터 및 분할 번역 동작과 감지 캐시 동시성 승자 반환을 보존한다. |
|
|
| LP-010 | 추가 방법과 기능별 순서 설정 예시, 실제 AWS 미구현 상태를 문서화한다. |
|
|
|
|
## 보안·오류 처리
|
|
|
|
외부 HTTP 응답과 실행 설정이 신뢰 경계다. 부분 번역·빈 응답은 정상 결과로 저장하지 않는다.
|
|
원문, 인증정보, 공급자 응답 본문이 신규 로그나 워커 오류 문자열에 노출되지 않도록 한다.
|
|
공급자 식별자·순서 외에 임의 URL을 사용자 요청으로 받지 않는다. 유료 API 테스트는 하지 않는다.
|
|
|
|
## 검증 시나리오
|
|
|
|
1. 파파고 단독 기본 설정에서 기존 감지·번역·캐시·분할·커뮤니티 동작이 유지된다.
|
|
2. 두 가짜 공급자로 감지/번역 순서 독립 변경, 첫 성공 중단, 실패 전환·전체 실패를 확인한다.
|
|
3. 가짜 보조 공급자의 감지/번역 결과가 실제 provider로 DB에 저장되고 다음 요청과 materializer가 재사용한다.
|
|
4. 잘못된 설정의 초기화 실패, 원문 정책 가드, 캐시 동시성·revision 회귀를 검증한다.
|
|
|
|
## 검증 범위
|
|
|
|
변경 전 관련 focused 테스트 60건과 추가 end-to-end 4건 통과를 확인했다.
|
|
구현 후 새 계약 테스트를 포함한 85건 및 부모 런타임·HTTP 검증을 통과했다. 상세 증거는 계획 문서에 기록한다.
|
|
전체 테스트는 저장소 정책에 따라 사용자 실행(`./gradlew test`)이며, 운영 DDL·실제 AWS/Papago 계정 호출은 제외한다.
|
|
LSP 도구가 제공되지 않으면 Kotlin 컴파일과 ktlint로 보완하고 그 한계를 기록한다.
|
|
|
|
## 확정 구현 계약
|
|
|
|
- 기존 `TranslationProvider`와 raw `TranslateResult`는 유지한다.
|
|
- `LanguageDetectionProvider`: `providerName: String`, `detect(query: String): String?`.
|
|
- `DetectedLanguage`: `detectedLanguage: String`, `providerName: String`.
|
|
- `ProviderTranslationResult`: `result: TranslateResult`, `providerName: String`, `providerVersion: String`.
|
|
- 기존 `LanguageDetectionCacheService.detectWithCache(query: String): DetectedLanguage?`가 감지 선택·캐시·전환을 담당한다.
|
|
기존 callback overload는 남기지 않고 모든 테스트 fixture도 새 계약에 맞춘다.
|
|
- `TranslationCoordinator.findCached(sourceHash, sourceLanguage, targetLanguage): TranslationMemory?`를
|
|
worker와 materializer가 함께 사용한다. 캐시 선택만 통일하며 DB 저장 트랜잭션은 기존 worker에 남긴다.
|
|
- `TranslationCoordinator.translate(request: TranslateRequest): ProviderTranslationResult`가 실제 성공 공급자를 반환한다.
|
|
결과 개수 불일치·blank 결과·공급자 예외는 다음 공급자로 전환한다. 부분 결과는 섞지 않고 전체 요청을 넘긴다.
|
|
- 새 번역 coordinator의 빈 입력/blank 텍스트/동일 언어/미지원 언어 요청은 고정 메시지의 `IllegalArgumentException`으로
|
|
외부 호출 전에 거부한다. 기존 Papago raw adapter 가드는 그대로 유지한다.
|
|
- 전체 번역 공급자 실패는 원문·원시 예외를 포함하지 않는 `IllegalStateException("all translation providers failed")`이다.
|
|
감지 전체 실패는 null이다. catch는 adapter 호출에만 적용하고 DB 실패를 공급자 실패로 처리하지 않는다.
|
|
- `sodalive.language-providers.detection-order`와 `translation-order`는 각각 기본 `papago`이다.
|
|
순서는 재시작/재배포 시 반영한다. 미등록 공급자는 설정 오류로 거부하며 AWS 구현 전에는 `aws`를 활성화하지 않는다.
|
|
- 감지 캐시의 정규화/hash 규칙과 `insertIfAbsent` 다음 `findDetectedLanguageForUpdate` 승자 반환을 유지한다.
|
|
adapter에는 기존처럼 원래 query를 전달한다. 유효한 ko/en/ja 밖 감지 결과는 공급자 실패로 재해석하지 않는다.
|
|
- 공통 `TranslationLanguages`로 서비스 언어 정책만 옮긴다. Papago 분할은 Papago adapter에 유지한다.
|
|
- 현재 entity에는 공급자별 cache key가 있으므로 DDL 변경 없이 실제 공급자를 기록한다.
|
|
캐시 두 개가 있으면 설정상 앞선 결과를 선택하며 특정 job의 공급자를 영구 고정하는 새 모델은 만들지 않는다.
|