Retrofit
은 HTTP API
요청 및 응답처리를 단순하게 만들어, 보다 손쉽게 네트워크 통신을 구현하도록 도와주는 라이브러리이다.
Retrofit
자체에서 비동기 작업을 지원하기 때문에 보다 손쉽게 비동기 처리를 할 수 있다.
Rtrofit
을 사용하기위해서는 Menifest
에 퍼미션을 설정해주고 Gradle
에 라이브러리를 추가해준다.
<application>
...
<!-- 어플리케이션 수준에서 usesCleartextTraffic를 true로 설정해야 http를 불러올 수 있음 -->
<!-- https만 사용할 예정이라면 따로 설정할 필요가 없다 -->
android:usesCleartextTraffic="true"
...
</application>
<!-- API는 인터넷을 사용함으로 인터넷 퍼미션을 설정해준다 -->
<uses-permission android:name="android.permission.INTERNET" />
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가지 요소가 있어야 사용이 가능하다.
요청한 데이터는 보통 XML
이나 Json
으로 받아올 수 있는데, 안드로이드의 경우 Json
데이터를 자동으로 변환해서 객체를 생성해주는 Gson
타입으로 변환이 가능하기 때문에 Json
데이터를 받아오면 보다 손쉽게 데이터의 변환이 가능하다.
데이터 타입은 API
가 제공하는 항목들을 보고 원하는 항목을 프로퍼티로 만들어야하며, 내부에 배열이 있는경우 각각의 데이터 클래스로 만들어줘야 한다.
{
"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으로 받아온 데이터를 정의하는 클래스
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
)
API제공처에서 원하는 요청 방식에 따라 인터페이스를 정의한다. 정의한 인터페이스를 Retrofit
에 연결해 인터페이스의 메서드로 원하는 동작을 할 수 있다.
단순하게 데이터를 가져오는 경우 @GET
어노테이션을 사용해 테이터를 가져올 수 있다.
interface DustInterface {
// 어노테이션 안쪽에 Http 메서드를 적어준다.
@GET("getCtprvnRltmMesureDnsty")
// suspend를 통해 비동기 작업을 진행한다.
// @QueryMap param을 통해 API제공처에서 요청한 요청변수를 전달한다.
suspend fun getDustItem(@QueryMap param: HashMap<String, String>): DustData
}
서버에서 데이터를 받아올때마다 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"
)
}
풀어놓고 보면 간단하지만 익숙해 지기 위해서는 제법 숙련이 필요할 듯 하다.
꾸역꾸역 집어넣었다... 지식...
살아남았다... 오늘도...