Android에서 네트워크 작업을 할 때 사용하는 대표적인 라이브러리는 아래와 같다.
- OkHttp
- Retrofit
두 라이브러리 모두 Square사에서 개발한 HTTP 통신 라이브러리이다.
OkHttp는 HTTP 통신을 간편하게 할 수 있는 기능을 지원한다.
Retrofit은 OkHttp를 조금 더 추상화하여, 직관적이고 편리하게 사용할 수 있다.
Retrofit은 OkHttp에 의존하고 있다.
OkHttpClient를 Retrofit의 Builder에 전달하여 생성하는 방식을 따른다.
결론적으로, Retrofit이 OkHttp에 비해 가진 장점은 크게 3가지라고 생각한다.
- interface와 어노테이션을 사용하여 직관적이며, Call을 직접 만들어줄 필요가 없다.
- String 타입으로 전달되는 JSON 형태의 body를 직접 파싱 할 필요가 없다.
- 응답에 대한 작업을 자동으로 Ui Thread에서 수행할 수 있도록 지원한다.
이러한 Retrofit의 장점을 OkHttp와 비교하여 살펴보겠다.
val okHttpClient = OkHttpClient.Builder() // 1.
.addInterceptor(...)
.build()
val productJson = Gson().toJson(Product(1, "과자")) // 2.
// 서버에 POST 하기 위한 Request Body
// Json 형태의 String을 RequestBody로 변환
val requestBody = productJson.toRequestBody("application/json".toMediaType()) // 3.
// 요청을 위한 Request
val request = Request.Builder().run { // 4.
url("https://localhost:8080/products")
header("Authorization", "Basic {token}")
post(requestBody)
build()
}
okHttpClient.newCall(request).enqueue(object : Callback { // 5.
override fun onFailure(call: Call, e: IOException) {
// Handle this
}
override fun onResponse(call: Call, response: Response) {
val jsonObject = JSONObject(response.body?.string() ?: "{}") // 6.
runOnUiThread {
... // Ui Logic
}
}
})
OkHttp를 사용하여 서버에 POST 요청을 보내는 방식이다.
1. OkHttpClient 인스턴스를 생성한다.
2. Body를 만들기 위해, 객체를 Json 형태로 변환해준다. (Gson, kotlinx-serialization 등 활용할 수 있음.)
3. productJson을 OkHttp에서 제공해주는 확장 함수 toRequestBody()를 통해 RequestBody 객체로 변환한다. 이때, MediaType을 지정해주어야 하는데, Content-Type을 "application/json"으로 지정해주었다. (Json 형태로 전달할 것이기 때문.)
4. 실질적으로 서버에 요청을 보내기 위한 request 객체를 생성한다. url, method, body를 지정해 준다.
5. 서버에 HTTP 통신을 요청한다.
- enqueue() 메서드를 사용하면 Background Thread에서 비동기적으로 수행된다.
- onResponse(), onFailure() 콜백을 통해, 성공 실패에 대한 처리를 쉽게 할 수 있다.
- execute() 메서드를 사용하면 현재 Thread에서 동기적으로 이루어진다.
6. 서버로부터 응답을 받는다.
- OkHttp는 response.body?.string()를 통해 Json 형태(String)를 만들고 이를 객체로 변환해주어야 한다.
- enqueue() 메서드를 사용했다면, Background에서 응답을 받는다.
위 방식은 과거 HttpUrlConnection을 통해 서버와 통신하는 방식에 비하면 정말 편리한 방식이다.
하지만 클라이언트들은 아직도 목마를 것이다.
필자가 OkHttp를 사용하면서 느낀 불편함은 아래와 같다.
1. 모든 Request, Response에 대해 , Json <-> Object 변환을 해주어야 한다.
2. 매번 Request 객체를 직접 만들어주는 것이 번거롭게 느껴지고, 가독성이 떨어진다.
3. onResponse가 Background Thread에서 이루어지기 때문에, UI 작업을 위해 매번 runOnUiThread, Handler(Looper.getMainLooper()).post { ... } 등의 코드가 필요하다.
4. 대부분의 경우, base url은 하나일 텐데 매번 full url을 지정해 주는 것이 불편하다.
이러한 단점을 보완하기 위해 Retrofit이 등장하였다.
위 코드를 Retrofit으로 변환해 보겠다.
private val okHttpClient = OkHttpClient.Builder().run {
addInterceptor(...)
build()
}
private val retrofit = Retrofit.Builder()
.baseUrl("https://localhost:8080")
.addConverterFactory(GsonConverterFactory.create()) // GSON 컨버터를 사용하여 JSON 자동 변환
.client(okHttpClient)
.build()
먼저, OkHttp와 다르게, Retrofit.Builder()를 통해 Retrofit 인스턴스를 생성해주어야 한다.
1. 공통적으로 사용될 baseUrl을 지정할 수 있다.
2. converterFactory를 지정함으로써, Json <-> Object 변환을 직접 해주지 않고 라이브러리가 대신해준다.
3. OkHttp의 OkHttpClient를 지정해주어야 한다. (각종 Request, Response에 대한 Intercept 처리가 가능하다.)
위 코드는 공통이므로, top-level function 또는 singleton 형태로 사용할 수 있다.
애플리케이션이 실행될 때 메모리에 올릴지, 사용하는 시점에 올릴지는 앱이 언제부터 서버와 통신하는지에 따라 개발자가 유연하게 선택하면 된다.
interface ProductService {
@GET("/products")
fun getProducts(): Call<List<Product>>
}
Retrofit은 Call을 반환해 주는 interface를 통해, 직관적이고 간편하게 개발할 수 있도록 도와준다.
OkHttp에서는 okHttpClient.newCall(request) 메서드를 호출하여 call을 만들어주어야 하기 때문에, Request를 만들어주는 작업이 불편하게 느껴졌지만, Retrofit은 이런 작업을 생략해 줄 수 있다.
또한, Annotation(GET, POST, PATCH 등)을 지정해 주어 훨씬 직관적이다.
+ ) 일반적으로 HTTP 통신을 위한 interface에는 Service Suffix를 붙여준다. ex) XxxService
val productService = retrofit.create(ProductService::class.java)
productService.getProducts().enqueue(object : Callback<List<Product>> {
override fun onResponse(
call: Call<List<Product>>,
response: Response<List<Product>>
) {
// Ui Thread에서 동작한다.
val products = response.body()
// ...
}
override fun onFailure(call: Call<List<Product>>, t: Throwable) {
// ...
}
})
retrofit.create(T::class.java)를 통해 T에 해당하는 Service 인스턴스를 생성해 줄 수 있다.
Call, Request는 자동으로 Retrofit에서 만들어주기 때문에, 개발자는 OkHttp와 동일하게 enqueue(), execute() 메서드를 호출하여 작업을 해주면 된다.
2가지 다른 점이 있다.
1. enqueue에 대한 Callback 작업이 Ui Thread에서 이루어진다.
2. response.body() 메서드 반환타입이 List<Product> 라는 점이다. 즉, 알아서 Json을 Object로 파싱 하여 개발자가 할 일이 매우 줄어든다.
2번이 Retrofit의 가장 큰 장점이라고 할 수 있는데,
-> 개발자가 Json의 Raw한 문자열을 잘못 파싱 할 수도 있는 문제를 줄여준다. (Type-safe 하다.)
-> 직접 파싱할 필요가 없기 때문에 코드가 간결해지고, 편리함을 제공해 준다.
Retrofit과 OkHttp 라이브러리를 적절히 사용하면 성능, 유지보수를 높일 수 있다.
"Retrofit이 OkHttp보다 성능이 우월하다" 라기보다는 이 둘을 적절히 활용하여 성능을 높일 수 있는 것이라고 생각한다.
OkHttp의 Interceptor를 통해, Request, Response가 직전, 직후에 작업을 처리할 수 있다.
예를 들어, Header에 Authorization 값을 지정하여 매 요청마다 Header를 지정해 줄 필요가 없는 등의 편리함이 존재한다.
'Android' 카테고리의 다른 글
[Android] Kerdy에서 다뤄온 Retrofit 에러 처리 (0) | 2023.09.04 |
---|---|
[Android] BroadcastReceiver 보안 이슈 (1) | 2023.05.17 |
[Android] API 33 onBackPressed() deprecated (0) | 2023.05.08 |
[Android] PendingIntent 공식문서 파헤치기 (0) | 2023.05.02 |
[Android] RecyclerView Animation (LayoutAnimation, ItemAnimator) (0) | 2023.04.28 |