[Retrofit] Call vs Response, and Kotlin Result

Minji Jeong·2022년 7월 28일
3

Android

목록 보기
29/39
post-thumbnail
Retrofit을 사용해서 서버에 요청을 하고 응답을 받아올 때 Call 또는 Response를 사용할 수 있다. 전부터 이 둘의 차이가 무엇인지 궁금하기도 했고, Response를 Kotlin Result 로 래핑해서 처리할 수 있는 라이브러리도 있어서 소개도 할겸 이렇게 포스팅을 남기기로 했다.

1. Call

@PATCH("END_POINT")
fun changeUserInfo(@Body user: User):Call<Void> 
RetrofitClient.userService.changeUserInfo(user)
	.enqueue(object : Callback<Void>{
		override fun onResponse(call: Call<Void>, response: Response<Void>) {
        	if (response.isSuccessful){ 
            	... 
            } else {
            	...
            }
        }
        override fun onFailure(call: Call<Void>, t: Throwable) {
           	Log.e(TAG, t.toString())
        }
 })

Call을 사용해 서버에 요청을 보낼 때, 각각의 Call은 자체적으로 HTTP 요청과 응답 쌍을 생성한다. Call은 execute()를 사용해 동기적으로, 또는 enqueue()를 사용해 비동기적으로 실행될 수 있다.

Call.execute()

execute()는 동기적으로 요청을 보내고 응답을 반환한다. execute()를 사용해서 요청을 하면 해당 요청이 차단되고 반환받은 응답을 바로 사용할 수 있다.

하지만 이 메서드를 사용하는건 권장되지 않는다. Retrofit에서 동기 호출은 메인 쓰레드에서 실행된다. 이것은 곧 execute()가 실행되는 동안 UI가 차단된다는 것을 의미하고, 이 기간동안 유저와 화면 간의 상호작용은 불가능하다.

Call.enqueue()

enqueue()는 비동기적으로 요청을 보내고 응답을 반환한다. 따라서 execute()와 다르게 UI를 차단하지 않고, 서버와의 통신 중에도 유저와 화면 간의 상호작용이 가능하기 때문에 권장되는 방법이다.

2. Response

 @POST("END_POINT")
 suspend fun login(@Body user: User): Response<Void> 
suspend fun login(user: User) {
	try {
    	val response = RetrofitClient.userService.login(user)
        if (response.isSuccessful){
       		...
        }else {
        	...
        }
    }catch (e: Exception){
    	Log.e(TAG, e.toString())
    }
}

Response를 사용하면 요청 이후의 응답을 받을 수 있다. Response는 성공일 수도, 실패일 수도 있기 때문에 각 케이스에 대한 핸들링을 해줘야 하는데, Response를 사용할 경우 Call.enqueue()를 사용할 때처럼 Callback methods(onResponse, onFailure)가 아닌 위 예제처럼 try ~ catch를 사용하거나 다른 방식을 사용해서 각 케이스에 대한 핸들링을 해줘야 한다.

3. Call vs Response

Call은 서버에 대한 요청과 응답 결과에 대한 반환을 한 번에 수행하고,
Response는 요청 이후 응답 결과에 대한 반환만 수행한다. 코드를 보면 Call.enqueue()를 사용했을 때는 응답 결과의 성공 유무에 대해 직관적으로 처리할 수 있다는 장점이 있고, Response를 사용했을 경우 조금 더 간결하게 코드를 작성할 수 있다는 장점이 있다. 둘 중 무엇을 사용하든 제대로만 쓴다면 문제는 없지만, RxJava나 Coroutine를 사용해서 서버와 통신하고자 한다면 Call.enqueue()를 사용할 필요가 없기 때문에 Response를 사용하는 것이 더 좋을 것 같다. 나는 Call보다 Response를 사용할 때 코드가 좀 더 간결해지는게 좋아서 Response를 선호한다.

4. Response as Kotlin Result

👉 skydoves / retrofit-adapters

이 라이브러리를 사용하면 Retrofit의 Response를 코틀린의 Result 클래스로 모델링 할 수 있다. Response와 비교했을 때 큰 차이는 없는 듯 해서 취향에 맞게 사용하면 될 것 같다. 다만 반환값이 Void일 경우에는 에러가 발생하기 때문에, 이런 경우는 Response로 대체하고 있다.

Response

suspend fun idCheck(user_id: String) {
	val response = RetrofitClient.userService.idCheck(user_id)
    if (response.isSuccessful){
    	val body = response.body()
		body?.let { ... }
    }else {
        ...
    }
}

Result

suspend fun idCheck(user_id: String) {
	val result = RetrofitClient.userService.idCheck(user_id)
    if (result.isSuccess) {
    	val body = result.getOrNull()
        body?.let { ... }
    } else {
    	Log.e(TAG, result.toString())
    }
}

result.isSuccess : 응답 결과가 성공적일 경우 true를 반환한다.
result.isFailure : 응답에 실패했을 경우 true를 반환한다.
getOrNull() : 응답 결과가 성공적인 경우 캡슐화된 값을 반환하고, 실패했을 경우 null을 반환한다.

References

https://howtodoinjava.com/retrofit2/retrofit-sync-async-calls/
https://stackoverflow.com/questions/64124670/call-or-response-in-retrofit
https://square.github.io/retrofit/2.x/retrofit/retrofit2/Call.html
https://square.github.io/retrofit/2.x/retrofit/retrofit2/Callback.html
https://square.github.io/retrofit/2.x/retrofit/retrofit2/Response.html
https://jeongupark-study-house.tistory.com/208
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/

profile
Mobile Software Engineer

0개의 댓글