민프

[Android][Kotlin] Retrofit2를 사용해보자!! (공공API 사용) 본문

[Android]

[Android][Kotlin] Retrofit2를 사용해보자!! (공공API 사용)

민프야 2021. 9. 24. 11:19

🎈준비하기

https://www.data.go.kr/iim/api/selectAPIAcountView.do

 

공공데이터 포털

국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase

www.data.go.kr

이곳에서 API를 발급 받으면 된다.

 

🎈Retrofit 이란?

- 안드로이드 http 통신 라이브러리 중 하나이다.

 

https://square.github.io/retrofit/

 

Retrofit

A type-safe HTTP client for Android and Java

square.github.io

 

🎈Retrofit 추가하기

    bulid.gradle(app) 라이브러리 추가
    
  	//레트로핏
    implementation 'com.squareup.retrofit2:retrofit:2.6.4'

    //GSON 컨버터
    implementation 'com.squareup.retrofit2:converter-gson:2.6.2'


    
    
    
    ===============================
    
    Manifest에 인터넷 권한 추가
    
    <uses-permission android:name="android.permission.INTERNET"/>

 

 

🎈POJO 클래스 만들기 (나중에 계속하기로)

블로그를 찾아보다가
데이터를 담기위해서 POJO클래스를 만든다고 공통적으로 말하길래 
POJO클래스에 대해서 찾아보았는데

POJO란?

어떠한 제한에도 묶이지 않는 자바 오브젝트라 무엇일까..??


일단 POJO 클래스를 만들어주는 사이트에서 POJO 클래스를 만들어보았다.

https://www.jsonschema2pojo.org/

 

jsonschema2pojo

Reference properties For each property present in the 'properties' definition, we add a property to a given Java class according to the JavaBeans spec. A private field is added to the parent class, along with accompanying accessor methods (getter and sette

www.jsonschema2pojo.org

 

 

Class 내용을 보면 변수 선언, getter, setter가 전부임을 알 수 있었다.

Retrofit을 이용하여
API를 적용하는 예제를 보면
데이터를 담는 그릇을 만드는 클래스를 만들때
POJO 클래스를 만든다고 하는거 보면

데이터를 담는 작업 외에는 별다는 작업을 하지 않고,

POJO 클래스는 상속과 인터페이스로부터 자유로운것을 확인할 수 있었다. 

 

*POJO클래스를 사용해보려고 했었는데ㅠㅠ 내가 잘 못해서 그런지
JSON 파싱을 해도 String 값으로는 잘 들어오는데 GsonConverter + POJO 클래스 이용하면

자꾸 에러가 떠서ㅠㅠ 일단은 진행중인 프로젝트가 있어서 중단하고 프로젝트가 끝나면 다시 도전해봐야겠다.

 

당장은 

API에서 받은 값들을

JSON Object클래스를 이용해서 JSON 문자열과 JSON 배열을 파싱해서

값들을 데이터Model에 저장하는 방식으로 해야겠다. 

 

나중에.. 언제 시간이날지는 모르겠지만
POJO클래스를 써서 하는 방법을 해봐야겠다ㅠㅠ

 

🎈Retrofit Inteface 만들기

여기서 Interface는 Retrofit에서 사용할 HTTP CRUD(메소드)들을 정의해놓은 인터페이스 입니다.

 *CRUD(Create, Read, Update, Delete) -> HTTP Method(POST/GET/PUT/DELETE)

interface RetroInterface {
    @GET("GetWheelchairChargerService/getWheelchairChargerInfo")
    fun getBoxOffice(
            @Query("serviceKey") key: String?,
            @Query("dataType") targetDt: String?
    ): Call<String>

- @GET => HTTP Method중 하나이고, baseUrl에 연결될 EndPoint 이다.

Retrofit 공식문서

  • 동적 URL

Retrofit 공식문서
Retrofit 공식문서

  • 서버에 값 보내기

Retrofit 공식문서
Retrofit 공식문서

서버에 값을 보낼때에는
@Query

@Field
@Body

가 있는데

 

차이점은 

@Query = @GET에서 사용하며 모든 매개 변수가 요청에 추가되고 사용자에게 표시되고,

@Field = @FormUrlEncoded(=key-Value, ) @POST,에서 사용하며 매개 변수를 숨기고 URL을 추가하지 않는다.

@Body = Gson 컨버터와 함께 쓰이기 때문에, Java Object를 통째로 직렬화 해서 보낼 수 있다. 

쉽게 말해서 Json형식으로 보내고 싶을때 사용한다.


- Call<String> = API에서 받은 데이터의 반환타입을 String으로 받겠다는 것 이다.

 

🎈Retrofit 인스턴스 만들기

Retrofit.Bulid를 통해서 Retrofit 인스턴스를 생성해보자

    	val KEY = "My Key"
        val BASE_URL = "http://apis.data.go.kr/4460000/"
        
        //레트로핏 빌더를 통해 인스턴스 생성
        val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(ScalarsConverterFactory.create())
            .build()
            
        /* Timeout 시간 늘리기
        *     val client: OkHttpClient = OkHttpClient.Builder()
                    .connectTimeout(300, TimeUnit.SECONDS)
                    .readTimeout(300, TimeUnit.SECONDS)
                    .writeTimeout(300, TimeUnit.SECONDS)
                    .addInterceptor(object :
                Interceptor {
                @Throws(IOException::class)
                override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
                    val newRequest: Request = chain.request().newBuilder()
                        .addHeader("Authorization", "Bearer " + userAccessTokenValue)
                        .build()
                    return chain.proceed(newRequest)
                }
            }).build()
        */
        
        //Retrofit 인스턴스로 인터페이스 객체 구현
        val api:RetroInterface = retrofit.create(RetroInterface::class.java)

	//인터페이스에서 정의한 메소드
        val callGetSearchNews = api.getBoxOffice(KEY, "JSON")


        //enequeue로 비동기 실행, Callback을 String
        callGetSearchNews.enqueue(object : Callback<String> {
            override fun onResponse(
                    call: Call<String>,
                    response: Response<String>
            ) {
                Log.d(TAG, "통신 성공 : ${response.raw()}")

            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                Log.d(TAG, "통신 실패 : $t")
            }
        })

 

🎈String To JSON

API 값들을 Callback 값으로 String으로 받았는데요
JSON구조에 맞게 JSON Objest, JSON Array로 나눠보겠습니다.

 

JSON 구조를 편하게 보기 위해서 밑 사이트를 참고하였습니다.

https://jsonformatter.curiousconcept.com/#

간단하게 JSONArray와  JSONObject를 설명하자면

- JSONArray

  • 배열 구조이다.
  • '[ ]' 대괄호를 이용하여 값들을 담으며, ' , '로 값을 구분한다. 
  • => ' { { }, { }, { } } '

 

JSONObject

  • 하나 이상의 Key-Value 을 '{ }' 의 중괄호를 이용하여 담고있는 객체 구조이다.
  • => ' [ { },{ },{ } ] ' 

그럼 받아온 데이터를 JSONArray, JSONObject로 구분해보자

 

        val KEY = "My Key"
        val BASE_URL = "http://apis.data.go.kr/4460000/"
        val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(ScalarsConverterFactory.create())
            .build()
        val api:RetroInterface = retrofit.create(RetroInterface::class.java)
        val callGetSearchNews = api.getBoxOffice(KEY, "JSON")
        start = System.currentTimeMillis()


        callGetSearchNews.enqueue(object : Callback<String> {
            override fun onResponse(
                    call: Call<String>,
                    response: Response<String>
            ) {

//                Log.d(TAG, "통신 성공 : ${response.raw()}")
//                Log.d(TAG, "통신 성공 : " + response.body().toString())

                val jsonObject = JSONObject(response.body().toString())
                Log.d(TAG+ " jsonObjectResponse", jsonObject.toString())

                //JsonObject, JsonArrary 구조 나누기
                val jsonObjectResponse = jsonObject.getJSONObject("response")
                val jsonObjectHeader = jsonObjectResponse.getJSONObject("header")
                val jsonObjectBody = jsonObjectResponse.getJSONObject("body")
                val jsonObjectItems = jsonObjectBody.getJSONObject("items")
                val jsonArraryItem = jsonObjectItems.getJSONArray("item")
                
                
                //response{ } -> header{resultCode, resultMsg}값 추출
                val resultCode = jsonObjectHeader.getInt("resultCode")
                val resultMsg = jsonObjectHeader.getString("resultMsg")
                Log.d(TAG+ " resultCode", resultCode.toString())
                Log.d(TAG+ " resultMsg", resultMsg.toString())


                //response{ } - body{ } -> items[ ] -> item{ } 값 추출
                Log.d(TAG+ " jsonArraryItem ", jsonArraryItem.length().toString())
                for (i in 0..jsonArraryItem.length()-1){
                 Log.d(TAG+ " i", i.toString())
                    val getObject = jsonArraryItem.getJSONObject(i)

                    val lotnoAddr = getObject.getString("lotnoAddr")
                    val fcNm = getObject.getString("fcNm")

     
                    Log.d(TAG+ " lotnoAddr", lotnoAddr.toString())
                    Log.d(TAG+ " fcNm", fcNm.toString())
                }
            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                Log.d(TAG, "통신 실패 : $t")
            }
        })

잘 나오는 것을 확인할 수 있다.

Comments