웹플럭스는 대부분의 코딩 방식을 람다 또는 함수형으로 표현하고 있다.
메소드형태도 대부분 체이닝 방식을 써서 작업 할 때 메소드명이 차레대로 이어지는 걸 보면 해당 코드의 이해가 쉬워진다.
그러면, 전에 만들었던 RoutsConfig 클래스에 매소드를 한개 추가하여보자.
새로 만들 메소드 이름은 requestGetParam이라는 메소드이다.
@Bean
public RouterFunction<ServerResponse> requestGetParam() {
RequestPredicate predicate = RequestPredicates.GET("/request").and(RequestPredicates.accept(MediaType.TEXT_PLAIN));
//1. Request + Predicate(요청 + ~이다) => 사용자의 요청 종류 및 형태
//2.웹플럭스에게 전달할 RouterFunction 클래스를 제작, 함수형태로 전달한다.
RouterFunction<ServerResponse> response = RouterFunctions.route(predicate, (request)->{ //3. 내부 람다식은 HandlerFunction 인터페이스의 모양이다.
System.out.println(request.queryParams());
String justText = "get param";
Mono<String> mapper = Mono.just(justText); //4.Mono클래스를 통해서 전달할 내용을 만들고
Mono<ServerResponse> res = ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).body(BodyInserters.fromProducer(mapper, String.class)); //5. 어떤 형태로 전달할지 작성후
return res; //6.전달!
});
return response; //7. 해당 함수를 웹플럭스에게 전달한다.
}
설명을 전부 한번 달아보았다.
맨 처음에 보이는 RequestPredicate 클래스는 사용자의 요청형태를 정의하는 클래스이다.
또한, 웹플럭스는 복잡한 설정 및 사용법을 줄이기 위해서 RouterFunctions 라는 클래스를 제공하여 해당 클래스의 route 메소드만 활용한 다면 대부분 행동을 쉽게 할 수 있도록 구성되어 있다.
메소드가 대부분 직관적으로 이름이 되어있어 어렵지가 않다.
요청에 대한 행동을 정의하는 HandlerFunction 인터페이스 같은 경우에는 ServerRequest라는 클래스 객체를 파라미터로 사용하여 사용자가 다룰 수 있게 해 주었다.
그래서 System.out.println(request.queryParams()); 를 통해서 요청에 대한 파리미터를 한번 출력해 보았다.
여기서 흥미로운 것은 함수형으로 구현되어 있기 때문에 재미있는 현상이 존재한다.
아래 requestPathAndParam이라는 메소드를 추가하여보자.
@Bean
public RouterFunction<ServerResponse> requestPathAndParam() {
System.out.println("METHOD START!!!!");
final int number[] = {10};
final RequestPredicate predicate = RequestPredicates.GET("/path/{name}").and(RequestPredicates.accept(MediaType.TEXT_PLAIN));
RouterFunction<ServerResponse> response = RouterFunctions.route(predicate, (request)->{
System.out.println(request.queryParams());
System.out.println(request.pathVariables()); // path같은 것도 되는구나!
String justText = "path and param";
Mono<String> mapper = Mono.just(justText);
Mono<ServerResponse> res = ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).body(BodyInserters.fromProducer(mapper, String.class));
number[0] = number[0]+1; // 출력하면 11이 계속 나오겠지??
System.out.printf("number : %d\n",number[0]);
return res;
});
System.out.println("METHOD END!!!!");
return response;
}
그리고 재시작을 하여보면 아래와같은 재미있는 현상을 볼 수 있다.
"METHOD START!!!!" 라는 문구와 "METHOD END!!!!" 문구가 딱 한번만 출력되었다.
그리고 숫자 배열로 선언한 number 배열의 첫번째 데이터가 요청을 할 때마다 1씩 증가하는 것도 보인다.
사용자의 요청이 들어 올 때 마다 requestPathAndParam 메소드가 호출될 줄 알았지만,
웹플럭스에서는requestPathAndParam을 계속해서 호출하는 것이 아니라, requestPathAndParam에 정의된 함수를 요청에 대해서 적용하기 때문에 마치 메소드 내부의 숫자배열 number가 static하게 사용되는 것을 볼 수가 있다.
일반 요청에 대해서 몇가지 메소드를 살펴 보았으니 파일업로드와 다운로드에 대한 형태도 잠시 살펴보자.
파일을 다운로드 하는 것도 크게 어렵지 않다.
fileDownload라는 메소드를 추가하여 보자.
//파일 다운로드
@Bean
public RouterFunction<ServerResponse> fileDownload() {
RequestPredicate predicate = RequestPredicates.GET("/file").and(RequestPredicates.accept(MediaType.TEXT_PLAIN));
RouterFunction<ServerResponse> response = RouterFunctions.route(predicate, (request)->{
Resource resource = new FileSystemResource("D:/test.csv");
Mono<Resource> mapper = Mono.just(resource); //Mono의 제네릭에 의해 다양한 클래스를 받을 수 있다.
//헤더라는 메소드와 바디라는 메소드는 이름만 봐도 어떠한 역할을 하는 지 알 것 같다.
Mono<ServerResponse> res = ServerResponse.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"test.csv\"")
.body(BodyInserters.fromProducer(mapper, Resource.class));
return res;
});
return response;
}
RouterFunction 클래스의 핵심은 HandlerFunction 인터페이스가 사용 하고 있는 Mono라는 클래스이다.
요녀석만 위 내용처럼 자유롭게 사용한다면 1차고비는 넘어가게 되는 것 같다.
단순하게 생각한다면 저 Mono라는 클래스에 제네릭 타입만 바꾸어서 작업하면 될 것 같은데..
그 생각을 깨버리는 구간이 바로 파일 업로드를 구현할 때 이다.
fileUpload 메소드를 한번 만들어 보았다. Multipart 요청에 대해서 응답하게 하였다.
//파일 업로드
@Bean
public RouterFunction<ServerResponse> fileUpload() {
RequestPredicate predicate = RequestPredicates.POST("/fileUpload").and(RequestPredicates.accept(MediaType.MULTIPART_FORM_DATA));
RouterFunction<ServerResponse> response = RouterFunctions.route(predicate, (request)->{
Mono<String> mapper = request.multipartData().map(it -> it.get("files"))
.flatMapMany(Flux::fromIterable)
.cast(FilePart.class)
.flatMap(it -> it.transferTo(Paths.get("D:/test_upload/" + it.filename())))
.then(Mono.just("OK"));
Mono<ServerResponse> res = ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).body(BodyInserters.fromProducer(mapper, String.class));
return res;
});
return response;
}
request 객체에서 multipartData 라는 메소드를 호출하면 Mono<MultiValueMap<String, Part>> 라는 객체를 리턴한다.
계속 사용한 메소드는 Mono의 just 메소드랑 subscribe 메소드인데..
Flux는 또 뭐란말인가..
그래도 다행인 것은 마치 Java의 컬렉션을 다룰 때 사용한 stream사용하는 방법과 비슷한 느낌으로의 메소드가 보이고 있다라는 점 이다.
결국 이 웹플럭스를 잘 다루고 못 다루고는 저 Mono라는 클래스에 대해서 제대로 살펴보아야 한다는 점 이다.
아래 사이트는 공식 홈페이지이다.
다음번에는 요 Mono와 Flux의 사용법에 대해서 좀 더 파 보아야 할 것 같다.
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 2 (웹플럭스 적용기, 기본 구조) (0) | 2020.03.11 |
spring webflux 1 (웹플럭스 적용기, 설치) (4) | 2020.03.10 |
댓글