개발 이모저모

카카오 주소 및 키워드 api를 활용한 주소 검색 서비스 제작

c0de_h0ng 2021. 11. 16. 22:21
728x90

 위치 서비스를 제공하는 앱이면 주소 검색 및 위치 검색 기능이 있기 마련이다. 배달의 민족, 요기요, 여기어때 등이 대표적이다. 이번에 기획팀에서 내 위치 주변의 매장만 보여줬던 기능에서 업그레이드하여 유저가 검색한 위치의 매장도 보이도록 기능을 추가해달라고 하였다. 주소 검색 서비스에서 정부 api, 카카오 api, 네이버 api 등 많은 오픈소스가 존재하였지만 이번 위치 검색은 카카오 api 기반으로 개발해 보았다.

API 선정

 통합 검색 기능의 Firebase Event Log 바탕으로 유저들의 위치에 관한 검색 케이스를 분석한 결과 크게 건물명, 주소 크게 두 가지 케이스로 검색하였다. 건물명? 주소? 그게 뭐가 달라? 라고 할 수 있지만 아래와 같이 예를 들면 검색 결과는 같아도 검색어는 다르다는 것을 느낄 수 있다. 예를 들어 A유저는 홍익대학교 정문을 검색하였고 B유저는 서울특별시 마포구 서교동라고 검색하였다. 결과는 어떨까? 결과는 같다. 여기서 차이점은 위에서 언급했듯이 A유저는 건물명을 검색한 것이고 B유저는 주소를 검색한 것이다.

  1. 건물명 검색 → 홍익대학교 정문
  2. 주소 검색 → 서울특별시 마포구 서교동

 이를 바탕으로 주소 검색과 건물명 검색 api를 제공하는 오픈소스를 분석한 결과 카카오 api가 페이지네이셔닝 구조의 response로 되어 있어서 이를 채택하여 개발하였다.

기능 설계

- 시나리오

유저가 주소 또는 키워드(건물명)를 검색하면 주소 검색 api를 호출하고 결과가 없을 경우에는 키워드 검색 api를 호출하도록 개발하였다.

- 카카오 주소 검색 Api 등록

@GET
fun searchAddress(@Url url: String, @Query("query") searchAddress: String, @Query("page") page: Int, @Query("size") size: Int): Call<KakaoAddress>
  • url
    • 주소 검색 Api 주소
    • 하드 코딩으로 하지 않고 @Url으로 하는 이유는 하드 코딩으로 하게 되면 Retrofit2에 설정한 호스트 Url이 붙어서 호출되기 때문
  • searchAddress
    • 검색할 주소
  • page
    • 호출할 페이지
  • size
    • 한 페이지당 호출할 아이템 갯수

- 카카오 주소 검색 Response

data class KakaoAddress(
    @SerializedName("documents") val addressResultList: ArrayList<AddressResultItem>,
    @SerializedName("meta") val pageData: PageData
)
data class AddressResultItem(
    @SerializedName("address_name") val addressMainName: String,
    @SerializedName("x") val longitude: Double,
    @SerializedName("y") val latitude: Double,
    @SerializedName("address") val earthAddress: AddressItem?,
    @SerializedName("road_address") val roadAddress: AddressItem?
)
data class AddressItem(@SerializedName("address_name") val addressName: String)
  • address_name
    • 대표 주소(도로명 주소 또는 지번 주소가 될 수 있음)
  • x
    • 경도
  • y
    • 위도
  • address
    • 지번 주소
  • road_address
    • 도로명 주소

- 카카오 키워드 검색 Api

@GET
fun searchKeyword(@Url url: String, @Query("query") searchKeyword: String,@Query("page") page: Int, @Query("size") size: Int): Call<KakaoKeyword>
  • url
    • 주소 검색 Api 주소
    • 하드 코딩으로 하지 않고 @Url으로 하는 이유는 하드 코딩으로 하게 되면 Retrofit2에 설정한 호스트 Url이 붙어서 호출되기 때문
  • searchKeyword
    • 검색할 주소
  • page
    • 호출할 페이지
  • size
    • 한 페이지당 호출할 아이템 개수

- Response

data class KakaoKeyword(
    @SerializedName("documents") val keywordResultList: ArrayList<KeywordResultItem>,
    @SerializedName("meta") val pageData: PageData
)
data class KeywordResultItem(
    @SerializedName("address_name") val earthAddress: String,
    @SerializedName("road_address_name") val roadAddress: String,
    @SerializedName("x") val longitude: Double,
    @SerializedName("y") val latitude: Double
)
  • address_name
    • 지번 주소
  • road_address_name
    • 도로명 주소
  • x
    • 경도
  • y
    • 위도

검색 결과 처리

- 주소 검색

private fun setSearchAddressResult() {
    viewModel.kakaoAddress.value?.let {
        if (resultAddressList.isNotEmpty()) {
						//주소 검색 결과가 있을 경우
            binding.resultAddressListAdapter?.refreshList(resultAddressList, true) ?: run { binding.resultAddressListAdapter = LocationSearchAddressListAdapter(resultAddressList, false) }
            showResultSearchAddressView(true)
        } else {
            //주소 검색 결과가 없을 경우 키워드 검색으로 호출한다.
            viewModel.keywordSearch()
        }
    } ?: run {
        viewModel.keywordSearch()
    }
}
  • 주소 검색 결과가 있을 경우
    • 주소 검색 결과 리스트를 RecyclerView로 표현
  • 주소 검색 결과가 없을 경우
    • 키워드 검색 Api 호출

- 키워드 검색

private fun setSearchKeywordResult() {
    viewModel.kakaoKeyword.value?.let {
        if (resultKeywordList.isNotEmpty()) {
            //키워드 검색 결과가 있을 경우
            binding.resultKeywordListAdapter?.refreshList(resultKeywordList, true) ?: run { binding.resultKeywordListAdapter = LocationSearchKeywordListAdapter(resultKeywordList) }
            showResultSearchAddressView(false)
        } else {
            emptySearchAddress() //예외처리(검색팁)
        }
    } ?: run {
        emptySearchAddress() //예외처리(검색팁)
    }
}
  • 키워드 검색 결과가 있을 경우
    • 키워드 검색 결과 리스트를 RecyclerView로 표현
  • 키워드 검색 결과가 없을 경우
    • Empty 케이스 UI 표현