[Android] Retrofit2을 사용할 때 Dispatcher.IO가 필요할까?

우발자·2025년 9월 8일
1

안드로이드 앱 개발을 할 때 retrofit을 많이 쓰시죠.
그때 Coroutine과 같이 사용을 할 때 Dispatcher를 설정해주고 있으신가요?

그렇다면 이제부터라도 안쓰시는 게 낫다.


Retrofit에서 interface를 정의하는 방식은 총 3가지가 있다.

1.execute()

이 메서드는 동기적으로 요청을 한다.
요청을 한 순간 해당 스레드가 막히고 따로 쓰레드를 지정안해주면
ANR이 발생할 수 있다.

2.enqueue()

이 메서드는 서버에 비동기적으로 요청을 하고 콜백을 통해 반환값을 받는다.
콜백을 통해 코드를 작성하면 가독성과 코드 양이 많아지므로 Coroutine과 같이 쓰는 걸 추천한다.

3.supend + enqueue()

// KotlinExtensions.kt
suspend fun <T : Any> Call<T>.await(): T {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        if (response.isSuccessful) {
          val body = response.body()
          if (body == null) {
            val invocation = call.request().tag(Invocation::class.java)!!
            val service = invocation.service()
            val method = invocation.method()
            val e = KotlinNullPointerException(
              "Response from ${service.name}.${method.name}" +
                " was null but response body type was declared as non-null",
            )
            continuation.resumeWithException(e)
          } else {
            continuation.resume(body)
          }
        } else {
          continuation.resumeWithException(HttpException(response))
        }
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

위에 코드를 보면 suspendCancellableCoroutine으로 블럭을 만들어서 enqueue를 이용하여 내부적으로 콜백을 구현한 걸 볼 수 있다. 그래서 별도에
enqueue()를 사용할 필요없이 호출만 해주면 내부적으로 처리해주기 때문에 가독성 측면에서 매우 좋다. 하지만 콜백을 직접 오버라이드 하는 게 아니여서 성공인지 실패인지는 직접 확인하여 처리해줘야된다.


왜 안써도 되는 것인지 이제 본격적으로 알아보자.

Retrofit 내부코드를 살펴보자


CallFactory란

CallFactory는 네트워크를 담당하는 OkHttpClient의 인터페이스다.

Dispatcher.kt에 있는 코드이다. 실제로 요청을 할 때 별도의 ThreadPool을 생성하여 동작시키고 있는 걸 확인할 수 있었다.

이걸 갑자기 왜 보여주냐면 enqueue을 실행하면 내부적으로 해당 dispatcher를 이용하여 네트워크 요청을 하게된다.!

enqueue내부 코드이다. promoteAndExecute()를 살펴보면


요약하자면 대기중인 요청을 검사하여 실행 가능하면 실행 리스트로 넘긴 후
스레드풀에서 동작하게 하는 로직이다. 여기서 executorService라는 스레드풀로 넘기는데

이게 바로 아까 봤던 별도로 생성된 스레드풀이다.

그래서 결론적으로는 io로 안해줘도 별도의 스레드풀을 생성하여 동작한다

callbackExecutor란

네트워크 통신이 끝난 후 콜백을 어느 스레드에서 받을 지 결정한다.


만약 아무 설정을 안해주면 Platform.callbackExecutor로 초기화가 되는데

안드로이드 개발이라면 Dalvik으로 가서 AndroidMainExecutor()로 생성이 될 것이다.
마지막으로 AndroidExecutor로 들어가보면 MainLooper에 가져와서 처리해주고 있는 걸 볼 수 있다.


이렇게 내부코드를 통해 말로만 듣던 어떻게 io에서 처리가 되고 콜백은 main에서 처리가 되는 지 눈으로 확인해보니깐 깊게 이해한 느낌이 들어 뿌듯하다.

앞으로 뭔가 궁금증이 생길때는 내부코드를 보는 버릇을 들여야 될 것 같다.

profile
나는 안드로이드 개발자다.

0개의 댓글