Android

[안드로이드]Retrofit2 에 관하여

나맘임 2023. 9. 17. 17:05

Retrofit2

sqaure 사에서 개발한 안드로이드 앱에서 웹 서버와 통신하기 위한 라이브러리 중 하나로, Http 요청을 쉽게 만들고 처리할 수 있게 해줍니다.

기존에 있던 OkHttp을 사용하는 라이브러리로 비슷한 기능을 하는 Volley와 비교해도 높은 성능을 가지고 있어 많이 사용됩니다.


Retrofit2의 장점

1. 높은 성능

그림 1. Retrofit vs Volley

출처 : Speed Comparison of Retrofit and Volley. | Download Scientific Diagram (researchgate.net)

비슷한 역할을 하는 Volley와 쿼리 성능을 비교한 그래프입니다.

위 그래프를 보시면 데이터 수가 많아지면 많아질수록 차이가 현저히 발생한다는 것을 알 수가 있습니다.

2. 간편한 사용성 및 높은 가독성

interface TourAPI {

    @GET("/6260000/AttractionService/getAttractionKr")
    fun requestData(
        @Query("serviceKey") serviceKey : String ,
        @Query("numOfRows") numOfRows : String = "10",
        @Query("pageNo") pageNo : String = "1",
        @Query("resultType") resultType : String = "json"
    ) : Call<TourData>
}

그림 2. Http 메소드 예시

 

Annotation으로 Http 통신 메소드를 작성하기 때문에 개발자 입장에서 코드의 구현이 쉽고 가독성이 좋습니다.

3. 다양한 요청 메소드 지원

GET, POST, PUT. DELETE와 같은 다양한 Http 요청 메소드를 지원합니다.

 


Retrofit2 구성요소

1. DTO(Data Transfer Object)  또는 POJO(Plain Old Java Object)

데이터를 전송하거나 저장하기 위한 일반적 객체 모델로 여기선 서버 API로 불러오는 데이터를 정의하기 위해 사용됩니다.

일반적으로 JSON 형태로 서버로부터 데이터를 수신하기 때문에 JSON 타입 변환에 사용됩니다.

코틀린에선 JSON 형태의 Response을 원하는 형태의 Object로 변환할 수 있게 하는 DataClass를 의미합니다.

2. Retrofit.Interface

Http 메소드를 정의하는 인터페이스 입니다.

위 그림 2와 같이 메소드를 정의합니다.

GET, POST, PUT, DELETE 메소드를 정의할 수 있습니다.

 

3. Retrofit.Builder

2번 Interface를 사용할 객체 인스턴스로 BaseURL(Endpoint)와 Json을 파싱할 Converter를 설정합니다.

 


Retrofit2 사용하는 방법

1. Gradle 설정

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")

2. AndroidManifest.xml 설정

<uses-permission android:name="android.permission.INTERNET"/>

통신을 위해 인터넷 권한을 설정해줍니다.

3. DTO 작성

부산광역시_부산명소정보 서비스 | 공공데이터포털 (data.go.kr)

 

부산광역시_부산명소정보 서비스

부산관광명소의 이름, 주소, 홈페이지, 전화번호, 좌표, 상세정보를 조회하는 서비스

www.data.go.kr

위 API를 기반으로 DTO를 작성해보겠습니다.

 

물론 직접하는 것도 좋지만 사실 위 API처럼 수신할 데이터가 많을 경우엔 너무나도 작성하기가 귀찮습니다.

 

그럴 때 사용하면 좋은 안드로이드 스튜디오 플러그인이 존재합니다.

 

wuseal/JsonToKotlinClass: 🚀 Plugin for Android Studio And IntelliJ Idea to generate Kotlin data class code from JSON text ( Json to Kotlin ) (github.com)

 

GitHub - wuseal/JsonToKotlinClass: 🚀 Plugin for Android Studio And IntelliJ Idea to generate Kotlin data class code from JSON

🚀 Plugin for Android Studio And IntelliJ Idea to generate Kotlin data class code from JSON text ( Json to Kotlin ) - GitHub - wuseal/JsonToKotlinClass: 🚀 Plugin for Android Studio And IntelliJ Idea...

github.com

그림 3. 설치 예시
그림 4. 공공데이터 포털 데이터 예시

먼저 API를 신청하여 데이터를 받아봅니다.

 

그 이후 위 데이터를 복사하고

 

그림 5. 설치한 플러그인 실행
그림 6. 플러그인 창

빈 칸에 JSON 데이터를 모두 입력해줍니다.

 

여기서 Retrofit은 gson을 기반으로 하기 때문에 gson 문법으로 수정해서 작성해야합니다.

 

왼쪽 밑에 있는 Advance를 눌러줍니다.

 

그림 7. Gson 어노테이션 설정

위와 같이 Gson으로 설정한 후 클래스를 만들어줍니다.

 

결과물

더보기

 

package com.github.socialverse.feature.api


import com.google.gson.annotations.SerializedName

data class TourData(
    @SerializedName("getAttractionKr")
    val getAttractionKr: GetAttractionKr
)

data class GetAttractionKr(
    @SerializedName("header")
    val header: Header,
    @SerializedName("item")
    val item: List<Item>,
    @SerializedName("numOfRows")
    val numOfRows: Int,
    @SerializedName("pageNo")
    val pageNo: Int,
    @SerializedName("totalCount")
    val totalCount: Int
)

data class Header(
    @SerializedName("code")
    val code: String,
    @SerializedName("message")
    val message: String
)


data class Item(
    @SerializedName("ADDR1")
    val aDDR1: String,
    @SerializedName("CNTCT_TEL")
    val cNTCTTEL: String,
    @SerializedName("GUGUN_NM")
    val gUGUNNM: String,
    @SerializedName("HLDY_INFO")
    val hLDYINFO: String,
    @SerializedName("HOMEPAGE_URL")
    val hOMEPAGEURL: String,
    @SerializedName("ITEMCNTNTS")
    val iTEMCNTNTS: String,
    @SerializedName("LAT")
    val lAT: Double,
    @SerializedName("LNG")
    val lNG: Double,
    @SerializedName("MAIN_IMG_NORMAL")
    val mAINIMGNORMAL: String,
    @SerializedName("MAIN_IMG_THUMB")
    val mAINIMGTHUMB: String,
    @SerializedName("MAIN_TITLE")
    val mAINTITLE: String,
    @SerializedName("MIDDLE_SIZE_RM1")
    val mIDDLESIZERM1: String,
    @SerializedName("PLACE")
    val pLACE: String,
    @SerializedName("SUBTITLE")
    val sUBTITLE: String,
    @SerializedName("TITLE")
    val tITLE: String,
    @SerializedName("TRFC_INFO")
    val tRFCINFO: String,
    @SerializedName("UC_SEQ")
    val uCSEQ: Int,
    @SerializedName("USAGE_AMOUNT")
    val uSAGEAMOUNT: String,
    @SerializedName("USAGE_DAY")
    val uSAGEDAY: String,
    @SerializedName("USAGE_DAY_WEEK_AND_TIME")
    val uSAGEDAYWEEKANDTIME: String
)

 

4. Retrofit.Interface 작성

interface TourAPI {

    @GET("/6260000/AttractionService/getAttractionKr")
    fun requestData(
        @Query("serviceKey") serviceKey : String ,
        @Query("numOfRows") numOfRows : String = "10",
        @Query("pageNo") pageNo : String = "1",
        @Query("resultType") resultType : String = "json"
    ) : Call<TourData>
}

GET 메소드를 사용하여 데이터를 읽고 URL에 파라메터를 입력해주기 위해서 @query 어노테이션을 사용하여

 

필요한 값들을 입력해줍니다.

 

위 예시에선 

 

/6260000/AttractionService/getAttractionKr?serviceKey=\&numOfRows=10&pageNo=1&resultType=json

 

가 되는 겁니다.

 

5. Retrofit.Builder 작성

BaseTourRetrofitBuilder.kt

open class BaseTourRetrofitBuilder {

    open val baseUrl = "https://apis.data.go.kr"

    private val gson = GsonBuilder()
        .setLenient()
        .create()

    private val clientBuilder = OkHttpClient.Builder().addInterceptor (
        HttpLoggingInterceptor().apply {
            level =
                HttpLoggingInterceptor.Level.BODY
        }
    )

    private val retrofit = Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .client(clientBuilder.build())
        .build()

    protected fun getRetrofit() : Retrofit {
        return retrofit
    }
}

Retrofit의 기본적인 설정을 하는 곳으로 차후 GET 요청 메소드를 보낼 때 사용합니다.

 

추가로 clientBuilder 프로퍼티를 초기화할 때 HttpLoggingInterceptor를 사용하는데

 

이는 통신의 진행 및 결과를 LogCat으로 확인할 수 있게 해주므로 

 

사용하면 오류를 확인하기 정말 좋습니다.

 

TourRetrofitBuilder.kt

package com.github.socialverse.feature.api

import android.util.Log
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

object TourRetrofitBuilder : BaseTourRetrofitBuilder(){
    override val baseUrl: String
        get() = "http://apis.data.go.kr"

    const val API_KEY = "api키"

    fun getItems(){
        val api = getRetrofit().create(TourAPI::class.java)
        api.requestData(
            API_KEY
        ).enqueue(object : Callback<TourData>{
            override fun onResponse(call: Call<TourData>, response: Response<TourData>) {
                val resultCode = response.body()?.getAttractionKr?.header?.code
                val resultMessage = response.body()?.getAttractionKr?.header?.message
                //정상 코드
                if (resultCode == "00"){
                    response.body()?.getAttractionKr?.item?.forEach { item ->
                        Log.d("sucesss", "${item.mAINTITLE}")
                    }
                } else {
                }
            }

            override fun onFailure(call: Call<TourData>, t: Throwable) {
                Log.d("error", t.message.toString())
            }
        })
    }



}

getItems는 API를 실질적으로 요청하는 메소드로 콜백 함수로 값을 불러옵니다.

 

resultCode가 00 이라는 것은 정상적으로 데이터가 수신했다는 뜻이므로 로직을 수행합니다.

 

onFailure는 네트워크 문제와 같은 기타 이유로 통신이 불가능할 때 콜백이 됩니다.

 

위 getItems는 수신한 Response의 MAINTITLE 값을 로그에 남기는 방식입니다.

 

6. 결과물