들어가는 글: 요즘 Rx 코딩을 현업에서 어떻게 쓸 수 있을까? 요리조리 연구를 해보고 있습니다. 배움이 짧아서 쉽지는 않네요^^;; 오늘은 구구단을 좀더 다듬어 보겠습니다.
3화에서는 내가 몇단인지 수동적으로 지정을 하고 그대로 출력하였습니다.
이제는 버튼도 추가하고 내가 원하는 dan을 출력하도록 하겠습니다.
UI 화면
이제 Print 버튼을 눌렀을 때 구구단을 출력하는 Rx 코드를 만들어보겠습니다.
먼저 3화에서 사용했던 코드입니다.
이제 '3단'을 하드코딩하지 않고 UI에서 가져와서 구구단을 출력합니다.
UI에서 9단으로 바꾸고 Print 버튼을 누르면 구구단이 출력됩니다.
여기가 끝일까요?
다시 코드로 돌아가봅니다. (위의 코드 리바이벌)
저는 여기까지 코딩하고 몇가지 의문이 들었습니다.
1) input을 받아오는 코드를 Rx 안으로 넣을 수 있을까?
2) guguResult.setText() 하는 코드도 Rx 안으로 넣을 수 있을까?
제가 생각하는 Rx적인 사고방식에서는
** input / operators / output 프레임워크는 제가 순수하게 공부하며 만들어낸 조어입니다.
1) input : 9라는 숫자를 받아온다.
2) operators:
9라는 숫자를 받아서 , range(1,9)로 뺑뺑이 돌리고 , 각각에 구구단 row를 만든다.
3) output: 만든 문장을 출력한다.
입니다.
그런데.. range() 까지는 알겠는데.. input값(예를 들면 9)을 range 뺑뺑이 돌면서 받아오는 방법을 모르겠습니다. (나중에 알게 되면 공유드릴께요^^;; 이런게 Rx적인 사고방식이 어려운 것 같습니다. rx operators 전집[링크: http://reactivex.io/documentation/operators.html]을 읽어봐도 확 감이 안오네요 T.T
1번은 포기하고..
2번을 Rx 안으로 넣어보겠습니다.
아마도 저를 포함한 Rx 초심자들의 고민은.. 어떻게 하면 기존 코드(명령형 사고방식으로 작성한 코드)를 얼마나 Rx코드(혹은 함수형 코드라고도 볼 수 있습니다)로 대체할 수 있느냐 일 것입니다.
대체율이 낮으면 기존코드와 뒤섞이게 되어 아주 '특수'한 경우에만 사용이 한정되거든요.
가능하면 대체율이 높았으면 좋겠다는 바램입니다.
목표는 2) guguResult.setText() 하는 코드도 Rx 안으로 넣을 수 있을까? 입니다.
제가 작성한 코드는 아래와 같습니다.
사실 가독성은 오히려 떨어진 것 같기도 합니다.
우리의 제1원칙인 '명령형 프로그래밍을 지양한다'에 위배되기 때문입니다.
여기서 유념할 것은 range() 다음에 row는 Integer 인데 , 그 다음 row는 String입니다. 그래도 같은 이름을 사용한 것은 이것이 이해하기 쉽기 때문입니다.
1..9 까지 range를 돌리는데 우리는 row를 만들어갈꺼야라는 작성자의 의도를 표현하기 용이합니다.
.map(row -> {
if (row == 1) guguResult.setText("");
return dan + " * " + row + " = " + (dan * row);
})
위의 코드를 보면 람다 표현식에 { } 가 추가되었습니다.
BEFORE 코드
.map(row -> dan + " * " + row + " = " + (dan * row))
BEFORE 코드는 원래 row -> { return dan + " * " + row + " = " + (dan * row);}
의 축약코드입니다.
3화의 구구단은 '3단'으로 한정되어 있기 때문에 예외처리할 필요가 없습니다.
하지만 4화에서는 사용자가 문자를 입력하거나 공백을 입력할 수도 있고 심지어는 음수도 가능합니다. 구구단은 말그대로 1~9까지의 숫자만 유효합니다.
그에 대한 예외처리를 해보겠습니다. 현재코드로 '-'라고 입력하면 죽습니다 T_T
예외처리가 추가된 코드입니다.
음.. 점점 코드가 지저분해집니다.
이제 올바른 그리고 우리가 실무에서 적용할 수 있는 Rx 코드일까요? 심각하게 고민을 해봐야겠습니다.
마지막의 subscribe() 문을 봐주세요. 무언가 추가되었습니다.
에러가 발생하면 Toast를 띄웁니다.
.subscribe(
guguResult::append,
e -> Toast.makeText(mContext,
"GuGuDan should be between 2 and 9 dan.",
Toast.LENGTH_SHORT).show());
subscribe()의 생명주기는 크게 3가지 이벤트를 가지고 있습니다.
1) onNext() : 우리가 맨날 보던거..
2) onError() : 예외 발생시 (새로 추가된 e-> ... 코드)
3) onComplete() : 마지막 데이터까지 처리했을 때 (이건 나중에 보여드릴께요)
subscribe() 함수는 다음과 같은 메소드 오버라이드를 지원합니다.
한마디로 subscribe(onNext, onError, onComplete) 라고 생각하시면 됩니다.
public final Subscription subscribe(final Action1<? super T> onNext)
public final Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError)
public final Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError, final Action0 onCompleted)
현재 위의 코드는 예외 발생이 두 군데서 발생하는 문제점이 있습니다.
1) Integer.parseInt()에서 예외 발생
2) Observable() 안에서 예외 발생
앞서 int dan = Integer.parseInt() 코드를 Rx 안으로 넣으려다가 실패했다고 말씀드렸죠,
이렇게 Rx 코드의 커버리지가 낮으면 예외 처리가 지저분해지는 문제가 발생합니다.
실무적으로는 이러면 못씁니다..
아주 단순한 구구단만 가지고도 Rx의 많은 부분을 얘기할 수 있습니다.
역시나 데이터 처리 로직을 어떻게 '깔끔하게' 작성할 수 있는지가 Rx 코드의 관건입니다.
이 부분은 저도 좀더 고민을 해볼께요.
다음은 EditText 뿐만 아니라 다른 UI 컴포넌트와도 붙여보도록 하겠습니다.
고수님들의 많은 의견 부탁드립니다.
2016.9.18 @Home