|
|
@@ -0,0 +1,429 @@
|
|
|
|
|
|
|
|
# AWS 언어 감지·번역 Provider 구현 계획
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
> **실행 방식:** 사용자 승인 Subagent-driven 실행 완료. 기능 범위 검증과 독립 최종 리뷰를 통과했다.
|
|
|
|
|
|
|
|
> **후속 회귀:** 2026-11-10 사용자 보고로 추가한 P4-R1 및 P4-GATE까지 완료했다. 기존 P1~P3 완료·검증 기록은 보존한다.
|
|
|
|
|
|
|
|
> **실행 skill:** `subagent-driven-development`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Goal:** 기존 언어 감지·번역 계약과 캐시를 유지하면서 AWS Comprehend 및 Translate를 Papago 다음 순서로 사용할 수 있게 한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Architecture:** 공급자에 종속되지 않는 UTF-8/UTF-16 code-point splitter를 공통 패키지에 추가하고 Papago 분할을 해당 helper로 옮긴다. AWS SDK for Java v2의 동기 client는 기존 명시적 AWS 자격 증명과 리전을 쓰는 lifecycle Bean으로 만들며, 두 adapter를 기존 coordinator 순서에 등록한다. 끝으로 실제 adapter를 SDK mock과 H2 저장 흐름에 연결해 캐시·worker·표시 경계를 검증한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Tech Stack:** Kotlin 1.6.21, Java 17, Spring Boot 2.7.14, Gradle, JUnit 5, AWS SDK for Java v2 BOM 2.57.11, Comprehend 및 Translate 모듈.
|
|
|
|
|
|
|
|
**Spec:** [prd.md](prd.md), 승인 결정 DEC-006. 제공 API 근거는 PRD의 공식 Comprehend 및 Translate 문서 링크를 따른다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Global Constraints
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- SDK v2 BOM은 `2.57.11`로 고정하고 `software.amazon.awssdk:comprehend`, `software.amazon.awssdk:translate`만 새 SDK 모듈로 추가한다. 기존 AWS SDK v1 의존성은 유지한다.
|
|
|
|
|
|
|
|
- 기존 `cloud.aws.credentials.access-key`, `cloud.aws.credentials.secret-key`, `cloud.aws.region.static` 설정을 사용한다. AWS 자격 증명 기본 체인, 환경변수 인증 설계, IAM 설정은 변경하지 않는다.
|
|
|
|
|
|
|
|
- AWS 동기 clients는 Spring `@Bean`과 `destroyMethod = "close"`로 관리한다. 기본 동기 HTTP runtime은 SDK의 전이 의존성으로 둔다.
|
|
|
|
|
|
|
|
- 모든 production 변경은 `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/`에, 대응 테스트는 `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/` 및 실제 영향받는 기존 테스트 디렉터리에 둔다.
|
|
|
|
|
|
|
|
- splitter는 Unicode code point 경계에서 자른다. Papago 크기는 UTF-16 code unit `5000`, AWS Translate 크기는 UTF-8 byte `10000`, 감지 prefix는 UTF-8 byte 최대 `100000`이다.
|
|
|
|
|
|
|
|
- 빈 raw Papago 문자열과 whitespace 보존을 포함한 기존 동작을 유지한다. 감지 prefix는 전체 원문 hash/cache key와 번역 입력을 바꾸지 않는다.
|
|
|
|
|
|
|
|
- 감지 순서 및 번역 순서는 각각 `PAPAGO, AWS`로 둔다. 지원 번역 언어는 기존 ko/en/ja만 허용한다.
|
|
|
|
|
|
|
|
- 공개 API, DB schema, provider interface, retry 동작, AWS IAM, 실계정 자격 증명, 유료 API 호출은 범위 밖이다.
|
|
|
|
|
|
|
|
- 구현 전에 각 구현 Task의 RED 테스트가 의도된 assertion 실패임을 확인한다. test/compiler setup 오류는 RED 증거가 아니다.
|
|
|
|
|
|
|
|
- 직접 영향받는 테스트만 `--tests`로 지정한다. 무필터 `test`, `check`, `build`, `clean build`는 자동 실행하지 않는다. 필요한 최종 산출물 확인은 `./gradlew bootJar`로 한다.
|
|
|
|
|
|
|
|
- 이후 `build.gradle.kts` 변경 시 `docs/agent-guides/실행명령어.md`의 AWS dependency/build 예시를, 신규 테스트 추가 시 해당 문서의 단일 테스트 예시를 갱신한다.
|
|
|
|
|
|
|
|
- 어떤 응답 원문, 인증정보, 민감 오류 메시지도 로그, fixture 또는 worker 오류 문자열에 노출하지 않는다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Review Focus
|
|
|
|
|
|
|
|
1. 경계 바로 앞의 한글 및 supplementary-plane 문자는 잘리지 않고 한 조각 안에 남으며, byte/UTF-16 한도는 초과하지 않는다. `CodePointTextSplitterTest`에서 각 공급자 한도 경계, 재구성 및 100000 byte prefix와 제외 tail을 고정한다.
|
|
|
|
|
|
|
|
2. blank 입력, 짧은 감지 입력, blank/누락 후보와 AWS 오류가 감지 cache에 잘못된 값으로 저장되지 않는다. `AwsLanguageDetectionProviderTest`와 `LanguageProviderCacheEndToEndTest`에서 호출 수와 결과를 확인한다.
|
|
|
|
|
|
|
|
3. AWS 번역 일부 조각의 오류 또는 공백 응답이 앞선 조각·원문의 성공값과 섞이거나 후속 호출을 유발하지 않는다. `AwsTranslationServiceTest` 및 필요 시 `TranslationCoordinatorTest`에서 전체 요청 폐기와 원문 단위 fallback을 확인한다.
|
|
|
|
|
|
|
|
4. AWS 기본 순서 전환 후 Papago 전용 fixture가 실제 AWS adapter를 가짜 AWS provider와 함께 로드하거나 PAPAGO 전용 검증을 바꾸지 않는다. `LanguageProviderSpringConfigurationTest` 및 아래 명시한 영향 테스트를 확인한다.
|
|
|
|
|
|
|
|
5. 서로 prefix가 같은 긴 본문도 원문 전체 hash/cache key로 구분되고, 성공 AWS 결과는 `aws` 및 `translate-2017-07-01`로 저장되며 재호출되지 않는다. `LanguageProviderCacheEndToEndTest`에서 H2 row, SDK 호출 수, 화면 결과를 확인한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 현재 상태
|
|
|
|
|
|
|
|
| Phase | 상태 | Task | 다음 Goal | 차단 조건 |
|
|
|
|
|
|
|
|
|---:|---|---:|---|---|
|
|
|
|
|
|
|
|
| 1 | 완료 | 1/1 | 없음 | 없음 |
|
|
|
|
|
|
|
|
| 2 | 완료 | 2/2 | 없음 | 없음 |
|
|
|
|
|
|
|
|
| 3 | 완료 | 1/1 | 없음 | 없음 |
|
|
|
|
|
|
|
|
| 4 | 완료 | 1/1 | 없음 | 없음 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
P1~P4의 모든 Task/Gate가 완료됐고 활성 Goal은 없다. 실제 IAM·유료 API·운영 DB 및 사용자 전체 테스트 검증은 아래 미검증 범위로 구분한다.
|
|
|
|
|
|
|
|
## 범위와 파일 책임
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 포함
|
|
|
|
|
|
|
|
- 공통 Unicode code point splitter 및 기존 Papago 분할의 helper 이전.
|
|
|
|
|
|
|
|
- AWS Comprehend 감지 adapter, AWS Translate adapter, AWS SDK v2 client 설정 Bean.
|
|
|
|
|
|
|
|
- 감지/번역 기본 순서를 PAPAGO 다음 AWS로 등록하고 PAPAGO 전용 테스트 fixture를 명시 설정으로 고정.
|
|
|
|
|
|
|
|
- 실제 adapter, cache, worker, H2 persistence, materializer 표시 및 현재 provider guide의 구현 상태 갱신.
|
|
|
|
|
|
|
|
### 제외
|
|
|
|
|
|
|
|
- 공개 interface나 API/DB schema 변경, 기존 AWS SDK v1 마이그레이션, AWS 실계정 호출/IAM 변경.
|
|
|
|
|
|
|
|
- AWS를 이유로 한 지원 언어 확대, confidence threshold, 다중 조각 감지 집계, retry·logging 확장.
|
|
|
|
|
|
|
|
- 일반적인 provider-guide 재작성 또는 역사적 PRD/계획/결정 기록 수정.
|
|
|
|
|
|
|
|
### 파일 맵
|
|
|
|
|
|
|
|
| 책임 | 파일 |
|
|
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
|
|
| 공유 code point 분할과 UTF-8 길이 계산 | `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/CodePointTextSplitter.kt` |
|
|
|
|
|
|
|
|
| Papago splitter 사용 | `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/PapagoTranslationService.kt` |
|
|
|
|
|
|
|
|
| AWS detection/translation adapter | `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsLanguageDetectionProvider.kt`, `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsTranslationService.kt` |
|
|
|
|
|
|
|
|
| client 생성 및 종료 | `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsLanguageProviderConfiguration.kt` |
|
|
|
|
|
|
|
|
| adapter 순서와 직접 영향 테스트 | `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt`, 아래 Task별 명시 파일 |
|
|
|
|
|
|
|
|
| 의존성과 실행 예시 | `build.gradle.kts`, `docs/agent-guides/실행명령어.md` |
|
|
|
|
|
|
|
|
| AWS 검증 및 가이드 | `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderCacheEndToEndTest.kt`, `docs/20261109_감지번역공급자공통화/provider-guide.md` |
|
|
|
|
|
|
|
|
## 요구사항 추적
|
|
|
|
|
|
|
|
| PRD 요구사항 | 구현 Goal | 검증 핵심 |
|
|
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
| AWS-001 | `P2-T1` | client configuration의 synthetic credentials, `Region`, 종료 |
|
|
|
|
|
|
|
|
| AWS-002, AWS-003, AWS-004 | `P2-T1` | blank/짧은 입력, 100000 byte prefix, 첫 후보만 검사하고 blank면 실패 null |
|
|
|
|
|
|
|
|
| AWS-005, AWS-007 | `P2-T1` | 명시 source/target, 순차 chunk, 전체 결과/중단, 안정 version |
|
|
|
|
|
|
|
|
| AWS-006 | `P1-T1` | Papago 5000 UTF-16 회귀와 AWS UTF-8/code point 경계 |
|
|
|
|
|
|
|
|
| 순서·실제 provider cache 귀속·전체 fallback | `P2-T2`, `P3-T1` | PAPAGO → AWS, H2 저장 및 재사용, 전체 원문 fallback |
|
|
|
|
|
|
|
|
| 보안·기존 API 및 DB 계약 | 모든 Goal, 특히 `P3-T1` | cause/error marker 비노출, 기존 hash 및 저장 계약 |
|
|
|
|
|
|
|
|
## TDD 및 실행 규칙
|
|
|
|
|
|
|
|
- 구현 Task는 각각 정확히 다섯 체크박스인 `RED`, `RED 확인`, `GREEN`, `GREEN 확인`, `REFACTOR`를 가진다.
|
|
|
|
|
|
|
|
- RED 테스트를 작성한 뒤 필요한 production type의 컴파일용 빈 shell만 추가할 수 있다. shell은 요구 동작을 구현하지 않으며, intended assertion failure를 확인한 뒤에만 행동 코드를 쓴다.
|
|
|
|
|
|
|
|
- RED 확인은 해당 focused test가 테스트 누락/compile failure가 아닌 요구 동작 부재 assertion으로 실패했음을 기록한다.
|
|
|
|
|
|
|
|
- Gate는 통합 검증용 대체 절차를 따르고 TDD exception을 명시한다. P3-T1은 test-only wiring 작업이므로 첫 실행이 통과할 수 있다. 실패를 만들기 위해 production이나 응답을 고의 변경하거나 assertion을 약화하지 않는다.
|
|
|
|
|
|
|
|
- 모든 향후 명령은 저장소 루트에서 실행한다. 신규 명령은 아래 Task의 focused 명령 및 Gate 범위에 한정한다.
|
|
|
|
|
|
|
|
### Phase 1: 공통 code point 분할
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Phase 결과:** Papago의 기존 동작을 보존하며 공급자별 크기 측정 함수를 받는 내부 splitter 계약을 갖는다.
|
|
|
|
|
|
|
|
### Task 1.1: 공통 splitter와 Papago 이전
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Task 1.1: 공통 splitter와 Papago 이전**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Goal `P1-T1`:** Papago의 기존 분할 경계를 유지하면서 code point 단위 공통 분할 및 prefix helper를 제공한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **시작 조건:** 사용자 승인된 PRD 및 DEC-004/DEC-005 확인, 계획 검토와 실행 방식 선택 완료. 변경 전 `./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationServiceTest'`로 기존 동작의 통과를 기록한다. 기존 실패가 있으면 변경 영향과 분리해 먼저 보고한다.
|
|
|
|
|
|
|
|
- **완료 증거:** 3개 helper의 단위 테스트, Papago 기존 assertion 보존, focused 테스트 및 lint 성공.
|
|
|
|
|
|
|
|
- **범위 밖:** AWS adapter 구현, Papago HTTP 흐름·응답 계약 변경.
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
|
|
|
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/CodePointTextSplitter.kt`
|
|
|
|
|
|
|
|
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/PapagoTranslationService.kt`
|
|
|
|
|
|
|
|
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/CodePointTextSplitterTest.kt`
|
|
|
|
|
|
|
|
- Preserve and verify: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/PapagoTranslationServiceTest.kt`
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
|
|
|
- **Consumes:** 기존 Papago의 `MAX_REQUEST_TEXT_LENGTH = 5000` 및 `String.length` 기준.
|
|
|
|
|
|
|
|
- **Produces:** `internal fun splitTextByCodePoint(text: String, maxSize: Int, codePointSize: (Int) -> Int): List<String>`; `internal fun takeTextPrefixByCodePoint(text: String, maxSize: Int, codePointSize: (Int) -> Int): String`; `internal fun utf8CodePointSize(codePoint: Int): Int`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **RED:** `CodePointTextSplitterTest`에 정확 한도/초과, Korean, supplementary character, whitespace 포함 재구성, empty input (`listOf("")`), 100000 UTF-8 byte prefix와 제외 tail 테스트를 작성한다. Papago 5001/10001 및 경계 surrogate pair 기대도 기존 `PapagoTranslationServiceTest`에 유지한다.
|
|
|
|
|
|
|
|
- [x] **RED 확인:** `./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest'`를 실행한다. helper 미구현에 따른 compile failure가 아니라 splitter 요구 결과 assertion 실패를 기록한다. 테스트 실행 명령 오류는 RED가 아니다.
|
|
|
|
|
|
|
|
- [x] **GREEN:** 위 세 internal 함수를 `CodePointTextSplitter.kt`에 구현한다. UTF-16 계수 callback에는 `Character.charCount(codePoint)`, UTF-8 계수에는 `utf8CodePointSize`를 쓴다. Prefix는 첫 허용 경계에서 멈춰 전체 입력을 split하지 않는다. 공통 boundary scanning은 중복이 실제 생길 때만 private helper로 공유한다.
|
|
|
|
|
|
|
|
- [x] **GREEN 확인:** `./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest'`를 실행해 경계·재구성 assertion이 통과함을 확인한다.
|
|
|
|
|
|
|
|
- [x] **REFACTOR:** `PapagoTranslationService.splitText`를 helper 호출로 바꾸고 5000 UTF-16 callback을 지정한다. `./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationServiceTest'`와 `./gradlew ktlintCheck`로 Papago 전체 HTTP 회귀를 확인한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**P1-T1 검증 기록 (2026-11-09):**
|
|
|
|
|
|
|
|
- 변경 전 `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationServiceTest'`: exit 0, 기존 17개 통과. 기존 assertion은 수정하지 않았고 빈 raw 문자열/공백 HTTP 회귀 2개만 추가했다.
|
|
|
|
|
|
|
|
- RED `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest'`: 최초 실행은 테스트 컴파일 중 120초 timeout으로 RED에서 제외했다. 300초 제한 재시도로 compile 후 33개 중 32개 assertion 실패를 관찰했고, 같은 명령 재실행에서 `COMMAND_EXIT_CODE=1`을 확인했다. UTF-8 크기 assertion 예: `expected: <1> but was: <0>`. 빈 prefix 1개만 기존 shell에서도 통과했다.
|
|
|
|
|
|
|
|
- GREEN 같은 splitter focused 명령: exit 0, 33개 통과. 공통 경계 scanner를 split과 prefix가 공유하며 prefix는 전체 tail을 순회하지 않는다. JVM UTF-8 인코딩과 일치하도록 짝 없는 surrogate의 1바이트 대체 인코딩도 검증했다.
|
|
|
|
|
|
|
|
- Papago 이전 후 `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationServiceTest'`: 최초 실행은 테스트 컴파일 중 120초 timeout, 300초 제한 재시도는 exit 0. splitter 33개와 Papago 19개, 합계 52개 통과(실패/오류/skip 모두 0).
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon ktlintCheck --continue`: exit 0. main/test/Kotlin script lint 모두 성공. LSP 도구 대신 Kotlin compile 및 lint를 사용했다. 기존 Gradle deprecation, 중복 `org.json.JSONObject` 및 변경하지 않은 테스트의 compiler warning은 수정하지 않았다.
|
|
|
|
|
|
|
|
- 신규 단일 테스트 예시만 `docs/agent-guides/실행명령어.md`에 추가했다. 상세 보고서: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/aws-implementation.7dG8YS/p1.W0mxDa/report.md`. 전체 테스트·빌드·외부 API는 실행하지 않았다. 사용자 전체 테스트 명령은 `./gradlew test`다. `P1-GATE`는 부모 reviewer 판정 전이며 완료 처리하지 않는다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 1 Gate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Phase 1 Gate: Papago 호환성·분할 경계 검증**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-11-09 Gate: 독립 reviewer 명세·품질 모두 Approved. 부모가 두 focused class 명령을 재실행해 exit 0(UP-TO-DATE)을 확인했다. 직전 실제 실행은 helper 33개 + Papago 19개, 총 52개 통과이며 ktlintCheck도 exit 0이다. LSP 도구 부재는 Kotlin 컴파일·lint로 보완했고 전체 테스트는 미실행이다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Goal `P1-GATE`:** 공통 helper가 Papago 호환성과 Unicode/byte 경계를 만족하는지 판정한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **시작 조건:** `P1-T1`의 다섯 체크와 결과 기록 완료.
|
|
|
|
|
|
|
|
- **완료 증거:** 두 focused test class와 ktlint 성공, assertion 및 실제 명령 결과가 기록됨.
|
|
|
|
|
|
|
|
- **범위 밖:** AWS SDK dependency 또는 adapter 추가.
|
|
|
|
|
|
|
|
- **TDD 예외 사유:** 구현을 추가하지 않는 단계별 통합 검증이다.
|
|
|
|
|
|
|
|
- **대체 검증 방법:** 아래 명령 결과와 splitter 경계 assertion을 대조하고, 기존 Papago 5000/5001/10001 및 surrogate 요청/결합 검증이 유지되는지 확인한다.
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
|
|
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationServiceTest'
|
|
|
|
|
|
|
|
./gradlew ktlintCheck
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Expected:** 두 테스트 범위와 lint가 exit code 0. 실패가 있으면 해당 Task의 미완료 상태를 보존한다.
|
|
|
|
|
|
|
|
### Phase 2: AWS adapters와 순서 등록
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Phase 결과:** Comprehend 및 Translate가 기존 provider contract를 구현하고 production 기본 순서 PAPAGO 다음에 AWS가 온다.
|
|
|
|
|
|
|
|
### Task 2.1: AWS SDK v2 clients와 adapters
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Task 2.1: AWS SDK v2 clients와 adapters**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Goal `P2-T1`:** 명시된 AWS 설정으로 client lifecycle을 관리하고 기존 provider contract에 맞는 감지·번역 adapter를 추가한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **시작 조건:** `P1-GATE` 통과 및 `CodePointTextSplitter` 계약 확정.
|
|
|
|
|
|
|
|
- **완료 증거:** SDK dependency, 세 production 클래스, 세 focused test class, 실행 명령 문서 갱신과 해당 테스트 성공.
|
|
|
|
|
|
|
|
- **범위 밖:** production provider order 변경, 실 AWS 연결, IAM 권한 검증.
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
|
|
|
- Modify: `build.gradle.kts`
|
|
|
|
|
|
|
|
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsLanguageDetectionProvider.kt`
|
|
|
|
|
|
|
|
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsTranslationService.kt`
|
|
|
|
|
|
|
|
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsLanguageProviderConfiguration.kt`
|
|
|
|
|
|
|
|
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsLanguageDetectionProviderTest.kt`
|
|
|
|
|
|
|
|
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsTranslationServiceTest.kt`
|
|
|
|
|
|
|
|
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsLanguageProviderConfigurationTest.kt`
|
|
|
|
|
|
|
|
- Modify: `docs/agent-guides/실행명령어.md`
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
|
|
|
- **Consumes:** `LanguageDetectionProvider.detect(query: String): String?`; `TranslationProvider.translate(request: TranslateRequest): TranslateResult`; P1 splitter three functions; existing cloud AWS `@Value` keys.
|
|
|
|
|
|
|
|
- **Produces:** `AwsLanguageDetectionProvider(ComprehendClient) : LanguageDetectionProvider`; `AwsTranslationService(TranslateClient) : TranslationProvider`; `@Configuration` bean methods `comprehendClient(): ComprehendClient`, `translateClient(): TranslateClient`, each with `@Bean(destroyMethod = "close")`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**테스트 실행 준비:** RED 테스트를 먼저 작성하고 SDK 타입을 컴파일하는 데 필요한 BOM `2.57.11`, `comprehend`, `translate` 의존성과 비동작 shell만 준비한다. dependency resolution 및 compile 성공 자체는 RED 증거가 아니다. 생산 동작은 아래 RED 확인 뒤 구현한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **RED:** 신규 세 테스트 클래스에 성공·실패 assertion을 작성한다. 감지는 짧은 입력 그대로 전달, blank 무호출, 100000바이트 prefix, 첫 후보의 낮은 score/미지원 코드 허용을 검증한다. 첫 후보가 blank이고 두 번째가 유효해도 결과는 null이며 빈 후보 목록·SDK 예외도 null이다. 번역은 10000바이트 정확 경계와 초과, 한글·보조 평면 문자·공백, source/target, 순서·원문별 결과 하나, 중간 조각 또는 다음 원문의 예외/blank/null 응답 시 전체 결과 폐기와 이후 무호출을 검증한다. 빈 목록·동일 언어·미지원 언어 가드도 검증한다. Configuration은 synthetic credentials/region과 실제 client 생성·종료를 좁은 Spring context에서 검증하고, adapter는 SDK interface만 mock한다.
|
|
|
|
|
|
|
|
- [x] **RED 확인:** 세 신규 test class를 `./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageDetectionProviderTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsTranslationServiceTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageProviderConfigurationTest'`로 실행한다. 각 intended assertion 실패를 확인하며 test compile 오류는 거부한다.
|
|
|
|
|
|
|
|
- [x] **GREEN:** static credentials provider에 기존 access/secret 키를 연결하고 `Region.of`와 종료 관리 client Bean을 구현한다. 감지는 blank 무호출·짧은 입력·100000바이트 prefix를 적용하고 첫 후보만 검사한다. 첫 후보의 blank/missing 또는 SDK 예외는 null이며 후속 후보를 대신 선택하지 않는다. PRD의 prefix 한계 `shortcut:` 주석을 남긴다. 번역은 source/target을 명시하고 10000 UTF-8 바이트 이하 조각을 순서대로 번역해 원문별로 결합한다. 예외·null/blank 번역이면 전체 요청 결과를 폐기하고 이후 호출을 중단한다. 기존 언어 가드와 `translate-2017-07-01` 버전을 적용한다.
|
|
|
|
|
|
|
|
- [x] **GREEN 확인:** 동일한 세 `--tests` 대상으로 실행해 client 설정/종료, 감지 boundary 및 응답 매핑, 번역 요청 assertions가 통과함을 확인한다.
|
|
|
|
|
|
|
|
- [x] **REFACTOR:** 통과한 동작을 바꾸지 않고 이번 Task가 만든 중복만 정리한다. `docs/agent-guides/실행명령어.md`에 신규 focused 테스트 예시를 추가하고 Papago 및 AWS 공통 focused suite와 `./gradlew ktlintCheck` 결과를 기록한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**P2-T1 검증 기록 (2026-11-09):**
|
|
|
|
|
|
|
|
- 아래 명령에 모두 `--no-daemon`을 추가하고 600000ms timeout 및 전용 TMPDIR/cache 경로를 사용했다. 테스트 세 클래스를 먼저 작성한 뒤 dependency와 비동작 shell만 추가했다. 최초 세 클래스 RED는 main/test compile 성공 후 48개 중 40개 실패(감지 19/16 실패, 번역 27/22 실패, 설정 2/2 실패), exit 1이었다. 실제 assertion은 감지 `expected: <ko> but was: <null>`, 번역 `expected: <[translated]> but was: <[]>`, 설정 Bean 수 `expected: <1> but was: <0>`이며 compiler/setup 오류가 아니다. 빈 입력/언어 가드 8개는 shell에서도 통과했다. 숫자 exit marker만 얻기 위한 동일 RED 재실행은 하지 않았다.
|
|
|
|
|
|
|
|
- 첫 구현 후 세 클래스 실행은 exit 1(48개 중 설정 2개 실패). 실제 client 생성에서 `BeanCreationException → BeanInstantiationException → NoClassDefFoundError: org/apache/hc/client5/http/ssl/TlsSocketStrategy`를 발견했다. `dependencyInsight --dependency httpclient5` 및 `--dependency httpcore5`를 `--configuration runtimeClasspath`로 실행(각 exit 0)해 Boot의 `5.1.4`/`5.1.5` 하향 관리를 확인했다. 공식 SDK POM의 `5.6.4`/`5.4.3`으로 build extra 두 값만 정렬했으며 transport 추가/제외와 v1 변경은 없다. 정렬 후 `dependencyInsight --dependency org.apache.httpcomponents --configuration runtimeClasspath`는 exit 0이며 HTTP4는 기존 `4.5.14`/`4.4.16`, SDK v1은 `1.12.380`이다.
|
|
|
|
|
|
|
|
- HTTP5 정렬 후 실행은 실제 생성/인증/region을 통과했으나 test proxy의 종료 관찰 assertion 2개가 실패해 exit 1이었다. Spring의 원래 Bean destruction 경로에 맞춰 proxy 대신 실제 두 Apache5 pool의 종료 전 false/종료 후 true를 관찰하도록 테스트만 수정했다. 동일 세 클래스 최종 GREEN은 exit 0, 48개 통과(감지 19, 번역 27, 설정 2; failure/error/skip 0). 실제 client는 synthetic credentials와 서울/대체 region으로 생성하며 네트워크 요청 없이 종료를 검증한다.
|
|
|
|
|
|
|
|
- 위 다섯 클래스 공통 focused 명령은 exit 0, 총 100개 통과(splitter 33, Papago 19, 감지 19, 번역 27, 설정 2; failure/error/skip 0). `./gradlew --no-daemon ktlintCheck --continue`는 신규 번역 클래스 한 줄 길이로 exit 1, 신규 설정 테스트 개행으로 exit 1, 두 줄만 정리한 최종 실행은 exit 0이다. 실행 문서는 새 세 단일 테스트 및 AWS dependency/build 예시만 추가했다.
|
|
|
|
|
|
|
|
- 원문·합성 민감 오류 marker의 신규 로그 비노출 assertion을 확인했다. LSP 도구가 없어 Kotlin compiler와 lint를 사용했다. 전체 테스트, build/check/clean, bootJar, 실 AWS/Papago 호출, 실제 자격 증명/IAM 및 SDK wire/signing은 미검증이다. 사용자 전체 테스트 명령은 `./gradlew test`다. 상세 명령·실패 이력·변경 파일 보고서는 `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/aws-implementation.7dG8YS/p2-adapters.wy5Eu6/report.md`에 저장했다. Provider order와 다른 Task/Gate는 변경하지 않았다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**검증 명령:**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
|
|
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageDetectionProviderTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsTranslationServiceTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageProviderConfigurationTest'
|
|
|
|
|
|
|
|
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationServiceTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageDetectionProviderTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsTranslationServiceTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageProviderConfigurationTest'
|
|
|
|
|
|
|
|
./gradlew ktlintCheck
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Maven Central BOM POM 확인 URL: `https://repo.maven.apache.org/maven2/software/amazon/awssdk/bom/2.57.11/bom-2.57.11.pom`. SDK 2.57.11 BOM/service POM과 Java 17 호환성은 부모 조사에서 확인된 근거다. 실제 Gradle dependency resolution 및 프로젝트 통합 호환성은 이 Task 실행 시 검증하며, 현재 이미 통과했다고 기록하지 않는다. 전이 기본 HTTP runtime 외의 transport 의존성/제외는 추가하지 않는다.
|
|
|
|
|
|
|
|
### Task 2.2: AWS 기본 순서와 Papago 전용 fixture
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Task 2.2: AWS 기본 순서와 Papago 전용 fixture**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Goal `P2-T2`:** 실제 두 AWS adapter를 등록하고 Papago 전용 테스트는 의도한 한 provider만 주입하도록 고정한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **시작 조건:** `P2-T1`의 adapter 테스트와 configuration 성공.
|
|
|
|
|
|
|
|
- **완료 증거:** 두 default list의 순서, 영향 테스트의 PAPAGO 전용 설정, 실제 AWS와 fake AWS bean의 중복 없는 Spring wiring을 검증.
|
|
|
|
|
|
|
|
- **범위 밖:** 공개 provider ID/code, properties 검증 규칙 및 기존 PAPAGO 우선순위 변경.
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
|
|
|
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt`
|
|
|
|
|
|
|
|
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderIdTest.kt`
|
|
|
|
|
|
|
|
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderSpringConfigurationTest.kt`
|
|
|
|
|
|
|
|
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/TranslationJobWorkerTest.kt`
|
|
|
|
|
|
|
|
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectionCacheServiceTest.kt`
|
|
|
|
|
|
|
|
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectTest.kt`
|
|
|
|
|
|
|
|
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectionCacheConcurrencyTest.kt`
|
|
|
|
|
|
|
|
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/community/translation/CreatorCommunityTranslationServiceTest.kt`
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
|
|
|
- **Consumes:** `AwsLanguageDetectionProvider` 및 `AwsTranslationService` 실제 Spring components, `LanguageProviderProperties(List<LanguageProviderId>, List<LanguageProviderId>)`.
|
|
|
|
|
|
|
|
- **Produces:** production `detectionOrder`와 `translationOrder` 각각 `listOf(PAPAGO, AWS)`; fake-only test runner와 actual-adapter runner가 구분된 Spring wiring.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **RED:** `LanguageProviderIdTest`의 기본 순서 assertion을 `[PAPAGO, AWS]`로 기대하고 production adapter identity 및 안정 code assertion은 보존한다. Spring 기본 순서/legacy override 테스트에 두 provider와 PAPAGO 첫 성공, AWS 미호출 assertion을 추가한다.
|
|
|
|
|
|
|
|
- [x] **RED 확인:** `./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.LanguageProviderIdTest' --tests 'kr.co.vividnext.sodalive.i18n.translation.LanguageProviderSpringConfigurationTest'` 실행 결과가 production default가 아직 `[PAPAGO]`인 데서 비롯된 intended assertion failure인지 확인한다.
|
|
|
|
|
|
|
|
- [x] **GREEN:** `LanguageProviderOrder.kt`의 두 목록에 AWS를 Papago 다음에 추가한다. Spring 테스트의 legacy property/env default 기대를 새 순서로 갱신하며 PAPAGO 우선 성공과 AWS 미호출을 검증한다. actual adapter를 검증할 runner는 SDK mock과 함께 별도로 구성하고 fake `AwsProviders`를 같은 capability에 로드하지 않는다.
|
|
|
|
|
|
|
|
- [x] **GREEN 확인:** 위 두 class를 다시 실행한다. startup uniqueness assertion을 유지하고 `shouldRejectInvalidOrdersAtStartup`의 `papagoRunner`가 AWS 없는 `[AWS]` 설정에서 실패하는 검증을 그대로 보존한다.
|
|
|
|
|
|
|
|
- [x] **REFACTOR:** 아래 영향 테스트 helper만 `LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO))`로 명시해 기존 단일 Papago 기대를 유지한다. `LanguageProviderPropertiesTest`의 invalid inventory 검증은 변경하지 않는다. 전체 지정 suite와 ktlint 결과를 기록한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PAPAGO-only fixture 대상은 `TranslationJobWorkerTest`의 여섯 coordinator 생성 위치, `LanguageDetectionCacheServiceTest.service`, `CreatorCommunityLanguageDetectTest`의 production 기본 properties import 및 `ControlledLanguageDetectionCacheService` super 생성자, `CreatorCommunityLanguageDetectionCacheConcurrencyTest`의 imported default properties, `CreatorCommunityTranslationServiceTest.materializer` helper다.
|
|
|
|
|
|
|
|
**검증 명령:**
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
|
|
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest'
|
|
|
|
|
|
|
|
./gradlew 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 ktlintCheck
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**P2-T2 검증 기록 (2026-11-10):**
|
|
|
|
|
|
|
|
- 모든 Gradle 명령은 저장소 루트에서 `--no-daemon`, timeout 600000ms 및 전용 TMPDIR/runtime cache 환경으로 실행했다. 두 default assertion 테스트부터 작성한 최초 RED는 exit 1, 10개 중 5개 실패였다(ID 3/1 실패, Spring 7/4 실패; 오류/skip 0). 실패는 모두 `expected: <[PAPAGO, AWS]> but was: <[PAPAGO]>`이며 compiler/setup 오류가 아니다. 두 production 목록 변경 후 같은 명령의 GREEN은 exit 0, 10개 전체 통과다.
|
|
|
|
|
|
|
|
- Production 변경은 `LanguageProviderOrder.kt` 두 목록뿐이다. Spring fake AWS runner와 실제 `AwsLanguageDetectionProvider`/`AwsTranslationService` + SDK interface mock runner를 분리했다. 실제 capability별 두 Bean, AWS enum/version, PAPAGO 첫 성공 뒤 AWS 무호출, 명시 AWS 우선 SDK 요청/응답을 확인했다. legacy property/env 무시, PAPAGO adapter identity/code 및 `papagoRunner`의 AWS 없는 inventory 거부는 유지했다. `LanguageProviderPropertiesTest`는 수정하지 않았다.
|
|
|
|
|
|
|
|
- 지정 Papago-only fixture만 명시 목록으로 격리했다(worker 여섯 coordinator, content cache helper, community 감지 테스트의 imported properties와 controlled service super 생성자, concurrency imported properties, community translation materializer). 기존 기대와 assertion은 유지했다. `i18n.translation.*` + `content.LanguageDetectionCacheServiceTest` 명령은 최초/최종 모두 exit 0, 각 142개 통과다. 별도 세 community class 명령도 최초/최종 모두 exit 0, 각 30개 통과(감지 12, concurrency 2, translation 16)이며 두 최종 실행의 실패/오류/skip은 모두 0이다.
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon ktlintCheck`는 최초에 신규 테스트 네 파일의 인자 개행/130자 위반으로 exit 1이었다. 해당 변경 줄만 개행한 최종 실행은 exit 0이며 format 명령은 실행하지 않았다. `./gradlew --no-daemon bootJar`는 exit 0이다. 최종 포맷 뒤 두 focused suite를 각각 다시 실제 실행했다. 산출물의 production 클래스와 실제 AWS 번역 adapter를 호출한 Java 드라이버는 exit 0으로 기본 순서, 첫 성공 무호출, AWS fallback, blank 입력 및 AWS 누락 거부를 확인했다. 네트워크 요청은 없다.
|
|
|
|
|
|
|
|
- 기존 Gradle deprecation, 중복 `org.json.JSONObject`, invalid-order startup 및 worker 실패 시나리오의 예상 WARN은 유지했다. LSP가 없어 Kotlin main/test compile 및 lint로 검증했다. 전체 테스트·실 AWS/Papago·실제 credentials/IAM·운영 DB는 미검증이며 사용자 전체 명령은 `./gradlew test`다. 상세 count/명령/로그 및 보고서는 `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/aws-implementation.7dG8YS/p2-order.NhauNM/report.md`에 기록했다. `P2-GATE`는 부모 독립 review 전이며 체크하지 않았다. 다른 Task/Gate와 현재 상태 표는 변경하지 않았다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 2 Gate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Phase 2 Gate: AWS 동작·순서·직접 영향 회귀 검증**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-11-10 Gate: 독립 reviewer 명세·품질 Approved. 부모가 모든 변경 diff와 suite/lint/build 증거를 대조했다. translation/cache 142개와 community 30개 모두 실패·오류·skip 0, ktlintCheck 및 bootJar exit 0. 실제 AWS adapter의 localhost SDK 요청 검증도 완료했다. 실 AWS IAM·번역 품질·전체 테스트는 미검증이다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Goal `P2-GATE`:** AWS adapter 동작, default provider order, Papago 전용 회귀 및 두 capability wiring을 판정한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **시작 조건:** `P2-T1`, `P2-T2` 완료 증거와 Progress 기록.
|
|
|
|
|
|
|
|
- **완료 증거:** 아래 두 focused suite, ktlint 및 `bootJar` exit code 0. Gradle에서 실제 dependency resolution도 성공.
|
|
|
|
|
|
|
|
- **범위 밖:** 전체 테스트, live cloud/API, IAM 권한 확인.
|
|
|
|
|
|
|
|
- **TDD 예외 사유:** 구현 결과에 대한 검증 gate다.
|
|
|
|
|
|
|
|
- **대체 검증 방법:** focused suites를 각각 실행하고 Papago-only fixtures, 실제 AWS adapter, provider uniqueness와 저장 계약 경계를 대조한다. 실제 AWS 권한은 미검증으로 기록한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
|
|
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest'
|
|
|
|
|
|
|
|
./gradlew 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 ktlintCheck
|
|
|
|
|
|
|
|
./gradlew bootJar
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Phase 2 관련 추가 회귀는 coordinator/worker/cache/end-to-end 테스트에 한정한다. 무필터 `test`나 `check`/`build`는 실행하지 않는다.
|
|
|
|
|
|
|
|
### Phase 3: 실제 adapter의 저장·표시 경로 검증
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Phase 결과:** SDK mock으로 실제 AWS adapter를 H2 기반 기존 worker/cache/materializer 경로에서 실행하고 현재 확장 가이드를 실제 구현과 일치시킨다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Task 3.1: adapter end-to-end와 현재 가이드
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Task 3.1: 실제 adapter 통합 검증과 현재 가이드**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Goal `P3-T1`:** 실제 AWS adapter 성공·cache 재사용·fallback·표시를 H2에서 검증하고 현재 provider guide 설명을 갱신한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **시작 조건:** `P2-GATE` 통과.
|
|
|
|
|
|
|
|
- **완료 증거:** actual SDK adapter mock을 이용한 H2 검증, 원문 full hash 및 output/providerVersion 저장 assertion, guide 대조 완료.
|
|
|
|
|
|
|
|
- **범위 밖:** 실 AWS 연결, 운영 MySQL claim query의 SKIP LOCKED 동작 증명, 역사적 PRD 변경.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Modify: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderCacheEndToEndTest.kt`
|
|
|
|
|
|
|
|
- Modify if required by a proven gap: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/TranslationCoordinatorTest.kt`
|
|
|
|
|
|
|
|
- Modify if required by a proven gap: `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/TranslationJobWorkerTest.kt`
|
|
|
|
|
|
|
|
- Modify: `docs/20261109_감지번역공급자공통화/provider-guide.md`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Consumes:** `AwsLanguageDetectionProvider(ComprehendClient)`, `AwsTranslationService(TranslateClient)`, production order `[PAPAGO, AWS]`, 기존 H2 cache/worker/materializer wiring.
|
|
|
|
|
|
|
|
- **Produces:** H2 integration evidence for original full-source cache identity, successful AWS provider/version attribution, SDK call count and displayed output; current guide description of AWS adapters, order, splitter and version.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **TDD (명시적 예외):** test-only integration wiring 및 검증이다. 새 전체 경로 검증은 첫 실행에 통과할 수 있으므로 인위적 RED를 만들지 않는다. Production behavior, SDK response 또는 assertion을 sabotage/약화하지 않는다.
|
|
|
|
|
|
|
|
- [x] **TDD 예외 대체 검증:** `LanguageProviderCacheEndToEndTest`에서 실제 adapters를 사용하고 AWS SDK interface만 mock한다. 중복 fake AWS beans/casts를 제거하고 assertion을 actual SDK call counts로 바꾼다. Comprehend에 동일 prefix와 서로 다른 tail을 가진 긴 원문 두 개를 보내 full hash/cache row가 구분되는지 확인하고 반복 조회는 AWS 호출이 없는지 확인한다.
|
|
|
|
|
|
|
|
- [x] **TDD 예외 대체 검증:** 번역 경로에서 Papago 실패 후 실제 AWS adapter의 성공 결과가 `aws`, `translate-2017-07-01`, 정확한 joined output으로 저장되고 worker 완료 및 materializer 화면에 보이는지 검증한다. cache 재사용 시 AWS 재호출이 없고, generic `Recording` provider의 version rewriting이 없음을 확인한다.
|
|
|
|
|
|
|
|
- [x] **TDD 예외 대체 검증:** 기존 `PAPAGO → AWS` production order를 보존한다. 실제 AWS adapter의 중간 조각 실패를 명시적 테스트 순서 `AWS → PAPAGO`로 실행해 다음 공급자에 원래 요청 전체가 전달됨을 검증한다. 모든 공급자 실패 시 cause 없는 고정 오류와 worker 오류 저장을 확인하고, 실패한 AWS의 부분 번역 memory가 저장되지 않음을 H2에서 확인한다. 기존 테스트가 이를 이미 검증하면 재사용하고 없으면 명시된 coordinator/worker 테스트에 추가한다. 합성 민감 오류 marker와 원문은 최종 오류·신규 로그에 없어야 한다.
|
|
|
|
|
|
|
|
- [x] **TDD 예외 대체 검증:** `provider-guide.md`의 현재 구현, default order, helper와 provider version 문장을 코드와 대조해 갱신한다. H2의 claim query spy는 기존 `FOR UPDATE SKIP LOCKED` 한정으로만 설명한다. Progress에 명령, 실제 결과, 운영 DB/live API 미검증을 기록한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**P3-T1 검증 기록 (2026-11-10):**
|
|
|
|
|
|
|
|
- Test-only 예외대로 production 변경이나 인위적 RED 없이 기존 E2E를 실제 `AwsLanguageDetectionProvider`/`AwsTranslationService`와 mock `ComprehendClient`/`TranslateClient`로 교체했다. Fake AWS 두 Bean과 해당 recording 주입을 제거하고 Papago 실패 fixture, 실제 H2 저장소·worker·materializer·표시 및 기존 claim query 한 곳의 spy를 유지했다. SDK mock은 매 테스트 `Mockito.reset`, Papago 기록 목록과 cache/job/read model은 매 테스트 비워 context 재사용을 격리한다.
|
|
|
|
|
|
|
|
- 최초 `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.LanguageProviderCacheEndToEndTest'`는 exit 0, 32초, 5개 전체 통과(실패/오류/skip 0)다. 같은 100000 UTF-8 바이트 prefix의 서로 다른 전체 원문 두 개가 같은 SDK prefix 두 번, 서로 다른 전체 hash 및 H2 aws row 두 개를 만들고 반복 조회에는 추가 SDK/Papago 호출이 없다. 첫 후보가 blank이고 둘째가 유효한 응답은 두 번 모두 null이며 H2 row는 0이다.
|
|
|
|
|
|
|
|
- 10000 UTF-8 바이트를 넘는 한글·보조 평면 원문은 Papago 실패 뒤 실제 AWS 두 조각으로 번역된다. H2 memory 한 개에 전체 원문/hash, `aws`, `translate-2017-07-01`, 정확한 `first tail` 결과를 저장하고 worker 완료와 유효 표시를 확인했다. read model/job을 삭제한 뒤 materializer 재생성과 새 job도 같은 memory를 사용하며 총 SDK 2회/Papago 1회가 늘지 않는다. 실제 중간 SDK 오류는 앞선 성공까지 버려 빈 결과를 반환하고 후속 조각/원문을 호출하지 않는다. 전체 실패의 cause는 null, worker 재시도 오류는 `all translation providers failed`, memory/read model은 0이며 합성 민감 marker와 원문은 출력에 없다. 별도 로컬 `AWS → PAPAGO` 순서에서는 여러 원문을 포함한 원래 요청 객체 전체가 Papago에 전달된다. 모든 경계를 E2E에서 검증하므로 coordinator/worker 단위 테스트의 중복 추가는 필요하지 않았다.
|
|
|
|
|
|
|
|
- 위 지정 translation/cache 명령에 `--no-daemon`을 추가한 실행은 exit 0, 25초, 145개 통과다(기존 142개에서 E2E 2→5로 3개 추가). 별도 세 community class 명령은 exit 0, 32초, 30개 통과(감지 12, concurrency 2, translation 16)다. 두 실행 모두 `:test`가 실제 실행됐고 실패/오류/skip은 0이다. `./gradlew --no-daemon ktlintCheck`는 exit 0, 24초(test lint 실제 실행), `./gradlew --no-daemon bootJar`는 exit 0, 11초(`UP-TO-DATE`)다. 모든 Gradle 명령은 timeout 600000ms와 전용 TMPDIR/runtime cache 환경으로 실행했다.
|
|
|
|
|
|
|
|
- 현재 `provider-guide.md`만 실제 adapter 가용성·기본 순서·공유 splitter·기존 명시 credentials/region·SDK/HTTP5 버전·providerVersion·테스트 경계에 맞췄고 역사적 PRD/계획은 수정하지 않았다. 기존 Gradle deprecation, 중복 `org.json.JSONObject`, negative startup/worker 시나리오의 예상 WARN은 유지했다. LSP 도구가 없어 Kotlin test compile과 lint로 보완했다. 전체 테스트·실 AWS/Papago·credentials/IAM·운영 MySQL claim 동시성 및 별도 SDK wire QA는 이 Task에서 실행하지 않았다. 사용자 전체 테스트 명령은 `./gradlew test`다. 상세 명령/count/파일/로그는 `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/aws-implementation.7dG8YS/p3-integration.QpAQTJ/report.md`에 기록했다. `P3-GATE`, 상단 상태 표와 다른 Task/Gate는 부모 소유로 남겼다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**검증 명령:**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
|
|
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest'
|
|
|
|
|
|
|
|
./gradlew 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 ktlintCheck
|
|
|
|
|
|
|
|
./gradlew bootJar
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**H2 검증 경계:** repository 저장·별도 transaction 조회·materializer/display는 실제 H2다. Claim query만 H2 호환 spy로 교체한다. 이 경로는 SDK serialization/signing, 실제 AWS 품질·권한, 운영 MySQL `SKIP LOCKED` 동시성을 증명하지 않는다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 3 Gate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Phase 3 Gate: 기능 범위 최종 검증**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-11-10 Gate: Oracle이 P3 명세·품질과 전체 변경을 승인했다. 부모가 별도 fresh 실행한 translation/cache 145건, community 30건 모두 통과했고 lint·bootJar·최종 산출물 기반 실제 SDK localhost QA도 성공했다. 실패·오류·skip 0이며 코드·가이드·PRD를 대조했다. 실제 IAM·AWS/Papago 품질·과금·운영 MySQL 및 전체 테스트는 미검증이며 기능 범위 완료와 구분한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Goal `P3-GATE`:** 세 Phase의 범위, 실제 adapter 사용자 흐름, 문서 최신성 및 검증 증거를 최종 판정한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **시작 조건:** `P3-T1`과 모든 이전 Goal 완료 증거 기록.
|
|
|
|
|
|
|
|
- **완료 증거:** 아래 focused suite 각각 성공, ktlint와 bootJar 성공, trace table의 요구사항과 실제 결과 대조, 전체 테스트 미실행 표시.
|
|
|
|
|
|
|
|
- **범위 밖:** test 전체 실행, 실 AWS 호출 및 PRD의 IAM/live 품질 미검증 상태 변경.
|
|
|
|
|
|
|
|
- **TDD 예외 사유:** 구현을 새로 하지 않는 final verification gate다.
|
|
|
|
|
|
|
|
- **대체 검증 방법:** 아래 명령 결과, H2 assertion, guide/current code/PRD 대조 및 문서 공백 검사를 Progress에 기록한다. 사용자의 전체 검증 명령은 별도로 안내한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
|
|
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest'
|
|
|
|
|
|
|
|
./gradlew 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 ktlintCheck
|
|
|
|
|
|
|
|
./gradlew bootJar
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Expected:** 각 명령이 exit code 0이고 문서/코드 대조에서 미해결 contract 차이가 없다. 전체 테스트는 사용자가 `./gradlew test`로 실행하며, 이 계획의 focused 성공을 전체 성공으로 표현하지 않는다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Goal 순서와 의존성
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 4: 공용 테스트 AWS 초기화 회귀 보정
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Task 4.1: 테스트 전용 인증정보와 공유 설정 회귀 (`P4-R1`)**
|
|
|
|
|
|
|
|
- **목표:** 공용 테스트 YAML로 전체 Spring 앱이 시작되며 운영의 빈 키 거부는 그대로 유지한다.
|
|
|
|
|
|
|
|
- **시작 조건:** `SpringBootIntegrationSampleTest`가 `Access key ID cannot be blank` 및 `AwsLanguageProviderConfiguration.kt:18`에서 실패함을 수정 전에 재현.
|
|
|
|
|
|
|
|
- **완료 증거:** 아래 RED/GREEN, 실제 H2·Redis 및 로컬 HTTP 통합 검증, 독립 리뷰와 결과 기록.
|
|
|
|
|
|
|
|
- **범위 밖:** 운영 코드·인증정보·Provider 순서·lazy/profile 우회·전체 테스트·실 AWS 호출 변경.
|
|
|
|
|
|
|
|
- **Files:** 수정 `src/test/resources/application.yml`, `src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/AwsLanguageProviderConfigurationTest.kt`; 검증 `src/test/kotlin/kr/co/vividnext/sodalive/support/SpringBootIntegrationSampleTest.kt`, `src/test/kotlin/kr/co/vividnext/sodalive/deployment/DeploymentEndpointIntegrationTest.kt`.
|
|
|
|
|
|
|
|
- **계약:** 공용 test YAML을 실제로 로드하고 인증정보 property를 테스트에서 덮어쓰지 않는다. 두 client와 두 provider가 생성되며 합성 static credentials/서울 설정을 사용한다. 운영 빈 access/secret 키는 계속 실패한다.
|
|
|
|
|
|
|
|
- [x] **RED:** 공용 YAML 기반 AWS 설정·provider 생성 assertion과 빈 키 거부 회귀를 기존 설정 테스트에 추가한다.
|
|
|
|
|
|
|
|
- [x] **RED 확인:** `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageProviderConfigurationTest'`에서 공용 빈 키가 원인인 실패를 확인한다.
|
|
|
|
|
|
|
|
- [x] **GREEN:** 공용 test YAML의 accessKey/secretKey 두 값만 명백한 테스트 전용 가짜 값으로 교체한다. production 설정·검증은 수정하지 않는다.
|
|
|
|
|
|
|
|
- [x] **GREEN 확인:** 같은 설정 테스트와 `SpringBootIntegrationSampleTest`, `DeploymentEndpointIntegrationTest`를 각각 focused 실행한다.
|
|
|
|
|
|
|
|
- [x] **REFACTOR:** 불필요한 추가 변경 없이 `i18n.translation.*` 회귀·`ktlintCheck`와 변경 공백 검사를 수행하고 실패·성공 증거를 기록한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**P4-R1 검증 기록 (2026-11-10):**
|
|
|
|
|
|
|
|
- `ConfigDataApplicationContextInitializer`로 실제 공용 `application.yml`을 읽는 설정 회귀를 기존 클래스에 추가했다. 이 회귀는 인증정보 property override나 SDK/provider mock 없이 실제 configuration·두 client·두 adapter를 로드하고 capability별 Bean 수, AWS ID, static synthetic credentials와 서울 리전을 확인한다. 기존 두 region의 실제 client 생성·종료 테스트는 수정하지 않았다. 별도 parameterized negative 2건은 동일 production configuration에서 blank access/secret 각각의 startup failure와 SDK root cause를 확인한다.
|
|
|
|
|
|
|
|
- YAML 수정 전 `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageProviderConfigurationTest'`: exit 1, 19초, 5건 중 공유 YAML 회귀 1건 실패(오류/skip 0). `assertNull(context.startupFailure)`가 `BeanCreationException → BeanInstantiationException → NullPointerException: Access key ID cannot be blank.`로 실패했다. main/test compile은 성공했으며 기존 lifecycle 2건과 빈 키 거부 2건은 통과했다. 원본 RED assertion/count는 아래 보고서 경로의 `01-config-red-evidence.log`에 보존했다.
|
|
|
|
|
|
|
|
- `src/test/resources/application.yml`의 두 값만 `test-access-key`/`test-secret-key`로 교체했다. 동일 설정 focused 명령은 exit 0, 13초, 5건 전체 통과다. 두 negative는 각각 `Access key ID cannot be blank.`와 `Secret access key cannot be blank.`를 계속 확인한다. production 코드·설정·build와 다른 도메인 테스트는 이번 수정에서 변경하지 않았다.
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.support.SpringBootIntegrationSampleTest'`: exit 0, 35초, 1건 통과. 실제 전체 Spring 앱에서 H2 `select 1`과 embedded Redis set/get을 확인했다. `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.deployment.DeploymentEndpointIntegrationTest'`: exit 0, 35초, 4건 통과. 실제 Tomcat 앱/management 포트 `65041`/`65042`에서 TestRestTemplate HTTP로 readiness 200 두 경로, deployment 기본 응답 및 앱 포트 비노출을 검증했다. 두 기존 클래스와 credential override는 추가·변경하지 않았다.
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.*'`: exit 0, 26초, 15개 클래스 146건 통과(AWS 설정/감지/번역 5/19/27건 포함). GREEN 실행은 모두 실패/오류/skip 0이며 `:test`가 실제 실행됐다. 중복 없는 검증 범위는 translation 146건과 전체 앱/HTTP 5건, 총 151건이다. `./gradlew --no-daemon ktlintCheck`: exit 0, 20초(test lint 실제 실행); `git diff --check`: exit 0. 불필요한 refactor/format과 bootJar는 실행하지 않았다.
|
|
|
|
|
|
|
|
- 모든 Gradle 명령은 timeout 600000ms와 지정 TMPDIR/cache export로 실행했다. 테스트 후 lsof/ps에서 해당 Spring test workers, Redis `65026`, HTTP `65041`/`65042`와 잔존 Redis listener가 없었으며 프로세스를 kill하지 않았다. LSP 도구가 없어 Kotlin compile·ktlint로 보완했다. Gradle deprecation, 기존 중복 JSONObject 경고와 JVM 종료 시 Redis 재접속 WARN은 수정하지 않았다. 전체 테스트·실 AWS/Papago·실계정/IAM은 미검증이며 사용자 전체 명령은 `./gradlew test`다.
|
|
|
|
|
|
|
|
- 상세 명령/exit/count/RED 증거 보고서: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/aws-test-fix.We4KIU/fix.4xaIEt/report.md`. 다섯 실행 체크는 완료했으며 완료 증거의 독립 리뷰 및 Task 최상위 완료 판정은 부모에게 남긴다. `P4-GATE`, 상단 상태 표, PRD와 기존 P1~P3 기록은 수정하지 않았다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- [x] **Phase 4 Gate (`P4-GATE`): 회귀 수정·운영 검증 보존 승인**
|
|
|
|
|
|
|
|
- **시작 조건:** P4-R1 결과 및 독립 리뷰 확인.
|
|
|
|
|
|
|
|
- **완료 증거:** 설정 회귀, H2/Redis 전체 앱, 실제 로컬 HTTP 테스트가 통과하고 production diff가 이번 수정에 없음을 확인한다.
|
|
|
|
|
|
|
|
- **범위 밖:** 전체 테스트 성공 또는 실 IAM 검증으로 확대 해석하지 않는다.
|
|
|
|
|
|
|
|
- **TDD 예외:** Gate는 검증·문서 마감이며 새 동작을 구현하지 않는다.
|
|
|
|
|
|
|
|
- **대체 검증:** 위 focused 명령의 실제 실행 결과, runtime context·HTTP 응답, diff·리뷰 대조.
|
|
|
|
|
|
|
|
- **진행 기록:** 2026-11-10 수정 전 `./gradlew --no-daemon test --rerun --tests 'kr.co.vividnext.sodalive.support.SpringBootIntegrationSampleTest'` 실패. root cause는 공용 test YAML의 빈 accessKey로 SDK v2의 eager credential 생성이 거부되는 것이다. 앞선 전용 AWS 설정 테스트는 합성 property를 직접 주입해 이 경로를 놓쳤다. 전체 테스트는 사용자 실행 `./gradlew test`로 남긴다.
|
|
|
|
|
|
|
|
- **부모 fresh 검증:** `./gradlew --no-daemon test --rerun --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.support.SpringBootIntegrationSampleTest' --tests 'kr.co.vividnext.sodalive.deployment.DeploymentEndpointIntegrationTest'` exit 0, 실제 재실행 17개 클래스 151건 실패·오류·skip 0을 XML에서 확인했다. `./gradlew --no-daemon ktlintCheck` 및 `git diff --check` exit 0. 실제 H2/Redis와 앱·management HTTP 경로가 포함되며 변경한 테스트 외 credential override는 없다.
|
|
|
|
|
|
|
|
- **독립 리뷰:** 명세·품질 및 S1(공용 설정 로딩), S2(빈 운영 키 거부), S3(실제 앱·HTTP) 모두 승인, 차단 사항 없음. H2가 지원하지 않는 background scheduler `SKIP LOCKED` 오류는 별도 기존 한계로 관찰했으며 이번 통과가 scheduler SQL의 정상 동작을 증명하는 것은 아니다.
|
|
|
|
|
|
|
|
- **실행 경계:** test worker PID 70305와 HTTP 포트 65088/65089가 종료됐음을 확인했다. production 코드·운영 설정·빌드에는 이번 수정이 없고, 전체 테스트·실 IAM·외부 API는 미검증이다. 위 임시 보고서의 핵심 RED/GREEN 증거는 이 문서에 보존하고 임시 산출물은 작업 종료 시 정리한다.
|
|
|
|
|
|
|
|
- **정리 영수증:** 소유한 회귀 조사·구현·리뷰 임시 root의 열린 파일이 없음을 확인하고 하위 로그·캐시·노트를 제거했다. 경로 부재 검사 exit 0. 공유 캐시·기존 프로세스·저장소 파일은 제거하지 않았다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 순서 | Goal | 의존성 | 실행 방식 |
|
|
|
|
|
|
|
|
|---:|---|---|---|
|
|
|
|
|
|
|
|
| 1 | `P1-T1` | 시작 조건 충족 | splitter 단일 Task |
|
|
|
|
|
|
|
|
| 2 | `P1-GATE` | `P1-T1` | focused 검증 |
|
|
|
|
|
|
|
|
| 3 | `P2-T1` | `P1-GATE` | SDK dependency 및 adapter |
|
|
|
|
|
|
|
|
| 4 | `P2-T2` | `P2-T1` | order 및 영향 fixture |
|
|
|
|
|
|
|
|
| 5 | `P2-GATE` | `P2-T1`, `P2-T2` | focused suite/lint/bootJar |
|
|
|
|
|
|
|
|
| 6 | `P3-T1` | `P2-GATE` | H2 adapter 통합 및 guide |
|
|
|
|
|
|
|
|
| 7 | `P3-GATE` | `P3-T1` | focused 최종 검증 |
|
|
|
|
|
|
|
|
| 8 | `P4-R1` | 사용자 후속 오류 재현 | 공용 테스트 설정 회귀 보정 |
|
|
|
|
|
|
|
|
| 9 | `P4-GATE` | `P4-R1` | 실제 앱·HTTP 및 리뷰 판정 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`P1-T1 → P1-GATE → P2-T1 → P2-T2 → P2-GATE → P3-T1 → P3-GATE`. 한 번에 하나의 goal만 active로 둔다. 선행 goal이 완료되지 않으면 뒤 goal을 시작하지 않는다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 문서 및 실행 기록
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 계획 작성 기록
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 상태: 계획 문서만 작성 완료, 구현 대기.
|
|
|
|
|
|
|
|
- 무엇을: 승인 PRD의 확정 요구사항을 세 Phase와 일곱 Goal로 추적하고 정확한 구현/테스트 경로 및 focused 명령을 계획했다.
|
|
|
|
|
|
|
|
- 어떻게: 필수 작업/문서/실행/테스트 가이드, 샘플, 현재 PRD, build 설정, splitter/order 및 직접 영향 테스트를 읽어 파일과 계약을 확인했다.
|
|
|
|
|
|
|
|
- 실행 검증: 문서 전용 변경이므로 Gradle, 설치, AWS/API 호출을 실행하지 않는다. 향후 구현 명령은 계획 예시이며 아직 실행하지 않았다.
|
|
|
|
|
|
|
|
- 다음 행동: 사용자가 계획을 검토하고 실행 방법을 선택한 뒤에만 첫 Goal을 시작한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Verification Record
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 부모 최종 실행 검증 — 2026-11-10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon test --rerun --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest'`: exit 0, 실제 test 재실행. 16개 클래스 145건, 실패·오류·skip 0을 XML에서 확인했다.
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon test --rerun --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'`: exit 0, 실제 test 재실행. 각각 12·2·16건, 총 30건 모두 통과했다. 두 범위 합계는 중복 없는 175건이다.
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon ktlintCheck bootJar --rerun`: exit 0. 변경 없는 lint는 UP-TO-DATE, bootJar는 재생성했다. 앞선 실제 lint 실행도 통과했다. 산출물은 `build/libs/sodalive-0.0.1-SNAPSHOT.jar`다.
|
|
|
|
|
|
|
|
- 부모가 산출물의 classes/libs를 임시 경로에 추출하고 `javac`/`java`로 로컬 HTTP 드라이버를 실행했다. 실제 SDK의 Comprehend JSON, Translate rpc-v2-cbor 요청·응답과 서울 리전 서명, 100000바이트 prefix, 10000바이트 번역 분할·재구성·결합, blank 응답 후 중단이 모두 PASS(exit 0)였다. 합성 인증정보와 localhost만 사용했고 SDK clients와 HTTP 서버 종료를 확인했다.
|
|
|
|
|
|
|
|
- `git diff --check`: exit 0. LSP 도구가 제공되지 않아 Kotlin 컴파일과 ktlint로 보완했다. 전체 테스트 `./gradlew test`, 실제 IAM·AWS/Papago 품질·과금, 운영 MySQL claim 동시성은 미검증이다. Gradle deprecation 및 기존 중복 `org.json` 경고는 범위 밖으로 유지했다.
|
|
|
|
|
|
|
|
- 전체 변경 최종 리뷰: Oracle이 P3 명세·품질 및 전체 변경을 승인했다. 구체적 차단 사항 없음. SDK lifecycle 테스트의 내부 reflection은 버전 변경 시 유지보수가 필요하다는 비차단 제약으로 기록했다.
|
|
|
|
|
|
|
|
- 실행 중 판단: 재현된 HTTP5 충돌만 두 버전 정렬로 수정했다. 기존 HTTP4·SDK v1은 그대로이며 다른 HTTP5 사용 경로의 전체 회귀는 사용자 전체 테스트 범위다. 자동 worktree·커밋·push 없이 기존 `test` 브랜치에 변경을 남긴다.
|
|
|
|
|
|
|
|
- 2026-11-10 정리 영수증: 실제 SDK QA 서버·clients가 종료된 뒤 작업 root의 열린 파일이 없음을 `lsof`로 확인했다. 소유한 임시 root와 모든 하위 작업 캐시·드라이버·추출 jar·임시 로그를 제거하고 경로 부재를 확인했다. 저장소의 구현 코드·문서·최종 `build/libs/sodalive-0.0.1-SNAPSHOT.jar`는 유지했다. 핵심 명령·결과·실패 이력은 이 계획에 보존했다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- P2-T1 부모 검증: 독립 reviewer 명세·품질 Approved. `./gradlew --no-daemon bootJar` exit 0. 산출물 라이브러리로 Java 드라이버를 컴파일하고 실제 SDK를 localhost HTTP 서버에 연결해 감지 prefix·서울 서명·번역 바이트 분할·결합·blank 응답 후 중단을 검증했다(exit 0). 서버 종료·client close 영수증을 확인했다. 첫 QA는 Translate가 JSON이라는 드라이버 가정으로 실패했으며 실제 rpc-v2-cbor에 맞춰 드라이버만 수정 후 통과했다. 실 AWS 호출은 없다.
|
|
|
|
|
|
|
|
- P2-T1 통합 결정: 재현된 `TlsSocketStrategy` 누락을 해결하기 위해 Spring Boot의 HTTP5 관리 버전만 SDK POM 기준 `httpclient5=5.6.4`, `httpcore5=5.4.3`으로 정렬했다. 기존 HTTP4·AWS SDK v1 선언은 유지한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
계획 작성 시 문서 간 정합성 및 경로 대조만 수행한다. 구현 전 테스트 결과나 실행 count는 없다. 구현 시 각 Goal의 실제 명령·결과를 해당 Goal에 누적하고, 공통 회귀·문서 검사는 여기에 추가한다. 전체 테스트는 사용자가 `./gradlew test`로 실행한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 2026-11-09 문서 검토: 첫 후보만 검사하는 감지 계약, SDK 준비와 RED/GREEN 순서, 기존 Papago-only fixture·미등록 공급자 거부 유지의 세 시나리오를 독립 검토해 모두 PASS를 받았다. 중요한 차단 사유 없음.
|
|
|
|
|
|
|
|
- 실제 파일 경로와 PRD 수용 기준을 대조하고 공백 검사를 수행했다. 문서 전용 변경이므로 테스트·빌드·LSP 진단·실제 AWS 호출은 수행하지 않았으며, 이 PASS는 구현 성공을 뜻하지 않는다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Self-review
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Coverage:** AWS-001 client credentials/region은 P2-T1; AWS-002~004 감지 입력/후보는 P2-T1 및 P3-T1; AWS-005 chunk/atomic fallback 및 AWS-007 version은 P2-T1/P3-T1; AWS-006 Papago 및 code-point 경계는 P1-T1; 순서/cache/schema/security 요구는 P2-T2/P3-T1에 추적했다.
|
|
|
|
|
|
|
|
- **Step scan:** 모든 구현 Task에 RED, assertion 중심 RED 확인, GREEN, GREEN 확인, REFACTOR가 다섯 개씩 있다. P3 test-only와 Gate는 TDD 예외 사유 및 대체 검증을 둔다.
|
|
|
|
|
|
|
|
- **Type consistency:** splitter는 `String`, `Int`, `(Int) -> Int`, `List<String>`/`String`을 사용한다. AWS client 생성 및 constructor 주입 타입은 AWS SDK v2의 `ComprehendClient`/`TranslateClient`다.
|
|
|
|
|
|
|
|
- **Review focus:** 다섯 항목이 owner test와 task에 각각 매핑돼 있다. 빈 Papago 원문, blank 입력, Unicode 경계, full hash, 에러 민감정보를 포함한다.
|
|
|
|
|
|
|
|
- **Proportion:** plan은 테스트 대상·contract·실행 명령·dependency 및 영향 fixture를 확정하는 범위다. 별도 subsystem 분할은 필요하지 않다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 실행 승인 기록
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
요구사항과 SDK v2 방향은 DEC-006으로 승인됐다. 실행 전 이 계획의 Task 경로와 범위를 검토한다. 구현 방식은 두 선택지 중 하나를 정한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Subagent-driven:** 각 Task를 독립 실행하고 단계별 fresh review를 거친다. Goal 사이 의존성과 AWS 설정/실제 provider 등록 회귀 위험이 있어 권장한다.
|
|
|
|
|
|
|
|
- **Native:** 한 구현자가 모든 Goal을 순서대로 실행하고 마지막에 전체 변경 review를 한다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-11-09 사용자가 "진행"으로 계획과 Subagent-driven 실행을 승인했다. 기존 `test` 브랜치에서 구현하며 자동 worktree 생성, 커밋·push, 실 AWS 호출은 하지 않는다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 커밋 전 검증 — 2026-11-10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 사용자 `/commit-with-validation` 요청으로 공용 커밋 절차를 적용했다. 기존 구현은 변경하지 않고 의존 순서별 구현·테스트 및 문서 커밋을 준비했다.
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.*' --tests 'kr.co.vividnext.sodalive.content.LanguageDetectionCacheServiceTest'`: exit 0, 33초, 16개 클래스 148건 통과.
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon 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'`: exit 0, 41초, 30건 통과.
|
|
|
|
|
|
|
|
- `./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.support.SpringBootIntegrationSampleTest' --tests 'kr.co.vividnext.sodalive.deployment.DeploymentEndpointIntegrationTest' ktlintCheck bootJar`: exit 0, 55초, 5건 통과. lint와 bootJar는 UP-TO-DATE다.
|
|
|
|
|
|
|
|
- 세 실행의 `:test`가 실제 실행됐고 XML 기준 총 183건, 실패·오류·skip 모두 0이다. `git diff --check`도 exit 0이다. 전체 테스트·실 AWS/Papago·IAM·운영 MySQL은 미검증이며 전체 테스트는 사용자가 `./gradlew test`로 실행한다.
|