60 KiB
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, 승인 결정 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 byte10000, 감지 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
- 경계 바로 앞의 한글 및 supplementary-plane 문자는 잘리지 않고 한 조각 안에 남으며, byte/UTF-16 한도는 초과하지 않는다.
CodePointTextSplitterTest에서 각 공급자 한도 경계, 재구성 및 100000 byte prefix와 제외 tail을 고정한다. - blank 입력, 짧은 감지 입력, blank/누락 후보와 AWS 오류가 감지 cache에 잘못된 값으로 저장되지 않는다.
AwsLanguageDetectionProviderTest와LanguageProviderCacheEndToEndTest에서 호출 수와 결과를 확인한다. - AWS 번역 일부 조각의 오류 또는 공백 응답이 앞선 조각·원문의 성공값과 섞이거나 후속 호출을 유발하지 않는다.
AwsTranslationServiceTest및 필요 시TranslationCoordinatorTest에서 전체 요청 폐기와 원문 단위 fallback을 확인한다. - AWS 기본 순서 전환 후 Papago 전용 fixture가 실제 AWS adapter를 가짜 AWS provider와 함께 로드하거나 PAPAGO 전용 검증을 바꾸지 않는다.
LanguageProviderSpringConfigurationTest및 아래 명시한 영향 테스트를 확인한다. - 서로 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 이전
- 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.ktInterfaces: -
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. -
RED:
CodePointTextSplitterTest에 정확 한도/초과, Korean, supplementary character, whitespace 포함 재구성, empty input (listOf("")), 100000 UTF-8 byte prefix와 제외 tail 테스트를 작성한다. Papago 5001/10001 및 경계 surrogate pair 기대도 기존PapagoTranslationServiceTest에 유지한다. -
RED 확인:
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest'를 실행한다. helper 미구현에 따른 compile failure가 아니라 splitter 요구 결과 assertion 실패를 기록한다. 테스트 실행 명령 오류는 RED가 아니다. -
GREEN: 위 세 internal 함수를
CodePointTextSplitter.kt에 구현한다. UTF-16 계수 callback에는Character.charCount(codePoint), UTF-8 계수에는utf8CodePointSize를 쓴다. Prefix는 첫 허용 경계에서 멈춰 전체 입력을 split하지 않는다. 공통 boundary scanning은 중복이 실제 생길 때만 private helper로 공유한다. -
GREEN 확인:
./gradlew test --tests 'kr.co.vividnext.sodalive.i18n.translation.CodePointTextSplitterTest'를 실행해 경계·재구성 assertion이 통과함을 확인한다. -
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
- 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 요청/결합 검증이 유지되는지 확인한다.
./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
- 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/실행명령어.mdInterfaces: - Consumes:
LanguageDetectionProvider.detect(query: String): String?;TranslationProvider.translate(request: TranslateRequest): TranslateResult; P1 splitter three functions; existing cloud AWS@Valuekeys. - Produces:
AwsLanguageDetectionProvider(ComprehendClient) : LanguageDetectionProvider;AwsTranslationService(TranslateClient) : TranslationProvider;@Configurationbean methodscomprehendClient(): ComprehendClient,translateClient(): TranslateClient, each with@Bean(destroyMethod = "close").
테스트 실행 준비: RED 테스트를 먼저 작성하고 SDK 타입을 컴파일하는 데 필요한 BOM 2.57.11, comprehend, translate 의존성과 비동작 shell만 준비한다. dependency resolution 및 compile 성공 자체는 RED 증거가 아니다. 생산 동작은 아래 RED 확인 뒤 구현한다.
- 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한다.
- 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 오류는 거부한다. - 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버전을 적용한다. - GREEN 확인: 동일한 세
--tests대상으로 실행해 client 설정/종료, 감지 boundary 및 응답 매핑, 번역 요청 assertions가 통과함을 확인한다. - 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는 변경하지 않았다.
검증 명령:
./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
- 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.ktInterfaces: -
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. -
RED:
LanguageProviderIdTest의 기본 순서 assertion을[PAPAGO, AWS]로 기대하고 production adapter identity 및 안정 code assertion은 보존한다. Spring 기본 순서/legacy override 테스트에 두 provider와 PAPAGO 첫 성공, AWS 미호출 assertion을 추가한다. -
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인지 확인한다. -
GREEN:
LanguageProviderOrder.kt의 두 목록에 AWS를 Papago 다음에 추가한다. Spring 테스트의 legacy property/env default 기대를 새 순서로 갱신하며 PAPAGO 우선 성공과 AWS 미호출을 검증한다. actual adapter를 검증할 runner는 SDK mock과 함께 별도로 구성하고 fakeAwsProviders를 같은 capability에 로드하지 않는다. -
GREEN 확인: 위 두 class를 다시 실행한다. startup uniqueness assertion을 유지하고
shouldRejectInvalidOrdersAtStartup의papagoRunner가 AWS 없는[AWS]설정에서 실패하는 검증을 그대로 보존한다. -
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다.
검증 명령:
./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
- 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 및
bootJarexit code 0. Gradle에서 실제 dependency resolution도 성공. - 범위 밖: 전체 테스트, live cloud/API, IAM 권한 확인.
- TDD 예외 사유: 구현 결과에 대한 검증 gate다.
- 대체 검증 방법: focused suites를 각각 실행하고 Papago-only fixtures, 실제 AWS adapter, provider uniqueness와 저장 계약 경계를 대조한다. 실제 AWS 권한은 미검증으로 기록한다.
./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와 현재 가이드
- 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.
-
TDD (명시적 예외): test-only integration wiring 및 검증이다. 새 전체 경로 검증은 첫 실행에 통과할 수 있으므로 인위적 RED를 만들지 않는다. Production behavior, SDK response 또는 assertion을 sabotage/약화하지 않는다.
-
TDD 예외 대체 검증:
LanguageProviderCacheEndToEndTest에서 실제 adapters를 사용하고 AWS SDK interface만 mock한다. 중복 fake AWS beans/casts를 제거하고 assertion을 actual SDK call counts로 바꾼다. Comprehend에 동일 prefix와 서로 다른 tail을 가진 긴 원문 두 개를 보내 full hash/cache row가 구분되는지 확인하고 반복 조회는 AWS 호출이 없는지 확인한다. -
TDD 예외 대체 검증: 번역 경로에서 Papago 실패 후 실제 AWS adapter의 성공 결과가
aws,translate-2017-07-01, 정확한 joined output으로 저장되고 worker 완료 및 materializer 화면에 보이는지 검증한다. cache 재사용 시 AWS 재호출이 없고, genericRecordingprovider의 version rewriting이 없음을 확인한다. -
TDD 예외 대체 검증: 기존
PAPAGO → AWSproduction order를 보존한다. 실제 AWS adapter의 중간 조각 실패를 명시적 테스트 순서AWS → PAPAGO로 실행해 다음 공급자에 원래 요청 전체가 전달됨을 검증한다. 모든 공급자 실패 시 cause 없는 고정 오류와 worker 오류 저장을 확인하고, 실패한 AWS의 부분 번역 memory가 저장되지 않음을 H2에서 확인한다. 기존 테스트가 이를 이미 검증하면 재사용하고 없으면 명시된 coordinator/worker 테스트에 추가한다. 합성 민감 오류 marker와 원문은 최종 오류·신규 로그에 없어야 한다. -
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와 mockComprehendClient/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는 부모 소유로 남겼다.
검증 명령:
./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
- 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에 기록한다. 사용자의 전체 검증 명령은 별도로 안내한다.
./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 초기화 회귀 보정
- 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 키는 계속 실패한다.
- RED: 공용 YAML 기반 AWS 설정·provider 생성 assertion과 빈 키 거부 회귀를 기존 설정 테스트에 추가한다.
- RED 확인:
./gradlew --no-daemon test --tests 'kr.co.vividnext.sodalive.i18n.translation.AwsLanguageProviderConfigurationTest'에서 공용 빈 키가 원인인 실패를 확인한다. - GREEN: 공용 test YAML의 accessKey/secretKey 두 값만 명백한 테스트 전용 가짜 값으로 교체한다. production 설정·검증은 수정하지 않는다.
- GREEN 확인: 같은 설정 테스트와
SpringBootIntegrationSampleTest,DeploymentEndpointIntegrationTest를 각각 focused 실행한다. - 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 앱에서 H2select 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, HTTP65041/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 기록은 수정하지 않았다. -
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 --checkexit 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 bootJarexit 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로 실행한다.