1. HttpMessageConverter 용도
- HTTP API처럼 JSON 데이터를 HTTP 메시지 바디 내 직접 읽거나 쓰는 경우 사용
- @ResponseBody 어노테이션을 사용할 때 HTTP Body 내 문자 내용을 직접 반환하므로 HttpMessageConverter가 동작
- String 문자 처리에는 StringHttpMessageConverter, 객체 처리에는 MappingJackson2HttpMessageConverter 사용
- 이외에도 다양한 HttpMessageConverter가 존재
* 응답의 경우 클라이언트의 HTTP Accept 헤더와 서버 컨트롤러의 반환 타입을 조합해 MessageConverter가 선택됨 (하단에 추가 설명)
2. 스프링 MVC의 HttpMessageConverter 적용 사례
- HTTP 요청: @RequestBody, HttpEntity(RequestEntity)
- HTTP 응답: @ResponseBody, HttpEntity(ResponseEntity)
3. HttpMessageConverter 인터페이스 뜯어보기
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**
* Strategy interface for converting from and to HTTP requests and responses.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 3.0
* @param <T> the converted object type
*/
public interface HttpMessageConverter<T> {
/**
* Indicates whether the given class can be read by this converter.
* @param clazz the class to test for readability
* @param mediaType the media type to read (can be {@code null} if not specified);
* typically the value of a {@code Content-Type} header.
* @return {@code true} if readable; {@code false} otherwise
*/
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
/**
* Indicates whether the given class can be written by this converter.
* @param clazz the class to test for writability
* @param mediaType the media type to write (can be {@code null} if not specified);
* typically the value of an {@code Accept} header.
* @return {@code true} if writable; {@code false} otherwise
*/
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
/**
* Return the list of media types supported by this converter. The list may
* not apply to every possible target element type and calls to this method
* should typically be guarded via {@link #canWrite(Class, MediaType)
* canWrite(clazz, null}. The list may also exclude MIME types supported
* only for a specific class. Alternatively, use
* {@link #getSupportedMediaTypes(Class)} for a more precise list.
* @return the list of supported media types
*/
List<MediaType> getSupportedMediaTypes();
/**
* Return the list of media types supported by this converter for the given
* class. The list may differ from {@link #getSupportedMediaTypes()} if the
* converter does not support the given Class or if it supports it only for
* a subset of media types.
* @param clazz the type of class to check
* @return the list of media types supported for the given class
* @since 5.3.4
*/
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
/**
* Read an object of the given type from the given input message, and returns it.
* @param clazz the type of object to return. This type must have previously been passed to the
* {@link #canRead canRead} method of this interface, which must have returned {@code true}.
* @param inputMessage the HTTP input message to read from
* @return the converted object
* @throws IOException in case of I/O errors
* @throws HttpMessageNotReadableException in case of conversion errors
*/
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
/**
* Write an given object to the given output message.
* @param t the object to write to the output message. The type of this object must have previously been
* passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
* @param contentType the content type to use when writing. May be {@code null} to indicate that the
* default content type of the converter must be used. If not {@code null}, this media type must have
* previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
* returned {@code true}.
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
/** | |
* Strategy interface for converting from and to HTTP requests and responses. | |
* | |
* @author Arjen Poutsma | |
* @author Juergen Hoeller | |
* @author Rossen Stoyanchev | |
* @since 3.0 | |
* @param <T> the converted object type | |
*/ | |
public interface HttpMessageConverter<T> { | |
/** | |
* Indicates whether the given class can be read by this converter. | |
* @param clazz the class to test for readability | |
* @param mediaType the media type to read (can be {@code null} if not specified); | |
* typically the value of a {@code Content-Type} header. | |
* @return {@code true} if readable; {@code false} otherwise | |
*/ | |
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType); | |
/** | |
* Indicates whether the given class can be written by this converter. | |
* @param clazz the class to test for writability | |
* @param mediaType the media type to write (can be {@code null} if not specified); | |
* typically the value of an {@code Accept} header. | |
* @return {@code true} if writable; {@code false} otherwise | |
*/ | |
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType); | |
/** | |
* Return the list of media types supported by this converter. The list may | |
* not apply to every possible target element type and calls to this method | |
* should typically be guarded via {@link #canWrite(Class, MediaType) | |
* canWrite(clazz, null}. The list may also exclude MIME types supported | |
* only for a specific class. Alternatively, use | |
* {@link #getSupportedMediaTypes(Class)} for a more precise list. | |
* @return the list of supported media types | |
*/ | |
List<MediaType> getSupportedMediaTypes(); | |
/** | |
* Return the list of media types supported by this converter for the given | |
* class. The list may differ from {@link #getSupportedMediaTypes()} if the | |
* converter does not support the given Class or if it supports it only for | |
* a subset of media types. | |
* @param clazz the type of class to check | |
* @return the list of media types supported for the given class | |
* @since 5.3.4 | |
*/ | |
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) { | |
return (canRead(clazz, null) || canWrite(clazz, null) ? | |
getSupportedMediaTypes() : Collections.emptyList()); | |
} | |
/** | |
* Read an object of the given type from the given input message, and returns it. | |
* @param clazz the type of object to return. This type must have previously been passed to the | |
* {@link #canRead canRead} method of this interface, which must have returned {@code true}. | |
* @param inputMessage the HTTP input message to read from | |
* @return the converted object | |
* @throws IOException in case of I/O errors | |
* @throws HttpMessageNotReadableException in case of conversion errors | |
*/ | |
T read(Class<? extends T> clazz, HttpInputMessage inputMessage) | |
throws IOException, HttpMessageNotReadableException; | |
/** | |
* Write an given object to the given output message. | |
* @param t the object to write to the output message. The type of this object must have previously been | |
* passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}. | |
* @param contentType the content type to use when writing. May be {@code null} to indicate that the | |
* default content type of the converter must be used. If not {@code null}, this media type must have | |
* previously been passed to the {@link #canWrite canWrite} method of this interface, which must have | |
* returned {@code true}. | |
* @param outputMessage the message to write to | |
* @throws IOException in case of I/O errors | |
* @throws HttpMessageNotWritableException in case of conversion errors | |
*/ | |
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) | |
throws IOException, HttpMessageNotWritableException; | |
} |
* canRead(), canWrite() 메서드: 메시지 컨버터가 해당 클래스 혹은 미디어타입을 지원하는지 체크하는 용도
* read(), write() 메서드: 메시지 컨버터를 통해서 메시지를 읽고 쓰는 기능 지원
3.1 HTTP 요청 데이터 읽는 과정
- HTTP 요청이 들어오고, 컨트롤러에서 @RequestBody 혹은 HttpEntity 파라미터 사용하는 상황
- MessageConverter가 메시지를 읽을 수 있는 확인하기 위해 canRead() 메서드 호출 (대상 클래스 타입을 지원하는지, HTTP 요청의 Content-Type 미디어 타입을 지원하는지 체크)
- canRead() 조건을 만족할 경우 read() 메서드를 호출해서 객체 생성 후 반환
3.2 HTTP 응답 데이터 생성하는 과정
- 컨트롤러에서 @ResponseBody 혹은 HttpEntity로 값이 반환되는 상황
- MessageConverter가 메시지를 쓸 수 있는지 확인하기 위해 canWrite() 메서드 호출 (대상 클래스 타입을 지원하는지, HTTP 요청의 Accept 미디어 타입을 지원하는지 체크)
- canWrite() 조건을 만족할 경우 write() 메서드를 호출해서 HTTP 응답 메시지 바디 내 데이터 생성
4. 주로 사용하는 메시지 컨버터 세 가지 (우선순위 순)
- ByteArrayHttpMessageConverter
- StringHttpMessageConverter
- MappingJackson2HttpMessageConverter
* 앞서 언급한 대로 스프링에서 메시지 컨버터를 선정할 때 대상 클래스 타입과 미디어 타입을 체크 후 사용 여부를 결정하고, 만족하지 않을 경우 다음 우선순위에 있는 메시지 컨버터로 넘어가 체크 진행
4.1 ByteArrayHttpMessageConverter
- byte [] 데이터 처리
- 클래스 타입: byte []
- 미디어 타입: */*
- HTTP 요청 예시: @RequestBody byte [] example
- HTTP 응답 예시: @ResponeBody return byte[] (쓰기 MediaType: application/octet-stream)
4.2 StringHttpMessageConverter
- String 문자열로 데이터 처리
- 클래스 타입: String
- 미디어 타입: */*
- HTTP 요청 예시: @RequestBody String example
- HTTP 응답 예시: @ResponseBody return example (쓰기 MediaType: text/plain)
content-type: text/plain | |
@RequestMapping | |
String stringHttpMessageConverterExample(@RequestBody String example) { | |
return example; | |
} |
4.3 MappingJackson2HttpMessageConverter
- application/json 처리
- 클래스 타입: 객체 또는 HashMap
- 미디어 타입: application/json
- HTTP 요청 예시: @RequestBody Example example
- HTTP 응답 예시: @ResponseBody return example (쓰기 MediaType: application/json)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
content-type: application/json
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json")
public Example responseBodyJson(@RequestBody Example example) {
return example;
}
content-type: application/json | |
@ResponseStatus(HttpStatus.OK) | |
@ResponseBody | |
@GetMapping("/response-body-json") | |
public Example responseBodyJson(@RequestBody Example example) { | |
return example; | |
} |
4.4 오류가 나는 경우
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
content-type: text/html
@RequestMapping
void ErrorExample(@RequestBody Example example) {
// 중략
}
content-type: text/html | |
@RequestMapping | |
void ErrorExample(@RequestBody Example example) { | |
// 중략 | |
} |
* 대상 클래스 타입과 미디어 타입이 매칭이 안되기 때문에 오류가 발생
HttpMessageConverter가 쓰이는 시점?
우선, 기존 게시물에서 작성한 Spring MVC 구조를 살펴봅시다. (https://jaimemin.tistory.com/1820)

* 위 그림에서는 HttpMessageConverter가 쓰이는 시점이 보이지 않지만, HttpMessageConverter는 아래 사진처럼 @RequestMapping을 처리하는 HandlerAdapter인 RequestMappingHandlerAdapter에서 쓰입니다.

- 어노테이션 기반 컨트롤러는 HttpServletRequest, Model, @RequestParam, @ModelAttribute, @RequestBody, HttpEntity와 같이 다양한 파라미터를 사용할 수 있는데 이렇게 유연하게 처리해줄 수 있는 이유는 ArgumentResovler 덕분
- 즉, 컨트롤러를 처리하는 RequestMappingHandlerAdapter가 ArgumentResolver를 호출하면서 필요로 하는 다양한 파라미터의 값을 생성한 후 컨트롤러를 호출하며 파라미터를 넘겨줌 (사진 내 1번과 2번 과정)
- ReturnValueHandler는 응답 값을 변환하고 처리하는데, 컨트롤러에서 String으로 뷰의 논리적 이름만 반환하더라도 동작하는 이유가 ReturnValueHandler 덕분 (사진 내 3번 과정)
- 이 과정에서 HttpMessageConverter는 요청과 응답을 처리하는 과정에서 모두 필요한데 아래와 같은 상황에서 사용이 됨
- 요청: @RequestBody 혹은 HttpEntity를 처리하는 ArgumentResovler가 HttpMessageConverter를 사용해서 필요하는 객체를 생성
- 응답: @ResponseBody 혹은 HttpEntity를 처리하는 ReturnValueHandler에서 HttpMessageConverter를 사용해서 응답 결과 생성
출처
인프런 스프링 MVC 1편 (김영한 강사님)
'Spring' 카테고리의 다른 글
[SpringBoot] Validation 간단 정리 - 1 (BindingResult, Validator) (6) | 2021.07.16 |
---|---|
[Spring Boot] 메시지, 국제화 간단 정리 (0) | 2021.07.10 |
Spring MVC 구조 정리 (0) | 2021.06.09 |
클라이언트에서 서버로 HTTP 요청 메시지 보내는 방법 (0) | 2021.06.02 |
빈 스코프 (Bean Scope) (0) | 2021.05.26 |