본문 바로가기
  • 오늘도 한걸음. 수고많았어요.^^
  • 조금씩 꾸준히 오래 가자.ㅎ
IT기술/spring

[Spring] MVC에서 메시지 컨버터로 응답 만들기

by 미노드 2024. 2. 16.

Spring 에서 메시지 컨버터 라는 인터터페이스를 제공합니다.

이는 HTTP 메시지를 View 템플릿으로 HTML을 생성해서 응답하는 것이 아니라, HTTP API처럼 JSON 데이터를 HTTP 메시지 바디에서 직접 읽거나 쓰는 경우, HTTP 메시지 컨버터를 사용하면 편리합니다.

즉 Response의 정보에 body를 HTML이 아닌 json이나 특정 형식 데이터로 보내는데, 이를 메시지컨버터를 사용해 스프링에서 처리 해줍니다.

1. 스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용합니다.

HTTP 요청: @RequestBody , HttpEntity(RequestEntity) , 
HTTP 응답: @ResponseBody , HttpEntity(ResponseEntity) ,

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 메시지 컨버터는 HTTP 요청, HTTP 응답 둘 다 사용합니다.
canRead() , canWrite() : 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크 
read() , write() : 메시지 컨버터를 통해서 메시지를 읽고 쓰는 기능

2. 기본적인 메시지 컨버터

스프링에서 다양한 메시지 컨버터를 제공하는데, 제공되는 컨버터에도 우선순위가 정해져 있으며, 다음과 같습니다.
대상 클래스 타입미디어 타입 둘을 체크해서 사용여부를 결정하는데, 만약 만족하지 않으면 다음 메시지 컨버터로 우선순위가 넘어갑니다.

0.  ByteArrayHttpMessageConverter
1.  StringHttpMessageConverter
2.  MappingJackson2HttpMessageConverter

ByteArrayHttpMessageConverter : byte[] 데이터를 처리
클래스 타입: byte[] , 미디어타입: */* , 
요청 예) @RequestBody byte[] data
응답 예) @ResponseBody return byte[] 쓰기 미디어타입 application/octet-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 관련

즉, 메시지 컨버터가 읽어들이는 데이터의 클래스 타입, 그리고 헤더에 적혀있는 미디어타입 을 기준으로
컨버터가 읽을 수 있는지 canRead(), canWrite()로 판단하여 실행됩니다.

3. HTTP 요청 데이터를 읽을 때

HTTP 요청이 오고, 컨트롤러에서 @RequestBody , HttpEntity 파라미터를 사용합니다.
메시지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 canRead() 를 호출하여 다음을 체크.
canRead() 조건을 만족하면 read() 를 호출해서 컨버터 객체 생성하고, 반환합니다.

  -  @RequestBody 가 대상 클래스를 지원하는지 ( byte[] , String , HelloData )
  -  HTTP 요청의 Content-Type 미디어 타입을 지원하는지 (text/plain , application/json , */* )

4. HTTP 응답 데이터를 만들 때

컨트롤러에서 @ResponseBody , HttpEntity 로 값을 반환합니다.
메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 canWrite() 를 호출합니다.
canWrite() 조건을 만족하면 write() 를 호출해서 HTTP 응답 메시지 바디에 데이터를 생성한다.

  -  return의 대상 클래스를 지원하는지 ( byte[] , String , HelloData )
  -  HTTP 요청의 Accept 미디어 타입을 지원하는지 (text/plain , application/json , */*)

그렇다면 메시지 컨버터는 언제 사용되는 건가?

HTTP 메시지 컨버터를 사용하는 @RequestBody 는 컨트롤러가 필요로 하는 파라미터의 값에 사용됩니다.  
@ResponseBody 의 경우도 컨트롤러의 반환 값을 사용합니다.

요청의 경우 @RequestBody 를 처리하는 ArgumentResolver 가 있고, HttpEntity 를 처리하는 ArgumentResolver 가 있습니다.
이 ArgumentResolver 들이 HTTP 메시지 컨버터를 사용해서 필요한 객체를 생성 하는데,
ArgumentResolver 는 Request를 매핑 시키기위해 컨트롤러에 응답결과를 주기 전에 여러 ArgumentResolver들 중에 알맞은것을 찾아 읽은 뒤 매핑된 값을 컨트롤러에 쏴 준다고 합니다.
그덕에 컨트롤러(핸들러)에서 값이 컨트롤러에 주어진대로 매핑되고, 개발자가 구현하기 쉬운 구조가 됩니다.

응답의 경우도 @ResponseBody 와 HttpEntity 를 처리하는 ReturnValueHandler 가 있는데, return 또는 response에 주어진 값들, HttpEntity에 들어간 응답 값들을 기준으로 HTTP 메시지 컨버터를 호출해서 응답 결과를 만듭니다.
스프링 MVC는 @RequestBody @ResponseBody 가 있으면 RequestResponseBodyMethodProcessor() 
HttpEntity 가 있으면 HttpEntityMethodProcessor() 를 사용합니다.
즉 응답 구현부만 선언해주면, 뒤에 메시지 컨버터들이 응답을 만들어서 사용자에게 만들어줍니다.

정리하며,

단순히 컨트롤러에 @RequestBody 나 파람 정보들만 써서 구현했었는데,
이렇게 내부구조도 이해하는 것도 한번은 필요한 부분 같습니다.
이를 이해하는게 이후에 어느부분에서 나에게 도움이 될지 생각해보면 인터페이스를 신규로 설계하는데 어떤 구조를 선택하는게 나을지 판단하는데 도움이 될 것 같습니다.