HTTP 메시지 컨버터 - http 메시지는 어떻게 파싱될까?!
들어가며
HTTP 메시지는 이렇게 생겼다.
body에 담긴 내용을 가져오는 어노테이션은 @RequestBody이 있고,
header+body내용을 가져올 수 있는 데이터타입으로는 HttpEntity가 있다.
저 HTTP 메시지를 잘 파싱해서, 원하는 타입으로 만들어 컨트롤러 함수의 파라미터로 넣어주는 과정에 대해 알아보자!
(== @RequestBody, HttpEntity가 컨트롤러 함수 파라미터에 있을 때, 어떻게 동작할까?!)
HttpMessageConverter 인터페이스
스프링은 이 상황에 필요한 인터페이스를 준비해두었다.
바로바로, HttpMessageConverter!
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> var1, MediaType var2); // 미디어 타입을 체크해서, 읽을 수 있는지 검사
boolean canWrite(Class<?> var1, MediaType var2); // 미디어 타입을 체크해서, 쓸 수 있는지 검사
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException; // 메시지 컨버터를 통해서 메시지를 읽음
void write(T var1, MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException; // 메시지 컨버터를 통해서 메시지를 씀
}
인터페이스를 봐서 유추할 수 있듯이, 미디어 타입에 따라 사용할 수 있는 여러 구현체 또한 준비해두었다.
0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter
// 일부 생략
*read 기준으로 작성
클래스 | 처리하는 클래스 타입 | 미디어 타입 | 예시 |
---|---|---|---|
ByteArrayHttpMessageConverter | byte [] | */* | @RequestBody byte[] burrito |
StringHttpMessageConverter | String | */* | @RequestBody String burrito |
MappingJackson2HttpMessageConverter | 객체, HashMap | application/json | @RequestBody Burrito burrito |
HttpMessageConverter 동작 방식
@RequestBody를 처리하는 HandlerMethodArgumentResolver인 RequestResponseBodyMethodProcessor
의 함수를 살펴보자!
(HandlerMethodArgumentResolver에 대해 궁금하다면, [링크추가해야함] 참고!)
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType()); // 메시지 컨버터를 활용해서 클라이언트 요청을 파싱
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return arg;
}
readWithMessageConverters()
함수부터 파고파고 들어가보면... 익숙한 흐름을 찾을 수 있다.
스프링이 미리 등록해둔 구현체를 순차적으로 탐색하면서, 클라이언트 요청으로 넘어온 HTTP 메시지를 처리할 수 있는 메시지 컨버터를 찾는다.
canRead()
함수가 미디어타입과 클래스타입을 체크해서 참을 반환하면, read()
함수를 통해 파싱을 진행한다.
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (inputMessage.getBody() != null) {
inputMessage = getAdvice().beforeBodyRead(inputMessage, param, targetType, converterType);
body = genericConverter.read(targetType, contextClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, param, targetType, converterType);
}
else {
body = null;
body = getAdvice().handleEmptyBody(body, inputMessage, param, targetType, converterType);
}
break;
}
}
// 이하 생략
HttpMessageConverter 는 요청 처리 과정 중 어디쯤에서 사용될까?!
HandlerMethodArgumentResolver 를 통해 컨트롤러 함수의 파라미터를 셋팅하는 방법에 대해 알아볼 때 봤던 흐름이다.
HttpMessageConverter는 이 과정에서 쓰인다.
(사실 처음부터 스포가 있었다...)
HttpMessageConverter는 어디에 쓰이는가?
HTTP 메시지를 파싱할 때 쓴다.
HTTP 메시지를 기반으로 파라미터를 셋팅할 때는 언제인가?
@RequestBody 어노테이션이 붙어있을 때, HttpEntity의 변수가 파라미터로 있을 때!
따라서, 이 두 가지 경우를 처리하는 HandlerMethodArgumentResolver에서 파라미터로 넘길 값을 셋팅하는 과정인 resolveArgument()
에서 쓰인다!
- @RequestBody -> RequestResponseBodyMethodProcessor
- HttpEntity -> HttpEntityMethodProcessor
도식화 하면 이런 흐름이다.
출처
- HTTP 메시지 구조 이미지: https://ohcodingdiary.tistory.com/5
- 전체적인 내용의 기반: 인프런 강의(스프링 MV 1편 - 백엔드 웹 개발 핵심 기술)
Author And Source
이 문제에 관하여(HTTP 메시지 컨버터 - http 메시지는 어떻게 파싱될까?!), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@seungyeon/스프링딥다이브2-http-메시지-직렬화저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)