어떤 작업을 체인에 포함시키고, 어떤 작업은 체인에 포함시키지 말아야 할까? 모든 작업은 다 체인에 포함되어야 할까? 블로킹 기능은 하나도 애플리케이션에 하나도 없어야 할까?

리액티브의 근본적인 원칙은, “구독하기 전까지는 아무일도 일어나지 않는다.” 이다.(nothing happens until you subscribe) 이는 핸들러 메서드는 실제 작업을 실행하는 곳이 아니라, 무엇을 해야하는지 기술하는 곳이어야 한다는 것임.

Publisher(Mono/Flux)들을 인스턴스화하고 연산자들을 조립할때까지도 아무런 처리가 일어나지 않는다. 이 시점을 조립시점이라고 함. (assembly time)

실제로 데이터가 흘러갈때는 시점은 구독 시점(subscription time)이다. 따라서 조립은 설명이 되고, 실제 작업은 구독 시점에 일어난다.

여기서 생기는 오해가 Publisher(Mono / Flux)로 감싸기만 하면 작업이 리액티브 해진다는 것임. Okta는 이를 가짜 리액티브 서비스라고도 한다. 대표적인 예가 Mono.just(T data) 인데, 여기서 data인스턴스화 시점에 캡쳐된다. 그래서 이건 지연평가되는게 아니고, 사실상 인스턴스 시점에 값이 결정되어 구독시점에 반환되는 것 뿐임. (조립 시점에 값이 즉시 평가된다!)

따라서 진짜로 lazy하게 하려면 Mono.fromCallable {} 이나 Mono.defer { } 처럼 람다 안에 넣어 구독 시점까지 실행을 지연시켜야 한다.

거기에 블로킹 작업을 이벤트 루프 스레드가 아니라 다른 스레드로 넘기고 싶다면 subscribeOn까지 추가해주자.

그래서 체인안에 둬, 말아. 고민이 들 수 있다. 다음의 경우에 하나라도 해당되면 체인안에 두자.

  1. 작업이 블로킹하는가? → 그냥 두면 조립시점에 쓰레드를 블로킹해버린다. 체인안에 넣고 subscribeOn(boundedElastic()) 으로 오프로딩까지 하자.
  2. 예외를 던질 수 있는가? → 구독전에 즉시 예외를 던져버리기 때문에 onError 신호가 발생하지 않는다.
  3. 부수효과 / 가변 상태가 있는가? → 구독마다 작업을 새로하는게 정상이다. 조립 시점에 한번 실행되고 상태를 부수효과로 퍼트리게 하지 말자.
  4. Reactor Context가 필요한가? → 이 컨텍스트는 체인 안에서만 유효하다.

그럼 이 네가지에 해당되지 않는 작업이 있단 말인가?

부수효과가 없고 (즉 순수하고), 예외를 던지지도 않고(완전함수, totalFucntion이라고도 한다.), 속도도 빠르고, 상태도 유지하지 않는, 이미 그 자체로 완전한 값에 대한 연산정도가 체인 밖에 둬도 문제가 없다.

다만 이런 케이스는 체인 밖에 둬도 문제가 없다. 수준이지, 체인밖에 둬야 한다! 는 아님. 여기가 이제 호불호의 영역이 된다.