From 02196eba4c90dd0a2c5225098f42922b1cfd5f4d Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 16 Mar 2026 11:17:03 +0900 Subject: [PATCH] =?UTF-8?q?fix(admin-chat-character):=20JP=20=EB=A6=AC?= =?UTF-8?q?=EC=A0=84=20=EC=BA=90=EB=A6=AD=ED=84=B0=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EC=84=B1=EB=B3=84=20=EA=B0=92=EC=9D=84=20=EC=9D=BC=EB=B3=B8?= =?UTF-8?q?=EC=96=B4=EB=A1=9C=20=EB=B3=80=ED=99=98=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/20260316_캐릭터등록JP성별일본어변환.md | 23 +++++++ .../character/AdminChatCharacterController.kt | 15 ++++- .../AdminChatCharacterControllerTest.kt | 63 +++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 docs/20260316_캐릭터등록JP성별일본어변환.md create mode 100644 src/test/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterControllerTest.kt diff --git a/docs/20260316_캐릭터등록JP성별일본어변환.md b/docs/20260316_캐릭터등록JP성별일본어변환.md new file mode 100644 index 00000000..80687490 --- /dev/null +++ b/docs/20260316_캐릭터등록JP성별일본어변환.md @@ -0,0 +1,23 @@ +# 캐릭터 등록 JP 성별 일본어 변환 + +- [x] `AdminChatCharacterController.registerCharacter`의 외부 API 호출 경로 확인 + - QA: `callExternalApi`에서 `region`/`gender` 바디 구성 위치 확인 +- [x] `region == JP`일 때 `gender` 값을 일본어로 변환하는 로직 추가 + - QA: `여성 -> 女性`, `남성 -> 男性`, `기타 -> その他` 매핑 확인 +- [x] 등록 API 외부 호출 시에만 변환이 적용되도록 구현 + - QA: DB 저장용 `request.gender`는 기존 값 유지 여부 확인 +- [x] 정적 진단 및 테스트 수행 + - QA: Kotlin LSP 미구성으로 `lsp_diagnostics` 불가 확인, `./gradlew test --tests "kr.co.vividnext.sodalive.admin.chat.character.AdminChatCharacterControllerTest"` 및 `./gradlew build -x test` 성공 + +## 검증 기록 + +### 1차 구현 +- 무엇을: `registerCharacter` 외부 API 호출 시 `region == JP` 조건에서만 `gender`를 일본어(`女性`/`男性`/`その他`)로 변환하도록 구현하고, 매핑 단위 테스트를 추가했다. +- 왜: JP 리전 요청에서 외부 API가 일본어 성별 값을 요구하므로 등록 API 요청 바디의 `gender` 값만 조건부 변환이 필요했다. +- 어떻게: + - 코드 확인: `src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt`에서 `callExternalApi` 바디 구성 지점 확인 후 `mapGenderForExternalApi` 헬퍼 추가 + - 매핑 검증: `src/test/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterControllerTest.kt`에서 JP 매핑(여성/남성/기타) 및 KR 유지 케이스 검증 + - 정적 진단: `lsp_diagnostics` 실행 시 Kotlin LSP 미구성으로 불가(환경 제약) + - 실행 검증 1: `./gradlew test --tests "kr.co.vividnext.sodalive.admin.chat.character.AdminChatCharacterControllerTest"` → 성공 + - 수동 확인: `build/test-results/test/TEST-kr.co.vividnext.sodalive.admin.chat.character.AdminChatCharacterControllerTest.xml`에서 `tests="4" failures="0" errors="0"` 확인 + - 실행 검증 2: `./gradlew build -x test` → 성공 diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt index f04c6e7f..dca09a61 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt @@ -206,7 +206,7 @@ class AdminChatCharacterController( body["description"] = request.description body["region"] = request.region request.age?.let { body["age"] = it } - request.gender?.let { body["gender"] = it } + request.gender?.let { body["gender"] = mapGenderForExternalApi(request.region, it) } request.mbti?.let { body["mbti"] = it } request.speechPattern?.let { body["speechPattern"] = it } request.speechStyle?.let { body["speechStyle"] = it } @@ -273,6 +273,19 @@ class AdminChatCharacterController( } } + private fun mapGenderForExternalApi(region: String, gender: String): String { + if (!region.equals("JP", ignoreCase = true)) { + return gender + } + + return when (gender) { + "여성" -> "女性" + "남성" -> "男性" + "기타" -> "その他" + else -> gender + } + } + /** * 캐릭터 수정 API * 1. JSON 문자열을 ChatCharacterUpdateRequest 객체로 변환 diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterControllerTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterControllerTest.kt new file mode 100644 index 00000000..551790d3 --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterControllerTest.kt @@ -0,0 +1,63 @@ +package kr.co.vividnext.sodalive.admin.chat.character + +import kr.co.vividnext.sodalive.admin.chat.character.service.AdminChatCharacterService +import kr.co.vividnext.sodalive.admin.chat.original.service.AdminOriginalWorkService +import kr.co.vividnext.sodalive.aws.s3.S3Uploader +import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.mockito.Mockito +import org.springframework.context.ApplicationEventPublisher + +class AdminChatCharacterControllerTest { + private val controller = AdminChatCharacterController( + service = Mockito.mock(ChatCharacterService::class.java), + adminService = Mockito.mock(AdminChatCharacterService::class.java), + s3Uploader = Mockito.mock(S3Uploader::class.java), + originalWorkService = Mockito.mock(AdminOriginalWorkService::class.java), + applicationEventPublisher = Mockito.mock(ApplicationEventPublisher::class.java), + apiKey = "test-api-key", + apiUrl = "https://example.com", + s3Bucket = "test-bucket", + imageHost = "https://cdn.example.com" + ) + + private fun mapGender(region: String, gender: String): String { + val method = AdminChatCharacterController::class.java.getDeclaredMethod( + "mapGenderForExternalApi", + String::class.java, + String::class.java + ) + method.isAccessible = true + + return method.invoke(controller, region, gender) as String + } + + @Test + fun shouldMapFemaleToJapaneseWhenRegionIsJp() { + val mappedGender = mapGender(region = "JP", gender = "여성") + + assertEquals("女性", mappedGender) + } + + @Test + fun shouldMapMaleToJapaneseWhenRegionIsJp() { + val mappedGender = mapGender(region = "JP", gender = "남성") + + assertEquals("男性", mappedGender) + } + + @Test + fun shouldMapOtherToJapaneseWhenRegionIsJp() { + val mappedGender = mapGender(region = "JP", gender = "기타") + + assertEquals("その他", mappedGender) + } + + @Test + fun shouldKeepGenderWhenRegionIsNotJp() { + val mappedGender = mapGender(region = "KR", gender = "여성") + + assertEquals("여성", mappedGender) + } +} -- 2.49.1