안드로이드 코틀린 기초 코드랩을 학습한 내용입니다.
https://developer.android.com/courses/kotlin-android-fundamentals/overview?hl=ko
서버로부터 json string 데이터를 잘 받아왔지만 실제로 우리가 필요한건 코틀린 객체지 json string이 아니다. 그래서 안드로이드에는 json string을 코틀린 객체로 바꿔주는 Moshi 라는 JSON parser 라이브러리를 제공해준다. retrofit은 Moshi를 이용해 작업하는 converter를 제공해주고 있다.
먼저 의존성을 gradle에 추가해주자
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
moshi 라이브러리를 사용하기 위해 의존성을 추가해준 다음, retrofit의 converter 의존성 코드도 수정 해줘야한다.
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
웹 서버로부터 받는 json 데이터는 다음과 같은 형태일 것이다.
[{"price":450000,
"id":"424906",
"type":"rent",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"},
...]
Moshi는 json data를 파싱해서 kotlin 객체로 변환 해준다고 했다. 해당 과정을 수행하기 위해 파싱된 결과를 저장할 데이터 클래스가 필요하다.
data class MarsProperty(
val id: String, val img_src: String,
val type: String, val price: Double
)
이름은 json data의 key가 되고 type은 json data value의 type과 동일하게 지정 해줘야한다.
변수에 _ 가 들어가면 코틀린 사용시 권장 하는 변수 표현식인 camel case와 달라 lint 경고가 발생할 수 있는데 그럴경우 다음과 같이 변경해주면된다.
@Json(name = "img_src") *val* imgSrcUrl: String,
그 다음 새로운 moshi 객체를 생성 해주고 retrofit 객체를 생성할 때 ScalarConverterFactory를 넣어줬던 부분을 MoshiConverterFactory로 변경 해준다.
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
그리고 기존에 `String 타입으로 받아왔던 response를 Data class로 정의 해준 MarsProperty로 받아올 수 있게 제너릭 타입을 변경 해준다.
interfaceMarsApiService{
@GET("realestate")
fungetProperties():
Call<List<MarsProperty>>
}
List 형태로 받은 이유는 api response가 json array 형태로 데이터를 반환 해주기 때문이다.
이제 해당 api를 호출하고 callback을 정의 해준 부분을 수정 해보자
MarsApi.retrofitService.getProperties().enqueue(
object: Callback<List<MarsProperty>> {
override fun onResponse(call: Call<List<MarsProperty>>, response: Response<List<MarsProperty>>) {
_response.value = "Success ${response.body()?.size} Mars properties retrived"
}
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {
_response.value = "Failure: " + t.message
}
})
지금까지 잘 따라왔다면 retrofit을 이용해 json array데이터를 받아서 moshi 라이브러리를 통해 kotlin object로 변경 해줄 수 있게 구현 했을것이다.
그런데 실제 api를 호출 할 때 enqueue()를 이용해 요청을 queue에 넣고 callback을 함께 전달해서 response와 failure 처리를 구현 해두었는데 코루틴을 사용하게 된다면 콜백을 사용하는 것 보다 훨씬 더 가독성이 좋아질 것이다. 그래서 이번 task에서는 콜백이 아닌 코루틴을 이용하는것으로 리팩토링 해보자.
콜백을 사용하는 것이 아닌 코루틴을 사용 하므로 List<MarProperty>를 반환할 수 있게 변경하자
//기존
interface MarsApiService {
@GET("realestate")
fun getProperties():
Call<List<MarsProperty>>
}
//코루틴 셋업
interface MarsApiService {
@GET("realestate")
suspend fun getProperties():
List<MarsProperty>
}
코드가 복잡해질 경우 가독성이 상당히 떨어지게 된다.
//기존
private fun getMarsRealEstateProperties() {
MarsApi.retrofitService.getProperties().enqueue(
object: Callback<List<MarsProperty>> {
override fun onResponse(call: Call<List<MarsProperty>>, response: Response<List<MarsProperty>>) {
_response.value = "Success ${response.body()?.size} Mars properties retrived"
}
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {
_response.value = "Failure: " + t.message
}
})
}
//코루틴 적용
private fun getMarsRealEstateProperties() {
viewModelScope.launch {
try {
val listResult = MarsApi.retrofitService.getProperties()
_response.value =
"Success: ${listResult.size} Mars properties retrieved"
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
}
}
쓰레드는 다른 코드 블럭을 실행 시키다가 response가 오면 다시 해당 지점에서부터 값을 받아와 listResult에 넣어줄 수 있게 된다.
이제 앱을 빌드하고 실행 해보면 기존과 동일하게 동작 하지만 코루틴을 적용 함으로써 훨씬 코드가 직관적이고 가독성이 좋아진다.