개발/기타

[RxJava] Single.just와 Single.create의 차이: 값을 계산하는 순서

MiJey 2023. 2. 3. 12:06

아래 두 코드의 차이는 무엇일까?

// fun updateConfigs(): Completable
// fun getMyConfig(): String

// 1번 코드
updateConfigs().andThen(
    Single.just(getMyConfig())
)

// 2번 코드
updateConfigs().andThen(
    Single.create { emitter ->
        emitter.onSuccess(getMyConfig())
    }
)

1번 코드는 업데이트 전 config를 반환하고 2번 코드는 업데이트 후 config를 반환한다.

RxJava 구현을 살펴보면 다음과 같다.

public static <T> Single<T> just(final T item)
public static <T> Single<T> create(SingleOnSubscribe<T> source)

Single.just는 값을 계산한 뒤 넘기고, Single.create는 함수를 넘긴 후 나중에 함수를 실행(계산)한다.

의도한 동작은 updateConfigs()에서 설정 값을 업데이트한 후, andThen에서 업데이트 된 값인 getMyConfig()를 반환하는 것이었다. 즉, 2번 코드가 의도한 대로 동작한다.


생각

이렇게 놓고 보면 기본적인 내용이고 별거 아닌 것 같은데, 이 코드가 포함된 부분을 한 4명이서 봤는데도 아무도 문제점을 깨닫지 못했다. 다른 문제를 디버깅 하던 중 andThen 안에 로그를 찍어보기 위해 Single.just 대신 Single.create로 '임시로' 바꿔서 로그를 찍어본 것이었는데, 호출되는 순서가 아예 달라져서 알게 되었다.

왜 놓쳤을까?

팀원들과 '우리는 어쩌다 이런 기본적인 걸 놓치게 되었는지' 얘기해보았다.

  1. Single.just는 그냥 Single.create에서 값 하나 내보내고 complete 한 거랑 똑같지 않나? 코드 짧은게 보기 편하지~'라고 안일하게 넘겼다.
  2. Rx를 Rx 답게 쓰지 못했다. 값을 '받아서' Single.just로 넘기는게 아니라 어딘가에 상태를 업데이트 한 후 사용하려고 했다. (사이드 이펙트를 배제하지 못했다)

어떻게 해결할 수 있을까?

이런저런 얘기를 했지만 결론은 '리팩토링이 필요하다'였다.

문제는 2015년에 만들어진 레거시 프로젝트에 기능이 얹히고 얹히다 보니, 이번에 문제가 된 '설정 가져오기' 같이 상대적으로 프로젝트 초창기에 만들어진 기본적인 기능들은 리팩토링이 엄두도 안날 만큼 여기저기에서 사용되고 있다는 것이다. 공개 API와 엮인 부분도 있어서 더 까다롭다.

요즘 리팩토링을 어떻게 해야할지 고민이 많은데, 숙제가 하나 더 늘어난 기분이다.

2023.02.07 추가

defer도 있었다. 근데 defer+Single.just이나 Single.create 쓰는거나 가독성 측면에서는 비슷해보인다.

updateConfigs().andThen(
    Single.defer {
        Single.just(getMyConfig())
    }
)