Skip to content

Commit 11eda1c

Browse files
committed
feat(be) TourResponse Dto 구조 변경에 따른 Client, Service 리팩토링
1 parent 9d73f85 commit 11eda1c

File tree

3 files changed

+42
-36
lines changed

3 files changed

+42
-36
lines changed

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

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package com.back.koreaTravelGuide.domain.ai.tour.client
22

3-
import com.back.koreaTravelGuide.domain.ai.tour.dto.InternalData
3+
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourItem
44
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourResponse
5+
import com.back.koreaTravelGuide.domain.ai.tour.dto.TourSearchParams
56
import com.fasterxml.jackson.databind.ObjectMapper
67
import org.slf4j.LoggerFactory
78
import org.springframework.beans.factory.annotation.Value
@@ -22,7 +23,7 @@ class TourApiClient(
2223
private val logger = LoggerFactory.getLogger(TourApiClient::class.java)
2324

2425
// 요청 URL 구성
25-
private fun buildUrl(params: InternalData): URI =
26+
private fun buildUrl(params: TourSearchParams): URI =
2627
UriComponentsBuilder.fromUri(URI.create(apiUrl))
2728
.path("/areaBasedList2")
2829
.queryParam("serviceKey", serviceKey)
@@ -39,7 +40,7 @@ class TourApiClient(
3940
.toUri()
4041

4142
// 지역 기반 관광 정보 조회 (areaBasedList2)
42-
fun fetchTourInfo(params: InternalData): List<TourResponse> {
43+
fun fetchTourInfo(params: TourSearchParams): TourResponse {
4344
logger.info("지역 기반 관광 정보 조회 시작")
4445

4546
val url = buildUrl(params)
@@ -51,14 +52,17 @@ class TourApiClient(
5152
* takeUnless { it.isNullOrBlank() }: 공백 응답을 걸러냄
5253
* ?.let { parseItems(it) } ?: emptyList(): 유효한 응답은 파싱, 아니면 빈 리스트 반환
5354
*/
54-
return runCatching { restTemplate.getForObject(url, String::class.java) }
55-
.onFailure { logger.error("관광 정보 조회 실패", it) }
56-
.getOrNull()
57-
.takeUnless { it.isNullOrBlank() }
58-
?.let { parseItems(it) } ?: emptyList()
55+
val response =
56+
runCatching { restTemplate.getForObject(url, String::class.java) }
57+
.onFailure { logger.error("관광 정보 조회 실패", it) }
58+
.getOrNull()
59+
.takeUnless { it.isNullOrBlank() }
60+
?.let { parseItems(it) }
61+
62+
return response ?: TourResponse(items = emptyList())
5963
}
6064

61-
private fun parseItems(json: String): List<TourResponse> {
65+
private fun parseItems(json: String): TourResponse {
6266
val root = objectMapper.readTree(json)
6367

6468
// header.resultCode 값 추출위한 노스 탐색 과정
@@ -72,7 +76,7 @@ class TourApiClient(
7276
// resultCode가 "0000"이 아닌 경우 체크
7377
if (resultCode != "0000") {
7478
logger.warn("관광 정보 API resultCode={}", resultCode)
75-
return emptyList()
79+
return TourResponse(items = emptyList())
7680
}
7781

7882
// path("키") 형태로 노드를 탐색, 응답 Json 형태의 순서에 따라 순차적으로 내려감
@@ -84,27 +88,30 @@ class TourApiClient(
8488
.path("item")
8589

8690
// 탐색 결과가 비어 있는 경우
87-
if (!itemsNode.isArray || itemsNode.isEmpty) return emptyList()
91+
if (!itemsNode.isArray || itemsNode.isEmpty) return TourResponse(items = emptyList())
8892

89-
// itemsNode가 배열이므로 map으로 각 노드를 TourResponse로 변환
90-
return itemsNode.map { node ->
91-
TourResponse(
92-
contentId = node.path("contentid").asText(),
93-
contentTypeId = node.path("contenttypeid").asText(),
94-
createdTime = node.path("createdtime").asText(),
95-
modifiedTime = node.path("modifiedtime").asText(),
96-
title = node.path("title").asText(),
97-
addr1 = node.path("addr1").takeIf { it.isTextual }?.asText(),
98-
areaCode = node.path("areacode").takeIf { it.isTextual }?.asText(),
99-
firstimage = node.path("firstimage").takeIf { it.isTextual }?.asText(),
100-
firstimage2 = node.path("firstimage2").takeIf { it.isTextual }?.asText(),
101-
mapX = node.path("mapx").takeIf { it.isTextual }?.asText(),
102-
mapY = node.path("mapy").takeIf { it.isTextual }?.asText(),
103-
mlevel = node.path("mlevel").takeIf { it.isTextual }?.asText(),
104-
sigunguCode = node.path("sigungucode").takeIf { it.isTextual }?.asText(),
105-
lDongRegnCd = node.path("lDongRegnCd").takeIf { it.isTextual }?.asText(),
106-
lDongSignguCd = node.path("lDongSignguCd").takeIf { it.isTextual }?.asText(),
107-
)
108-
}
93+
// itemsNode가 배열이므로 map으로 각 노드를 TourItem으로 변환 후 컨테이너로 감싼다.
94+
val items =
95+
itemsNode.map { node ->
96+
TourItem(
97+
contentId = node.path("contentid").asText(),
98+
contentTypeId = node.path("contenttypeid").asText(),
99+
createdTime = node.path("createdtime").asText(),
100+
modifiedTime = node.path("modifiedtime").asText(),
101+
title = node.path("title").asText(),
102+
addr1 = node.path("addr1").takeIf { it.isTextual }?.asText(),
103+
areaCode = node.path("areacode").takeIf { it.isTextual }?.asText(),
104+
firstimage = node.path("firstimage").takeIf { it.isTextual }?.asText(),
105+
firstimage2 = node.path("firstimage2").takeIf { it.isTextual }?.asText(),
106+
mapX = node.path("mapx").takeIf { it.isTextual }?.asText(),
107+
mapY = node.path("mapy").takeIf { it.isTextual }?.asText(),
108+
mlevel = node.path("mlevel").takeIf { it.isTextual }?.asText(),
109+
sigunguCode = node.path("sigungucode").takeIf { it.isTextual }?.asText(),
110+
lDongRegnCd = node.path("lDongRegnCd").takeIf { it.isTextual }?.asText(),
111+
lDongSignguCd = node.path("lDongSignguCd").takeIf { it.isTextual }?.asText(),
112+
)
113+
}
114+
115+
return TourResponse(items = items)
109116
}
110117
}

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/dto/TourResponse.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package com.back.koreaTravelGuide.domain.ai.tour.dto
66
* API 매뉴얼에서 필수인 값은 NonNull로 지정.
77
*/
88

9-
// 관광 정보 응답
109
data class TourResponse(
1110
val items: List<TourItem>,
1211
)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ class TourService(
2020
contentTypeId: String? = null,
2121
areaCode: String? = null,
2222
sigunguCode: String? = null,
23-
): List<TourResponse> {
24-
// InternalData 객체 생성, null 또는 비정상 값은 기본값으로 대체
23+
): TourResponse {
24+
// null 또는 비정상 값은 기본값으로 대체
2525
val request =
2626
TourSearchParams(
2727
numOfRows = numOfRows?.takeIf { it > 0 } ?: TourSearchParams.DEFAULT_ROWS,
@@ -35,15 +35,15 @@ class TourService(
3535
val tours = tourApiClient.fetchTourInfo(request)
3636

3737
// 관광 정보 결과 로깅
38-
if (tours.isEmpty()) {
38+
if (tours.items.isEmpty()) {
3939
logger.info(
4040
"관광 정보 없음: params={} / {} {}",
4141
request.areaCode,
4242
request.sigunguCode,
4343
request.contentTypeId,
4444
)
4545
} else {
46-
logger.info("관광 정보 {}건 조회 성공", tours.size)
46+
logger.info("관광 정보 {}건 조회 성공", tours.items.size)
4747
}
4848
return tours
4949
}

0 commit comments

Comments
 (0)