[응용] 안드로이드 TMAP API로 위도,경도를 주소로 반환하기

쓰리원·2022년 4월 30일
0

안드로이드 지도API

목록 보기
2/12
post-thumbnail

YU Market에서 구현한 위도,경도를 주소로 반환하기를 따로 코드로 구현해보려고 합니다.

안드로이드-GPS-위도-경도-불러오기

이전에 작성한 위도 경도 받아오기 글 입니다. 위 글을 읽고 위도 경도부터 이해 한 뒤 오셔야 글이 이해됩니다.

개념-Retrofit2-사용법
레트로핏에 대한 이해도가 부족하다면 이 글도 읽고 오셔야 합니다.

API 와 EndPoint 란?
API에 대한 이해도가 부족하다면 이 글도 읽고 오셔야 합니다.

1. Endpoint를 생성

위와 같이 Endpoint interface를 만들어서 API에 접근해서 통신할 수 있게 해줍니다. Endpoint에서 URL에 대한 결과값을 반환 받을 메서드는 GET으로 하였습니다.

@GET은 HTTP 통신 방식으로 Parameter들이 URL에 추가되는 GET 방식으로 하겠다는 의미이고 @Header는 Request Header에 TMAP_API의 Key값을 추가한다는 의미입니다. @Query는 Request시 담을 Parameter들이며 아래와 같이 URL이 구성되며 Request를 보내게 됩니다.

https://apis.openapi.sk.com/tmap/geo/reversegeocoding?version={version}&lat={lat}&lon={lon}&coordType={coordType}&addressType={addressType}&callback={callback}&appKey={appKey}

2. Retrofit과 OkHttpClient

object RetrofitUtil {

    private var instance: Retrofit? = null

    val mapApiService: MapApiService by lazy {
        getRetrofit().create(MapApiService::class.java)
    }

    private fun getRetrofit(): Retrofit {
        if(instance == null) {
            Retrofit.Builder()
                .baseUrl(Url.TMAP_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(buildOkHttpClient())
                .build()
        }
        return instance!!
    }

    private fun buildOkHttpClient(): OkHttpClient {

        val interceptor = HttpLoggingInterceptor()

        if (BuildConfig.DEBUG) {
            interceptor.level = HttpLoggingInterceptor.Level.BODY
        } else {
            interceptor.level = HttpLoggingInterceptor.Level.NONE
        }

        return OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .addInterceptor(interceptor)
            .build()
    }
}

RetrofitUtil은 기존의 객체를 재활용 하기 위해서 싱글턴으로 구현하였습니다. 그리고 Response와 Request에 대한 정보를 보기 위해서 OkHttpClient를 사용하여 val interceptor = HttpLoggingInterceptor() 객체를 생성해서 Retrofit에 추가해 줍니다.

3. T MAP API가 Response(JSON)를 반환

Request를 해서 API에서 Response를 받으면 GsonConverter를 이용하여 AddressInfoResponse 객체로 변환(JSON -> 객체)하게 됩니다. ReverseGeo의 Response JSON은 아래와 같습니다.

{"addressInfo": {
    "fullAddress": "서울시 마포구 상수동",
    "addressKey": "3004545827",
    "roadAddressKey": "4001807949",
    "addressType": "A10",
    "city_do": "서울시",
    "gu_gun": "마포구",
    "eup_myun": "",
    "adminDong": "망원2동",
    "adminDongCode": "1150056000",
    "legalDong": "상수1동",
    "legalDongCode": "1132010500",
    "ri": "죽전리",
    "roadName": "강남대로",
    "buildingIndex": "11-11",
    "buildingName": "영진그린빌라",
    "mappingDistance": 0,
    "roadCode": "261104175064",
    "bunji": "121-12",
    "adminDongCoord":{
    "lat": "37.56624167",
    "lon": "126.90194167",
    "latEntr": "37.56610000",
    "lonEntr": "126.90173889"
    },
    "legalDongCoord":{
    "lat": "37.56624167",
    "lon": "126.90194167",
    "latEntr": "37.56610000",
    "lonEntr": "126.90173889"
    },
    "roadCoord":{
    "lat": "37.56631111",
    "lon": "126.90161389",
    "latEntr": "37.56610000",
    "lonEntr": "126.90173889"
}}

위와 같은 형식의 JSON이 AddressInfo를 담고 있는 AddressInfoResponse로 변환이 됩니다. AddressInfoResponse 내부에 존재하는 AddressInfo의 구조는 아래와 같습니다.

4. data class 작성하기

data class AddressInfo(
    @SerializedName("fullAddress")
    @Expose val fullAddress: String?,
    @SerializedName("addressType")
    @Expose val addressType: String?,
    @SerializedName("city_do")
    @Expose val cityDo: String?,
    @SerializedName("gu_gun")
    @Expose val guGun: String?,
    @SerializedName("eup_myun")
    @Expose val eupMyun: String?,
    @SerializedName("adminDong")
    @Expose val adminDong: String?,
    @SerializedName("adminDongCode")
    @Expose val adminDongCode: String?,
    @SerializedName("legalDong")
    @Expose val legalDong: String?,
    @SerializedName("legalDongCode")
    @Expose val legalDongCode: String?,
    @SerializedName("ri")
    @Expose val ri: String?,
    @SerializedName("bunji")
    @Expose val bunji: String?,
    @SerializedName("roadName")
    @Expose val roadName: String?,
    @SerializedName("buildingIndex")
    @Expose val buildingIndex: String?,
    @SerializedName("buildingName")
    @Expose val buildingName: String?,
    @SerializedName("mappingDistance")
    @Expose val mappingDistance: String?,
    @SerializedName("roadCode")
    @Expose val roadCode: String?
)

JSON의 구조와 같은 것을 확인해 볼 수 있습니다. @SerializedName은 Annotation내의 문자열을 Key로 가지는 JSON의 값을 할당하라는 의미입니다. 즉 val roadCode의 경우 roadCode를 Key로 가지는 JSON의 값이 할당이 됩니다.

5. MainActivity.kt

위의 코드중 API 부분만 다뤄보겠습니다.

inner class MyLocationListener : LocationListener {
        override fun onLocationChanged(location: Location) {
        
            //binding.locationTitleTextView.text = "${location.latitude}, ${location.longitude}"

            getReverseGeoInformation( LocationEntity(
                    latitude = location.latitude,
                    longitude = location.longitude
                )
            )
            removeLocationListener()
        }

        private fun removeLocationListener() {
            if (::locationManager.isInitialized && ::myLocationListener.isInitialized) {
                locationManager.removeUpdates(myLocationListener)
            }
        }
    }

위 코드에서 getReverseGeoInformation() 함수에 위도 경도를 넘겨주는 것을 알 수 있습니다. 이 위도 경도를 넘겨주고 우리는 콜백으로 주소값을 반환 받을 수 있는 것 입니다.

    fun getReverseGeoInformation(locationLatLngEntity: LocationEntity) {

        uiScope.launch {
            withContext(Dispatchers.IO) {

                val response = RetrofitUtil.mapApiService.getReverseGeoCode(
                    lat = locationLatLngEntity.latitude,
                    lon = locationLatLngEntity.longitude
                )

                if (response.isSuccessful) {
                    val body = response.body()
                    withContext(Dispatchers.Main) {
                        binding.locationTitleTextView.text = "${body?.addressInfo?.fullAddress}"
                    }
                }
                else {
                    null
                }
            }
        }
    }

getReverseGeoInformation()에서는 RetrofitUtil.mapApiService.getReverseGeoCode()에 다시 위도 경도값을 선언하는 것을 볼 수 있습니다. 위 과정에서 코루틴을 사용하여 비동기적으로 진행하고 있습니다.

과정 요약

  1. MapApiService Interface의 End point를 생성
  2. MapApiService End point의 메서드 및 파라미터 URL 구성
  3. Retrofit을 이용하여 TMAP API에 HTTP Request 전송
  4. TMAP API가 Response(JSON)를 반환
  5. 반환된 Response를 GsonConverter를 이용하여 객체로 생성
  6. 생성된 객체를 받아 사용

완성된 코드 : https://github.com/ilil1/GeocodingAPI

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글