@ResponseBody 를 사용했을 때 귀찮게 getWriter().write() 하고 또 IOExeption을 추가하지 않아도 됐었다. 왜냐하면 HTTP의 BODY에 문자 내용을 직접 반환했기 때문이다.
이는 viewResolver 대신에 HttpMessageConverter 가 동작한 것이고
반환 타입이 String일 땐 기본 문자처리: StringHttpMessageConverter,
reference 타입일 땐 기본 객체처리: MappingJackson2HttpMessageConverter가 동작한다.
이땐 응답의 경우 클라이언트의 "HTTP Accept 해더"와 "서버의 컨트롤러 반환 타입 정보" 둘을 조합해서
HttpMessageConverter 가 선택된다.
또한 byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있다.
HTTP message converter를 사용하는 경우.
HTTP 요청: @RequestBody , HttpEntity(RequestEntity) ,
HTTP 응답: @ResponseBody , HttpEntity(ResponseEntity) ,
HTTP 메시지 컨버터는 HTTP 요청, HTTP 응답 둘 다 사용된다.
canRead() , canWrite() : 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크
read() , write() : 메시지 컨버터를 통해서 메시지를 읽고 쓰는 기능
0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter
HTTP 요청이 오고, 컨트롤러에서 @RequestBody , HttpEntity 파라미터를 사용한다.
메시지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 위에 컨버터 종류를 쫙돌면서 canRead() 를 호출한다.
컨트롤러에서 @ResponseBody , HttpEntity 로 값이 반환된다.
메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 canWrite() 를 호출한다.
content-type: application/json
@RequestMapping
void hello(@RequestBody String data){}
content-type: application/json
@RequestMapping
void hello(@RequestBody HelloData data){}
content-type: text/html
@ReqeustMapping
void hello(@RequestBody HelloData data){}
그럼 Http message Converter는 어디에 위치하고있을까?
바로 애노테이션 기반의 컨트롤러, 즉 @RequestMapping 을 처리하는 핸들러 어댑터인
RequestMappingHandlerAdapter (요청 매핑 헨들러 어뎁터)에 있다.
가만 보면 위 사진말고도 더 많은 파라미터를 처리할 수 있다.
HttpServeltRequest, Model, @RequestParam, @ModelAttribute, @ReqeustBody, HttpEntity 등 같은 HTTP 메시지를 처리하는데 매우 유연하데 대부분의 경우의 수를 다 받을 수 있었다.
이게 가능하게 해준것이 바로 ArgumentResolver이다.
애노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdapter 는 바로 이 ArgumentResolver 를 호출해서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성한다.
그리고 이렇게 파리미터의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다.
참고로 얘가 처리할 수 있는 파라미터는 30개 넘는데 자세한건 Docs에서 찾아볼 수 있다.
즉, ArgumentResolver의 역할을 RequestMappingHandlerAdapter가 요청 파라미터를 보고 ArgumentResolver에 객체를 요청하면 갖다주는 역할을 한다.
이렇게 view를 반환할 땐 ModelAndView, String, 혹은 void로 아예 반환이 없기도 하고 혹은 또 @RestController나 @ResponseBody를 사용하여 Body를 반환할 수도 있었다.
이 반환하는 역할을 해주는게 ReturnValueHandler이다.
그래서 View를 반환할 때 void로 반환하거나 String으로 반환해도 알아서 찰떡같이 알아먹는게 얘가 다 알하서 해줬기 때문이다.
얘도 마찬가지로 위의 공식문서에도 똑같이 확인할 수 있고 둘다 확장이 인터페이스로써 가능하다.
결국 Http message converter는 둘다 사용하는데 모든 파라미터에 대해 Http message converter가 사용되는 것이 아닌, @ReqeustBody, @ResponseBody, HttpEntity 를 요청 혹은 응답할 때 사용된다.
맥 단축키 cmd + [
: 이전 위치로 이동
shift + opt + cmd + U
: 상속관계 보기
ctrl + alt + shift + u
그래서 스프링 MVC는 @RequestBody @ResponseBody 가 있으면 RequestResponseBodyMethodProcessor() 를,
HttpEntity 가 있으면 HttpEntityMethodProcessor() 를 사용한다.
뭐 사실 확장할 일이 거의 없겠지만 참고로 확장이 가능하다는 것을 알아두면 좋다.
참고로 기능 확장은 WebMvcConfigurer 를 상속 받아서 스프링 빈으로 등록하면 된다. (예제)
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
//...
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//...
}
};
}