카테고리 목록 조회에 언어별 번역 적용

LangContext에 따라 카테고리명을 번역해 반환한다. 번역본이 없으면

Papago API로 번역 후 CategoryTranslation에 저장하고 즉시 결과를

반환한다. 공개 API의 getCategoryList 응답이 요청 로케일을 반영한다.
This commit is contained in:
2025-12-19 02:32:20 +09:00
parent 68cfa201eb
commit 31242a1f76
2 changed files with 82 additions and 2 deletions

View File

@@ -4,8 +4,11 @@ import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.LanguageDetectEvent
import kr.co.vividnext.sodalive.content.LanguageDetectTargetType
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationEvent
import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationTargetType
import kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationService
import kr.co.vividnext.sodalive.i18n.translation.TranslateRequest
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
import org.springframework.context.ApplicationEventPublisher
@@ -18,8 +21,11 @@ class CategoryService(
private val contentRepository: AudioContentRepository,
private val blockMemberRepository: BlockMemberRepository,
private val categoryContentRepository: CategoryContentRepository,
private val categoryTranslationRepository: CategoryTranslationRepository,
private val applicationEventPublisher: ApplicationEventPublisher
private val langContext: LangContext,
private val applicationEventPublisher: ApplicationEventPublisher,
private val translationService: PapagoTranslationService
) {
@Transactional
fun createCategory(request: CreateCategoryRequest, member: Member) {
@@ -119,11 +125,83 @@ class CategoryService(
}
}
@Transactional
fun getCategoryList(creatorId: Long, memberId: Long): List<GetCategoryListResponse> {
val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = creatorId)
if (isBlocked) throw SodaException("잘못된 접근입니다.")
return repository.findByCreatorId(creatorId = creatorId)
// 기본 카테고리 목록 조회 (원본 언어 기준)
val baseList = repository.findByCreatorId(creatorId = creatorId)
if (baseList.isEmpty()) return baseList
val locale = langContext.lang.code
// 원본 엔티티를 조회하여 languageCode 파악
val categoryIds = baseList.map { it.categoryId }
val entities = repository.findAllById(categoryIds)
val entityMap = entities.associateBy { it.id!! }
// 요청 로케일로 이미 저장된 번역 일괄 조회
val translations = categoryTranslationRepository
.findByCategoryIdInAndLocale(categoryIds, locale)
.associateBy { it.categoryId }
// 각 항목에 대해 번역 적용. 없으면 Papago로 번역 저장 후 적용
val result = mutableListOf<GetCategoryListResponse>()
for (item in baseList) {
val entity = entityMap[item.categoryId]
if (entity == null) {
result.add(item)
continue
}
val sourceLang = entity.languageCode
if (!sourceLang.isNullOrBlank() && sourceLang != locale) {
val existing = translations[item.categoryId]
if (existing != null && !existing.category.isNullOrBlank()) {
result.add(GetCategoryListResponse(categoryId = item.categoryId, category = existing.category))
continue
}
// 번역본이 없으면 Papago 번역 후 저장
val texts = listOf(entity.title)
val response = translationService.translate(
request = TranslateRequest(
texts = texts,
sourceLanguage = sourceLang,
targetLanguage = locale
)
)
val translatedTexts = response.translatedText
if (translatedTexts.size == texts.size) {
val translatedCategory = translatedTexts[0]
val existingOne = categoryTranslationRepository
.findByCategoryIdAndLocale(entity.id!!, locale)
if (existingOne == null) {
categoryTranslationRepository.save(
CategoryTranslation(
categoryId = entity.id!!,
locale = locale,
category = translatedCategory
)
)
} else {
existingOne.category = translatedCategory
categoryTranslationRepository.save(existingOne)
}
result.add(GetCategoryListResponse(categoryId = item.categoryId, category = translatedCategory))
continue
}
}
// 번역이 필요 없거나 실패한 경우 원본 사용
result.add(item)
}
return result
}
private fun validateTitle(title: String) {

View File

@@ -4,4 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository
interface CategoryTranslationRepository : JpaRepository<CategoryTranslation, Long> {
fun findByCategoryIdAndLocale(categoryId: Long, locale: String): CategoryTranslation?
fun findByCategoryIdInAndLocale(categoryIds: Collection<Long>, locale: String): List<CategoryTranslation>
}