웹플럭스에 대한 자료를 구글링해보면 영어로된 원문이 참 많다.
그런데 아직 한글로 제대로된 튜토리얼은 안보인다..흠..
웹플럭스라는 프레임워크는 반응형+함수형 프로그래밍을 기초로 두고 있어서 아직까지는 보편화되어 많이 사용되지 않는 것 같다.
* 반응형에 대한 간단한 정의 ───────────
반응형에 대한 내용을 간단히 정리해보자.
기존에 대부분의 프로그래밍은 아래 사진처럼 명령형으로 되어있다.
프로그래머가 만든 코드를 순서대로 실행하는 것이 기본 베이스였다.
위 내용을 자바스크립트로 표현하면 아래처럼 나타낼 수 있다.
var numberA = 2;
var numberB = 2;
var resultC = 0;
var func = (a,b)=>{resultC = a*b};
func(numberA, numberB); //이때 resultC의 값은 4 이다.
console.log(resultC);
//numberA와 numberB가 데이터가 바뀌었다.
numberA = 4;
numberB = 4;
console.log(resultC); //이때 resultC의 값은 당연히 4 이다.
따라서 데이터가 변하더라도 따로 프로그래밍을 해 주지 않는 한 resultC의 값은 여전히 그대로를 유지한다.
왜냐하면 따로 곱하기 기능을 가지고 있는 func를 호출하지 않았기 때문이다.
그런데, 반응형 프로그래밍은 이와는 조금 다르다.
반응형 프로그래밍에서의 가장 중요한 내용은 바로 "구독"이다.
위 사진은 숫자A와 숫자B를 바라보게 하고 해당 데이터를 곱하기 함수에게 "구독"하게 하였다.
곱하기 함수는 숫자A와 숫자B를 바라보면서 데이터의 흐름과 변동이 있을 때 마다 곱하기 동작을 하게 하였다.
그러므로 구독을 하는 행위를 종료하지 않는다면 곱하기 함수는 두개의 데이터의 변화가 있을 때 마다 곱하기를 수행하여 결과를 넣어 줄 것 이다.
이걸 신문에 비유하여보면,
1. 오늘 나는 신문을 보겠다고 신문사A에 돈을 지불하고 아침마다 신문을 받게 하였다.
-> 곱하기 함수에게 숫자A와 숫자B를 구독하게 한다.
2. 아침에 신문이 배달되어 나는 신문을 읽는다.
-> 데이터가(숫자A, 숫자B) 변동이 된다. (배달되어~)
-> 곱하기 함수에 정의된 곱하기 연산이 동작하여 결과 값을 결과C에 넣어준다. (읽는다~)
3. 해당 신문사A에 돈을 주지 않을 때 까지 신문은 계속 배달되며, 나는 신문을 계속 읽는다.
-> 따로 종료하지 않는 한 2번행위가 반복된다.
반응형은 위와 같은 내용으로 동작을 한다.
웹플럭스는 이러한 반응형 기법으로 구성된 프레임워크이며 "구독"을 통한 행위가 대부분이라 볼 수 있다.
* 다시 웹플럭스로 ───────────
첫번째장에서 내용을 토대로 동작원리를 파해쳐보자.
처음에 만들었던 FirstHandler 클래스의 hello메소드는 Mono<T>라는 클래스를 리턴해 주고 있다.
해당 클래스를 사용하는 RoutsConfig클래스에서는 이중콜론연산자(::) 를 활용해서 해당 메소드를 사용했다.
백문이 불여일견!
앞에서 만들었던 FirstHandler 클래스를 다시 살펴보자.
//import 생략
@Component
public class FirstHandler {
private HashMap<Object, Object> result = new HashMap<>();
private Mono<HashMap<Object, Object>> mapper = Mono.just(result);
public Mono<ServerResponse> hello(ServerRequest request) {
result.put("number", 1234);
result.put("text", "webFlux");
mapper.subscribe( (arg)->{
System.out.println(arg);
});
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromProducer(mapper, HashMap.class));
}
}
요렇게 생겼었는데.. hello메소드에서 기록한 내용이 RoutsConfig클래스에서 사용자의 요청에 대해서 응답했었다.
위 클래스를 만들어준 이유는 코드를 분리화 하기 위해서였다.
RoutsConfig클래스를 만약에 FirstHandler 클래스없이 사용하려면 아래처럼 코드를 바꾸어야한다.
//..import 생략
@Configuration
public class RoutsConfig implements ApplicationContextAware, WebFluxConfigurer {
ApplicationContext context;
/*
* 기존 코드
@Bean
public RouterFunction<ServerResponse> route(FirstHandler handler) {
return RouterFunctions.route(
RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::hello);
}
*/
//변경 후 코드
@Bean
public RouterFunction<ServerResponse> route(FirstHandler handler) {
return RouterFunctions.route(
RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), arg->{
HashMap<Object, Object> result = new HashMap<Object, Object>();
result.put("number", 1234);
result.put("text", "webFlux");
Mono<HashMap<Object, Object>> mapper = Mono.just(result);
Mono<ServerResponse> response = ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromProducer(mapper, HashMap.class));
return response;
});
}
//..하단부 생략
}
딱 봐도 무거워진 느낌이 든다.
그래서 FirstHandler 클래스를 만들어 코드를 분리하고 재사용 가능하도록 한 것 이다.
자세히 보면 RoutsConfig 클래스에서 route의 메소드와 index 메소드는 둘다 Bean 객체이면서 RouterFunction 클래스를 리턴한다.
모든 메소드의 역할을 알 수는 없지만 결국 사용자에게 전달하는 데이터, 화면 등 이러한 모든 내용은 결국에는 RouterFunction라는 클래스가 있어야 하는 것 으로 보인다.
* 웹플럭스의 핵심 RouterFunction 인터페이스
웹플럭스에서는 RouterFunction 라는 클래스를 활용해서 사용자의 요청에 대한 응답 행위를 정의하도록 한다.
즉, 사용자는 RouterFunction 클래스만 채워주면 기타 동작은 전부 웹플럭스가 대신 해 주는 것 이다.
RouterFunction은 인터페이스로 이루어져 있으며 인터페이스 내부의 route 라는 메소드는 HandlerFunction을 사용하는 Mono라는 클래스를 되돌려 주게 되어있다.
역시...말이 너무 어렵다..
아주 단순하게 생각하면, RouterFunction은 사용자의 요청에 대한 행위를 의미하며, 그 행위를 만드는 것은 HandlerFunction 형태로 되어있는 Mono클래스라고 보면 된다.
즉, 웹플럭스를 통해서 사용자에게 일반 화면을 보여주거나, 단순 Json형식으로 데이터를 보내주는 등 이러한 대부분 행위는 사용자가 정의한 RouterFunction 클래스 내부에 기록된 함수형태를 따르게 된 다는 것 이다.
스프링(Spring Frameworkd)에 비유하자면,
Controller의 역할을 Bean 객체로 등록된 메소드가 담당을 하며, 해당 메소드는 RouterFunction을 리턴하도록 해야 되는 것이라고 생각하면 될 것 같다.
-> 의미가 이상하면 말씀 부탁드립니다!!!
* 웹플럭스의 세부행동은 Mono 클래스가 담당!
HandlerFunction 인터페이스의 내부에 보여진 Mono클래스의 설명과 사용법은 다음과 같다.
https://tech.kakao.com/2018/05/29/reactor-programming/
점입가경이라는 말이 딱 나온다.. 뭔 말인지 점점 더 어렵다.
Mono라는 녀석의 핵심은 "행동(구독)" 이다.
사용자는 데이터나 행위에 대해서 행동(구독)을 정의하고나면 그에 따른 실행은 Mono가 알아서 하는 것 이다.
위 내용이 어렵다면 아래 샘플로 만든 JustTest클래스를 구동하여보자.
import java.util.concurrent.CompletableFuture;
import reactor.core.publisher.Mono;
public class JustTest {
public static void main(String[] args) {
//1번내용 ----------------
String text = "abcd";
Mono<String> mono = Mono.just(text);
mono.subscribe( str->{ //변수 text를 살펴보고, 그에대한 콜백행위는 아래의 출력
System.out.println(str);
});
//2번내용 ----------------
Mono<Object> function = Mono.create( sink->{ //마치 자바스크립트의 Promise 같은 느낌!
//sink.error(new Exception("익셉션")); -> 에러를 발생시킨다.
sink.success("success"); //-> success값을 전달하여준다.
});
function.subscribe( arg->{ //위에서 전달한 success값
System.out.println(arg);
});
//3번내용 ----------------
mono.doOnNext(str->{ //맨 처음 구독한 다음행위에 대한 지정
System.out.println("next : "+str);
}).subscribe();
//4번내용 ----------------
CompletableFuture<?> future = CompletableFuture.supplyAsync(()->{
System.out.println("run!");
return "PARAM";
});
Mono<Object> mono2 = Mono.fromFuture(future); //비동기에 대한 구독!
mono2.subscribe( (param)->{
System.out.println("param : " + param);
});
}
}
요렇게 1차로 정리하면,
1. RouterFunction 클래스는 사용자의 요청에 대한 행동(데이터 전달, 데이터 처리, 화면 전달 등)을 정의한다.
2. RouterFunction 클래스를 빈 객체로 등록하여 웹플럭스에게 맞기면 RouterFunction클래스에 정의된 내용을 실행하여 준다.
3. RouterFunction 클래스는 HandlerFunction라는 인터페이스를 활용하여 행위를 받게 되어있고, HandlerFunction 인터페이스는 Mono라는 클래스의 내용으로 구성되어 있다.
으앜...역시나 어렵다.
다음번에는 위 내용을 토대로 샘플코드를 좀 만들면서 거꾸로 이해하도록 해 보아야겠다.
spring webflux!
* 악성코드나 바이러스 없습니다!!! >ㅁ<
* 내용을 채우고 수정중입니다.
* 튜토리얼이나 가이드 목적보다도 개념정리에 목적을 두고 쓰고있습니다. 틀린부분이나 누락된 부분은 꼭 알려주세요!
'Spring framework > Spring Webflux' 카테고리의 다른 글
spring webflux 5 (웹플럭스 적용기, Mono와 Flux의 Processor) (0) | 2020.03.25 |
---|---|
spring webflux 5 (웹플럭스 적용기, Mono와 Flux 자주 보이는 map) (0) | 2020.03.20 |
spring webflux 4 (웹플럭스 적용기, Mono와 Flux) (0) | 2020.03.18 |
spring webflux 3 (웹플럭스 적용기, 함수형과 반응형) (2) | 2020.03.12 |
spring webflux 1 (웹플럭스 적용기, 설치) (4) | 2020.03.10 |
댓글