위치 서비스를 제공하는 앱이면 주소 검색 및 위치 검색 기능이 있기 마련이다. 배달의 민족, 요기요, 여기어때 등이 대표적이다. 이번에 기획팀에서 내 위치 주변의 매장만 보여줬던 기능에서 업그레이드하여 유저가 검색한 위치의 매장도 보이도록 기능을 추가해달라고 하였다. 주소 검색 서비스에서 정부 api, 카카오 api, 네이버 api 등 많은 오픈소스가 존재하였지만 이번 위치 검색은 카카오 api 기반으로 개발해 보았다.
API 선정
통합 검색 기능의 Firebase Event Log 바탕으로 유저들의 위치에 관한 검색 케이스를 분석한 결과 크게 건물명, 주소 크게 두 가지 케이스로 검색하였다. 건물명? 주소? 그게 뭐가 달라? 라고 할 수 있지만 아래와 같이 예를 들면 검색 결과는 같아도 검색어는 다르다는 것을 느낄 수 있다. 예를 들어 A유저는 홍익대학교 정문을 검색하였고 B유저는 서울특별시 마포구 서교동라고 검색하였다. 결과는 어떨까? 결과는 같다. 여기서 차이점은 위에서 언급했듯이 A유저는 건물명을 검색한 것이고 B유저는 주소를 검색한 것이다.
- 건물명 검색 → 홍익대학교 정문
- 주소 검색 → 서울특별시 마포구 서교동
이를 바탕으로 주소 검색과 건물명 검색 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 표현
'개발 이모저모' 카테고리의 다른 글
[안드로이드] 귀찮니즘이 만들어 준 contains() 함수 응용 (0) | 2022.12.20 |
---|---|
시작일부터 종료일 표현해보기 (0) | 2022.12.15 |
지도에 중복 위경도 마커를 찍어보자 (0) | 2021.11.16 |
SharedPreference를 이용한 최근 검색어 리스트 만들기 (0) | 2021.11.16 |
HTTP 통신 로그 이쁘장하게 찍어보기 (0) | 2021.11.16 |