본문 바로가기
도서/코틀린 코루틴

Retrofit 과 Coroutine 간의 Viewmodel에선 Dispacher.IO로 전환해야 할까?

by 안스 인민군 2023. 11. 30.

요즘 코루틴을 공부하며 서버 통신 및 내부 저장은 Dispacher.IO안에서 실행 한 후 Main으로 넘겨줘 ui에 반영해준다는 말을 지겹게 들었다.

이유는 Dispacher.IO는 필요에 따라 추가적으로 스레드를 더 생성하거나 줄일 수 있으며 최대 64개까지 생성이 가능하기 때문이라고 한다.

또한 Main을 점유하고 있으면 UI에서 작업하기에 ANR이 발생 할 수 있다고 한다.

그래서 구글에 나온 예시 코드를 보면 ViewModel.scope 안에 Dispacher.IO를 감싼 형태거나

먼저 Dispacher.IO 에서 코루틴 스코프를 설정하여 데이터를 가져온 후 내부에서 Dispacher.Main 으로 감싼 형태를 볼 수 있다.

viewmodel 예시

이후 UseCase - Reopository - DataSorce - Service를 통해 Retrofit 통신을 통해 네트워크 작업을 하기 위한 내부 코드들을 suspend를 감싸 통신을 하곤 한다.

그런데 김준비님의 Coroutine Deep Dive 영상을 보고 알아낸 사실이 있다.

How to use Coroutine in Retrofit 목차를 보면 Retrofit2.6.0에서 Retrofit 함수앞에 suspend 를 붙여주면 레트로핏 내부에서 Call.await()함수를 통해 enqueue를 통해 비동기로 받은 값을 중단점으로 잡은 후 자연스럽게 IO 에서 네트워크 처리 후 UI에서 처리할 수 있는 값으로 넘겨준다. 그러므로 별도의 withContext 를 이용하여 Dispatcher.IO와 같은 스케쥴러를 지정해 줄 필요성이 없는 것 이다.

또한 Dispacher.Main 보단 Dispacher.Main.immediate 를 사용하는 것이 즉시 실행하므로 이전 작업들의 queue에 쌓여 ANR이 발생하느 것을 막아준다.

그런데 ViewModelScope의 내부를 보면 Dispacher.Main.immediate으로 되어 있는 것을 알 수 있다.

public val ViewModel.viewModelScope: CoroutineScope
// ...
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
// ...

그러므로 이를 바탕으로 Retrofit을 사용하여 통신을 할때 withContext(Dispatcher.IO)를 벗겨내도 성능 저하나 ANR을 일으키지 않는다.

 

추가로....

그렇다면 언제 withContext(Dispatcher.IO)를 사용하면 좋을까?

링크에 따르면 일반적으로 IO -> UI -> IO로 돌려주는 과정이 있기에 withContext(Dispatcher.IO)를 지정할 필요는 없다고 한다.

대신 Room을 이용한 백업이나 데이터 캐싱을 하는 경우는 좋다고 한다.

나 또한 동의하는 부분은 UI -> Retrofit 통신 -> UI 와 Room 의 형식으로 UI에 업데이트함과 동시에 Room에 저장하는 동작이라면 IO로 감싼 후 Flow를 통해 작업을 해주는 것이 옳다고 본다.

 


참고

https://androidpangyo.tistory.com/192

https://thdev.tech/kotlin/2021/01/12/Retrofit-Coroutines/

https://developer.android.com/kotlin/coroutines/coroutines-best-practices?hl=ko