https://sundaland.tistory.com/359
[ ▶ RounterFunction ]
Spring Web MVC의 RouterFunction은 HTTP 요청을 적절한 HandlerFunction에 라우팅하는 데 사용되는 함수형 프로그래밍 방식의 요소이다. 이 모델은 애너테이션 기반 방식의 대안으로, 함수형 프로그래밍 스타일을 지원하여 라우팅과 요청 처리를 더 유연하게 할 수 있다. WebMvc.fn에서 사용되며, Spring WebFlux의 Reactive Stack과 유사한 구조를 가지고 있다.
[ ▷ RouterFunction의 개념과 사용법 ]
- RouterFunction은 HTTP 요청을 적절한 HandlerFunction으로 라우팅하는 함수이다.
- @RequestMapping을 사용한 애너테이션 기반 라우팅과 동일한 역할을 하지만, 함수형 스타일로 요청을 라우팅한다.
- 요청을 라우팅할 때는 RequestPredicate을 사용하여 경로, HTTP 메서드, 헤더 등 다양한 조건을 적용할 수 있다.
RouterFunctions.route() 메서드는 라우터 함수 빌더를 제공하며, 이를 통해 여러 가지 라우팅 규칙을 간결하게 정의할 수 있다.
[ ▷ RouterFunction을 정의하는 방법 ]
[ ▷ 기본적인 RouterFunction 정의 ]
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().body("Hello World"))
.build();
- GET("/hello-world"): HTTP GET 요청이 /hello-world 경로로 들어오면 매칭된다.
- accept(MediaType.TEXT_PLAIN): Accept 헤더가 text/plain인 경우에만 매칭된다 .
- HandlerFunction은 요청을 처리하고 응답을 리턴하는 역할을 합니다. 여기서는 간단히 "Hello World"라는 문자열을 응답으로 보내고 있다.
[ ▷ RequestPredicate 사용 ]
RequestPredicate는 요청의 경로, HTTP 메서드, 헤더 등의 조건을 정의하는데 사용된다. RequestPredicates 유틸리티 클래스를 통해 자주 사용되는 다양한 조건을 미리 제공받을 수 있다.
- RequestPredicate.and(RequestPredicate): 두 가지 조건이 모두 일치해야 요청이 매칭된다.
- RequestPredicate.or(RequestPredicate): 두 가지 조건 중 하나만 일치해도 요청이 매칭된다.
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN).and(method(HttpMethod.GET)),
request -> ServerResponse.ok().body("Hello World"))
.build();
GET 요청과 text/plain 헤더를 가진 요청만 매칭한다.
[ ▷ 라우터 함수 빌더와 추가적인 요청 조건 ]
RouterFunctions.route()를 사용한 라우터 함수 빌더는 다양한 HTTP 메서드에 대한 간편한 매핑을 제공한다.
- GET(String path, HandlerFunction handler): GET 요청을 특정 경로에 매핑한다.
- POST(String path, HandlerFunction handler): POST 요청을 특정 경로에 매핑한다.
[ ▷ RouterFunction의 조합 ]
RouterFunction은 서로 조합할 수 있다. 여러 라우터 함수를 하나로 합쳐서 복잡한 라우팅 규칙을 만들 수 있다. 이를 위해 RouterFunction.and(), RouterFunction.andRoute() 등의 메서드를 사용할 수 있다.
[ ▷ 라우터 함수 조합 ]
RouterFunction<ServerResponse> otherRoute = ...;
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/person/{id}", accept(MediaType.APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(MediaType.APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.add(otherRoute) // 다른 RouterFunction 추가
.build();
- add(otherRoute): 다른 라우터 함수를 추가하여 라우팅 규칙을 확장할 수 있다.
- GET, POST 메서드를 통해 각 경로에 요청을 라우팅하는 규칙을 정의한다.
[ ▷ 라우팅 순서와 우선순위 ]
RouterFunction은 정의된 순서대로 평가된다. 가장 먼저 정의된 라우터가 먼저 매칭을 시도하고, 매칭되지 않으면 다음 라우터가 평가된다. 따라서 더 구체적인 라우팅 규칙을 먼저 정의하고, 일반적인 규칙을 나중에 정의하는 것이 중요하다.
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/person/{id}", accept(MediaType.APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(MediaType.APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
위에서 /person/{id} 경로가 /person 경로보다 먼저 평가된다. 이는 /person 경로가 더 일반적인 규칙이기 때문에, 특정 ID를 가진 /person/{id}가 먼저 평가되는 것이 바람직하기 때문이다.
[ ▷ 라우트 중첩 (Nested Routes) ]
라우트 중첩은 공통되는 경로나 조건을 묶어서 라우터의 중복을 줄이는 데 유용하다. 중첩된 경로를 정의하여 코드의 간결성을 높일 수 있습니다. 이는 path() 메서드를 사용하여 구현할 수 있다.
[ ▷ 중첩된 경로 정의 ]
RouterFunction<ServerResponse> route = RouterFunctions.route()
.path("/person", builder -> builder
.GET("/{id}", accept(MediaType.APPLICATION_JSON), handler::getPerson)
.GET(accept(MediaType.APPLICATION_JSON), handler::listPeople)
.POST(handler::createPerson))
.build();
- path("/person"): /person 경로를 공유하는 모든 요청에 대해 하위 경로(/person/{id}, /person)를 정의할 수 있다.
- 중복된 경로를 묶어서 코드의 반복을 줄이고, 구조적으로 더 명확한 라우팅을 구현할 수 있다.
[ ▷ 중첩된 라우트와 복합 조건 ]
nest() 메서드를 사용하여 특정 조건(Accept 헤더)이 동일한 경로들을 묶을 수 있다. 이는 중첩된 경로에서 자주 사용하는 헤더나 조건을 함께 사용할 때 유용하다.
[ ▷ nest() 메서드를 사용한 복합 조건 ]
RouterFunction<ServerResponse> route = RouterFunctions.route()
.path("/person", b1 -> b1
.nest(accept(MediaType.APPLICATION_JSON), b2 -> b2
.GET("/{id}", handler::getPerson)
.GET(handler::listPeople))
.POST(handler::createPerson))
.build();
- nest(accept(MediaType.APPLICATION_JSON)): 공통된 Accept 헤더가 application/json인 경로들을 묶어서 정의한다.
- 이를 통해 조건을 한 번만 선언하고, 하위의 여러 경로에 적용할 수 있다.
[ ▷ RouterFunction을 Spring Bean으로 등록하기 ]
RouterFunction은 일반적으로 Spring Bean으로 등록된다. 이렇게 하면 Spring이 자동으로 라우터를 감지하고 설정할 수 있다. 이를 위해 @Configuration 클래스를 사용하여 RouterFunction을 Bean으로 등록할 수 있다.
[ ▷ RouterFunction을 Spring Bean으로 등록 ]
@Configuration
public class RouterConfig {
private final PersonHandler handler;
public RouterConfig(PersonHandler handler) {
this.handler = handler;
}
@Bean
public RouterFunction<ServerResponse> personRoutes() {
return RouterFunctions.route()
.GET("/person/{id}", accept(MediaType.APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(MediaType.APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
}
}
- @Configuration 클래스에서 라우터 함수 정의를 Spring Bean으로 등록한다.
- @Bean을 통해 정의된 라우터 함수는 Spring이 자동으로 인식하고, DispatcherServlet에 의해 사용된다.
[ ▷ 요약 ]
- RouterFunction은 함수형 스타일로 HTTP 요청을 라우팅하는 기능을 제공한다.
- 다양한 RequestPredicate를 사용해 HTTP 메서드, 경로, 헤더 등에 대한 조건을 적용할 수 있다.
- 여러 라우터 함수를 조합하거나 중첩하여 더 복잡한 라우팅을 간결하게 구현할 수 있다.
- 라우터 함수는 Spring Bean으로 등록되어 DispatcherServlet에서 자동으로 감지되고 동작한다.
이 방식은 기존의 애너테이션 기반 라우팅보다 더 유연하고, 함수형 프로그래밍 스타일을 선호하는 개발자들에게는 직관적이고 효율적인 대안이 될 수 있다.
'Web on Servlet Stack' 카테고리의 다른 글
Functional Endpoints [Serving Resources,Running a Server,Filtering Handler Functions] (0) | 2024.10.14 |
---|---|
DispatcherServlet (0) | 2024.10.14 |
Functional Endpoints Overview (0) | 2024.10.14 |
Exceptions (0) | 2024.10.14 |
Validation (0) | 2024.10.14 |