본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Spring framework/Spring Webflux

spring webflux 4 (웹플럭스 적용기, Mono와 Flux)

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2020. 3. 18.

웹플럭스는 리엑터 객체인 Mono와 Flux 클래스를 얼마나 잘 다룰줄 아는지가 가장 중요한 것 같다.

웹플럭스를 조금 더 파보기 위해서는 저 Mono와 Flux에 대해서 친숙해질 필요가 있다.

 

새로 프로젝트를 만들자.

그냥 Maven 프로젝트나 Gradle 프로젝트를 만든 다음에 웹플럭스 라이브러리를 추가하자.

버전은 본인의 구성에 맞게 설정하자~

 

* 메이븐

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

* Gradle

compile group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '2.2.5.RELEASE'

 

Mono와 Flux는 둘다 하는 행위가 비슷하다.

Mono라는 녀석은 0~1개의 결과를 주는 반면에 Flux는 1~n개의 결과를 주는 것이라 생각하면 된다. 

 

먼저 시중에(?) 돌아다니는 많은 예제이다.

import java.util.*;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

public class JustTest {
    public static void main(String[] args) {
        String text = "STRING";
        Mono<String> mono = Mono.just(text); //단지!	
        mono.subscribe( str->{ //구독!
            System.out.println(str);
        });
    }
}    

 

Mono 클래스에서 just 메소드를 호출 하였다.

just 메소드는 말 그대로 "단지" 라는 의미이다. text 변수에 대해서 "단지 나는 너를 바라볼꺼야~" 라는 의미이다.

저렇게 해놓고 subscribe를 하지 않으면 아무일도 일어나지 않는다.

모든 reactor 객체는 subscribe 메소드를 만나서 "구독"해야만 동작을 한다.

 

그러면, 조금 다른 예제를 살펴보자.

Publisher라는 인터페이스에 대한 내용이다.

    //상단 생략..
    
    private static Publisher<String> PBS = new Publisher<String>() {  //퍼블리셔..?
        @Override
        public void subscribe(Subscriber<? super String> sbs) {
            Subscription subscript = new Subscription() {  //신청..?
                @Override
                public void request(long n) {
                    sbs.onNext("abcd");
                    sbs.onComplete();
                }
                @Override
                public void cancel() {

                }
            };
            sbs.onSubscribe(subscript);			
        }
    };

    private static void one(){  //요기 메소드를 주목하자~
        Mono.from( PBS ).map( arg-> arg.length()).subscribe( arg->{
            System.out.println("Mono : "+arg);
        });
    }        
    
    //하단 생략..

 

아마 Mono나 Flux 를 다루다보면 가장 많이 만나는 메소드가 저 from으로 시작하는 메소드이다.

대부분 from으로 시작하는 메소드는 Mono<T> 또는 Flux<T>형태의 객체를 반환한다.

위 내용은 Publisher<T> 인터페이스를 실제 생성하여 만든 모습이다.

내용이 전부 이해하기에는 어렵지만 onSubscribe 메소드를 호출한 것으로 보아하니 저게 우리가 정의한 행동으로 보인다.

실제 코드를 동작시키면 아래사진처럼 내용이 나온다.

숫자 4가 나왔다.

 

Mono.from 을 통해서 PBS로 선언한 Publisher 인터페이스를 구독 대상에 선정하여 주었다.

그리고 map을 통해서 아직 무엇인지 모르는 arg 값을 length 메소드를 통해서 변환하였고, subscribe를 통해서 출력하게 하였다.

만약 저기서 subscribe 메소드를 통해 구독을 하지 않았다면 아무런 결과가 없었을 것 이다.

 

코드 이해를 위해 내용을 조금만 수정하여보자.

    //상단 생략..
    
    private static Publisher<String> PBS = new Publisher<String>() {  //퍼블리셔..?
        @Override
        public void subscribe(Subscriber<? super String> sbs) {
            Subscription subscript = new Subscription() {  //신청..?
                @Override
                public void request(long n) {
                    sbs.onNext("abcd");
                    sbs.onComplete();
                    System.out.println("내용동작");
                }
                @Override
                public void cancel() {

                }
            };
            System.out.println("시작");
            sbs.onSubscribe(subscript);			
            System.out.println("종료");
        }
    };

    private static void one(){  //요기 메소드를 주목하자~
        Mono.from( PBS ).map( arg-> arg.length()).subscribe( arg->{
            System.out.println("Mono : "+arg);
        });
    }        
    
    //하단 생략..

 

요렇게 하고선 실행을 하여보면,

순서에 맞게 콘솔이 출력된다.

 

자세히는 모르겠지만 Publisher라는 인터페이스를 from 을 통해서 구독을 하게되면 Publisher 내부에 작성된 내용이 동작하는 것을 알 수 있다.

그리고 Publisher가 제네릭을 통해 타입매개변수를 전달 받기 때문에 여기서는 String으로 선언한 Publisher에 의해서 Mono.from에서 map으로 가공된 데이터가 String임을 알 수 있다.

 

Mono나 Flux에서 사용되는 Publisher<T>인터페이스는 아래의 규칙을 따른다.

1. Publisher 인터페이스를 오버라이드한 subscribe 메소드는 시작을 의미한다. (stream의 시작)

   여기서부터 어떠한 행위의 시작이 된다고 생각하면 될 것 같다.

2. subscribe 메소드가 받는 클래스는 Subscriber 클래스이다.

   해당 클래스는 사용자가 콜백 형식으로 정의한 인터페이스 의미한다.

   해당 인터페이스에 onNext 메소드로 데이터 또는 이벤트를 전달하거나 onComplete로 종료를 알릴 수 있다.

   또한 onError 메소드를 통해서 에러를 전달 할 수 있다.

3. Subscription 인터페이스는 이러한 사용자에게 전달할 행위를 정리해 둘 도화지 같은, 마치 도면같은 인터페이스이다.

 

더욱 요약하면!

1. Publisher는 구독할 대상이다.

2. Subscriber는 사용자에게 전달할 대상이다.

3. Subscription는 사용자에게 전달할 내용을 정리한 도화지이다.

 

내용이 역시나 어렵다...

Publisher 인터페이스를 일일이 뜯어보며 이해하려면 상당한 시간이 필요한 듯 하다.

내용을 정리하여보았지만 글 쓰기도 어려운게 사실인 것 같다.

연습을 통해서 코드가 숙달 되기 전 까지는 시간이 필요할 것 같다.

조금 더 손쉬운 이해가 필요하면 rxjs를 통해서 연습해도 좋을 것 같다. (의미와 개념이 아주 비슷합니다.)

 

 

이어서, Mono와 Flux의 차이를 살펴보자.

그러기 위해서는 위 소스코드를 조금 수정해야 한다.

    //상단 생략..
    
    private static Publisher<String> PBS = new Publisher<String>() {  //발행 클래스
        @Override
        public void subscribe(Subscriber<? super String> sbs) {  //시작의 의미, 스트림의 생성
            Subscription subscript = new Subscription() {  //행위
                @Override
                public void request(long n) {
                    sbs.onNext("abcd");
                    sbs.onNext("EFGHqq");  //onNext추가, 행위를 한번 더!
                    sbs.onComplete();
                }
                @Override
                public void cancel() {

                }
            };
            sbs.onSubscribe(subscript);  //사용자의 콜백에 대한 행위 넣기			
        }
    };

    private static void one(){
        //요녀석은 Mono
        Mono.from( PBS ).map( arg-> arg.length()).subscribe( arg->{
            System.out.println("Mono : "+arg);
        });
		System.out.println(" - ");
        
        //요녀석은 플럭스
        Flux.from(PBS).map(arg -> arg.length()).subscribe( arg->{
            System.out.println("Flux : "+arg);
        });        
    }        
    
    //하단 생략..

 

요래하고 출력해보면..

Mono에서는 두번째 대상이 Drop되었다는 출력이 나온다.

 

Mono에서의 행위는 0~1개이므로 뒤에 추가한 sbs.onNext("EFGHqq") 내용이  실행되지 않았고,

Flux에서의 행위는 0~n개이므로 뒤에 추가한 내용이 실행됨을 알 수 있다.

 

위 소스코드의 전체의 모습이다.

import java.util.*;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

public class JustTest {
    public static void main(String[] args) {
        String text = "STRING";
        Mono<String> mono = Mono.just(text); //단지!	
        mono.subscribe( str->{ //구독!
            System.out.println(str);
        });
        
        one();
    }
    
    
    private static Publisher<String> PBS = new Publisher<String>() {  //발행 클래스
        @Override
        public void subscribe(Subscriber<? super String> sbs) {  //시작의 의미, 스트림의 생성
            Subscription subscript = new Subscription() {  //행위
                @Override
                public void request(long n) {
                    sbs.onNext("abcd");
                    sbs.onNext("EFGHqq");  //onNext추가, 행위를 한번 더!
                    sbs.onComplete();
                }
                @Override
                public void cancel() {

                }
            };
            sbs.onSubscribe(subscript);  //사용자의 콜백에 대한 행위 넣기			
        }
    };

    private static void one(){
        //요녀석은 Mono
        Mono.from( PBS ).map( arg-> arg.length()).subscribe( arg->{
            System.out.println("Mono : "+arg);
        });
		System.out.println(" - ");
        
        //요녀석은 플럭스
        Flux.from(PBS).map(arg -> arg.length()).subscribe( arg->{
            System.out.println("Flux : "+arg);
        });        
    }        
}    

 

2가지로 크게 정리하면,

1. Mono에서의 행위는 0~1개, Flux에서의 행위는 0~n개에 대해서 사용된다.

2. Publisher 인터페이스로 subscribe에 필요한 구독행위를 만들 수 있다.

 

Mono랑 Flux에 대해서는 아직도 삽질이 더 필요하여보인다.

spring webflux!

 

* 내용을 채우고 수정중입니다.

* 튜토리얼이나 가이드 목적보다도 개념정리에 목적을 두고 쓰고있습니다. 틀린부분이나 누락된 부분은 꼭 알려주세요!

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글