Skip to content

Commit e100ad3

Browse files
committed
refactor(be) : language.yml을 추가해 언어 문자열을 BuildConfig로 생성, TourTool에서 사용할 수 있도록 Client와 Service 수정
1 parent 3965484 commit e100ad3

File tree

11 files changed

+57
-93
lines changed

11 files changed

+57
-93
lines changed

build.gradle.kts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dependencies {
3434
implementation("org.springframework.boot:spring-boot-starter-web")
3535
implementation("org.springframework.boot:spring-boot-starter-webflux")
3636
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
37+
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml")
3738
implementation("org.jetbrains.kotlin:kotlin-reflect")
3839

3940
// jwt
@@ -140,6 +141,17 @@ buildConfig {
140141
"${parts[0].trim()}: ${parts[1].trim().replace("\"", "")}"
141142
}
142143

144+
val languageCodesDescription =
145+
file("src/main/resources/language.yml")
146+
.readText()
147+
.substringAfter("codes:")
148+
.lines()
149+
.filter { it.contains(":") }
150+
.joinToString(", ") { line ->
151+
val parts = line.split(":")
152+
"${parts[0].trim()}: ${parts[1].trim().replace("\"", "")}"
153+
}
154+
143155
val regionCodes =
144156
file("src/main/resources/region-codes.yml")
145157
.readText()
@@ -165,6 +177,7 @@ buildConfig {
165177

166178
buildConfigField("String", "AREA_CODES_DESCRIPTION", "\"\"\"$areaCodes\"\"\"")
167179
buildConfigField("String", "CONTENT_TYPE_CODES_DESCRIPTION", "\"\"\"$contentTypeCodes\"\"\"")
180+
buildConfigField("String", "LANGUAGE_CODES_DESCRIPTION", "\"\"\"$languageCodesDescription\"\"\"")
168181
buildConfigField("String", "REGION_CODES_DESCRIPTION", "\"\"\"$regionCodes\"\"\"")
169182
buildConfigField("String", "KOREA_TRAVEL_GUIDE_SYSTEM", "\"\"\"$systemPrompt\"\"\"")
170183
buildConfigField("String", "AI_ERROR_FALLBACK", "\"\"\"$errorPrompt\"\"\"")

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/client/TourApiClient.kt

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@ class TourApiClient(
2727
// 지역 기반 관광 정보 조회 (areaBasedList2)
2828
fun fetchTourInfo(
2929
params: TourParams,
30-
language: TourLanguage = TourLanguage.KOREAN,
30+
serviceSegment: String,
3131
): TourResponse {
3232
val url =
33-
buildTourUri(language, "areaBasedList2") {
33+
buildTourUri(serviceSegment, "areaBasedList2") {
3434
queryParam("contentTypeId", params.contentTypeId)
3535
queryParam("areaCode", params.areaCode)
3636
queryParam("sigunguCode", params.sigunguCode)
3737
}
3838

3939
val body =
4040
runCatching { restTemplate.getForObject(url, String::class.java) }
41-
.onFailure { log.error("관광 정보 조회 실패", it) }
41+
.onFailure { log.error("관광 정보 조회 실패 - serviceSegment={}", serviceSegment, it) }
4242
.getOrNull()
4343

4444
return body
@@ -51,10 +51,10 @@ class TourApiClient(
5151
fun fetchLocationBasedTours(
5252
tourParams: TourParams,
5353
locationParams: TourLocationBasedParams,
54-
language: TourLanguage = TourLanguage.KOREAN,
54+
serviceSegment: String,
5555
): TourResponse {
5656
val url =
57-
buildTourUri(language, "locationBasedList2") {
57+
buildTourUri(serviceSegment, "locationBasedList2") {
5858
queryParam("mapX", locationParams.mapX)
5959
queryParam("mapY", locationParams.mapY)
6060
queryParam("radius", locationParams.radius)
@@ -65,7 +65,7 @@ class TourApiClient(
6565

6666
val body =
6767
runCatching { restTemplate.getForObject(url, String::class.java) }
68-
.onFailure { log.error("위치기반 관광 정보 조회 실패", it) }
68+
.onFailure { log.error("위치기반 관광 정보 조회 실패 - serviceSegment={}", serviceSegment, it) }
6969
.getOrNull()
7070

7171
return body
@@ -77,16 +77,16 @@ class TourApiClient(
7777
// 공통정보 조회 (detailCommon2)
7878
fun fetchTourDetail(
7979
params: TourDetailParams,
80-
language: TourLanguage = TourLanguage.KOREAN,
80+
serviceSegment: String,
8181
): TourDetailResponse {
8282
val url =
83-
buildTourUri(language, "detailCommon2") {
83+
buildTourUri(serviceSegment, "detailCommon2") {
8484
queryParam("contentId", params.contentId)
8585
}
8686

8787
val body =
8888
runCatching { restTemplate.getForObject(url, String::class.java) }
89-
.onFailure { log.error("공통정보 조회 실패", it) }
89+
.onFailure { log.error("공통정보 조회 실패 - serviceSegment={}", serviceSegment, it) }
9090
.getOrNull()
9191

9292
return body
@@ -95,10 +95,6 @@ class TourApiClient(
9595
?: TourDetailResponse(items = emptyList())
9696
}
9797

98-
/*
99-
* areaBasedList2/locationBasedList2 JSON 응답에서 `response.body.items.item` 배열만 추출
100-
* Kotlin 도메인 모델(TourItem) 목록으로 변환한다. item 노드가 비어 있으면 빈 목록을 반환한다.
101-
*/
10298
private fun parseItems(json: String): TourResponse {
10399
val itemNodes = extractItemNodes(json, "관광 정보")
104100
if (itemNodes.isEmpty()) return TourResponse(items = emptyList())
@@ -128,10 +124,6 @@ class TourApiClient(
128124
return TourResponse(items = items)
129125
}
130126

131-
/*
132-
* detailCommon2 JSON 응답을 파싱해 `TourDetailItem` 목록으로 변환한다.
133-
* 항목이 없으면 빈 응답을 돌려준다. 필드 구성이 다르기 때문에 detailCommon2 은 별도 파서로 분리한다.
134-
*/
135127
private fun parseDetailItems(json: String): TourDetailResponse {
136128
val itemNodes = extractItemNodes(json, "공통정보")
137129
if (itemNodes.isEmpty()) return TourDetailResponse(items = emptyList())
@@ -154,10 +146,6 @@ class TourApiClient(
154146
return TourDetailResponse(items = items)
155147
}
156148

157-
/*
158-
* 공통 응답 구조에서 resultCode가 성공(0000)인지 검사한 뒤 `item` 배열 노드를 리스트로 반환한다.
159-
* 실패 코드거나 배열이 비어 있으면 빈 리스트를 돌려준다.
160-
*/
161149
private fun extractItemNodes(
162150
json: String,
163151
apiName: String,
@@ -187,18 +175,13 @@ class TourApiClient(
187175
return itemsNode.map { it }
188176
}
189177

190-
/**
191-
* 공통 URL 빌더
192-
* - 설정된 기본 URL을 바탕으로 언어 세그먼트(e.g. KorService2, EngService2)와 공통 쿼리 파라미터
193-
* - (`serviceKey`, `MobileOS`, `MobileApp`, `_type`)를 자동으로 붙여 관광 API 호출용 URI를 만든다.
194-
*/
195178
private fun buildTourUri(
196-
language: TourLanguage,
179+
serviceSegment: String,
197180
vararg pathSegments: String,
198181
customize: UriComponentsBuilder.() -> Unit = {},
199182
): URI =
200183
UriComponentsBuilder.fromUri(URI.create(apiUrl))
201-
.pathSegment(language.serviceSegment, *pathSegments)
184+
.pathSegment(serviceSegment, *pathSegments)
202185
.apply {
203186
queryParam("serviceKey", serviceKey)
204187
queryParam("MobileOS", "WEB")

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/client/TourLanguage.kt

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/service/TourService.kt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.service
22

3-
import com.back.koreaTravelGuide.domain.ai.tour.client.TourLanguage
43
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourDetailParams
54
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourDetailResponse
65
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourLocationBasedParams
@@ -28,38 +27,42 @@ class TourService(
2827

2928
/**
3029
* 지역 기반 관광 정보를 조회한다.
31-
* 언어 문자열을 enum으로 정규화해 다국어 엔드포인트에 맞춰 전달한다.
30+
* 언어 문자열을 설정으로 정규화해 다국어 엔드포인트에 맞춰 전달한다.
3231
*/
3332
fun fetchTours(
3433
tourParams: TourParams,
3534
languageCode: String? = null,
3635
): TourResponse {
37-
val language = TourLanguage.from(languageCode)
38-
return tourAreaBasedUseCase.fetchAreaBasedTours(tourParams, language)
36+
val serviceSegment = languageCode?.takeIf { it.isNotBlank() } ?: DEFAULT_LANGUAGE_SEGMENT
37+
return tourAreaBasedUseCase.fetchAreaBasedTours(tourParams, serviceSegment)
3938
}
4039

4140
/**
4241
* 위치 기반 관광 정보를 조회한다.
43-
* 전달받은 언어 코드를 enum으로 치환해 API 클라이언트에 전달한다.
42+
* 전달받은 언어 값을 설정 기반 서비스 세그먼트로 치환해 API 클라이언트를 호출한다.
4443
*/
4544
fun fetchLocationBasedTours(
4645
tourParams: TourParams,
4746
locationParams: TourLocationBasedParams,
4847
languageCode: String? = null,
4948
): TourResponse {
50-
val language = TourLanguage.from(languageCode)
51-
return tourLocationBasedUseCase.fetchLocationBasedTours(tourParams, locationParams, language)
49+
val serviceSegment = languageCode?.takeIf { it.isNotBlank() } ?: DEFAULT_LANGUAGE_SEGMENT
50+
return tourLocationBasedUseCase.fetchLocationBasedTours(tourParams, locationParams, serviceSegment)
5251
}
5352

5453
/**
5554
* 관광지 상세 정보를 조회한다.
56-
* 언어 코드를 정규화해 상세 API 호출 시 사용한다.
55+
* 언어 값을 정규화해 상세 API 호출 시 사용한다.
5756
*/
5857
fun fetchTourDetail(
5958
detailParams: TourDetailParams,
6059
languageCode: String? = null,
6160
): TourDetailResponse {
62-
val language = TourLanguage.from(languageCode)
63-
return tourDetailUseCase.fetchTourDetail(detailParams, language)
61+
val serviceSegment = languageCode?.takeIf { it.isNotBlank() } ?: DEFAULT_LANGUAGE_SEGMENT
62+
return tourDetailUseCase.fetchTourDetail(detailParams, serviceSegment)
63+
}
64+
65+
companion object {
66+
private const val DEFAULT_LANGUAGE_SEGMENT = "KorService2"
6467
}
6568
}

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/service/core/TourAreaBasedServiceCore.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.service.core
22

33
import com.back.koreaTravelGuide.domain.ai.tour.client.TourApiClient
4-
import com.back.koreaTravelGuide.domain.ai.tour.client.TourLanguage
54
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourItem
65
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourParams
76
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourResponse
@@ -16,12 +15,12 @@ class TourAreaBasedServiceCore(
1615
@Cacheable(
1716
"tourAreaBased",
1817
key =
19-
"#tourParams.contentTypeId + '_' + #tourParams.areaCode + '_' + #tourParams.sigunguCode + '_' + #language.serviceSegment",
18+
"#tourParams.contentTypeId + '_' + #tourParams.areaCode + '_' + #tourParams.sigunguCode + '_' + #serviceSegment",
2019
unless = "#result == null",
2120
)
2221
override fun fetchAreaBasedTours(
2322
tourParams: TourParams,
24-
language: TourLanguage,
23+
serviceSegment: String,
2524
): TourResponse {
2625
if (
2726
tourParams.contentTypeId == "12" &&
@@ -31,7 +30,7 @@ class TourAreaBasedServiceCore(
3130
return PRESET_AREA_TOUR_RESPONSE
3231
}
3332

34-
return tourApiClient.fetchTourInfo(tourParams, language)
33+
return tourApiClient.fetchTourInfo(tourParams, serviceSegment)
3534
}
3635

3736
private companion object {

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/service/core/TourDetailServiceCore.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.service.core
22

33
import com.back.koreaTravelGuide.domain.ai.tour.client.TourApiClient
4-
import com.back.koreaTravelGuide.domain.ai.tour.client.TourLanguage
54
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourDetailItem
65
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourDetailParams
76
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourDetailResponse
@@ -15,18 +14,18 @@ class TourDetailServiceCore(
1514
) : TourDetailUseCase {
1615
@Cacheable(
1716
"tourDetail",
18-
key = "#detailParams.contentId + '_' + #language.serviceSegment",
17+
key = "#detailParams.contentId + '_' + #serviceSegment",
1918
unless = "#result == null",
2019
)
2120
override fun fetchTourDetail(
2221
detailParams: TourDetailParams,
23-
language: TourLanguage,
22+
serviceSegment: String,
2423
): TourDetailResponse {
2524
if (detailParams.contentId == "127974") {
2625
return PRESET_DETAIL_RESPONSE
2726
}
2827

29-
return tourApiClient.fetchTourDetail(detailParams, language)
28+
return tourApiClient.fetchTourDetail(detailParams, serviceSegment)
3029
}
3130

3231
private companion object {

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/service/core/TourLocationBasedServiceCore.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.service.core
22

33
import com.back.koreaTravelGuide.domain.ai.tour.client.TourApiClient
4-
import com.back.koreaTravelGuide.domain.ai.tour.client.TourLanguage
54
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourItem
65
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourLocationBasedParams
76
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourParams
@@ -19,13 +18,13 @@ class TourLocationBasedServiceCore(
1918
key =
2019
"#tourParams.contentTypeId + '_' + #tourParams.areaCode + '_' + #tourParams.sigunguCode + '_' + " +
2120
"#locationParams.mapX + '_' + #locationParams.mapY + '_' + #locationParams.radius + '_' + " +
22-
"#language.serviceSegment",
21+
"#serviceSegment",
2322
unless = "#result == null",
2423
)
2524
override fun fetchLocationBasedTours(
2625
tourParams: TourParams,
2726
locationParams: TourLocationBasedParams,
28-
language: TourLanguage,
27+
serviceSegment: String,
2928
): TourResponse {
3029
if (
3130
tourParams.contentTypeId == "39" &&
@@ -38,7 +37,7 @@ class TourLocationBasedServiceCore(
3837
return PRESET_LOCATION_BASED_RESPONSE
3938
}
4039

41-
return tourApiClient.fetchLocationBasedTours(tourParams, locationParams, language)
40+
return tourApiClient.fetchLocationBasedTours(tourParams, locationParams, serviceSegment)
4241
}
4342

4443
private companion object {
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.service.usecase
22

3-
import com.back.koreaTravelGuide.domain.ai.tour.client.TourLanguage
43
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourParams
54
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourResponse
65

76
interface TourAreaBasedUseCase {
87
fun fetchAreaBasedTours(
98
tourParams: TourParams,
10-
language: TourLanguage,
9+
serviceSegment: String,
1110
): TourResponse
1211
}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.service.usecase
22

3-
import com.back.koreaTravelGuide.domain.ai.tour.client.TourLanguage
43
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourDetailParams
54
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourDetailResponse
65

76
interface TourDetailUseCase {
87
fun fetchTourDetail(
98
detailParams: TourDetailParams,
10-
language: TourLanguage,
9+
serviceSegment: String,
1110
): TourDetailResponse
1211
}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.service.usecase
22

3-
import com.back.koreaTravelGuide.domain.ai.tour.client.TourLanguage
43
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourLocationBasedParams
54
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourParams
65
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourResponse
@@ -9,6 +8,6 @@ interface TourLocationBasedUseCase {
98
fun fetchLocationBasedTours(
109
tourParams: TourParams,
1110
locationParams: TourLocationBasedParams,
12-
language: TourLanguage,
11+
serviceSegment: String,
1312
): TourResponse
1413
}

0 commit comments

Comments
 (0)