21.07.05
공부한 것을 정리하는 용도의 글이므로 100% 정확하지 않을 수 있습니다.
참고용으로만 봐주시고, 내용이 부족하다고 느끼신다면 다른 글도 보시는 것이 좋습니다.
+ 틀린 부분, 수정해야 할 부분은 언제든지 피드백 주세요. 😊
by. ryalya
21년 8월부터 Android API 수준 30(Android 11)을 설정해야 한다. 그런데 Android11 부터는 AsyncTask가 공식적으로 Deprecated됐다.
AsyncTask was intended to enable proper and easy use of the UI thread. However, the most common use case was for integrating into UI, and that would cause Context leaks, missed callbacks, or crashes on configuration changes. It also has inconsistent behavior on different versions of the platform, swallows exceptions from {@code doInBackground}, and does not provide much utility over using {@link Executor}s directly
출처 : Android source project
위의 커밋을 해석하면
AsyncTask api는 Android에서 UI Thread에 직접 접근할 수 있게 해주는데, Thread 복제, 핸들러를 사용하지 않고도 백그라운드 작업을 하게 해준다. (백그라운드 작업과 UI간 상호작용을 단순화해주는 아주 좋은 아이였던 것...)
비동기 작업을 단순화하는데 효율적이긴 하지만 Context leak, 콜백 누락, Configuration 변경시 Crash에 대한 처리 문제를 거론하며 Deprecated를 선언했고, 그에 대한 대체재로 RxJava나 Kotlin의 Coroutines 사용을 권고하고 있다.
그래서 7월 말~8월 초에 런칭을 앞두고 있는 어플의 AsyncTask 부분을 모두 리팩토링 해야하는 상황이며, 우리 회사는 RxJava를 선택한듯 했다.
나는 런칭 이후, Android 앱의 유지보수를 맡게 되겠지만 RxJava에 대한 이해 없이 문제가 생길 때 마다 스택오버플로우와 구글링에 매달려 하루살이처럼 버티고 싶지는 않았다.
그래서 이번 기회에 RxJava에 대해 알아보려 한다.
그런데 러닝커브가 굉장히 높다해서 조금 걱정이 된다... 이해 할 수 있겠지...?😅
RxJava는 Java로 Reactive Programming을 할 수 있는 라이브러리이며, 비동기 프로그래밍과 함수형 프로그래밍 기법을 함께 활용한다.
ReactiveX는 관찰 가능한(Observable) 스트림을 사용하는 비동기 프로그래밍을 위한 API이다.
Reactive Programming은 무엇일까?
기존의 프로그래밍은 명령형(imperative)이다. 명령형 프로그래밍은 작성된 코드가 정해진 순서대로 실행된다.
개발자가 작성한 조건문, 반복문, 함수를 따라 compiler가 다른 코드로 이동한다.
그렇다면 반응형 프로그래밍(Reactive Programming)이란 무엇일까?
반응형 프로그래밍은 주변환경과 끊임없이 상호작용을 하는 프로그래밍을 의미한다.
하지만, 이는 프로그램이 주도하는 것이 아니라 환경이 변하면 이벤트를 받아 동작한다. 데이터가 변하면 알아서 캐치하여 반영한다고 한다.
쉽게 말해,
명령형(impereative)은 Pull 방식 : 데이터를 사용하는 곳(Consumer)에서 데이터를 직접 가져와서 사용하는 것이고,
반응형(Reactive)는 Push 방식 : 데이터의 변화가 발생한 곳에서 새로운 데이터를 Consumer에게 전달하는 방식인 것이다.
애플리케이션에서 RxJava와 같은 Reactive Programming을 하려면 누군가 Reactive Programming을 할 수 있는 기반 시설을 제공해주어야 한다.
즉, 데이터 소스를 정의할 수 있고 그것의 변경사항을 받아서 프로그램에 알려줄(push) 존재가 필요하다. JVM 위의 자바 언어로 구현해놓은 라이브러리가 RxJava이다.
Rx Java는 2013년 2월 넷플릭스에서 처음 소개했다.
ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.
It extends the observer pattern to support sequences of data and/or events and adds operators that allow you to compose sequences together declaratively while abstracting away concerns about things like low-level threading, synchronization, thread-safety, concurrent data structures, and non-blocking I/O.
출처 : Reactive 공식 사이트
위의 내용을 해석하면
java의 동시성 처리 이슈 해결
→ 클라이언트 요청 처리 시, 다수의 비동기 실행 흐름(스레드)을 생성하고, 그 결과를 취합하여 최종 리턴하도록 내부 로직 변경
다수의 비동기 흐름을 가능하게 해줌
→ 비동기 흐름을 compose해주는 연산자(Operator)제공.
개발자와 프로그램의 콜백 문제 해결
→ 콜백을 사용하지 않는 방향으로 설계
그런데 Rx Java에서 관찰 가능하다( Observable)는게 무슨 의미일까?
RxJava에서는 연산자(Operator)를 통해 기존 데이터를 참조, 변형하여 Observable을 생성하고, 비동기 연산을 필터링, 변환, 조합할 수 있다.
Observable
: 데이터의 변화가 발생하는 데이터 소스로서, 데이터 흐름에 맞게 Consumer에게 알림을 보내는 class.
RxJava에는 Observable을 구독하는 Observer가 존재하고, Observable이 순차적으로 발행하는 데이터에 대해서 반응한다.
Observable Class는 옵저버(Observer) 패턴을 구현한다. 옵저버 패턴은 객체의 상태 변화를 관찰하는 관찰자(옵저버) 목록을 객체에 등록한다.
그리고 상태 변화가 있을때마다 메서드를 호출하여 객체가 직접 목록의 각 옵저버에게 변화를 알려주며, 라이프 사이클은 존재하지 않고, 보통 단일 함수를 통해 변화만 알린다.
onNext()
: 하나의 소스 Observable에서 Observer까지 한 번에 하나씩 순차적으로 데이터를 발행한다.onComplete()
: 데이터 발행이 끝났음을 알리는 완료 이벤트를 Observer에 전달하여 onNext()를 더 호출하지 않음을 나타낸다.onError()
: 오류가 발생했음을 Observer에 전달한다.subscribe()
: Observable은 구독(subscribe)을 해야 데이터가 발행된다.
just() 함수만 호출하면 데이터를 발행하지 않는다.
따라서 Observable을 구독하여 데이터를 발행 후, 수신한 데이터를 원하는 방식으로 사용(System.out::println)한다.
자주 사용하는 연산자 몇 개만 알아보겠다.
create()
: onNext, onComplete, onError등의 알림을 개발자가 직접 설정(호출)해야 하는 Observable.
Observable<String> source = Observable.create(emitter -> {
emitter.onNext("Hello");
emitter.onNext("ryalya");
emitter.onComplete();
});
source.subscribe(System.out::println);
// 출력값
// Hello
// ryalya
Just
: 아이템을 그대로 발행하는 Observable을 생성. just()연산자의 인자로 넣은 아이템을 차례로 발행하며, 한 개의 아이템을 넣을 수도 있고, 타입이 같은 여러 아이템을 넣을 수도 있다.
Observable<String> source = Observable.just("Hello", "ryalya");
source.subscribe(System.out::println());
// 출력값
// Hello
// ryalya
map()
: RxJava의 연산자. 데이터를 원하는 형태로 매핑해준다.
ex) String to String, String to Integer 등
map() 의 인자로는 변수가 아닌 '어떤 함수'가 들어간다.
Marble Diagram(마블 다이어그램)은 RxJava를 이해하는 핵심 도구. 데이터의 흐름과 map(), flatMap() 함수 등의 연산자를 이해하는 데 큰 도움을 준다.
먼저 아래의 그림을 살펴보자.
출처 : RxJava 프로그래밍(1) - 리액티브 프로그래밍
맨 위 실선
: Observable의 시간 표시줄(timeline)이다. 시간순(왼쪽→오른쪽으로 흐름)으로 데이터가 발행되는 것을 표현.
별, 역삼각형 등의 도형들
: Observable에서 발행하는 데이터. 시간 순서대로 별, 삼각형, 오각형, 원 등의 도형을 발행한다. 데이터를 발행할때는 onNext 알림이 발생.
파이프 (|)
: Observable에서 데이터 발행을 완료했다는 의미이다. 한번 완료하면 이후에는 더이상 데이터를 발행할 수 없다. 완료하면 onComplete 알림이 발생.
⇣ (점선 화살표)
: 각 함수의 입력과 출력데이터.
가운데 박스
: 함수를 의미한다. 여기서 flip() 함수는 입력값을 뒤집는 함수이다. 따라서 입력값의 색상은 그대로 두고 모양을 위아래 180도 회전하여 뒤집는다.
아래 실선
: 함수의 결과가 출력된 시간 표시줄(timeline)
엑스 (X)
: 함수가 입력값을 처리할때 발생한 에러를 의미한다. 에러 발생 시에는 onError 알림이 발생.
이번에는 조금 더 복잡한 마블을 이해해보자.
위의 그림은 combineLatest() 함수를 사용하고 있는데 2개 이상의 Observable을 처리하고 있는데 시간 표시줄이 2개로 늘었다.
첫번째 Observable
: 같은 모양(원)이지만 색깔이 다른 도형을 발행.
두번째 Observable
: 모양은 다르지만 번호가 없는 도형을 발행.
combineLatest() 함수
: 첫번째 Observable의 도형과 두번째 Observable의 도형이 모두 들어오면 둘을 합성.
가장 아래 Observable
: combineLatest() 함수의 실행 결과
첫번째 Observable에서는 색상을 취하고, 두번째 Observable에서는 도형의 모향을 취하고 있다.
마무리하며
RxJava는 함수형 프로그래밍의 지원을 받기 때문에 함수형을 선호한다.
람다(lambda)를 사용하면 코드를 더 간소화할 수 있다.
위에서 사용한 샘플 예제들은 간소화 없이 사용했지만, 길고 복잡한 코드는 lambda를 사용하면 불필요한 익명 객체나 멤버 변수를 기재하지 않고 꼭 필요한 변수만 소스 코드에 작성하면 되므로 코드가 간단해져 가독성이 높아진다.
하지만 이 부분은 따로 좀더 깊게 공부해야 할 것 같다.
Observable 클래스 이해. ( ⭐ Hot Observable & Cold Observable개념)
map(), filter(), reduce(), flatMap() 사용법 이해.
생성 연산자, 결합 연산자, 변환 연산자 등 카테고리별 주요 함수를 이해.
subscribeOn()과 observeOn() 함수 차이 이해