목차
HTTP API처럼 뷰가 아닌 JSON 데이터를 HTTP 메시지 바디에서 직접 읽거나 쓰는 경우, HTTP 메시지 컨버터를 사용합니다.
HttpMessageConverter
@ResponseBody 사용 원리
- HTTP의 Body에 문자 내용을 직접 반환합니다.
- viewResolver 대신 HttpMessageConverter가 동작합니다.
- 기본 문자 처리 : StringHttpMessageConverter
- 기본 객체 처리 : MappingJackson2HttpMessageConverter
- 이외에도, byte 처리 등 기타 여러 HttpMessageConverter가 등록되어 있습니다.
- 응답의 경우, 클라이언트의 Http Accept 헤더와 서버의 컨트롤러 반환 타입 둘을 조합해 HttpMessageConverter가 선택됩니다.
Spring MVC의 HTTP 메시지 컨버터
스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용합니다.
- HTTP 요청 : @RequestBody, HttpEntity(RequestEntity)
- HTTP 응답 : @ResponseBody, HttpEntity(ResponseEntity)
HttpMessageConverter 인터페이스
org.springframework.http.converter.HttpMessageConverter
package org.springframework.http.converter;
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}
HttpMessageConverter는 HTTP 요청, HTTP 응답 둘 다 사용됩니다.
- canRead(), canWrite() : 해당 메시지 컨버터가 해당 클래스와 미디어타입을 지원하는지 체크
- read(), write() : 메시지 컨터버를 통해 읽고 쓰는 기능
스프링 부트 기본 메시지 컨버터
0, 1, 2 : 우선순위 (일부 생략)
0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter
스프링부트는 다양한 메시지 컨버터를 제공하는데, 대상 클래스 타입과 미디어 타입 둘을 체크하여 사용 여부를 결정합니다.
만약, 만족하지 않는다면 다음 메시지 컨버터로 우선순위가 넘어갑니다.
- ByteArrayHttpMessageConverter : byte[] 데이터를 처리합니다.
- 클래스 타입: byte[]
- 미디어 타입 : */*
- 요청 예) @RequestBody byte[] data
- 응답 예) @ResponseBbody return byte[] , 쓰기 미디어 타입 (응답 헤더의 Content-type) : application/octect-stream
- StringHttpMessageConverter : String 문자로 데이터를 처리합니다.
- 클래스 타입 : String
- 미디어 타입 : */*
- 요청 예) @RequestBody String data
- 응답 예) @ResponseBody return "ok", 쓰기 미디어 타입 : text/plain
- MappingJackson2HttpMessageConverter : application/json 타입의 데이터를 처리합니다.
- 클래스 타입 ; 객체 또는 HashMap
- 미디어 타입 ; application/json 관련
- 요청 예) @RequestBody HelloData data
- 응답 예) @ResponseBody return helloData, 쓰기 미디어 타입 : application/json 관련
예제: Http 메시지 컨버터 선택
1. StringHttpMessageConverter
content-type: application/json
@RequestMapping
void hello(@RequestBody String data) {
...
}
→ StringHttpMessageConverter가 선택됩니다.
(StringHttpMessageConverter는 미디어타입이 */* 이므로 어떠한 미디어타입도 상관이 없습니다.)
2. MappingJackson2HttpMessageConverter
content-type: application/json
@RequestMapping
void hello(@RequestBody HelloData data) {
...
}
→ MappingJackson2HttpMessageConverter가 선택됩니다.
3. X
content-type: text/html
@RequestMapping
void hello(@RequestBody HelloData data) {
....
}
→ 메시지 컨버터가 동작하지 않습니다.
RequestBody의 타입이 객체이기 때문에 우선순위부터 차례대로 살펴보면 ByteArrayHttpMessageConverter, StringHttpMessageConverter 모두 탈락됩니다.
마지막 MappingJackson2HttpMessageConverter의 클래스 타입은 맞으나, 미디어 타입이 application/json이 아니므로 어떠한 메시지 컨버터도 동작하지 않습니다.
HttpMessageConverter 동작 과정
HTTP 요청 데이터
- HTTP 요청이 오고, 컨트롤러에서 @RequestBody 또는 HttpEntity 파라미터를 사용합니다.
- 해당 메세지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 canRead()를 호출합니다.
- 대상 클래스 타입을 지원하는가?
예) @RequestBody의 대상 클래스 : byte[], string, HelloData - HTTP 요청의 Content-type 미디어 타입을 지원하는가?
예) text/plain, application/json, */*
- 대상 클래스 타입을 지원하는가?
- canRead() 조건에 만족하면 read()를 호출해 객체를 생성하고 반환합니다.
HTTP 응답 데이터
- 컨트롤러에서 @ResponseBody 또는 HttpEntity를 통해 값이 변환됩니다.
- 해당 메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 canWriter()를 호출합니다.
- 대상 클래스 타입을 지원하는가?
예) byte[], String, HelloData - HTTP 요청의 Accept 미디어 타입을 지원하는가? (더 정확히는 @RequestMapping의 produces)
예) text/plain, application/json, */*
- 대상 클래스 타입을 지원하는가?
- canWrite() 조건을 만족하면 write()를 호출하여 HTTP 응답 메시지 바디에 데이터를 입력합니다.
요청 핸들러 어댑터
그렇다면 HTTP 메시지 컨버터는 스프링 MVC 구조 중 어디에서 사용되는 것일까요?
SpringMVC 구조
답은 애노테이션 기반의 컨트롤러 즉, @RequestMapping 을 처리하는 핸들러 어댑터인 RequestMappingHandlerAdapter에 있습니다.
RequestMappingHandlerAdater 동작 방식
ArugumentResolver
애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있습니다.
HttpServletRequest, Model 은 물론이고, @RequestParam, @ModelAttribute 같은 애노테이션, @RequestBody, HttpEntity 같은 HTTP 메시지를 처리하는 부분까지 매우 큰 유연함을 보여줍니다.
이렇게 여러 파라미터를 유연하게 처리할 수 있는 이유는 ArgumentResovler 덕분입니다.
애노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdapter는 이 ArgumentResolver를 호출해 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터 값(객체)를 생성합니다.
스프링은 30개가 넘는 ArgumentResolver를 기본으로 제공합니다.
참고 가능한 파라미터 목록은 다음 공식 메뉴얼에서 확인할 수 있습니다.
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html
ArgumentResolver의 정확한 이름은 HandlerMethodArgumentResolve인데, 줄여서 ArgumentResolver라고 부릅니다.
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
- supportsParameter를 호출해 해당 파라미터를 지원하는지 체크합니다.
- 지원한다면, resolveArgument()를 호출하여 실제 객체를 생성합니다.
- 이렇게 생성된 객체가 컨트롤러 호출 시 파라미터로 넘어갑니다.
참고
여러분이 원한다면 직접 이 인터페이스를 확장해 원하는 ArgumentResolver를 만들 수도 있습니다.
ReturnValueHandler
HandlerMethodReturnValueHandler를 줄여서 ReturnValueHandler라 부릅니다.
ArgumentResolver와 비슷합니다. 단, 요청 값이 아닌 응답 값을 변환하고 처리합니다.
컨트롤러에서 String으로 뷰 이름을 반환해도 동작하는 이유는 바로 이 ReturnValueHandler 덕분입니다.
스프링은 10여개가 넘는 ReturnValueHandler를 지원합니다.
예) ModelAndView, @ResponseBody, HttpEntity, String
참고 가능한 응답 값 목록은 다음 공식 메뉴얼에서 확인할 수 있습니다.
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/return-types.html
HTTP 메시지 컨버터 위치
- 위에서 설명한 @RequestBody와 HttpEntity를 처리하는 ArumentResolver들이 HTTP 메시지 컨버터를 사용해 객체를 생성합니다.
- 또한, @ResponseBody와 HttpEntity를 처리하는 ReturnValueHandler가 HTTP 메시지 컨버터를 호출하여 응답 결과를 만듭니다.
- 스프링 MVC는 @RequestBody 또는 @ResponseBody가 있다면 RequestResponseBodyMethodProcessor(ArgumentResolver)를, HttpEntity가 있다면 HttpEntityMethodProcessor(ArugumentResolver)를 사용합니다.
확장
스프링은 다음을 모두 인터페이스로 제공합니다. 따라서 필요하면 언제든지 기능을 확장할 수 있습니다.
- HandlerMethodArugmentResolver
- HandlerMethodReturnValueHandler
- HttpMessageConverter
참고
필요한 대부분의 기능을 제공하기 때문에 실제 기능을 확장할 일이 많지는 않습니다.
기능 확장은 WebMvcConfigurer 를 상속 받아 스프링 빈으로 등록하면 됩니다.
실제 자주 사용하지는 않으니 실제 기능 확장이 필요할 때 WebMvcConfigurer를 검색해봅시다.
WebMvcConfigurer
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
...
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
...
}
};
}
출처
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
'Backend > Spring | SpringBoot' 카테고리의 다른 글
[SpringBoot] 필터와 인터셉터 (ArgumentResolver + @Login 활용) (0) | 2023.09.21 |
---|---|
[SpringBoot] 쿠키와 세션(HttpSession) (0) | 2023.09.19 |
[SpringBoot] 로깅 간단히 알아보기 (0) | 2023.08.31 |
[SpringBoot] HTTP 응답 관련 애노테이션 (0) | 2023.08.25 |
[SpringBoot] HTTP 요청 조회 애노테이션 (0) | 2023.08.25 |