[Spring] Ajax 정리
웹 서비스를 만들 때 자주 사용되는 비동기 통신 기술인 Ajax를 스프링 프레임워크와 연계하여 활용하는 다양한 방식에 대해서 알아보고자 한다.
JQuery.ajax
제이쿼리에서 제공하는 함수는 다음과 같은 구조로 구성되어 있다. 물론 이외에도 생략된 다양한 프로퍼티들이 존재하므로 더 찾아보시면 좋을 것 같다. 아래의 형태는 아마도 자주 사용되는 프로퍼티만 모아놓은 부분이라고 할 수 있다.
$.ajax({
type : "GET"/"POST", //요청 메소드 타입
url : "url", //요청 경로
async : true, //비동기 여부
data : {key : value}, //요청 시 포함되어질 데이터
processData : true, //데이터를 컨텐트 타입에 맞게 변환 여부
cache : true, //캐시 여부
contentType : "application/json", //요청 컨텐트 타입 "application/x-www-form-urlencoded; charset=UTF-8"
dataType : "json", //응답 데이터 형식 명시하지 않을 경우 자동으로 추측
beforeSend : function(){
// XHR Header를 포함해서 HTTP Request를 하기전에 호출됩니다.
},
success : function(data, status, xhr){
// 정상적으로 응답 받았을 경우에는 success 콜백이 호출되게 됩니다.
// 이 콜백 함수의 파라미터에서는 응답 바디, 응답 코드 그리고 XHR 헤더를 확인할 수 있습니다.
},
error : function(xhr, status, error){
// 응답을 받지 못하였다거나 정상적인 응답이지만 데이터 형식을 확인할 수 없기 때문에 error 콜백이 호출될 수 있습니다.
// 예를 들어, dataType을 지정해서 응답 받을 데이터 형식을 지정하였지만, 서버에서는 다른 데이터형식으로 응답하면 error 콜백이 호출되게 됩니다.
},
complete : function(xhr, status){
// success와 error 콜백이 호출된 후에 반드시 호출됩니다.
// try - catch - finally의 finally 구문과 동일합니다.
}
});
# GET과 POST의 차이는?
바로 데이터가 어디에 위치하는가에 있다. POST 요청시에 URL에 파라미터가 보이지 않는 이유는 데이터가 요청 바디에 포함되기 때문이다. 그렇기 때문에 GET과 POST에 따라 데이터를 URL에 추가해야할지 요청 바디에 추가해야 할지를 알고 있어야만 한다.
예를 들어,processData라는 속성은 GET 요청인데 data에 오브젝트가 지정될 경우에 요청하기전에 그 데이터를 파라미터 형식으로 URL에 추가해주는 역할을 하게 된다.
이외에도 JQuery에서는 다양한 ajax 기능을 제공하고 있다. 만약, 스프링 시큐리티를 적용해서 HTTP 통신시에 CSRF 토큰이 필요하다면 다음과 같이 XHR Header에 CSRF 토큰을 추가해서 보낼 수 있다.
// 스프링 시큐리티 태그라이브러리로 메타 태그에 토큰 정보를 적용했다는 가정입니다.
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token);
}
});
Spring Controller
스프링 프레임워크에서는 ajax 통신을 위해서 스프링 @MVC로 다양한 어노테이션을 지원한다. 다양한 어노테이션을 확인하면서 구조를 익혀보도록 하자. 스프링 애플리케이션은 기본적으로 뷰 리졸버를 통해서 요청에 대한 응답을 하게 된다. 일반적인 HTTP 요청의 경우에는 JstlView로써 응답을 하게 되지만, XHR 요청에 의해서 다양한 데이터 형식으로 응답하기 위한 메시지 컨버터라는 것을 지원한다.
Message Converter List
- StringHttpMessageConverter
- FormHttpMessageConverter
- ByteArrayMessageConverter
- MarshallingHttpMessageConverter
- MappingJacksonHttpMessageConverter
- MappingJackson2HttpMessageConverter
- SourceHttpMessageConverter
- BufferedImagedHttpMessageConverter
# MappingJacksonHttpMessageConverter는 jackson 라이브러리가 존재할때만 등록한다.
우리가 AJAX를 이용할 때 데이터 형식을 JSON으로 많이 사용한다. 따라서, List, Map등과 같은 오브젝트들을 JSON 형태로 응답하고 싶다면, ModelAndView를 이용하거나 메시지컨버터를 등록해야한다.
그러나 스프링 3 이상 부터는 jackson 라이브러리를 의존성으로 추가할 경우에 자동적으로 MappingJacksonHttpMessageConverter를 적용해준다.
스프링 부트에서는 이러한 부분도 관리해주므로 추가적으로 jackson 라이브러리를 pom.xml에 추가할 필요가 없다.
- 버전별 메시지 컨버터 지원표
Spring Version | org.codehaus.jackson(1.8 or 1.9) | com.fasterxml.jackson(2.0) | gson |
3.0.x | MappingJacksonHttpMessageConverter | ||
3.1.2 | MappingJacksonHttpMessageConverter | MappingJackson2HttpMessageConverter | |
4.+ | MappingJackson2HttpMessageConverter | GsonHttpMessageConverter |
Annotations
@RequestMapping
@RequestMapping에는 요청과 응답과 관련한 프로퍼티를 설정할 수 있습니다. produces와 consumes는 확실히 알고 넘어가야 한다.
- method=RequestMethod.GET
method는 어떠한 요청 타입을 처리할 것인가를 결정하는 부분 - produces=MediaType.APPLICATION_JSON_VALUE
produces는 어떠한 데이터 형식으로 응답할 것인가를 결정하는 부분 - consumes=MediaType.APPLICATION_JSON_VALUE
consumes는 어떠한 요청에 대해서 처리할 것인가를 결정하는 부분
@ModelAttribute, @ReqeustParam
이 두개의 어노테이션은 GET과 DELETE 요청에서 활용할 수 있다. 그 이유는 파라미터 값을 확인해서 데이터를 바인딩해주기 때문이다. @RequestParam은 request.getParameter()로써 가져오는 반면에 @ModelAttribute는 자바 클래스의 Getter, Setter에 의해 데이터를 바인딩시키는 것이다. 그렇기 때문에 만약 객체 단위로 바인딩하고 싶다면 @ModelAttribute를 이용해야 한다.
# 바인딩이란?
메서드들을 재사용하고 싶지만, 이 객체와 부모-자식관계까지 만들고 싶지 않을 때 사용하는 것이 바인딩이다.
예를 들어 a라는 객체가 있고, 전역함수로써 혹은 b객체에 test()라는 메서드가 있다고 가정해보자.
b.test()라고 실행되는게 보통이지만 이것을 마치 a객체에서 실행되게 ( a.test() 와 같은 효과 )하는 경우라 할 수 있다.
즉, 메서드와 객체를 묶어놓는 것을 바인딩이라고 한다.
@RequestBody
이 어노테이션은 POST와 PUT 처럼 데이터가 HTTP 요청 바디에 포함되는 경우에 이를 확인해서 데이터를 바인딩 해준다. 이 어노테이션의 중요한 부분은 GET 요청과 같이 파라미터를 통해 제공되는 데이터는 바인딩할 수 없다는 점이다.
@ResponseBody
이 어노테이션은 응답되는 데이터에 대하여 등록된 메시지 컨버터를 통해 변환시켜 응답하게 된다. 따라서, 뷰에 모델로서 데이터를 추가시켜 응답하는 것이 아니라 데이터를 HTTP 본문으로 응답하게 된다.
@RestController
이 어노테이션은 스프링 4 부터 지원한다. 해당 컨트롤러의 메소드들에 @ResponseBody 어노테이션을 적용한다. 좀 더 편의성을 제공한다고 보시면 된다.