brunch

You can make anything
by writing

C.S.Lewis

[RxJava] #5 구구단 Complete

들어가는 글: RxJava를 공부한지 일주일이 다 되어 갑니다. 그 간단한 구구단 하나를 만드는데도 쉽지가 않네요. 4화에서 많은 고수님들이 힌트를 주셨습니다. 모두들 감사합니다. 드디어 제가 원하는 구구단이 나왔습니다. 함께 감상하시죠^^ 


1. 구구단 Complete


4화에서 제 구구단에는 세 가지 문제점이 있었습니다. 


1) input을 받아오는 코드를 Rx 안으로 넣을 수 있을까? 

2) guguResult.setText() 하는 코드도 Rx 안으로 넣을 수 있을까? 

3) <예외처리> 코드는 예외 발생이 두 군데서 발생하는 문제점이 있습니다. 

     A) Integer.parseInt()에서 예외 발생 

     B) Observable() 안에서 예외 발생 


BEFORE 코드입니다. (4화) 


AFTER 코드입니다. 

훨씬 rx스럽죠? ^^ 


첫번째 이슈, input을 받아오는 코드를 Observable.just()에 넣었습니다. 

그리고 Integer.parseInt()로 다시 map() 처리하였습니다. 두개를 한번에 쓸 수도 있지만 이렇게 


값을 받아와서 / 

Integer로 바꾸고/ 

처리한 것입니다. 이것이 코드의 가독성이 더 좋다고 생각합니다. 


두번째 이슈, guguResult.setText()를 rx로 넣는 것은 결과적으로 바람직하지 않은 것 같아서 오히려 밖으로 뺐습니다. BEFORE코드의 경우 억지로 rx로 넣으려고 했더니 오히려 코드의 간결성이 훼손되었습니다. 


실무에서 어느 코드를 rx로 넣어야 할지 아니면 외부로 빼야 할지를 결정하는 것이 단순하지는 않습니다. 


2. flatMap의 마법 


위에서 첫번째 이슈를 해결한 것을 아주 쉬운 것 처럼 얘기했지만 (몇일동안 고민을 하여 T_T)  

사실 결과적으로 flatMap()을 쓰지 않았다면 just()에 guguEdit.getText().toString()을 넣을 수 없었습니다. 


flatMap의 부분만 다시 인용합니다. 

.flatMap(dan -> Observable.range(1,9),
        (dan, row ) -> dan + " * " + row + " = " + (dan*row))


어찌보면 구구단의 가장 핵심코드입니다. 

많은 문서에도 나와 있듯이 flatMap()의 개념을 이해하고 실제로 적용해보는 것은 꽤나 도전적입니다. 

제가 flatMap에 관하여 따로 읽어본 문서만... 

1) RxJava Essentials(79p~ 80p) : 먼 소린지 알 수가 없다 T_T 

2) [번역] RxJava에 대해서 찾아보고 써봤다 [링크]  : 잘 설명되어 있어요. 그래도 먼가 부족..

3) FlatMap (part 1) [링크] : 영어 자료.. 아직 내공이 @.@ 

4) 공식 홈페이지의 flatMap 설명 [링크] : 영어 자료.. 그림이 있으나 예제가 없어서 좀.. 


이정도 입니다. ^^;; 

그러다가 결정적인 힌트를 발견하고 답을 작성하게 되었습니다. 


간단히 제가 이해한데로 서술해보면 

flatMap()은 값을 또다른 Observable로 변환해주는 operator 입니다. 위의 예에서 처럼 연속적으로 매핑을 할 수도 있습니다. 위의 코드의 (두번째줄)을 보시면 처음에 받아온 dan 값을 두번째 줄에서도 그대로 활용할 수 있습니다. dan을 입력으로 받아와서 range(1,9)로 뻥튀기 합니다. 그 다음줄에서 dan을 그대로 받아오고 뻥튀기 한 것을 row 값으로 다시 받아옵니다. 


flapMap의 기본 개념은 (출처: http://reactivex.io/documentation/operators/flatmap.html

빨간 공을 가져와서 다이아몬드 2개로 , 녹색 공을 가져와서 녹색 다이아몬드 2개로, 파란 공을 가져와서 파란 다이아몬드 2개로.. 만들어줍니다. 녹색 다이아몬드 두번째를 만들기전에 파란 공이 들어오게 되면 파란 다이아몬드가 먼저 생성되는데 이를 끼어들기(interleaving)이라고 합니다. 


자매품인 concatMap()에서는 이러한 끼어들기를 방지합니다. 


flatMap()은 한번쯤 뛰어넘어야 하는 첫번째 관문입니다. 저도 아직 완벽하게는 몰라요. 

 

3. 예외처리의 통합 


세번째 이슈는 예외처리를 한곳에서 하지 못한다는 것이었습니다. 

이래서는 실무에서 사용할 수 없었기에 제 나름대로는 "치명적인 단점"이라 표현하였습니다. 


내가 원하는 코드를 rx로 넣으니 자연스럽게 한 곳에서 예외 처리를 할 수 있습니다. 

예외가 발생할 수 있는 부분만 재인용합니다. 

.map(dan -> Integer.parseInt(dan))
.filter(dan -> 1 < dan && dan < 10)


아래는 예외 발생코드입니다. 

.subscribe(
    guguResult::append,                                                       //이건 정상적인 경우 
    e -> Toast.makeText(mContext,                                     //onError의 경우 
            "GuGuDan should be between 2 and 9 dan.",
            Toast.LENGTH_SHORT).show());

첫번째, EditText에 있는 값을 파싱하면서 NumberFormatException인지 알 수 있습니다. 

두번째, 2단에서 9단까지만 강제할 수 있습니다. 

(** 원래는 이 경우에도 예외 toast를 발생하고 싶었는데.. 깔끔한 코드가 나오지 않아서 일보 후퇴했습니다. 아시는 고수님들의 많은 참여를 부탁드립니다^^;;


4. 고수님들의 조언들 


4화가 나가고 많은 분들이 페북 댓글 / 브런치 댓글을 달아주셔서 큰 힘이 되었습니다. 

그리고 공부할게 더 많다는 것도 알게되었습니다. T_T 


향후 공부 방향을 정하는데 큰 도움이 될 것 같아 인용합니다. 

(정승욱님) 

BehaviorSubject 를 이용하시고 클릭 or textchange 일 때 subject.onNext 하시면 좀 더 Reactive 에 맞는 사용법이 될거라 생각해요.
현재의 예제는 Reactive 라기보단 Stream-api 의 Backport 에 가까운 사용법이에요.

>> 제가 현재까지 공부하기론 Subject는 Observable + Observer의 개념이라 좀더 순수하게 Observable과 Subscriber를 사용해보고 싶었습니다. 피드백 감사합니다. 


(김호진님) 

다른 걸 더 해보기 전에 지금 코드를 개선해보는 게 좋을 것 같아요
range는 없던 데이터를 만들어내는 거니까 입력된 걸 쓰려면 시작점이 달라져야겠죠. 중간에 들어간 if도 다 operator로 대체할 수 있어요 훨씬 그럴듯해질 거예요

>> 원래 ListView나 다른 UI 컴포넌트에 연동하는 걸 해보려고 했는데, 맞는 말씀 같아서 5화를 준비하는데 큰 힘이 되었습니다. 구구단 Complete라는 제목을 붙이니 더욱 뿌듯하네요. 감사합니다^^ 


(한주영님) 

"Rx안으로 넣을 수 있을까?" 라는 의문은 매우 좋은 것 같습니다. 저 같으면 안드로이드와의 접점부분을 얇게 유지하려고 노력할 것 같습니다. Rx로 그리고자 하는 로직이나 그래프에서 시작부분과 끝부분으로 최대한 밀어내는 거죠. 그리고 그러다 보면 그 얇은 층이 대부분 라이브러리로 공유되어 있을 것 같아요. RxBinding같은 것이 이에 해당하겠네요. (하략) 

>> 네네 말씀하신데로 어느 코드를 Rx안으로 넣을지 아니면 밖으로 넣을지 (커버리지)에 대한 이슈는 실무에 적용하게 되면 많은 쟁점들이 나올 것 같습니다. 갈 길이 머네요. 감사합니다. 


모두들 감사합니다. 

앞으로 좀더 쉽게 쉽게 접근할 수 있는 Rx코드를 만들어보겠습니다. 

6화에서 만나요 


2016.9.20 @Home

매거진의 이전글 [RxJava] #4 구구단에 UI 연결하기
작품 선택
키워드 선택 0 / 3 0
댓글여부
afliean
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari