From 9b0d1b43d5493cf0d48119ad2502d7290839df20 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 9 Jan 2026 11:51:42 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BA=94=20=EC=82=AC=EC=9A=A9=20=EC=8B=9C=20?= =?UTF-8?q?=EA=B5=AD=EA=B0=80=20=EC=BD=94=EB=93=9C=20=EA=B8=B0=EB=A1=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CloudFront-Viewer-Country 헤더를 통해 국가 코드를 수집하고 캔 사용 내역(UseCan) 저장 시 함께 기록하도록 수정 요청별 국가 정보 관리를 위한 컨텍스트와 인터셉터를 구현 --- .../sodalive/can/payment/CanPaymentService.kt | 13 ++++++++---- .../co/vividnext/sodalive/can/use/UseCan.kt | 4 +++- .../sodalive/common/CountryContext.kt | 15 +++++++++++++ .../sodalive/common/CountryInterceptor.kt | 21 +++++++++++++++++++ .../vividnext/sodalive/configs/WebConfig.kt | 5 ++++- 5 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/common/CountryContext.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/common/CountryInterceptor.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt index ffc8101b..8a93aaa7 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt @@ -14,6 +14,7 @@ import kr.co.vividnext.sodalive.can.use.UseCanCalculateRepository import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus import kr.co.vividnext.sodalive.can.use.UseCanRepository import kr.co.vividnext.sodalive.chat.character.image.CharacterImage +import kr.co.vividnext.sodalive.common.CountryContext import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.AudioContent import kr.co.vividnext.sodalive.content.order.Order @@ -35,7 +36,8 @@ class CanPaymentService( private val useCanRepository: UseCanRepository, private val useCanCalculateRepository: UseCanCalculateRepository, private val messageSource: SodaMessageSource, - private val langContext: LangContext + private val langContext: LangContext, + private val countryContext: CountryContext ) { @Transactional fun spendCan( @@ -76,7 +78,8 @@ class CanPaymentService( canUsage = canUsage, can = useChargeCan?.total ?: 0, rewardCan = useRewardCan.total, - isSecret = isSecret + isSecret = isSecret, + countryCode = countryContext.countryCode ) var recipientId: Long? = null @@ -378,7 +381,8 @@ class CanPaymentService( canUsage = CanUsage.CHARACTER_IMAGE_PURCHASE, can = useChargeCan?.total ?: 0, rewardCan = useRewardCan.total, - isSecret = false + isSecret = false, + countryCode = countryContext.countryCode ) useCan.member = member useCan.characterImage = image @@ -424,7 +428,8 @@ class CanPaymentService( canUsage = CanUsage.CHAT_MESSAGE_PURCHASE, can = useChargeCan?.total ?: 0, rewardCan = useRewardCan.total, - isSecret = false + isSecret = false, + countryCode = countryContext.countryCode ) useCan.member = member useCan.chatMessage = message diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt index dfb0b2ba..f2af2e2b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt @@ -34,7 +34,9 @@ data class UseCan( // 채팅 연동을 위한 식별자 (옵션) var chatRoomId: Long? = null, - var characterId: Long? = null + var characterId: Long? = null, + + var countryCode: String? = null ) : BaseEntity() { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/CountryContext.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/CountryContext.kt new file mode 100644 index 00000000..5c71f37a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/common/CountryContext.kt @@ -0,0 +1,15 @@ +package kr.co.vividnext.sodalive.common + +import org.springframework.stereotype.Component +import org.springframework.web.context.annotation.RequestScope + +@Component +@RequestScope +class CountryContext { + var countryCode: String? = null + internal set + + fun setCountryCode(code: String?) { + this.countryCode = code + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/CountryInterceptor.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/CountryInterceptor.kt new file mode 100644 index 00000000..9c1bf24d --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/common/CountryInterceptor.kt @@ -0,0 +1,21 @@ +package kr.co.vividnext.sodalive.common + +import org.springframework.stereotype.Component +import org.springframework.web.servlet.HandlerInterceptor +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +@Component +class CountryInterceptor( + private val countryContext: CountryContext +) : HandlerInterceptor { + override fun preHandle( + request: HttpServletRequest, + response: HttpServletResponse, + handler: Any + ): Boolean { + val countryCode = request.getHeader("CloudFront-Viewer-Country") + countryContext.setCountryCode(countryCode) + return true + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/configs/WebConfig.kt b/src/main/kotlin/kr/co/vividnext/sodalive/configs/WebConfig.kt index 6ef72f95..fe0b9c9e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/configs/WebConfig.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/configs/WebConfig.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.configs +import kr.co.vividnext.sodalive.common.CountryInterceptor import kr.co.vividnext.sodalive.i18n.LangInterceptor import org.springframework.context.annotation.Configuration import org.springframework.web.servlet.config.annotation.CorsRegistry @@ -8,10 +9,12 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @Configuration class WebConfig( - private val langInterceptor: LangInterceptor + private val langInterceptor: LangInterceptor, + private val countryInterceptor: CountryInterceptor ) : WebMvcConfigurer { override fun addInterceptors(registry: InterceptorRegistry) { registry.addInterceptor(langInterceptor).addPathPatterns("/**") + registry.addInterceptor(countryInterceptor).addPathPatterns("/**") } override fun addCorsMappings(registry: CorsRegistry) {