[TIL] Retrofit

박봉팔·2024년 1월 25일
0

Retrofit

RetrofitHTTP API요청 및 응답처리를 단순하게 만들어, 보다 손쉽게 네트워크 통신을 구현하도록 도와주는 라이브러리이다.

Retrofit자체에서 비동기 작업을 지원하기 때문에 보다 손쉽게 비동기 처리를 할 수 있다.


Retrofit사용해보기

Rtrofit을 사용하기위해서는 Menifest에 퍼미션을 설정해주고 Gradle에 라이브러리를 추가해준다.


  • Manifest
    <application>
        ...
        <!-- 어플리케이션 수준에서 usesCleartextTraffic를 true로 설정해야 http를 불러올 수 있음 -->
        <!-- https만 사용할 예정이라면 따로 설정할 필요가 없다 -->
        android:usesCleartextTraffic="true" 
        ...
    </application>

	<!-- API는 인터넷을 사용함으로 인터넷 퍼미션을 설정해준다 -->
    <uses-permission android:name="android.permission.INTERNET" />

  • Gradle
dependencies {
	
    ...
    
    // 레트로핏 인디펜던시
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")

    //레트로핏은 로그를 안찍어주기떄문에 로그를 찍어주는 역할을 해주는 okhttp추가
    implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
    implementation("com.squareup.okhttp3:okhttp:4.10.0")

	//Gson을 사용하기위해 추가
    implementation("com.google.code.gson:gson:2.10.1")
}

Retrofit은 크게 3가지 요소가 있어야 사용이 가능하다.


1. Json데이터를 받아오기 위한 데이터 클래스

요청한 데이터는 보통 XML이나 Json으로 받아올 수 있는데, 안드로이드의 경우 Json데이터를 자동으로 변환해서 객체를 생성해주는 Gson타입으로 변환이 가능하기 때문에 Json데이터를 받아오면 보다 손쉽게 데이터의 변환이 가능하다.

데이터 타입은 API가 제공하는 항목들을 보고 원하는 항목을 프로퍼티로 만들어야하며, 내부에 배열이 있는경우 각각의 데이터 클래스로 만들어줘야 한다.


  • Json으로 이루어진 시/도별 미세먼지 데이터
{
   "response":{
      "body":{
         "totalCount":40,
         "items":[
            {
               "so2Grade":"1",
               "coFlag":null,
               "khaiValue":"43",
               "so2Value":"0.003",
               "coValue":"0.4",
               "pm25Flag":null,
               "pm10Flag":null,
               "o3Grade":"1",
               "pm10Value":"14",
               "khaiGrade":"1",
               "pm25Value":"13",
               "sidoName":"서울",
               "no2Flag":null,
               "no2Grade":"1",
               "o3Flag":null,
               "pm25Grade":"1",
               "so2Flag":null,
               "dataTime":"2024-01-26 02:00",
               "coGrade":"1",
               "no2Value":"0.019",
               "stationName":"중구",
               "pm10Grade":"1",
               "o3Value":"0.023"
            }
         ],
         "pageNo":1,
         "numOfRows":1
      },
      "header":{
         "resultMsg":"NORMAL_CODE",
         "resultCode":"00"
      }
   }
}

  • Json을 저장하기 위한 데이터 클래스
// Json으로 받아온 데이터를 정의하는 클래스
data class DustData(val response: DustResponse)

// DustData에 넘길 response를 정의하는 클래스
data class DustResponse(
	// 만약 Json의 값과 다른 이름을 사용하고 싶다면 @SerializedName 어노테이션을 사용해
    // Json데이터에서 사용된 키값을 적어준다.
    @SerializedName("body")
    // Json의 response에 들어가있는 값중 Body를 가져온다
    val dustBody: DustBody
)

// 아래의 클래스들은 각각 하위에 있는 값들을 나타낸다.
data class DustBody(
    @SerializedName("items")
    val dustItems: MutableList<DustItem>?
)

data class DustItem(
    val so2Grade: String,
    val coFlag: String?, // null인 값들은 nullable타입으로 선언
    val khaiValue: String,
    val so2Value: String,
    val coValue: String,
    val pm25Flag: String?,
    val pm10Flag: String?,
    val o3Grade: String,
    val pm10Value: String,
    val khaiGrade: String,
    val pm25Value: String,
    val sidoName: String,
    val no2Flag: String?,
    val no2Grade: String,
    val o3Flag: String?,
    val pm25Grade: String,
    val so2Flag: String?,
    val dataTime: String,
    val coGrade: String,
    val no2Value: String,
    val stationName: String,
    val pm10Grade: String,
    val o3Value: String
)

2. HTTP통신을 위한 인터페이스를 정의한다.

API제공처에서 원하는 요청 방식에 따라 인터페이스를 정의한다. 정의한 인터페이스를 Retrofit에 연결해 인터페이스의 메서드로 원하는 동작을 할 수 있다.

단순하게 데이터를 가져오는 경우 @GET어노테이션을 사용해 테이터를 가져올 수 있다.

interface DustInterface {
	// 어노테이션 안쪽에 Http 메서드를 적어준다.
    @GET("getCtprvnRltmMesureDnsty")
    // suspend를 통해 비동기 작업을 진행한다.
    // @QueryMap param을 통해 API제공처에서 요청한 요청변수를 전달한다.
    suspend fun getDustItem(@QueryMap param: HashMap<String, String>): DustData
}

3. 싱글톤 객체로 Retrofit빌더를 만든다.

서버에서 데이터를 받아올때마다 Retrofit Builder를 생성하고 삭제하는건 상당히 비효율적이기때문에 싱글톤으로 Retrofit Builder를 생성한 뒤 인터페이스를 연결해준다.

object DustClient {
	// 로깅을 위한 OkHttp클라이언트를 만들어주는 메서드
    private fun createOkHttpClient() : OkHttpClient {
        val interceptor = HttpLoggingInterceptor()

        interceptor.level = HttpLoggingInterceptor.Level.BODY
        return OkHttpClient.Builder()
            .connectTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .addNetworkInterceptor(interceptor)
            .build()
    }
    
    // 레트로 핏에 사용될 Base URL을 변수로 저장
    val BASE_URL = "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/"
    // 레트로 핏 빌더로 레트로핏 생성
    val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create()) // Json을 Gson으로 변환 하기위해 추가
        .client(createOkHttpClient()) // 로깅을 위한 클라이언트 추가
        .build()
    // 빌드된 레트로핏에 인터페이스를 연결해준다
    val DustClient: RetroInterface = retrofit.create(DustInterface::class.java)
}

3가지를 준비하면 Retrofit을 사용하기 위한 준비가 끝난다.

이제 원하는 곳에서 Retrofit을 통해 데이터를 받아서 사용할 수 있다.


데이터 받기

	...
    private var items: MutableList<DustItem> = mutableListOf()

    private fun getItems() {
    	// suspend를 통해 비동기 작업을 선언한 경우에는 백그라운드 코루틴에서 실행되어야한다.
        lifecycleScope.launch {
            val responseDust = RetroClient.retroAPI.getDustItem(setUpDustParameter())
            
            items = responseDust.response.dustBody.dustItems!!
           	// View를 업데이트 하기위해서 IO코루틴에서 실행
            withContext(Dispatchers.IO) {
                var list = mutableListOf<String>()
                items.forEach {
                    list += "${it.sidoName} ${it.stationName}: ${it.pm10Value}\n"
                }
                binding.tvOutput.text = list.joinToString("")
            }
        }
    }
    
    // getDustItem으로 전달될 해시맵을 생성하는 함수
    // 해당 해시맵이 전달되면 API의 요청변수로 전달되며 해당 변수에 맞는 값을 가져온다.
    private fun setUpDustParameter(): HashMap<String, String> {
        val authKey = "API 제공처에서 전달받은 인증키"

        return hashMapOf(
            "serviceKey" to authKey,
            "returnType" to "json",
            "numOfRows" to "100",
            "pageNo" to "1",
            "sidoName" to "서울",
            "ver" to "1.0"
        )
    }

풀어놓고 보면 간단하지만 익숙해 지기 위해서는 제법 숙련이 필요할 듯 하다.


오늘은 어땠나요?

꾸역꾸역 집어넣었다... 지식...

살아남았다... 오늘도...

profile
개발 첫걸음! 가보자구!

0개의 댓글