Skip to content

Commit

Permalink
Merge pull request #90 from le2sky/fix/89
Browse files Browse the repository at this point in the history
[문제] 카카오 주소 검색 API 연동 문제(issue#89)
  • Loading branch information
le2sky authored Oct 13, 2023
2 parents ed00405 + 86f6097 commit 99f7c95
Show file tree
Hide file tree
Showing 6 changed files with 414 additions and 89 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ktlintVersion=11.0.0
springBootVersion=2.7.11
springDependencyManagementVersion=1.0.15.RELEASE
# project
applicationVersion=0.5.1
applicationVersion=0.6.0
projectGroup=com.mealkitary
# test
kotestVersion=4.4.3
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,79 @@
package com.mealkitary.address

import com.mealkitary.address.payload.Document
import com.mealkitary.address.payload.Meta
import com.mealkitary.address.payload.RoadAddress
import com.mealkitary.common.model.Address
import com.mealkitary.common.model.Coordinates
import com.mealkitary.shop.domain.shop.ShopAddress
import com.mealkitary.shop.domain.shop.factory.AddressResolver
import org.springframework.context.annotation.Primary
import org.springframework.stereotype.Component

private const val INVALID_ROAD_ADDR_MESSAGE = "올바른 도로명 주소를 입력해주세요."
private const val INVALID_COORDINATE_FORMAT = "유효하지 않은 좌표 범위입니다."
private const val BUILDING_NO_DELIMITER = "-"
private const val VALID_ADDR_TYPE = "ROAD_ADDR"

@Primary
@Component
class KakaoApiAddressResolver(
private val kakaoApiWebClient: KakaoApiWebClient
) : AddressResolver {

override fun resolve(fullAddress: String): ShopAddress {
val kakaoApiAddressResponse = kakaoApiWebClient.requestAddress(fullAddress)
val (documents, meta) = kakaoApiWebClient.requestAddress(fullAddress, "https://dapi.kakao.com")
checkHasDocument(meta, documents)
val document = findFirstMatchedDocument(documents)

val (x, y, address, roadAddress) = kakaoApiAddressResponse.document
return ShopAddress.of(resolveCityCode(document), resolveCoordinates(document), resolveAddress(document))
}

val (longitude, latitude) = listOf(x, y).map {
it.toDoubleOrNull() ?: throw IllegalArgumentException("유효하지 않은 좌표 범위입니다.")
private fun checkHasDocument(meta: Meta, documents: List<Document>) {
if (meta.total_count < 0 || documents.isEmpty()) {
throw IllegalArgumentException(INVALID_ROAD_ADDR_MESSAGE)
}
}

private fun findFirstMatchedDocument(documents: List<Document>): Document {
val firstMatchedDocument = documents[0]
checkIsValidAddressType(firstMatchedDocument)

return firstMatchedDocument
}

private fun checkIsValidAddressType(addressDocument: Document) {
if (addressDocument.address_type != VALID_ADDR_TYPE) {
throw IllegalArgumentException(INVALID_ROAD_ADDR_MESSAGE)
}
}

private fun resolveCityCode(document: Document): String {
requireNotNull(document.address) { INVALID_ROAD_ADDR_MESSAGE }

return document.address.h_code
}

private fun resolveCoordinates(loadAddressDocument: Document): Coordinates {
val (longitude, latitude) = listOf(loadAddressDocument.x, loadAddressDocument.y).map {
it.toDoubleOrNull() ?: throw IllegalArgumentException(INVALID_COORDINATE_FORMAT)
}

return Coordinates.of(longitude, latitude)
}

private fun resolveAddress(document: Document): Address {
val roadAddress: RoadAddress = requireNotNull(document.road_address) { INVALID_ROAD_ADDR_MESSAGE }
val roadName =
"${roadAddress.road_name} ${roadAddress.main_building_no}-${roadAddress.sub_building_no}".trim()

return ShopAddress.of(
roadAddress.h_code,
Coordinates.of(
longitude,
latitude
),
Address.of(
address.region_1depth_name,
address.region_2depth_name,
address.region_3depth_name,
roadAddress.road_name
)
return Address.of(
roadAddress.region_1depth_name,
roadAddress.region_2depth_name,
roadAddress.region_3depth_name,
if (roadName.endsWith(BUILDING_NO_DELIMITER)) {
roadName.replace(BUILDING_NO_DELIMITER, "")
} else roadName
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import com.mealkitary.address.payload.KakaoApiAddressResponse
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.client.WebClient

private const val KAKAO_API_BASE_URL = "/v2/local/search/address"
private const val FORMAT = "json"
import org.springframework.web.util.DefaultUriBuilderFactory

@Component
class KakaoApiWebClient(
Expand All @@ -15,13 +13,13 @@ class KakaoApiWebClient(
private val serviceKey: String,
) {

fun requestAddress(query: String): KakaoApiAddressResponse {
val kakaoApiAddressResponse = webClient.get()
.uri { uriBuilder ->
uriBuilder.path("$KAKAO_API_BASE_URL.$FORMAT")
.queryParam("query", query)
.build()
}
fun requestAddress(query: String, baseUrl: String): KakaoApiAddressResponse {
val kakaoApiAddressResponse = webClient
.mutate()
.uriBuilderFactory(DefaultUriBuilderFactory())
.build()
.get()
.uri("$baseUrl/v2/local/search/address.json?query=$query")
.header("Authorization", "KakaoAK $serviceKey")
.retrieve()
.bodyToMono(KakaoApiAddressResponse::class.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,52 @@
package com.mealkitary.address.payload

data class KakaoApiAddressResponse(
val document: Document
) {
data class Document(
val x: String,
val y: String,
val address: Address,
val road_address: RoadAddress
)
val documents: List<Document>,
val meta: Meta
)

data class Address(
val region_1depth_name: String,
val region_2depth_name: String,
val region_3depth_name: String,
)
data class Meta(
val is_end: Boolean,
val pageable_count: Int,
val total_count: Int
)

data class RoadAddress(
val road_name: String,
val h_code: String
)
}
data class Document(
val address_name: String,
val address_type: String,
val address: Address?,
val road_address: RoadAddress?,
val x: String,
val y: String
)

class Address(
val address_name: String,
val b_code: String,
val h_code: String,
val main_address_no: String,
val mountain_yn: String,
val region_1depth_name: String,
val region_2depth_name: String,
val region_3depth_h_name: String,
val region_3depth_name: String,
val sub_address_no: String,
val x: String,
val y: String

)

class RoadAddress(
val address_name: String,
val building_name: String,
val main_building_no: String,
val region_1depth_name: String,
val region_2depth_name: String,
val region_3depth_name: String,
val road_name: String,
val sub_building_no: String,
val underground_yn: String,
val x: String,
val y: String,
val zone_no: String
)
Loading

0 comments on commit 99f7c95

Please sign in to comment.