Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[기능 구현] 카카오 주소 조회 api 연동 (issue#84) #86

Merged
merged 14 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/mealkitary-main-develop-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ jobs:
./mealkitary-infrastructure/adapter-firebase-notification/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-business-registration-number-validator/simple-brn-validator/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-business-registration-number-validator/open-api-brn-validator/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-address-resolver/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-address-resolver/simple-address-resolver/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-address-resolver/kakao-api-address-resolver/build/test-results/**/*.xml

- name: Jacoco Coverage 리포트 전송
uses: codecov/codecov-action@v3
Expand All @@ -71,7 +72,8 @@ jobs:
./mealkitary-infrastructure/adapter-firebase-notification/build/reports/jacoco/test/jacocoTestReport.xml,
./mealkitary-infrastructure/adapter-business-registration-number-validator/simple-brn-validator/build/reports/jacoco/test/jacocoTestReport.xml,
./mealkitary-infrastructure/adapter-business-registration-number-validator/open-api-brn-validator/build/reports/jacoco/test/jacocoTestReport.xml,
./mealkitary-infrastructure/adapter-address-resolver/build/reports/jacoco/test/jacocoTestReport.xml
./mealkitary-infrastructure/adapter-address-resolver/simple-address-resolver/build/reports/jacoco/test/jacocoTestReport.xml,
./mealkitary-infrastructure/adapter-address-resolver/kakao-api-address-resolver/build/reports/jacoco/test/jacocoTestReport.xml
name: mealkitary-codecov
verbose: true

Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/mealkitary-test-coverage-automation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ jobs:
./mealkitary-infrastructure/adapter-firebase-notification/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-business-registration-number-validator/simple-brn-validator/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-business-registration-number-validator/open-api-brn-validator/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-address-resolver/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-address-resolver/simple-address-resolver/build/test-results/**/*.xml
./mealkitary-infrastructure/adapter-address-resolver/kakao-api-address-resolver/build/test-results/**/*.xml

- name: Jacoco Coverage 리포트 전송
uses: codecov/codecov-action@v3
Expand All @@ -59,6 +60,7 @@ jobs:
./mealkitary-infrastructure/adapter-firebase-notification/build/reports/jacoco/test/jacocoTestReport.xml,
./mealkitary-infrastructure/adapter-business-registration-number-validator/simple-brn-validator/build/reports/jacoco/test/jacocoTestReport.xml,
./mealkitary-infrastructure/adapter-business-registration-number-validator/open-api-brn-validator/build/reports/jacoco/test/jacocoTestReport.xml,
./mealkitary-infrastructure/adapter-address-resolver/build/reports/jacoco/test/jacocoTestReport.xml
./mealkitary-infrastructure/adapter-address-resolver/simple-address-resolver/build/reports/jacoco/test/jacocoTestReport.xml,
./mealkitary-infrastructure/adapter-address-resolver/kakao-api-address-resolver/build/reports/jacoco/test/jacocoTestReport.xml
name: mealkitary-codecov
verbose: true
11 changes: 10 additions & 1 deletion mealkitary-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,16 @@ dependencies {
implementation(project(":mealkitary-infrastructure:adapter-paymentgateway-tosspayments"))
implementation(project(":mealkitary-infrastructure:adapter-firebase-notification"))
implementation(project(":mealkitary-infrastructure:adapter-configuration"))
implementation(project(":mealkitary-infrastructure:adapter-address-resolver"))
implementation(
project(
":mealkitary-infrastructure:adapter-address-resolver:kakao-api-address-resolver",
)
)
implementation(
project(
":mealkitary-infrastructure:adapter-address-resolver:simple-address-resolver",
)
)
implementation(
project(
":mealkitary-infrastructure:adapter-business-registration-number-validator:open-api-brn-validator",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class GetShopServiceTest : AnnotationSpec() {
ShopAddress.of(
"1234567890",
Coordinates.of(
126.99599512792346,
35.976749396987046
"126.99599512792346",
"35.976749396987046"
),
Address.of(
"region1DepthName",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class RegisterShopServiceTest : AnnotationSpec() {
val shopSlot = slot<Shop>()
val request = RegisterShopRequest("집밥뚝딱 안양점", "123-23-12345", "경기도 안양시 동안구 벌말로 40")
val expectedShopAddress =
ShopAddress.of("1234567890", Coordinates.of(0.0, 0.0), Address.of("경기도", "안양시 동안구", "벌말로", "40"))
ShopAddress.of("1234567890", Coordinates.of("0.0", "0.0"), Address.of("경기도", "안양시 동안구", "벌말로", "40"))

val mockedShop = Shop(
ShopTitle.from(request.title),
Expand Down Expand Up @@ -67,14 +67,14 @@ class RegisterShopServiceTest : AnnotationSpec() {
fun `service unit test - 가게 이름 형식에 맞지 않으면 예외를 발생한다`() {
val request = RegisterShopRequest("invalid!#@", "123-23-12345", "경기도 안양시 동안구 벌말로 40")
val expectedShopAddress =
ShopAddress.of("1234567890", Coordinates.of(0.0, 0.0), Address.of("경기도", "안양시 동안구", "벌말로", "40"))
ShopAddress.of("1234567890", Coordinates.of("0.0", "0.0"), Address.of("경기도", "안양시 동안구", "벌말로", "40"))

every {
shopFactory.createOne(any(), any(), any())
} throws IllegalArgumentException("올바른 가게 이름 형식이 아닙니다.(한글, 영문, 공백, 숫자만 포함 가능)")
every { saveShopPort.saveOne(any()) } answers { 1L }
every { shopBusinessNumberValidator.validate(any()) } answers {}
every { addressResolver.resolveAddress("경기도 안양시 동안구 벌말로 40") } returns expectedShopAddress
every { addressResolver.resolve("경기도 안양시 동안구 벌말로 40") } returns expectedShopAddress

shouldThrow<IllegalArgumentException> {
registerShopService.register(request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class UpdateShopStatusServiceTest : AnnotationSpec() {
ShopAddress.of(
"1234567890",
Coordinates.of(
126.99599512792346,
35.976749396987046
"126.99599512792346",
"35.976749396987046"
),
Address.of(
"region1DepthName",
Expand Down Expand Up @@ -63,8 +63,8 @@ class UpdateShopStatusServiceTest : AnnotationSpec() {
ShopAddress.of(
"1234567890",
Coordinates.of(
126.99599512792346,
35.976749396987046
"126.99599512792346",
"35.976749396987046"
),
Address.of(
"region1DepthName",
Expand Down Expand Up @@ -95,8 +95,8 @@ class UpdateShopStatusServiceTest : AnnotationSpec() {
ShopAddress.of(
"1234567890",
Coordinates.of(
126.99599512792346,
35.976749396987046
"126.99599512792346",
"35.976749396987046"
),
Address.of(
"region1DepthName",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import javax.persistence.Embeddable
@Embeddable
class Coordinates(
@Column(name = "longitude", nullable = false)
val longitude: Double,
val longitude: String,
@Column(name = "latitude", nullable = false)
val latitude: Double
val latitude: String
le2sky marked this conversation as resolved.
Show resolved Hide resolved
) {

companion object {
fun of(longitude: Double, latitude: Double): Coordinates {
fun of(longitude: String, latitude: String): Coordinates {
checkIsCoordinateRange(longitude, latitude)

return Coordinates(longitude, latitude)
}

private fun checkIsCoordinateRange(longitude: Double, latitude: Double) {
if (longitude !in -180.0..180.0 || latitude !in -90.0..90.0) {
private fun checkIsCoordinateRange(longitude: String, latitude: String) {
if (longitude.toDouble() !in -180.0..180.0 || latitude.toDouble() !in -90.0..90.0) {
throw IllegalArgumentException("유효하지 않은 좌표 범위입니다.")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import com.mealkitary.shop.domain.shop.address.ShopAddress

interface AddressResolver {

fun resolveAddress(address: String): ShopAddress
fun resolve(fullAddress: String): ShopAddress
le2sky marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class ShopFactory(
private val addressResolver: AddressResolver
) {

fun createOne(title: String, brn: String, address: String): Shop {
fun createOne(title: String, brn: String, fullAddress: String): Shop {
val shopBusinessNumber = ShopBusinessNumber.from(brn)

val shopAddress: ShopAddress = addressResolver.resolveAddress(address)
val shopAddress: ShopAddress = addressResolver.resolve(fullAddress)

shopBusinessNumberValidator.validate(shopBusinessNumber)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class CoordinatesTest : AnnotationSpec() {

@Test
fun `범위를 벗어나는 좌표일 경우 예외를 발생한다`() {
val longitude = -188.023
val latitude = 999.7412
val longitude = "-188.023"
val latitude = "999.7412"

shouldThrow<IllegalArgumentException> {
Coordinates.of(longitude, latitude)
Expand All @@ -20,8 +20,8 @@ class CoordinatesTest : AnnotationSpec() {

@Test
fun `올바른 좌표를 입력했을 경우 객체를 생성한다`() {
val longitude = -150.653
val latitude = 46.492
val longitude = "-150.653"
val latitude = "46.492"

val coordinates = Coordinates.of(longitude, latitude)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ class ShopAddressTest : AnnotationSpec() {
fun `올바른 값들을 입력했을 경우 객체를 생성할 수 있다`() {
val cityCode = "1234567890"
val coordinates = Coordinates.of(
-150.653,
46.492
"-150.653",
"46.492"
)
val address = Address.of(
"region1DepthName",
Expand All @@ -39,8 +39,8 @@ class ShopAddressTest : AnnotationSpec() {
fun `지역 코드가 올바르지 않을 경우 예외를 발생한다`() {
val cityCode = "25231491723109"
val coordinates = Coordinates.of(
-150.653,
46.492
"-150.653",
"46.492"
)
val address = Address.of(
"region1DepthName",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ class ShopFactoryTest : AnnotationSpec() {
@Test
fun `실제로 유효한 사업자번호와 가게이름, 주소라면 가게를 생성한다`() {
val expectedShopAddress =
ShopAddress.of("1234567890", Coordinates.of(0.0, 0.0), Address.of("경기도", "안양시 동안구", "벌말로", "40"))
ShopAddress.of("1234567890", Coordinates.of("0.0", " 0.0"), Address.of("경기도", "안양시 동안구", "벌말로", "40"))

every { shopBusinessNumberValidator.validate(any()) } answers { }
every { addressResolver.resolveAddress("경기도 안양시 동안구 벌말로 40") } returns expectedShopAddress
every { addressResolver.resolve("경기도 안양시 동안구 벌말로 40") } returns expectedShopAddress

val shop = shopFactory.createOne("집밥뚝딱 안양점", "321-23-12345", "경기도 안양시 동안구 벌말로 40")

Expand All @@ -41,10 +41,10 @@ class ShopFactoryTest : AnnotationSpec() {
@Test
fun `가게 이름이 유효하지 않으면 예외를 발생한다`() {
val expectedShopAddress =
ShopAddress.of("1234567890", Coordinates.of(0.0, 0.0), Address.of("경기도", "안양시 동안구", "벌말로", "40"))
ShopAddress.of("1234567890", Coordinates.of("0.0", "0.0"), Address.of("경기도", "안양시 동안구", "벌말로", "40"))

every { shopBusinessNumberValidator.validate(any()) } answers { }
every { addressResolver.resolveAddress("경기도 안양시 동안구 벌말로 40") } returns expectedShopAddress
every { addressResolver.resolve("경기도 안양시 동안구 벌말로 40") } returns expectedShopAddress

shouldThrow<IllegalArgumentException> {
shopFactory.createOne("집밥뚝딱 ! 안양점", "321-23-12345", "경기도 안양시 동안구 벌말로 40")
Expand All @@ -57,8 +57,8 @@ class ShopFactoryTest : AnnotationSpec() {
val shopAddress = ShopAddress.of(
"1234567890",
Coordinates.of(
127.0,
40.0
"127.0",
"40.0"
),
Address.of(
"경기도",
Expand All @@ -69,7 +69,7 @@ class ShopFactoryTest : AnnotationSpec() {
)

every { shopBusinessNumberValidator.validate(any()) } answers { }
every { addressResolver.resolveAddress(address) } answers { shopAddress }
every { addressResolver.resolve(address) } answers { shopAddress }

shouldThrow<IllegalArgumentException> {
shopFactory.createOne("집밥뚝딱 ! 안양점", "321-23-12345", "경기도 안양시 동안구 벌말로 40")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class ShopTestData {
private var shopAddress: ShopAddress = ShopAddress.of(
"1234567890",
Coordinates.of(
126.99599512792346,
35.976749396987046
"126.99599512792346",
"35.976749396987046"
),
Address.of(
"region1DepthName",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencies {
val mockWebServerVersion: String by properties
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation(project(":mealkitary-domain"))
testImplementation("com.squareup.okhttp3:mockwebserver:$mockWebServerVersion")
testImplementation("io.projectreactor:reactor-test")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.mealkitary.address

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

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

override fun resolve(fullAddress: String): ShopAddress {
val kakaoApiAddressResponse = kakaoApiWebClient.requestAddress(fullAddress)

val x = kakaoApiAddressResponse.document.x
val y = kakaoApiAddressResponse.document.y
val roadAddress = kakaoApiAddressResponse.document.road_address
val address = kakaoApiAddressResponse.document.address
le2sky marked this conversation as resolved.
Show resolved Hide resolved

return ShopAddress.of(
roadAddress.h_code,
Coordinates.of(
x,
y
),
Address.of(
address.region_1depth_name,
address.region_2depth_name,
address.region_3depth_name,
roadAddress.road_name
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.mealkitary.address

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"

@Component
class KakaoApiWebClient(
private val webClient: WebClient,
@Value("\${kakaoapi.address.serviceKey}")
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()
}
.header("Authorization", "KakaoAK $serviceKey")
.retrieve()
.bodyToMono(KakaoApiAddressResponse::class.java)
.block()

return kakaoApiAddressResponse!!
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.mealkitary.address.payload

class KakaoApiAddressResponse(
val document: Document
) {
class Document(
val x: String,
val y: String,
val address: Address,
val road_address: RoadAddress
)

class Address(
val region_1depth_name: String,
val region_2depth_name: String,
val region_3depth_name: String,
)

class RoadAddress(
val road_name: String,
val h_code: String
)
}
Loading