Reactor defer란
Reactor의 defer에 대해 알아보려고 한다.
public static <T> Mono<T> defer(Supplier<? extends Mono<? extends T>> supplier)
defer의 코드 정의는 위와 같다. Mono 값을 가지는 supplier를 인자로 받는다.
사실 코드 정의는 그렇다쳐도 defer라는 네이밍만 봐도 무언가 작업을 지연해서 처리하는 메소드라고 짐작할 수 있다.
관련 문서를 살펴보면,
Create a Mono provider that will supply a target Mono to subscribe to for each Subscriber downstream.
즉, downstream에 Mono provider를 만들어서 보낸다. 이 말이 이해가 잘 되지 않을 수 있으니 마블 다이어그램을 보도록 하자.
전체적으로 subscribe가 2번인데, 바깥에서 안쪽에서 각각 한 번씩 일어난다.
(바깥에서 subscribe가 발생한 이후에 defer 내부에서 supplier에 다시 한 번 subscribe)
Reactor이기 때문에 조금 어렵게 느낄 수도 있지만, 사실상 lazy 기능과 비슷하다.
(코틀린이라면 lateinit 과 같은 느낌??)
한 번 예시 코드를 살펴보자.
int a = 5;
@Override
public void run(String... args) throws Exception {
Mono<Integer> monoJust = Mono.just(a);
Mono<Integer> monoDefer = Mono.defer(() -> Mono.just(a));
monoJust.subscribe(integer1 -> System.out.println(integer1));
monoDefer.subscribe(integer1 -> System.out.println(integer1));
a = 7;
monoJust.subscribe(integer1 -> System.out.println(integer1));
monoDefer.subscribe(integer1 -> System.out.println(integer1));
}
just와 defer를 비교한 것이다.
just는 알다시피 값을 바로 emit하는 것인데, 위 코드를 실행시켜보면, 아래의 결과 값이 나온다.
5, 5, 5, 7 |
비슷한 내용으로 switchIfEmpty가 있다.
말 그대로 비어있을 때, 새로운 값을 반환하는 메소드인데 just와 defer에 따라 내부 동작 방식이 조금 다르다.
Mono.just("foo")
.switchIfEmpty(Mono.just("bar"))
.subscribe();
}
Mono.just("foo")
.switchIfEmpty(Mono.defer(() -> Mono.just("bar")))
.subscribe();
}
먼저 첫 번째 코드는 Mono.just("foo") 값이 emit 되어 있지 않더라도 Mono.just("bar")는 호출 될 때마다 emit 한다. (물론 결과는 반영되지 않는다.) 두 번째 코드는 empty 값이라면 그제서야 defer 안에 있는 Mono.just를 실행한다. 이는 defer 동작이 subscribe에 의해 동작하기 때문이다.
결과적으로 defer를 사용하면서 쓸데없는 호출을 막고 유연하게 flow를 만들 수 있다.
참고로 swtchIfEmpty의 extension을 사용하게 되면 defer가 내장되어 있기 때문에 굳이 Mono.defer()를 사용하지 않아도 된다. (아래 예시 참조)
Mono.just("foo")
.switchIfEmpty { Mono.just("bar")) }
.subscribe();
}
Reference
- stackoverflow.com/questions/55955567/what-does-mono-defer-do