HttpMessageConverter란?

웹 브라우저 같은 클라이언트에서 보여지는 HTML 컨텐츠가 렌더링(Rendering)되는 방식은 크게 두 가지입니다.

하나는 웹 애플리케이션 서버에서 동적으로 변하는 데이터를 포함하는 HTML을 만들어서 HTML 자체를 한번에 클라이언트 쪽으로 내려주는 방식입니다.
이 방식은 JSP나 타임리프(Thymeleaf) 같은 기술을 사용해서 HTML을 템플릿화 한 다음에 Controller의 핸들러 메서드에서 리턴하는 모델 데이터를 템플릿에 동적으로 채워 넣은 후, 최종적으로 완성된 HTML을 클라이언트 쪽으로 내려주는 방식입니다. 이 방식을 바로 서버 사이드 렌더링(Server Side Rendering)이라고 합니다.

또 하나는 클라이언트 쪽을 담당하는 Apache나 NginX 같은 웹 서버에 HTML을 올려 놓은 후, 자바스크립트의 Ajax 기술을 이용해서 웹 애플리케이션 서버에 데이터를 요청 합니다.
그리고 응답으로 전달받은 데이터를 웹 서버에 올려둔 HTML에 동적으로 채워 넣은 후, 최종적으로 완성된 HTML을 클라이언트 쪽으로 내려주는 방식입니다. 이 방식을 바로 클라이언트 사이드 렌더링(Client Side Rendering)이라고 합니다.

위 두가지 방식 중에서 HttpMessageConverter는 두 번째 방식인 클라이언트 사이드 렌더링과 관련이 있습니다.
즉, 클라이언트 사이드 렌더링을 위해 필요한 데이터를 특정 형식으로 변환해 주는 것이 바로 HttpMessageConverter입니다.

HttpMessageConverter의 사용 목적

HttpMessageConverter는 크게 두 가지 목적으로 사용됩니다.

하나는 웹 서버에서 전송된 Reqeust Body를 DTO 같은 클래스의 객체로 변환해서 웹 애플리케이션 서버 쪽에서 사용할 수 있도록 해주는 것이고, 

다른 하나는 웹 서버 쪽으로 전달할 응답 데이터를 Response Body로 변환해주는 것입니다.

 

HttpMessageConverter의 동작 과정

[그림 1-1] Spring MVC 요청/응답 동작 과정

 

[그림 1-1]은 클라이언트의 요청을 처리하는 Spring MVC의 기본 동작 과정입니다.
이 Spring MVC의 동작 과정에서 HandlerAdapter의 동작 과정을 조금 더 구체적으로 설명하겠습니다.

요청 처리 시 HandlerAdapter의 동작 과정

  • HandlerMapping을 통해 적절한 HandlerAdapter를 찾으면 HandlerAdapterController로 넘겨줄 파라미터를 결정하기 위해 이 작업을 HandlerMethodArgumentResolver에게 위임합니다.
  • HandlerMethodArgumentResolverHttpMessageConverter에게 HTTP Request Body를 특정 타입의 객체로 변환해주기를 요청합니다.
  • HttpMessageConverter는 HTTP Request Body를 특정 타입의 객체로 변환합니다.
  • HandlerMethodArgumentResolver는 변환된 데이터를 전달 받아서 이 데이터를 다시 HandlerAdapter에게 전달합니다.
  • HandlerAdapterHandlerMethodArgumentResolver로부터 전달 받은 데이터를 핸들러 메서드의 파라미터로 포함 시킨 후, 핸들러 메서드를 호출합니다.

 

응답 처리 시 HandlerAdapter의 동작 과정

  • 핸들러 메서드가 응답으로 전달할 데이터를 리턴합니다.
  • HandlerMethodReturnValueHandler는 핸들러 메서드로부터 전달 받은 응답 데이터를 HttpMessageConverter에게 전달합니다.
  • HttpMessageConverterHandlerMethodReturnValueHandler로부터 전달 받은 데이터를 HTTP Response Body에 포함되는 형식의 데이터로 변환합니다.
  • HandlerMethodReturnValueHandlerHttpMessageConverter로부터 전달 받은 데이터를 HandlerAdapter에게 전달합니다.

 

REST API에서의 HttpMessageConverter

Spring MVC에서는 기본적으로 아래의 HttpMessageConverter 구현체가 활성화 되어 있습니다.

  • ByteArrayHttpMessageConverter
  • StringHttpMessageConverter
  • ResourceHttpMessageConverter
  • SourceHttpMessageConverter
  • FormHttpMessageConverter
  • Jaxb2RootElementHttpMessageConverter
  • MappingJackson2HttpMessageConverter
  • MappingJacksonHttpMessageConverter
  • AtomFeedHttpMessageConverter
  • RssChannelHttpMessageConverter

 

만약 우리가 HTTP Request 또는 Response를 위한 통신 프로토콜로 JSON을 사용하는 REST API Controller를 구현했다고 가정해 봅시다.

특정 Controller의 핸들러 메서드 파라미터에 @RequestBody 애너테이션이 추가되거나 파라미터의 타입이 HttpEntity라면 위에서 나열한 HttpMessageConverter가 순서대로 HTTP Request Body를 체크해서 핸들러 파라미터에 지정한 클래스로 변환이 되는지, HTTP Message의 Content Type을 지원하는지 여부를 확인하게 됩니다.

이 두가지 조건을 모두 만족한다면 아마도 MappingJackson2HttpMessageConverter가 Request Body를 해당 클래스의 객체로 변환할 것이고, 조건에 부합하는 HttpMessageConverter가 존재하지 않는다면 예외가 발생할 것입니다.

이제 응답 데이터를 JSON 형식으로 전달하는 상황을 생각해 봅시다.
핸들러 메서드에 @ResponseBody 애너테이션을 추가하거나 핸들러 메서드의 리턴 타입이 HttpEntity라면 역시 위에서 나열한 HttpMessageConverter가 순서대로 핸들러 메서드의 리턴 값을 체크해서 해당 클래스 타입을 지원하는지, Accept Header에 명시된 MediaType으로 변환이 되는지 여부를 확인하게 됩니다.

 

HttpMessageConverter가 동작하는 시점

HttpMessageConverter가 동작하는 시점은 JSON을 DTO로 deserialization 할 때와 DTO를 JSON으로 serialization 할 때가 다릅니다. 아래는 HttpMessageConverter의 대략적인 동작 흐름입니다. 참고하세요.

JSON -> DTO로 deserialization

HandlerMethodArgumentResolverComposite.resolveArgument() 

RequestResponseBodyMethodProcessor.resolveArgument()

RequestResponseBodyMethodProcessor.readWithMessageConverters() > converter.read()

objectReader.readValue()

 

DTO -> JSON serialization

HandlerMethodReturnValueHandlerComposite.handleReturnValue() 
→ HttpEntityMethodProcessor.handleReturnValue() 
→ AbstractMessageConverterMethodProcessor.writeWithMessageConverters() > genericConverter.write() 
→ AbstractGenericHttpMessageConverter.write() 
→ AbstractJackson2HttpMessageConverter.writeInternal() > objectWriter.writeValue(generator, value)
→ write() and flush() 과정을 거친다.
→ ...
→ ...
CoyoteAdapter.service() > response.finishResponse() // response가 close되면서 클라이언트 쪽에 출력

HttpMessageConverter 참고 자료

 

Spring Framework 5.3.19 API

 

docs.spring.io

 

+ Recent posts

출처: http://large.tistory.com/23 [Large]