2024. 10. 13. 20:33ㆍSpring Framework/Web on Servlet Stack
Spring Web MVC의 Functional Endpoints 모델인 WebMvc.fn에서 HandlerFunction과 ServerRequest, ServerResponse에 대한 자세한 설명을 제공하고, 이 기능이 어떻게 HTTP 요청을 처리하는지에 대한 구체적인 내용을 설명하겠습니다.
핵심 개념
1. HandlerFunction
HandlerFunction
은 HTTP 요청을 처리하는 함수입니다. 이것은 애너테이션 기반의@RequestMapping
메서드 본체와 동일한 역할을 하며, HTTP 요청을 받아 이를 처리하고 응답을 반환합니다.- 이 함수는
ServerRequest
객체를 받아ServerResponse
객체를 반환하는 형태입니다. 즉, 요청을 처리하는 로직을 정의한 함수로, 클라이언트로부터의 요청을 받아 응답을 생성합니다.
HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().body("Hello World");
위 예시는 간단한 핸들러 함수로, Hello World
라는 메시지를 반환하는 예입니다. 그러나 여러 개의 람다 함수가 많아지면 코드가 복잡해질 수 있으므로, 이를 Handler 클래스로 묶어서 관리할 수 있습니다.
2. ServerRequest
ServerRequest
는 HTTP 요청에 대한 정보를 제공합니다. 이를 통해 HTTP 메서드, URI, 헤더, 쿼리 파라미터 등에 접근할 수 있습니다. HTTP 본문(Body)에 접근하기 위해서는body()
메서드를 사용합니다.
예시: 본문(body)을String stringBody = request.body(String.class);
List<Person>
으로 추출하는 경우:예시: 쿼리 파라미터를 추출하는 경우:ServerRequest
를 사용하여 HTTP 요청의 모든 구성 요소를 쉽게 추출할 수 있습니다.MultiValueMap<String, String> params = request.params();
List<Person> people = request.body(new ParameterizedTypeReference<List<Person>>() {});
- 예시: 본문(body)을
String
으로 추출하는 경우:
3. ServerResponse
ServerResponse
는 HTTP 응답을 표현하는 객체입니다.ServerResponse
는 불변성을 가지고 있어, 빌더 패턴을 사용하여 응답을 생성합니다.- 응답의 상태 코드, 헤더, 본문 등을 설정할 수 있으며, JSON 등의 다양한 포맷으로 응답을 보낼 수 있습니다.
예시: Location 헤더를 포함한 201 (Created) 응답을 생성하는 경우:이처럼 ServerResponse는 HTTP 응답을 구성하는 다양한 방법을 제공하여 클라이언트에게 적절한 HTTP 응답을 보낼 수 있습니다.Person person = ...; ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
URI location = ...; ServerResponse.created(location).build();
- 예시: JSON 응답을 생성하는 경우:
Functional Endpoints의 주요 기능
1. HandlerFunction과 Handler 클래스
- HandlerFunction은 요청을 처리하는 단일 함수입니다. 그러나 실제 애플리케이션에서는 여러 함수가 필요하기 때문에 관련된 핸들러들을 한 클래스에 모아 관리할 수 있습니다. 이렇게 하면 애너테이션 기반의
@Controller
와 비슷한 역할을 하게 됩니다.public class PersonHandler { private final PersonRepository repository; public PersonHandler(PersonRepository repository) { this.repository = repository; } // 모든 사람 목록을 반환 public ServerResponse listPeople(ServerRequest request) { List<Person> people = repository.allPeople(); return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(people); } // 새로운 사람을 생성 public ServerResponse createPerson(ServerRequest request) throws Exception { Person person = request.body(Person.class); repository.savePerson(person); return ServerResponse.ok().build(); } // 특정 사람을 조회 public ServerResponse getPerson(ServerRequest request) { int personId = Integer.parseInt(request.pathVariable("id")); Person person = repository.getPerson(personId); if (person != null) { return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person); } else { return ServerResponse.notFound().build(); } } }
listPeople
: 모든 사람 목록을 JSON으로 반환하는 핸들러 함수입니다.createPerson
: 요청 본문에 포함된Person
객체를 데이터베이스에 저장하는 핸들러입니다.getPerson
: 주어진 ID로 특정 사람을 조회하며, 존재하지 않으면 404 Not Found 응답을 보냅니다.
- 예시:
2. 비동기 응답 (Async Responses)
- 비동기 처리 방식으로 응답을 반환할 수 있습니다. 예를 들어,
CompletableFuture
나Publisher
를 사용하여 비동기 데이터를 처리하고 응답할 수 있습니다.
이 예시는Mono<Person> person = webClient.get().retrieve().bodyToMono(Person.class); ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
Mono<Person>
타입의 비동기 데이터를 처리하여 응답을 생성하는 방법을 보여줍니다. - 예시: 비동기 응답을 생성하는 경우:
3. Server-Sent Events (SSE)
- SSE (Server-Sent Events)는 서버에서 클라이언트로 지속적인 데이터를 푸시하는 기술입니다. Spring Web MVC에서는
ServerResponse.sse()
메서드를 통해 SSE를 지원합니다.public RouterFunction<ServerResponse> sse() { return route(GET("/sse"), request -> ServerResponse.sse(sseBuilder -> { // SSE 처리 로직 })); } // 다른 쓰레드에서 이벤트 전송 sseBuilder.send("Hello world");
sseBuilder.send()
를 사용해 문자열이나 객체를 클라이언트로 전송할 수 있습니다.- JSON 형식으로도 데이터를 보낼 수 있으며, 이벤트 ID 및 타입도 설정할 수 있습니다.
- 예시: SSE 응답을 생성하는 경우:
4. 유효성 검사 (Validation)
- 요청 본문에 포함된 데이터를 유효성 검사할 수 있습니다. Spring의
Validator
를 사용하거나 JSR-303 표준 유효성 검사를 적용할 수 있습니다.public class PersonHandler { private final Validator validator = new PersonValidator(); public ServerResponse createPerson(ServerRequest request) { Person person = request.body(Person.class); validate(person); // 유효성 검사 적용 repository.savePerson(person); return ServerResponse.ok().build(); } private void validate(Person person) { Errors errors = new BeanPropertyBindingResult(person, "person"); validator.validate(person, errors); if (errors.hasErrors()) { throw new ServerWebInputException(errors.toString()); } } }
validate()
메서드를 사용해Person
객체에 대한 유효성 검사를 수행하고, 오류가 있으면 400 Bad Request 응답을 발생시킵니다.
- 예시: 유효성 검사 적용
Functional Endpoint의 라우팅
RouterFunction
은 요청을 적절한 핸들러로 라우팅하는 역할을 합니다. RouterFunction
은 주로 라우터 빌더를 통해 정의되며, 빌드된 라우터는 Spring Web MVC에서 자동으로 감지되어 사용됩니다.
import static org.springframework.web.servlet.function.RouterFunctions.route;
import static org.springframework.http.MediaType.APPLICATION_JSON;
RouterFunction<ServerResponse> personRoutes = route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
이 라우팅 설정은 HTTP 메서드와 경로에 따라 적절한 핸들러를 호출하는 구조입니다.
Spring Web MVC의 Functional Endpoints는 기존의 애너테이션 기반 방식과는 다른 함수형 스타일의 프로그래밍을 지원하며, 더 간결하고 명확한 요청 처리 방식입니다. HandlerFunction
과 RouterFunction
을 사용하여 HTTP 요청을 처리할 수 있으며, 비동기 응답, SSE, 유효성 검사 등의 기능도 지원합니다. 이 모델은 간결한 코드 작성과 유지보수를 용이하게 해주는 동시에, 함수형 스타일을 통해 더 유연한 라우팅과 요청 처리를 제공합니다.
'Spring Framework > Web on Servlet Stack' 카테고리의 다른 글
Functional Endpoints[Serving Resources,Running a Server,Filtering Handler Functions] (0) | 2024.10.13 |
---|---|
RounterFunction (0) | 2024.10.13 |
Functional Endpoints Overview (0) | 2024.10.13 |
Annotated Controllers (0) | 2024.10.09 |
Type Conversion (0) | 2024.10.09 |