일단 지금까지는 KakaoMap Api v2를 사용해서 카카오지도를 띄워주는 부분까지 했다
내가 구현하고 싶은것은, 지역에 해당하는 검색어를 입력하면 해당 지역의 위경도를 가져와서 해당지역으로 지도가 이동하면서 마커를 찍어주는 부분을 구현하고싶었다.
그럴려면, 일단 검색한 지역에 해당하는 위경도를 가져와야하는데, 마침 kakao에서 지역을 검색하면 해당 지역의 위경도값과 다른 기타값들을 받아오는 주소검색 API가 존재해서 이 API를 사용해서 가져와보려고한다
여기서 첫번째 나와있는 주소검색하기 API를 사용했다
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
먼저 postman으로 테스트해본결과, 잘 받아온것을 확인할 수 있었고, 이를 토대로 코드를 작성해주었다
네트워크 요청을 위해서 아래 코드를 작성해준다
우리 프로젝트에 API로 받아오는 코드가 많아서 @Named 어노테이션을 사용해서 구분을 시켜줬다
<@Named 어노테이션에 대해 공부해보기>
@Module
@InstallIn(SingletonComponent::class)
object RetrofitModule {
@Singleton
@Provides
fun provideOkHttpClient(): OkHttpClient {
val loggingInterceptor = HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY)
return OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
}
@Singleton
@Provides
@Named("SearchRegion")
fun provideRetrofitSearchRegion(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.baseUrl(SEARCH_API_BASE)
.build()
}
@Singleton
@Provides
@Named("SearchService")
fun provideApiServiceSearch(
@Named("SearchRegion") retrofit: Retrofit
): SearchService {
return retrofit.create(SearchService::class.java)
}
}
RetrofitModule.kt
공식문서에 적혀있는대로 헤더값과 쿼리값을 작성해준다
interface SearchService {
@GET("v2/local/search/address")
@Headers("Authorization: KakaoAK ${SEARCH_REST_API_KEY}")
suspend fun getSearch(
@Query("query") query : String,
@Query("page") page : Int
): SearchResponse
}
SearchService.kt
역시 공식문서에 적힌 형식대로 데이터클래스를 만들어준다.
실질적으로 쓰이는값은 위경도에 해당하는 x,y값이긴하지만 그래도 다 작성해줬다..ㅎㅎ
프로젝트 끝나고 필요없는 값들은 지울까 생각중..
data class SearchResponse(
@SerializedName("meta") val searchMeta : SearchMetaResponse,
@SerializedName("documents") val documents : List<SearchDocumentsResponse>
)
data class SearchMetaResponse(
@SerializedName("is_end") val isEnd : Boolean,
@SerializedName("pageable_count") val pageableCount : Int,
@SerializedName("total_count") val totalCount : Int
)
data class SearchDocumentsResponse(
@SerializedName("address") val address : Address?,
@SerializedName("address_name") val addressName : String,
@SerializedName("address_type") val addressType : String,
@SerializedName("x") val x : Double, // x=128.601805491072
@SerializedName("y") val y : Double, // y=35.8713802646197
)
data class Address(
@SerializedName("address_name") val addressName : String?,
@SerializedName("region_1depth_name") val regionDepthName1 : String?,
@SerializedName("region_2depth_name") val regionDepthName2 : String?,
@SerializedName("region_3depth_h_name") val regionDepthHName3 : String?,
@SerializedName("region_3depth_name") val regionDepthName3 : String?,
@SerializedName("x") val x : Double?,
@SerializedName("y") val y : Double?,
)
SearchResponse.kt
그리고 우리 프로젝트는 Repository pattern을 사용해서 진행하기로 했기때문에 해당 패턴을 사용해서 구현해주었다
suspend fun을 통해 requestSearch를 선언해주고
interface SearchRepository {
suspend fun requestSearch(query : String) : SearchResponse
}
SearchRepository.kt
SearchRepositoryImpl 클래스에서 requestSearch를 오버라이딩해줘서 실제 구현부분을 작성해준다
@Singleton
class SearchRepositoryImpl @Inject constructor(
@Named("SearchService") private val searchService: SearchService)
: SearchRepository{
override suspend fun requestSearch(query: String): SearchResponse {
return searchService.getSearch(query = query, page = 1)
}
}
SearchRepositoryImpl.kt
입력한 검색어가 바뀔때마다 값을 감지해서 view를 변경해줄 것이기 때문에,
viewModel에서 stateFlow를 사용해서, 검색어값을 emit을 통해서 방출해준다
그리고 프로젝트 전체에 Hilt를 적용했기때문에 어노테이션을 사용해서 searchRepository에 Hilt를 주입해준다
@HiltViewModel
class MapViewModel @Inject constructor(
private val searchRepository: SearchRepository,
) : ViewModel() {
// 검색어
private val _regionSearch = MutableStateFlow<SearchResponse?>(null)
val regionSearch : StateFlow<SearchResponse?> = _regionSearch
fun getRegionSearch(query: String) = viewModelScope.launch {
_regionSearch.emit(searchRepository.requestSearch(query))
}
}
MapViewModel.kt
이제 view부분에서 mapViewModel.getRegionSearch(searchText) 해당 코드를 작성해줘서
키보드에 있는 검색버튼을 누르면 검색어가 바뀔때마다 값을 감지해서 MapFragment에도 값을 감지해서 바뀌도록 해준다
@AndroidEntryPoint
class MapFragment : Fragment() {
...
private val mapViewModel: MapViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMapBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
setSearch()
}
private fun setSearch() = with(binding){
searchEt.setOnEditorActionListener { v, actionId, event ->
if(actionId == EditorInfo.IME_ACTION_SEARCH){
val searchText = searchEt.text.toString()
mapViewModel.getRegionSearch(searchText)
// 키보드 아래로 내리는코드
val imm = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(requireActivity().window.decorView.applicationWindowToken, 0)
return@setOnEditorActionListener true
}
return@setOnEditorActionListener false
}
}
}
MapFragment.kt
이제 값을 잘 받아오는지 확인하기위해서 검색 부분에 파주를 입력하고 로그캣값을 확인해보면 아래와같이 파주에 해당하는 값을 잘 받아오는것을 확인할 수 있다
'개발 노트 > Kotlin' 카테고리의 다른 글
[Android/Kotlin] StateFlow -> launchWhenCreated deprecated 해결 (0) | 2024.05.31 |
---|---|
[Android] 로그캣 오류가 세로로 뜨는 이상한 문제 (0) | 2024.05.31 |
[Android/Kotlin] KakaoMap 주소 검색 API [HTTP 401에러] (0) | 2024.05.29 |
[Android/Kotlin] Bottom Navigation View 구현 (0) | 2024.05.28 |
[Android/Kotlin] KakaoMap API Android v2 사용하기 (1) | 2024.05.27 |