Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- spring
- Vmagent
- Mirror
- webflux
- VictoriaMetrics
- springboot
- JVM
- consumer
- JDK
- raft
- broker
- OpenJDK
- Zookeeper
- 비동기
- Reassign
- Reactive
- Brooklin
- java
- OFFSET
- API문서
- ExecutableJar
- NoClassDefFoundError
- swagger
- ProjectLoom
- kafka
- Vmalert
- Rebalance
- tsdb
- restdocs
Archives
- Today
- Total
거북이 developer
Spring Webflux - Request Mapping 본문
Request Mapping
- Spring Webflux 에선 아래와 같이 2가지 programming model 이 있고 그에 맞게 Request Mapping 을 해줄 수 있다.
Annotated Controllers
- 스프링 MVC와 동일하며 spring-web 모듈에 있는 같은 어노테이션을 사용한다. 스프링 MVC와 웹플럭스 컨트롤러 모두 리액티브(Reactor, RxJava) 리턴 타입을 지원하기 때문에 이 둘을 구분하기 어렵다. 한 가지 눈에 띄는 차이는 웹플럭스에선 @RequestBody로 리액티브 인자를 받을 수 있다.
- 스프링 웹플럭스는 어노테이션 기반 프로그래밍 모델을 지원하기 때문에, @Controller, @RestController 컴포넌트로 요청을 매핑하고, 입력을 받고, exception을 처리할 수 있다.
- 컨트롤러는 메소드를 여러 가지로 활용할 수 있어서 클래스를 상속하거나 인터페이스를 구현하지 않아도 된다.
@RestController
public class HelloController {
@GetMapping("/hello")
public Mono<String> handle(@RequestBody Mono<String> request) {
return request.map(name -> "Hello Webflux. My name is " + name);
}
}
Functional Endpoints
- 스프링 웹플럭스는 경량화된 함수형 프로그래밍 모델을 지원한다. WebFlux.fn이라고도 하는 이 모델은, 함수로 요청을 라우팅하고 핸들링하기 때문에 불변성(Immutablility)을 보장한다.
- WebFlux.fn에선 HandlerFunction이 HTTP 요청을 처리한다. HandlerFunction은 ServerRequest를 받아 비동기 ServerResponse(i.e. Mono<ServerResponse>)를 리턴하는 함수다. 요청, 응답 객체 모두 불변(immutable)이기 때문에 JDK 8 방식으로 HTTP 요청, 응답에 접근할 수 있다. HandlerFunction 역할은 어노테이션 프로그래밍 모델로 치면 @RequestMapping 메소드가 하던 일과 동일하다.
- 요청은 RouterFunction이 핸들러 펑션에 라우팅한다. RouterFunction은 ServerRequest를 받아 비동기 HandlerFunction(i.e. Mono<HandlerFunction>)을 리턴하는 함수다. 매칭되는 라우터 펑션이 있으면 핸들러 펑션을 리턴하고 그 외는 비어있는 Mono를 리턴한다. RouterFunction이 하는 일은 @RequestMapping 어노테이션과 동일하지만, 라우터 펑션은 데이터뿐 아니라 행동까지 제공한다는 점이 다르다.
- 라우터를 만들 때는 아래 예제처럼 RouterFunctions.route()가 제공하는 빌더를 사용할 수 있다.
- RouterFunction을 실행하는 방법 중 하나는 HttpHandler로 변환해 내장된 서버 어댑터에 등록하는 것인데, 라우터를 bean 으로 등록하면 Spring 에서 이러한 처리를 자동으로 해준다.
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<?> personRouter(PersonHandler handler) {
return RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/person/{id}", RequestPredicates.accept(APPLICATION_JSON), handler::getPerson)
.GET("/person", RequestPredicates.accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
}
@Bean
public RouterFunction<?> routerFunctionB() {
// ...
}
}
RouterFunction
- 라우터 펑션은 요청을 그에 맞는 HandlerFunction으로 라우팅한다.
- 라우터 펑션을 직접 만들기보단, 보통 RouterFunctions 유틸리티 클래스를 사용한다.
- RouterFunctions.route()가 리턴하는 빌더를 사용하거나, RouterFunctions.route(RequestPredicate, HandlerFunction)으로 직접 라우터를 만들 수 있다.
Predicates
- RequestPredicate를 직접 만들어도 되지만, 요청 path, HTTP 메소드, 컨텐츠 타입 등 자주 사용하는 구현체는 RequestPredicates 유틸리티 클래스에 준비돼 있다.
- 다음은 유틸리티 클래스로 Accept 헤더 조건을 추가하는 예제다.
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", RequestPredicates.accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().bodyValue("Hello World")).build();
- and, or 조건을 걸 수 있다.
- requestPredicate.and(RequestPredicate other) : requestPredicate 와 other predicate 가 둘다 만족해야한다.
- requestPredicate.or(RequestPredicate other) : requestPredicate 와 other predicate 중 1개만 만족해도 된다.
- RequestPredicates 가 제공하는 함수는 위의 and 와 or 를 조합해서 만든 것이 많다. 예를 들어 RequestPredicates.GET(String) API 는 내부적으로 RequestPredicates.method(Method) 와 RequestPredicates.path(String) 을 and 로 연결시켰다고 생각하면 된다.
Routes
- 라우터 펑션은 정해진 순서대로 실행한다. 첫 번째 조건과 일치하지 않으면 두 번째를 실행하는 식이다. 따라서 구체적인 조건을 앞에 선언해야 한다. 어노테이션 프로그래밍 모델(Spring MVC)에선 자동으로 가장 구체적인 컨트롤러 메소드를 실행하지만, 함수형 모델에선 그렇지 않다 점에 주의해야한다.
- build()를 호출하면 빌더에 정의한 모든 라우터 펑션을 RouterFunction 한 개로 합친다. 다음 방법으로도 여러 라우터 펑션을 조합할 수 있다.
- RouterFunctions.route() 빌더의 add(RouterFunction)
- RouterFunction.and(RouterFunction)
- RouterFunction.andRoute(RequestPredicate, HandlerFunction) — RouterFunctions.route()를 RouterFunction.and()로 감싸고 있는 축약 버전
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<?> personRouter(PersonHandler handler) {
return RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/person/{id}", RequestPredicates.accept(APPLICATION_JSON), handler::getPerson) // (1)
.GET("/person", RequestPredicates.accept(APPLICATION_JSON), handler::listPeople) // (2)
.POST("/person", handler::createPerson) // (3)
.add(otherRoute) // (4)
.build();
}
}
(1) Accept 헤더가 JSON인 GET /person/{id}는 PersonHandler.getPerson으로 라우팅한다.
(2) Accept 헤더가 JSON인 GET /person은 PersonHandler.listPeople로 라우팅한다.
(3) POST /person은 다른 조건 없이 PersonHandler.createPerson로 라우팅한다.
(4) 마지막으로 나머지 요청을 처리할 otherRoute 펑션을 route에 추가한다.
Nested Routes
- path가 같으면 대부분 같은 조건을 사용하므로, 라우터 펑션을 그룹핑하는 경우가 많다. 위의 Routes 예제는 라우터 펑션 세 개가 /person을 path 조건으로 사용했다.
- 어노테이션을 사용했다면 클래스 레벨에 @RequestMapping을 선언해 중복 코드를 줄였을 거다.
- WebFlux.fn 에선 공통 조건을 가지고있는 RouterFunction 을 공유하기 위해 Builder 를 받는 Consumer 인터페이스를 활용하도록 하고있다.
- 위의 Routes 에 나온 예제를 아래와 같이 변경 가능하다.
RouterFunction<ServerResponse> route = RouterFunctions.route()
.path("/person", builder -> builder // (1)
.GET("/{id}", RequestPredicates.accept(APPLICATION_JSON), handler::getPerson)
.GET("", RequestPredicates.accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson))
.add(otherRoute)
.build();
(1) path 가 /person 인 조건의 RouterFunction 에 Builder 를 받는 Consumer 를 구현하여 분기처리를 하고있다.
- builder 에서 제공하는 nest 함수를 활용하면 하위 RouterFunction 에 다른 공통 조건도 추가가 가능하다. 위 예에서 /person/{id}' 와 /person` 의 accept 조건이 같으므로 다음과 같이 변경이 가능하다.
RouterFunction<ServerResponse> route = RouterFunctions.route()
.path("/person", b1 -> b1
.nest(RequestPredicates.accept(APPLICATION_JSON), b2 -> b2
.GET("/{id}", handler::getPerson)
.GET("", handler::listPeople))
.POST("/person", handler::createPerson))
.add(otherRoute)
.build();
'Spring Framework' 카테고리의 다른 글
Spring Webflux - Server Configuration (0) | 2022.03.14 |
---|---|
Spring Webflux - WebClient (0) | 2022.03.14 |
Spring Webflux - Filter (0) | 2022.03.14 |
Spring Webflux - HandlerFunction (0) | 2022.03.14 |
Spring Webflux - Overview (0) | 2022.03.14 |
Comments