@RequestBody, @ResponseBody
시작하기에 앞서,
@RequestParam은 요청 파라미터에 있는 값을 수신하여 저장했고,
@ModelAttribute는 폼에 있는 작성된 파라미터 값을 수신하여 저장했다.
그렇다면 실제 HTTP 통신이 이루어질 때, 메세지 바디에 담긴 메세지를 수신하고 송신하려면 어떻게 해야할까?
이를 위해 존재하는 것이 HttpEntity, Request(Response)Entity, @Requestbody, @ResponseBody이다.
근데 이것들은 이전에 배운 @RequestParam, @ModelAttribute와는 다르게 메세지 컨버터(Message Converter)를 사용한다.
메세지 컨버터는 메세지를 자바의 심플 타입 혹은 복합 타입에 매핑될 수 있도록 컨터빙해주는 역할을 한다.
이 과정이 실제로 언제, 어떻게 일어나는지 살펴보고 그 이후에 사용방법을 알아보자.
HTTP 메세지 송수신 원리
HTTP 메세지의 송수신 원리를 이해하기 위해 서버의 시작부터 보내는 것까지 천천히 알아보도록 하자.
(1) 서버 시작시 메세지 컨버터 등록
- 우리가 서버를 키면 WebMvcConfigurationSupport에서 지원하는 메세지 컨버터들과 우리가 실제로 인터페이스를 확장하여 우리가 추가한 Custom 메세지 컨버터들을 등록해준다.
(2) 사용자의 요청
- 사용자가 임의의 요청을 진행하게 되고 HTTP 통신을 통해 서버에 전달된다.
(3) 핸들러와 핸들러 어댑터 꺼내기
DispatcherServlet에서 DispatcherServlet에서 이에 맞는 핸들러와 핸들러 어댑터를 꺼내온다.
(RequestMapping에 대한 자세한 이해가 없다면 RequestMapping에서 확인)
(4) 핸들러 어댑터 handle 메소드 실행
- 핸들러 어댑터로 handle을 진행하여 찾아진 HandlerMethod로 진행한다.
(5) 핸들러메소드 실행
- 실제로 핸들러 메소드를 실행한다.
- 그러기 위해 실행가능한 핸들러메소드에서 invokeAndHandle을 진행해주고 필요한 정보를 invokeForRequest로 받아온다.
(6) Argument Value 추출하기
- 여기서 실제로 우리가 메소드에서 필요로하는 파라미터를 받아온다.
- 우리는 실행할 때, 여러 Argument Resolver들을 등록하는데 그 중에서 지원하는지 체크를 한 뒤 지원되는 resolver로 실행한다.
- 우리는 현재 @RequestBody를 사용하고 있기에 해당 애노테이션을 통해 파라미터를 받을 수 있는 Resolver인 RequestResponseBodyMethodProcessor를 통해 받아온다.
- 실제로 추후에 설명하겠지만 return 하기 위해 쓸 때는 RequestResponseBodyMethodProcessor의 handleReturnValue 메소드를 통해 컨버팅 후 전송한다.
간단히 정리해보면, 우리가 핸들러와 핸들러 어댑터를 찾고 Argument Resolver를 통해 파라미터를 찾는 중 우리가 HttpEntity, RequestEntity, @RequestBody 혹은 HttpEntity, ResponseEntity, @ResponseBody가 존재한다면 RequestResponseBodyMethodProcessor라는 Argument Resolver가 실행되어 메세지 컨버터를 통해 송수신된다는 것이다.
그러면, 메세지 컨버터를 통해 가능한 타입이 많지만 우리가 대표적으로 사용하는 3가지에 대해 알아보면 다음과 같다.
- 수신과 송신에 있는 타입이 맞을 경우에 해당 컨버터를 선택한다.
- 선택된 컨버터를 이용해 결과를 반환하지만, 어떤 컨버터도 채택되지 않는다면 오류가 발생한다.
HTTP 메세지 송수신 사용
(1) HttpEntity
(1) String
@Slf4j
@Controller
public class request_mapping {
@RequestMapping("/request_mapping")
public HttpEntity<String> visit(HttpEntity<String> person) {
return new HttpEntity<>(person.getBody());
}
}
(2) Json
@Slf4j
@Controller
public class request_mapping {
@RequestMapping("/request_mapping")
public HttpEntity<String> visit(HttpEntity<Person> person) {
return new HttpEntity<>(person.getBody().getName() + ", " + person.getBody().getAge());
}
}
- HttpEntiy 객체를 선언하여 메세지를 송수신할 수 있다.
(2) RequestEntity
(1) String
@Slf4j
@Controller
@ResponseBody
public class request_mapping {
@RequestMapping("/request_mapping")
public ResponseEntity<String> visit(RequestEntity<String> person) {
return new ResponseEntity<>(person.getBody(), HttpStatus.ACCEPTED);
}
}
(2) Json
@Slf4j
@Controller
public class request_mapping {
@RequestMapping("/request_mapping")
public ResponseEntity<String> visit(RequestEntity<Person> person) {
return new ResponseEntity<>(person.getBody().getName() + ", " + person.getBody().getAge(), HttpStatus.ACCEPTED);
}
}
- RequestEntity 객체를 선언하여 메세지를 수신할 수 있다.
- ResponseEntity 객체를 선언하여 메세지를 송신할 수 있다.
- ResponseEntity 객체를 사용할 때 두번째 파라미터 값에 HTTP 상태 코드 값을 기입한다.
(3) Requestbody
(1) String
@Slf4j
@Controller
public class request_mapping {
@ResponseBody
@RequestMapping("/request_mapping")
public String visit(@RequestBody String person) {
return person;
}
}
(2) Json
@Slf4j
@Controller
public class request_mapping {
@ResponseBody
@RequestMapping("/request_mapping")
public Person visit(@RequestBody Person person) {
return person;
}
}
- @RequestBody 애노테이션을 통해 문자열은 문자열, 객체는 객체로 매핑하여 메세지를 수신한다.
- @ResponseBody 애노테이션을 통해 문자열 혹은 객체 그 상태로 메세지를 송신한다.
- 객체의 경우 메세지 컨버터가 자동으로 Json으로 변경해준다.