Android 비동기 처리 Coroutine vs RxJava

김규성
지디지인천,송도 & 플러터송도
8 min readMay 5, 2023
Coroutine vs RxJava

최근 몇 년 동안 안드로이드 개발자들은 비동기 프로그래밍을 처리하는 데
두 가지 인기 있는 라이브러리를 사용했습니다.

바로 Coroutine과 RxJava입니다.

개인적으로 어떤 기술이 무조건 옳다는 생각은 좋지 않다고 생각합니다. Coroutine RxJava를 비교한 다른 글의 결론도 이러했습니다.

RxJava와 Coroutine간에 승자와 패자는 없다!

그렇다면, 어떤 상황에서 Coroutine과 RxJava를 사용하면 좋을지
사례를 중심으로 정리
해 보도록 하겠습니다.

Case1. 회사의 프로젝트가 Java 기반의 프로젝트일 때

아시다시피 CoroutineJava에서 사용할 수 없습니다. 따라서 Java 기반의
프로젝트에서 비동기 처리를 할 때 RxJava를 사용하는 것이 좋은 선택이 될 수 있습니다. 저 또한 이전에 회사의 Java 프로젝트에서 생긴 문제를 RxJava의 연산자를 이용해 해결한 경험이 있습니다.

아래 코드처럼 Java 코드로 비동기 처리와 관련된 문제를 바로 해결할 수
있다는 것이 RxJava의 장점
입니다.

//거래량을 emit할 PublishSubject 생성
private PublishSubject<Int> volumePublishSubject = PublishSubject.create();

//아래 emit한 거래량을 observe하는 코드
volumePublishSubject.subscribe(value -> /* value를 통해 차트를 갱신하는 코드 */);

//거래량을 받아 PublishSubject에 값을 emit하는 함수
private void emitVolume(int volume) {
volumePublishSubject.onNext(volume);
}

Case2. 앱의 성능 개선이 중요한 이슈일 때

이 상황에서는 Coroutine을 사용하는 것이 적절합니다.

Kotlin coroutines are very lightweight and efficient (as you’ll see later in these simple tests, the amount of memory used by RxJava is generally higher compared to coroutines and this leads to a slower app given the higher CPU usage for the garbage collection of all the objects generated by RxJava; this also translates into higher battery consumption of course)

RxJavaCoroutine을 벤치마크한 글에 따르면 Coroutine은 매우 가볍고
효율적이며 RxJava가 일반적으로 Coroutine보다 메모리를 많이 소비하고
다양한 앱의 성능 이슈로 이어질 수 있다고 합니다.

그러므로 성능이 중요한 이슈라면, 같은 상황에서는 RxJava보다는 Coroutine을 선택하는 것이 좋습니다.

Coroutine은 왜 RxJava보다 가벼울까요?

RxJava는 스레드를 직접적으로 조작할 수는 없지만, Scheduler라는 개념이 있는데, Observable을 선언할 때 Schedulers.io()와 같은 형태로 설정할 수 있습니다.

Scheduler는 다양한 종류의 작업을 수행하기 위한 스레드 또는 스레드 풀
(스레드 모음)로 생각할 수 있습니다. 특정 스레드에서 작업을 실행해야 하는 경우 해당 스레드에 적합한 Scheduler를 선택하면 해당 Scheduler가 사용 가능한 스레드 풀에서 스레드를 가져와 작업을 실행하게 됩니다.

즉, RxJava는 내부 Scheduler를 통해 스레드 기반으로 작업 된다는 것을
알 수 있습니다.

스레드 기반의 RxJava는 어떤 작업을 기다릴 때 해당 스레드를 blocking 상태로 만들고, 해당 스레드가 사용되지 않는 상황에서도 다른 작업을 할 수 없는 상태로 둡니다. 하지만, 같은 상황에서 Coroutine은 스레드를 blocking 하지 않고, suspend 하기 때문에, 작업을 기다리는 동안 다른 작업을 진행할 수 있습니다.

숫자로 비교해 보면, 각 스레드에는 일반적으로 1MB 크기의 자체 스택이 있습니다. JVM에서는 스레드당 최소 64kb 스택 공간을 가져야 한다고 명시되어 있지만, Coroutine은 수십 바이트의 힙 메모리만 차지합니다.
Coroutine은 매우 적은 리소스로 매우 높은 수준의 동시성을 제공할 수
있습니다.

멀티 스레드도 병렬성을 제공할 수 있지만 blocking과 스레드 간 Context Switching으로 인해 리소스(메모리, 시간) 소모가 큽니다.

Coroutine은 스레드를 suspend하고 다른 작업으로 전환할 수 있도록 스레드를 blocking하지 않습니다. 많은 Coroutine이 아주 작은 작업을 수행하고 서로 자발적으로 전환하기 때문에 Scheduler로는 절대 얻을 수 없는 효율성과 빠른 실행을 제공할 수 있습니다.

위와 같은 이유로 Android 공식 문서에서도 CoroutineLightweight 하다고 소개하지 않나 싶습니다 :)

Case3. 대용량의 데이터 스트림을 처리할 때

Coroutine만으로는 단일 값만 반환하므로, 연속적인 데이터 스트림을 처리할 때는 RxJava가 적절할 수 있습니다. 또한 RxJava는 데이터 스트림을 처리할 수 있는 다양한 연산자(operator)Backpressure(배압) 전략을 제공하고
있습니다.

하지만, Coroutine에서도 Channel, Flow 등의 데이터 스트림을 처리할 수
있는 인터페이스를 제공
하고 있습니다. RxJava로 처리할 수 있는 작업은 모두 Coroutine으로 대체될 수 있기 때문에 Coroutine만 사용할 때 앱이 의존하게 되는 라이브러리가 하나 줄어들게 되어 앱의 의존성을 줄여준다는 장점이 있습니다.

그렇다면 RxJava를 모두 Coroutine으로 바꿔야 할까요?

반드시 그럴 필요는 없다고 생각합니다. RxJava로 작업한 것이 성능상
큰 문제가 없고, 규모가 큰 프로젝트라면 Coroutine으로 바꾸는 것이
의미 없이 큰 리스크를 지는 작업이 될 수 있다고 생각합니다.

둘 다 사용하면 안 될까요?

당연히 둘 다 사용할 수 있다고 생각합니다. 예를 들어 개발하고 있던
안드로이드 앱에서 RxJava를 기반으로 데이터 스트림을 주로 처리하고
있지만, 간단한 비동기 처리 작업에는 Coroutine으로 작성했을 때 동기적
코드와 유사해 더 직관적인 경우(박상권 님의 글에 좋은 예시가 있습니다.) RxJava와 같이 사용할 수 있을 것입니다. 이 외에도 여러 상황을 유동적으로 판단하는 것이 중요합니다.

--

--