HTTP Message Converter?
HTTP API처럼 데이터를 HTTP 메시지 바디에서 직접 읽거나 쓰는 경우 사용된다.
@ResponseBody를 사용하면 HTTP Body에 문자 내용을 직접 반환하게 되는데, ViewResolver 대신에 HTTP Message Converter가 동작한다.
HTTP Messge Converter는 Json이나 String 중 선택하여 데이터를 전달하게 된다.
- 기본 문자 처리: StringHttpMessageConverter (String)
- 기본 객체 처리: MappingJackson2HttpMessageConverter (Json)
String이든 Json이든 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;
}
HTTP Message Converter는 HTTP Request, Response 둘 다 사용된다.
- canRead, canWrite: MessageConverter가 해당 클래스나 미디어 타입을 지원하는지 확인한다.
- read(), write(): MessageConverter를 통해 메시지를 읽고 쓰는 기능
기본적으로 스프링 부트는 메시지 컨버터를 스프링 부트가 올라올 때 등록한다.
0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter
..
스프링 부트는 다양한 메시지 컨버터를 제공하는데, 대상으로 하는 ①클래스 타입과 ②미디어 타입을 지원하는지 확인한 후에 만약 만족하지 않다면 다음 메시지 컨버터로 우선 순위가 넘어간다.
HTTP 요청 데이터 읽기
① HTTP 요청이 오고, 컨트롤러에서 @RequestBody, HttpEntity 파라미터를 사용한다.
② Message Converter가 메시지를 읽을 수 있는지 확인하기 위해 canRead()를 호출한다.
- 대상 클래스를 지원하는가? (e.g. byte[], String, HelloData)
- HTTP Request의 Content-Type을 지원하는가? (e.g. text/plain, application/json, */*)
③ canRead() 조건을 만족하면 read()를 호출해서 객체를 생성하고, 반환한다.
HTTP 응답 데이터
① 컨트롤러에서 @ResponseBody, HttpEntity로 값이 반환된다.
② Message Converter가 메시지를 쓸 수 있는지 확인하기 위해 canWrite()를 호출한다.
- 대상 클래스 타입을 지원하는가? (e.g. byte[], String, HelloData)
- HTTP 요청의 Accept 미디어 타입을 지원하는가? (정확히는 @RequestMapping의 produces) (e.g. text/plain, application/json, */*)
③ canWrite() 조건을 만족하면 write()를 호출해서 HTTP 응답 메시지 바디에 데이터를 생성한다.
예시
# Case 1
content-type: application/json
@RequestMapping("~~~")
public void hello(@RequestBody String data) { }
0번 Message Converter: 대상 클래스 타입이 String → 0번 PASS
1번 Message Converter: String이어서 클래스 타입 조건은 부합한다. 미디어 타입도 */*이기 때문에 부합하므로 위 케이스에는 1번 Message Converter이 동작
# Case 2
content-type: application/json
@RequestMapping("~~~")
public void hello(@RequestBody HelloData data) { }
0번 Message Converter: 대상 클래스 타입이 String → 0번 PASS
1번 Message Converter: 대상 클래스 타입이 HelloData인 객체 → 1번 PASS
2번 Message Converter: HelloData인 객체이므로 클래스 타입 조건도 부합하고, content-type도 application/json 이기 때문에 위 케이스 2는 2번 Message Converter가 동작.
Spring MVC 아키텍차에서 HTTP Message Converter는 어디서 사용되는 건가?
HTTP Message Convter는 핸들러 어댑터에서 핸들러(컨트롤러)를 호출하는 때에 사용된다.
RequestMappingHandlerAdapter 동작 방식
1. ArgumemtResolver
어노테이션 기반의 컨트롤러에서는 매우 다양한 파라미터를 Argument로 넣기만 해도 어디선가 가져와서 마음대로 사용할 수 있었다. (e.g. ServletRequest, @RequestParam, HttpEntity, @RequestBody..)
이렇게 파라미터를 유연하게 처리할 수 있도록 도와주는 친구가 바로 ArgumentResolver이다.
@Slf4j
@Controller
public class ResponseBodyController {
@GetMapping("/response-body-string-v1")
public void responseBodyV1(HttpServletResponse response) throws IOException { }
@GetMapping("/response-body-string-v2")
public ResponseEntity<String> responseBodyV2() throws IOException { }
@GetMapping("/response-body-string-v3")
public String responseBodyV3() { }
@GetMapping("/response-body-json-v1")
public ResponseEntity<HelloData> responseBodyJsonV1() { }
@GetMapping("/response-body-json-v2")
public HelloData responseBodyJsonV2() { }
}
어노테이션 기반 컨트롤러를 처리하는 'RequestMappingHandlerAdaptor'는 이 'ArgumentResolver'를 호출하여 컨트롤러가 필요로 하는 다양한 파라미터를 생성한다. 이렇게 파라미터 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다.
# 동작 방식
ArgumentResolver의 supportsParameter()를 호출하여 해당 파라미터를 지원하는지 체크하고, 지원하면 resolveArgument()를 호출하여 실체 객체를 생성한다. 그리고 컨트롤러 호출 시 생성된 객체가 전달된다.
2. ReturnValueHandler
Controller에서 String으로 뷰 이름을 반환해도, 동작하는 이유가 ReturnValueHandler이다.
3. 그럼 HTTP Message Converter의 사용 위치는?
HTTP Message Converter는 Argument Resolver와 ReturnValueHandler가 사용한다.
# '요청'의 경우
@RequestBody를 처리하는 ArgumentResolver가 있고, HttpEntity를 처리하는 ArgumentResolver가 있다. 이 ArgumentResolver들이 HTTP Message Converter를 사용해서 필요한 객체를 생성하는 것이다.
# '응답'의 경우
@ResponseBody와 HttpEntity를 처리하는 ReturnValueHandler가 있다. 그리고 여기에서 HTTP Message Converter를 호출해서 응답 결과를 만든다.
스프링 MVC는 @RequestBody, @ResponseBody 가 있으면 RequestResponseBodyMethodProcessor (ArgumentResolver)
HttpEntity 가 있으면 HttpEntityMethodProcessor (ArgumentResolver)를 사용한다.
'🌱 Spring > MVC ①' 카테고리의 다른 글
[Spring] Thymeleaf View로 변경 (0) | 2023.07.27 |
---|---|
[Spring] Item 도메인 개발 (0) | 2023.07.27 |
[Spring] HTTP 응답 _ HTTP API, 메시지 바디에 직접 입력 (0) | 2023.07.24 |
[Spring] HTTP 응답 _ 정적 리소스, 뷰 템플릿 (0) | 2023.07.24 |
[Spring] HTTP message body에 데이터 담아서 요청하기 (0) | 2023.07.24 |