RxJava - debounce

thsamajiki·2022년 11월 7일
0

RxJava

목록 보기
3/4
post-thumbnail

Debounce 개념 잡기

Rx Java를 사용하다가 접한 개념인데 정리하지 않으면 차이점이 햇갈릴거 같아서 글로 남긴다. 리서치를 해보니 전반적으로 사용되는 기술인듯 하다. (예제는 웹쪽이 압도적으로 많았다.)


들어가기 전에

어떤 기술의 핵심을 이해하는데 가장 좋은 방법은 해당 기술이 나온 이유를 먼저 아는것이라고 생각한다!

스크롤을 이벤트가 발생할때 마다 호출되는 콜백이 있다고 가정해보자. 우리는 잘 못느낄 수 있으나 사실 스크롤을 한번 드르륵 해주기만 해도 수많은 스크롤 이벤트가 발생하게 된다.

이런 스크롤 이벤트에 매번 콜백이 발생한다면? 그리고 그 콜백이 수행하는 일이 매우 큰 리소스를 먹는 작업이라면?

상상하기 싫다. Debounce는 이렇게 이벤트가 과도한 횟수로 발생하는 것에 제약을 걸어 제어할 수 있는 수준으로 이벤트를 발생시키는 것을 목표로 한다.



Throttle

이벤트를 일정한 주기마다 발생하도록 하는 기술이다. 예를 들어 Throttle의 설정시간으로 1ms를 주게되면 해당 이벤트는 1ms 동안 최대 한번만 발생하게 된다. 위의 Throttle 기법이 적용될 경우 다음과 같이 작동하게 된다.

설정시간 = 1ms

일반 : 이벤트1 이벤트2 이벤트3 이벤트4
Throttle 예제 : 이벤트1 ( — 1ms —) 이벤트2 ( — 1ms — ) 이벤트3



Debounce

이벤트를 그룹화하여 특정시간이 지난 후 하나의 이벤트만 발생하도록 하는 기술이다. 말이 어려우므로 바로 예시를 통해 이해해보자.

설정시간 = 5ms

일반 : 이벤트이벤트이벤트이벤트이벤트

Debounce 예제 1 : 이벤트1 이벤트2 ( —5ms — )

→ 이벤트2발생!

Debounce 예제 2 : 이벤트1 이벤트2 (— 4.9ms— ) 이벤트4 이벤트5(— 5ms — )

→ 이벤트6발생!


예제

입력한 키워드가 1초 뒤에 TextView에 업데이트 되는 것으로 debounce 사용 예제를 만들어봤다. text watcher를 등록해서 타이핑한 내용이 변할 때마다 textChange 오브젝트에 이벤트를 보내고 debounce를 1초 걸어서 1초를 기다린 다음 마지막으로 들어온 스트링 값에 대해서 화면에 표현하도록 구현했다.

val textChange: PublishSubject<String> = PublishSubject.create()

fr_rx_edit.addTextChangedListener(object: TextWatcher{
    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        textChange.onNext(s.toString())
    }
    override fun afterTextChanged(s: Editable?) {}
    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
})

textChange
    .debounce(1, TimeUnit.SECONDS)
    .observeOn(AndroidSchedulers.mainThread())
    .doOnNext {
        fr_rx_tv.text = it
    }
    .subscribe()

결과 아래 그림처럼 타이핑 후 1초를 기다린 다음에 화면에 업데이트 된다.


Throttle과의 차이는?

Debounce는 아무리 많은 이벤트가 발생해도 모두 무시하고 특정 시간사이에 어떤 이벤트도 발생하지 않았을 때 딱 한번만 마지막 이벤트를 발생시키는 기법이다.

따라서 5ms가 지나기 전에 계속 이벤트가 발생할 경우 콜백에 반응하는 이벤트는 발생하지 않고 계속 무시된다. 예제 2에서는 실제로 6개의 이벤트가 발생했음에도 불구하고 실제로 콜백이 반응하는 이벤트는 ‘이벤트 6’ 단 하나 뿐이다.

Debounce는 검색기능을 구현할 때 매우 유용하게 사용할 수 있다.

EditText에서 내용이 바뀔 때마다 해당 내용을 검색하는 API를 날린다고 가정해보자. ‘자바’를 검색한다고 했을 때 아무런 처리도 하지않을 경우 EditText 리스너는 모든 변화를 캐치하여 (ㅈ), (자), (자ㅂ), (자바) 총 4번의 API를 날릴 것이다.

사실 사용자가 검색하고싶은 내용은 ‘자바’라는 내용이므로 이전 내용의 검색결과는 아무 쓸모가 없으며, 해당 검색결과를 계속 그려주는 리소스낭비도 매우 심할 것이다.

만약 여기에 Debounce (500ms)를 적용할 경우 다음과 같이 작동시킬 수 있다.


Debounce 예제 3 :
‘ㅈ’ ( — 300ms — ) ‘자’ ( — 250ms — ) ‘자ㅂ’ (— 310ms— ) ‘자바’ ( — 500ms — )
→ ‘자바’라는 내용으로 한 번만 검색 API를 호출!



마치며

한번만 제대로 이해한다면 그리 어려운 개념은 아니라고 생각한다.

중요한 점은 Throttle 이나 Debounce 를 적용시킬때 설정하는 시간을 잘 선택해야한다는 것이다. 시간을 너무 길게 잡을경우 사용자 입장에서는 화면이 멈추거나 렉이 걸린것 같은 효과를 주고 이는 매우 좋지않은 사용자 경험이기 때문이다. 경험상 200ms ~ 400ms 사이가 경험도 해치지 않으면서 성능 개선 효과도 얻을 수 있는 수치라고 느꼈다.

profile
안드로이드 개발자

0개의 댓글