2024. 10. 13. 20:25ㆍSpring Framework/Web on Servlet Stack
Spring Web MVC: Functional Endpoints
Spring Web MVC에서는 기존의 @RequestMapping
기반 애너테이션 스타일을 대체할 수 있는 또 다른 방식으로 WebMvc.fn이라는 함수형 프로그래밍 모델을 제공합니다. 이 모델은 함수형 스타일을 사용하여 HTTP 요청을 라우팅하고 처리합니다. WebMvc.fn은 Spring WebFlux의 WebFlux.fn과 매우 유사한 구조를 가지고 있지만, 반응형(Reactive) 모델이 아닌 일반적인 서블릿 기반의 Web MVC와 함께 동작합니다.
핵심 개념
- HandlerFunction:
- HandlerFunction은
@RequestMapping
메서드 본체에 해당하는 개념입니다. 즉, HTTP 요청을 처리하는 함수입니다. 이 함수는ServerRequest
를 아규먼트로 받아ServerResponse
를 리턴합니다. ServerRequest
는 HTTP 요청 데이터를 포함하고 있으며, 불변성을 지니고 있습니다.ServerResponse
는 HTTP 응답을 처리하기 위한 객체로, 역시 불변성을 가지고 있습니다.
public interface HandlerFunction<T extends ServerResponse> { T handle(ServerRequest request); }
- HandlerFunction은
- RouterFunction:
- RouterFunction은 요청을 적절한 HandlerFunction으로 라우팅하는 역할을 합니다. 즉, RouterFunction은 요청이 들어오면 이를 처리할 HandlerFunction을 찾고, 해당 함수를 반환하는 구조입니다.
@RequestMapping
애너테이션과 유사한 역할을 하지만, 라우터 함수는 데이터뿐만 아니라 행동(비즈니스 로직)을 정의할 수 있다는 차이점이 있습니다.- 라우터 함수는 주어진 요청과 매칭되면
Optional<HandlerFunction>
을 반환하고, 매칭되지 않으면 빈Optional
을 반환합니다.
public interface RouterFunction<T extends ServerResponse> { Optional<HandlerFunction<T>> route(ServerRequest request); }
- 라우터 빌더:
- RouterFunctions.route()는 라우터 빌더를 제공하여 라우터를 쉽게 생성할 수 있도록 돕습니다. 빌더 패턴을 사용하여 HTTP 메서드와 경로, 핸들러를 설정하고 빌드를 통해 최종 라우터를 생성합니다.
GET
,POST
,PUT
,DELETE
등의 HTTP 메서드를 쉽게 매핑할 수 있으며, 경로와accept
조건을 활용해 다양한 요청 조건에 맞는 핸들러를 설정할 수 있습니다.
기능적 엔드포인트의 주요 장점
- 함수형 스타일:
- WebMvc.fn은 함수형 스타일을 지원하므로, 불변성을 유지하면서 코드를 더 간결하게 작성할 수 있습니다.
- JDK 8의 람다 및 메서드 참조 활용:
- 함수형 프로그래밍 모델을 지원하기 때문에 람다식과 메서드 참조를 통해 직관적이고 간결한 코드를 작성할 수 있습니다.
- 가벼운 대안:
- 기존의 애너테이션 기반 모델과 동일한 DispatcherServlet에서 실행되지만, 애너테이션을 사용하지 않고도 같은 작업을 할 수 있는 가벼운 대안입니다.
- 간결한 라우팅 및 핸들링:
- URL 매핑, 요청 메서드에 대한 정의가 라우터 함수로 한 곳에서 간결하게 이루어질 수 있습니다.
구체적인 예시
다음 예시는 PersonHandler
라는 클래스에서 사람 관련 요청을 처리하는 기능을 가진 라우터와 핸들러를 정의하는 코드입니다.
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.servlet.function.RequestPredicates.*;
import static org.springframework.web.servlet.function.RouterFunctions.route;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
import org.springframework.web.servlet.function.RouterFunction;
public class PersonHandler {
private final PersonRepository repository;
public PersonHandler(PersonRepository repository) {
this.repository = repository;
}
// 사람 목록을 반환하는 핸들러
public ServerResponse listPeople(ServerRequest request) {
List<Person> people = repository.findAll();
return ServerResponse.ok().contentType(APPLICATION_JSON).body(people);
}
// 새로운 사람을 생성하는 핸들러
public ServerResponse createPerson(ServerRequest request) {
Person person = request.body(Person.class);
repository.save(person);
return ServerResponse.ok().build();
}
// 특정 ID로 사람을 조회하는 핸들러
public ServerResponse getPerson(ServerRequest request) {
String id = request.pathVariable("id");
Person person = repository.findById(id);
if (person != null) {
return ServerResponse.ok().contentType(APPLICATION_JSON).body(person);
} else {
return ServerResponse.notFound().build();
}
}
}
이 코드에서 PersonHandler
는 PersonRepository
를 통해 데이터베이스에서 사람 정보를 가져오고, 새로운 사람을 생성하거나 조회할 수 있는 기능을 제공합니다.
라우터 정의
RouterFunction<ServerResponse> route = route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
- 라우터 빌더 사용:
route()
메서드를 통해 라우터 빌더를 시작합니다.GET("/person/{id}")
: 특정 ID의 사람 정보를 가져옵니다.GET("/person")
: 사람 목록을 가져옵니다.POST("/person")
: 새로운 사람을 추가하는 요청을 처리합니다.- 각 요청에 대해 해당 요청을 처리하는 HandlerFunction이 지정됩니다.
구체적인 핸들러 함수와 라우터 빌더 설명
GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
:- HTTP GET 요청으로
/person/{id}
경로를 매핑하며,APPLICATION_JSON
헤더를 수락하는 요청에 대해getPerson
핸들러 함수를 호출합니다. handler::getPerson
은PersonHandler
클래스의getPerson
메서드를 참조합니다.
- HTTP GET 요청으로
POST("/person", handler::createPerson)
:- HTTP POST 요청으로
/person
경로를 매핑하며, JSON 형식으로 새로운 사람 정보를 추가할 때createPerson
핸들러가 호출됩니다.
- HTTP POST 요청으로
RouterFunction을 Bean으로 등록하기
RouterFunction
은 일반적으로 Spring Bean으로 등록되어 DispatcherServlet에 의해 자동으로 감지됩니다. 이를 위해 @Configuration
클래스를 사용하여 정의할 수 있습니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
@Configuration
public class RouterConfig {
private final PersonHandler handler;
public RouterConfig(PersonHandler handler) {
this.handler = handler;
}
@Bean
public RouterFunction<ServerResponse> personRoutes() {
return route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
}
}
@Configuration
클래스에서RouterFunction<ServerResponse>
타입의 빈을 정의하여 라우터를 설정합니다. Spring은 이 빈을 자동으로 감지하고 설정된 라우팅 규칙에 따라 요청을 처리합니다.
Spring Web MVC의 WebMvc.fn은 기존 애너테이션 기반 프로그래밍 모델과 동일한 서블릿 기반 환경에서 함수형 스타일의 라우팅과 요청 처리를 제공합니다. 이 모델은 불변성을 유지하고, 람다식과 메서드 참조를 활용해 더 간결하고 직관적인 코드를 작성할 수 있게 합니다.
핵심 개념은 HandlerFunction과 RouterFunction이며, 이 둘을 활용해 HTTP 요청을 처리하고, 필요한 데이터를 제공하거나 비즈니스 로직을 수행하는 방식을 설계할 수 있습니다.
'Spring Framework > Web on Servlet Stack' 카테고리의 다른 글
RounterFunction (0) | 2024.10.13 |
---|---|
HandlerFunction (0) | 2024.10.13 |
Annotated Controllers (0) | 2024.10.09 |
Type Conversion (0) | 2024.10.09 |
Return Values (0) | 2024.10.09 |