HomeService fetchData 리팩토링 및 DB JOIN 기반 번역 적용

fetchData 함수에서 별도로 수행하던 번역 데이터 조회를 DB JOIN 및
COALESCE를 사용하도록 개선하여 성능을 최적화함.

- AudioContentRepository, RankingRepository 등에 locale 파라미터 추가
- DB 레벨에서 번역된 제목을 조회하도록 쿼리 수정
- HomeService에서 불필요한 getTranslatedContentList 호출 제거
This commit is contained in:
2026-02-13 10:37:06 +09:00
parent 46b0989795
commit 341f24c643
7 changed files with 102 additions and 138 deletions

View File

@@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
import kr.co.vividnext.sodalive.content.translation.QContentTranslation.contentTranslation
import kr.co.vividnext.sodalive.member.MemberRole
import kr.co.vividnext.sodalive.member.QMember.member
import kr.co.vividnext.sodalive.member.auth.QAuth.auth
@@ -83,7 +84,8 @@ class RecommendChannelQueryRepository(
fun getContentsByCreatorIdLikeDesc(
creatorId: Long,
memberId: Long?,
isAdult: Boolean
isAdult: Boolean,
locale: String? = null
): List<RecommendChannelContentItem> {
val blockMemberCondition = if (memberId != null) {
blockMember.member.id.eq(audioContent.member.id)
@@ -99,11 +101,26 @@ class RecommendChannelQueryRepository(
where = where.and(audioContent.isAdult.isFalse)
}
val titleExpression = if (locale != null) {
val translatedTitle = Expressions.stringTemplate(
"JSON_EXTRACT({0}, '$.title')",
contentTranslation.renderedPayload
)
val coalesceTitle = Expressions.stringTemplate(
"COALESCE(NULLIF({0}, ''), {1})",
translatedTitle,
audioContent.title
)
coalesceTitle
} else {
audioContent.title
}
var select = queryFactory
.select(
QRecommendChannelContentItem(
audioContent.id,
audioContent.title,
titleExpression,
audioContent.coverImage.prepend("/").prepend(imageHost),
audioContentLike.id.countDistinct(),
audioContentComment.id.countDistinct()
@@ -121,6 +138,14 @@ class RecommendChannelQueryRepository(
.and(audioContentComment.isActive.isTrue)
)
if (locale != null) {
select = select.leftJoin(contentTranslation)
.on(
contentTranslation.contentId.eq(audioContent.id)
.and(contentTranslation.locale.eq(locale))
)
}
if (memberId != null) {
where = where.and(blockMember.id.isNull)
select = select.leftJoin(blockMember).on(blockMemberCondition)

View File

@@ -1,13 +1,17 @@
package kr.co.vividnext.sodalive.query.recommend
import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.i18n.LangContext
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Service
@Transactional(readOnly = true)
class RecommendChannelQueryService(private val repository: RecommendChannelQueryRepository) {
class RecommendChannelQueryService(
private val repository: RecommendChannelQueryRepository,
private val langContext: LangContext
) {
@Cacheable(
cacheNames = ["default"],
key = "'recommendChannel:' + (#memberId ?: 'guest') + ':' + #isAdult + ':' + #contentType"
@@ -27,7 +31,8 @@ class RecommendChannelQueryService(private val repository: RecommendChannelQuery
it.contentList = repository.getContentsByCreatorIdLikeDesc(
creatorId = it.channelId,
memberId = memberId,
isAdult = isAdult
isAdult = isAdult,
locale = langContext.lang.code
)
it