1. 메시지 컨버터 소개
- 메시지 컨버터는 HTTP 요청/응답 메시지 본문 자체를 메시지로 다룬다.
- 메시지 컨버터는 AnnotationMethodHandlerAdapter를 통해 등록할 수 있다. 여러 개의 메시지 컨버터를 등록해두고 요청 타입이나 오브젝트 타입에 따라 선택되게 하는 것이 일반적이다.
2. 메시지 컨버터의 종류
1) 디폴트로 등록된 메시지 컨버터
(1) ByteArrayHttpMessageConverter
- byte[] 타입의 오브젝트를 지원한다.
- 미디어 타입은 모든 종류를 다 허용한다.
- @RequestBody로 전달받을 때는 모든 종류의 HTTP 요청 메시지 본문을 byte[] 타입으로 가져올 수 있고, @ResponseBody로 응답할 때는 application/octet-stream이라는 컨텐트 타입으로 전달된다.
(2) StringHttpMessageConverter
- String 타입의 오브젝트를 지원한다.
- 미디어 타입은 모든 종류를 다 허용한다.
- HTTP 요청의 본문을 그대로 String 타입으로 가져올 수 있고, 응답할 때는 text/plain이라는 컨텐트 타입으로 전달된다. 단순 문자열로 응답을 보낼 때는 @ResponseBody와 함께 String 타입으로 리턴해주면 된다.
(3) FormHttpMessageConverter
- MultiValueMap<K, V> 타입의 오브젝트를 지원한다. 이 타입은 Map<K,List<V>>을 상속받기 때문에, 하나의 이름을 가진 여러 파라미터가 존재할 수 있는 요청 파라미터를 처리할 수 있다.
- 컨텐트 타입이 application/x-www-form-urlencoded인 폼 데이터를 주고 받을 때 사용한다.
- 폼 정보를 받을 때 FormHttpMessageConverter보다는 @ModelAttribute를 사용하는 것이 나을 것 같다.
(4) SourceHttpMessageConverter
- javax.xml.transform.Source 타입인 DOMSource, SAXSource, StreamSource 타입의 오브젝트를 지원한다.
- 지원하는 컨텐트 타입은 application/xml, applitation/*+xml, text/xml이다.
- XML 문서를 Source 타입의 오브젝트로 전환할 때 사용된다.
2) 디폴트로 등록되지 않은 메시지 컨버터
- 필요시에 AnnotationMethodHandlerAdapter bean의 messageConverters 프로퍼티에 직접 등록해야 하는 메시지 컨버터들이다. 전략 프로퍼티를 직접 등록하면 디폴트 전략은 자동으로 추가되지 않기 때문에 모두 직접 추가해줘야 한다.
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
- 커스텀 메시지 컨버터를 만들기 위해서는 HttpMessageConverter를 구현하면 된다.
(1) Jaxb2RootElementHttpMessageConverter
- JAXB2의 @XmlRootElement와 @XmlType이 붙은 클래스를 이용해서 XML과 자바 오브젝트 사이의 변환을 지원한다.
- 지원하는 컨텐트 타입은 기본적으로 application/xml, applitation/*+xml, text/xml이다.
- JAXB2의 스키마 컴파일러를 통해 생성된 바인딩용 클래스를 이용해서 손쉽게 XML과 자바 오브젝트 사이의 메시지 변환이 가능하다.
(2) MarshallingHttpMessageConverter
- Spring OXM 추상화의 Marshaller와 Unmarshaller를 이용해서 XML과 자바 오브젝트 사이의 변환을 지원한다. Marshaller은 오브젝트를 XML로 변환하는 기능을 추상화한 인터페이스고, Unmarshaller은 XML을 오브젝트로 변환해주는 기능을 추상화한 인터페이스다.
- OXM 기술을 자유롭게 선택해서 MarshallingHttpMessageConverter bean의 marshaller와 unmarshaller 프로퍼티에 넣어주면 된다.
- 지원하는 오브젝트는 unmarshaller의 support() 메소드를 호출해서 판단한다.
- 지원하는 컨텐트 타입은 기본적으로 application/xml, applitation/*+xml, text/xml이다.
(3) MappingJacksonHttpMessageConverter
- Jackson ObjectMapper을 이용해서 JSON과 자바 오브젝트 사이의 변환을 지원한다. 부가적인 변환 기능이 필요하다면 ObjectMapper를 확장해서 적용하면 된다.
- 오브젝트 타입에 제한은 없지만 자바빈 스타일이거나 HashMap을 이용해야 정확한 변환이 가능하다.
- 지원하는 컨텐트 타입은 application/json이다.
3. JSON 기반의 AJAX 컨트롤러
- MappingJacksonHttpMessageConverter를 AnnotationMethodHandlerAdapter bean의 messageConverters 프로퍼티에 등록해야 한다.
- JSON 오브젝트를 반환하는 컨트롤러를 만들기 위한 방법으로는 아래 두 가지가 있다. 아래의 방법을 사용하면 컨트롤러가 리턴하는 자바 오브젝트는 JSON 메시지로 변환된다.
- JSON 지원 뷰 사용 : MappingJacksonJsonView를 이용한다. 다른 포맷의 뷰를 동시에 사용할 경우에는 .json 확장자를 자동인식하는 ContentNegotiatingViewResolver를 사용한다.
- @ResponseBody 사용 : 컨트롤러 메소드에 @ResponseBody를 붙여준다. 항상 JSON 오브젝트로 반환하는 경우에 사용하기 편리한 방법이다.
※ Spring 4.0부터는 @RestController 어노테이션을 지원한다. 컨트롤러에 @Controller 대신에 @RestController 어노테이션을 붙이면 @Controller 역할도 수행하면서 모든 컨트롤러 메소드에 일괄적으로 @ResponseBody가 적용된다. 필자가 개인적으로 추천하는 방법이다.
- Ajax 컨트롤러에서 post 요청을 처리하는 메소드가 필요할 수도 있다. 대표적으로 폼 정보를 받는 경우다.
@RequestMapping(value="/register", method=RequestMethod.POST)
@ResponseBody
public User register(@RequestBody User user) {
// user 검증과 등록
return user;
}
메소드 파라미터에 @RequestBody를 붙여서 application/json 콘텐트 타입으로 전달되는 요청을 MappingJacksonHttpMessageConverter에 의해 자바 오브젝트로 변환되도록 하면 된다. 자바 오브젝트를 굳이 만들고 싶지 않다면 Map<String, Object> 타입으로 받아도 된다.
자바스크립트 코드에 의해 서버로 전송되는 HTTP 요청의 메시지 본문은 다음과 같이 JSON 형식으로 만들어져야 한다.
{"id":"jooyeok", "password":"1234", "name":"Kim Joo Yeok"}
그리고 실제 폼을 받아버리면 웹 페이지가 갱신되어버리기 때문에 ajax를 사용하는 의미가 없어진다. 그래서 아래 자바스크립트 코드와 같이 submit 이벤트 메소드에서 false를 반환하도록 해야한다. 폼의 id는 "user"라고 가정한다.
$('#user').submit(function() {
// JSON 형식의 메시지 본문 생성
// POST 요청
// 요청 결과에 기반하여 웹 페이지에 안내 메시지 출력
return false;
});
Javascript 예시 코드 (JQuery 사용)
// 메소드 참고 : https://cofs.tistory.com/m/184
jQuery.fn.serializeObject = function() {
var obj = null;
try {
if (this[0].tagName && this[0].tagName.toUpperCase() == "FORM") {
var arr = this.serializeArray();
if (arr) {
obj = {};
jQuery.each(arr, function() {
obj[this.name] = this.value;
});
}
}
} catch (e) {
alert(e.message);
} finally {}
return obj;
};
$('#answer-form').submit(function() {
startTimeout();
var serializedFormData = $('#answer-form').serializeObject();
console.log(serializedFormData)
$.ajax({
url:"http://localhost:8080/test/grading",
data: JSON.stringify(serializedFormData),
type: "POST",
dataType: "json",
contentType : "application/json"
})
.done(function(json) {
console.log(json);
})
.fail(function(xhr, status, errorThrown){
alert("요청 실패");
})
return false;
});
● 참고 자료 : 토비의 스프링 3.1 Vol.2
'Spring Series > Spring Framework' 카테고리의 다른 글
[Spring] AnnotationMethodHandlerAdapter의 확장 (0) | 2022.09.29 |
---|---|
[Spring] MVC 전용 태그 (0) | 2022.09.29 |
[Spring] 프로퍼티 파일에서 BindingResult 메시지 관리하기 (0) | 2022.09.25 |
[Spring] JSR-303 Bean 검증 방식 (0) | 2022.09.25 |
[Spring] 자주 사용되는 WebDataBinder 설정 항목 (0) | 2022.09.22 |
댓글