프로그래밍/Spring Framework

@Requestmapping produces, @ResponseBody

모지사바하 2013. 5. 14. 11:37

어떤 요청을 할 때, 응답 형태를 text/html 로 할 수도 있고, application/xml 로 할 수도 있고,

application/json 형태로 할 수도 있다.

 

이런 경우, 지금까지 난 ContentNegotiatingViewResolver 를 통해 ViewResolover와 View를

등록하고 요청 url 끝에 확장자를 붙여서 ContentNegotiatingViewResolver  가 적절한 ViewResolver 를 선택하여 원하는 형태의 View 로 출력하게끔 했었다.

 

헌데, Model 의 정보를 나타내야할 형태가 고작 html, xml이나 json 을 쓰는게 다 인 경우가 많고,  이런 경우

Model 오브젝트를 xml로 컨버트 해주는 MarshallingHttpMessageConverter 와

json 으로 컨버트 해주는 MappingJackson2HttpMessageConverter 를 등록 하여 사용하는 것이 훨씬 깔끔하다고 느껴졌다.

게다가 spring 3.2 에선 @RequestMapping 의 속성에 produces와 consume 이 생겨 요청에 대한 응답을 축소 할 수 있어서 더욱 ContentNegotiatingViewResolver  보단 @ResponseBody, @RequestBody 와 Convert 하여 처리해 주는 것이 깔끔하게 느껴졌다.

 

우선, 컨버터를 등록해줘야 모델 오브젝트를 적절한 형태로 컨버트 할 수 있다.

난 스프링 설정 파일로 그동안 쭉 xml 을 써왔지만 스프링 3.1 부턴 @Configuration 을 통한 자바 설정 파일로 바꿨다..

 

훨씬 강력하고, 이해하기 쉽고, 관리도 쉽기 때문이다.

 

@Configuration 자바 설정 파일에서 mvc 를 확장하기 위해선 서블릿 컨텍스트에 해당하는 설정파일에서,

 

WebMvcConfigurer를 구현하여야하는데, 일반적으로 모든 확장포인트를 일일히 구현하기가 매우 번거롭고, 귀찮다.

또한 mvc의 모든 기능을 확장 할 일은 사실상 거의 없다고 봐도 된다. 그렇기 때문에 스프링에서 이미 WebMvcConfigurer를 구현해서 제공하고 있는 WebMvcConfigurerAdapter 를 상속해서 필요한 부분만 확장 하면 편하다.

WebMvcConfigurerAdapter 는 추상 클래스기 때문에 단독으로 사용할 수는 없다.

@Override
 public void configureMessageConverters(
   List<HttpMessageConverter<?>> converters) {
  MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
  jackson2HttpMessageConverter.setPrettyPrint(true);
  logger.info("Creating Jackson converter: "+ jackson2HttpMessageConverter.getClass().getSimpleName());
  
  converters.add(jackson2HttpMessageConverter);
  converters.add(xmlConverter());
  
 }
 
 @Bean
 public CastorMarshaller marshaller(){
  CastorMarshaller castorMarshaller = new CastorMarshaller();
  String[] targetPackages = {"me.anna.dommain"};
  castorMarshaller.setTargetPackages(targetPackages);
  
  return castorMarshaller;  
 }
 
 @Bean
 public MarshallingHttpMessageConverter xmlConverter(){
  MarshallingHttpMessageConverter marshallingHttpMessageConverter = new MarshallingHttpMessageConverter(marshaller());
  List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
  supportedMediaTypes.add(MediaType.APPLICATION_XML);
  
  marshallingHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
  
  return marshallingHttpMessageConverter;
  
  
 }

위 와 같이 컨버터를 등록 해주고(자세한 설명은 생략- 궁금하신 분은 댓글 달아주시면 답글 드릴께요),

 

@Override
 public void configureContentNegotiation(
   ContentNegotiationConfigurer configurer) {
  configurer.
  useJaf(false).
  defaultContentType(MediaType.APPLICATION_JSON).
  mediaType("xml", MediaType.APPLICATION_XML).
  mediaType("json",  MediaType.APPLICATION_JSON);
  
 }

확장자에 따른 미디어 타입을 결정해주는 contentNegotiation 설정을 해준다..

이렇게 해주면 기본적으로 컨버터를 통한 Model Object 컨버트 하여 표현하는 기능을 사용할 준비가 다 된것이다..

 

간단한 예제를 살펴보자..

@RequestMapping(value = "/filelist", produces={"application/xml", "application/json"})
 @ResponseStatus(HttpStatus.OK)
 public @ResponseBody List<FileDomain> listWithMarshalling(){
  return fileService.getFileList();
 }
 
 @RequestMapping(value = "/filelist")
 public  String fileList(Model model){
  model.addAttribute("fileList", listWithMarshalling());
  return "filelist";
 }

 

위 와 같은 형태로 @ResponseBody 와 컨버터를 통한 Model Object 를 컨버트 하여 표현할 수 있다..

 

localhost/filelist 를 호출 하면 일반적인 html 형태의 파일리스트가 호출 된다.

 

localhost/filelist.xml 을 호출하면 MarshallingHttpMessageConverter 에 의해 xml 형태로,

localhost/filelist.json 을 호출하면 MappingJackson2HttpMessageConverter 에 의해 json 형태로 화면에 출력 된다.

 

ContentNegotiatingViewResolver 를 통해 적절한 ViewResolver 를 통해 화면에 표현할 지, @ResponseBody 와 Converter 를 통해 화면에 표시할 지는 상황에 따라 적절히 선택하면 될 것이다..

 

@ResponseBody 와 Converter 가 더 깔끔해보인다는건 다분히 내 개인적인 생각일 뿐이다..

 

단, 위 예제와 거의 동일한 예제가 2013년 5월 13일에 스프링 공식 블로그에 개제되었다..