Person 클래스
@Getter
@Setter
@ToString
public class Person {
private String name;
private int age;
}
<form> 태그에서 데이터를 전달해보자.
@RequestParam,@ModelAttribute,@RequestBody 차이
스프링에서 객체-파라미터 매핑은 요청상황이나 응답상황 모두 MessageConverter가 담당한다.
상황에 따라 스프링이 적절한 MessageConverter의 구현체를 통해 객체-파라미터 매핑을 한다.
요청에서 @RequestParam, @ModelAttribute가 붙은면 FormHttpMessageConverter가,
@RequestBody가 붙으면 MappingJacksonHttpMessageConverter가 매핑을 한다.
(jackson-databind 라이브러리를 추가했다면)
@RequestParam - FormHttpMessageConverter
요청의 파라미터를 1:1로 매핑해준다.
요청 파라미터인 name과 age 각각에 대해서 설정하는 것으로
객체에 직접 매핑할 수 없다.
@RequestMapping("/result")
public String post(@RequestParam Person person) {
System.out.println(person);
return "result";
}
GET,POST방식 둘다 person이라는 파라미터가 없다는 에러가 발생한다.
@ModelAttribute - FormHttpMessageConverter
요청의 여러 파라미터들을 한번에 Person의 필드들에 매핑해준다.
여러 파라미터들을 얻은 다음에 객체에 setting해주는 것이기 때문에
setter가 없다면 값이 setting되지 않는다.
GET,POST 상관없이 잘 세팅된다.
public String post(@ModelAttribute("person") Person person) {
System.out.println(person);
return "result";
}
아무런 @없이 사용하는 경우는 파라미터-객체 매핑을 하는 경우
@ModelAttribute처럼 매핑한다고 보면 된다.
Model객체에 person을 담느냐 안 담느냐의 차이만 있을 뿐이라
아래 코드는 @ModelAttribute처럼 동작한다.
@RequestMapping("/result")
public String post(Model model, Person person) {
System.out.println(person);
model.addAttribute("person" , person);
return "result";
}
@RequestBody - MappingJacksonHttpMessageConverter
@RequestMapping("/result")
public String post(@RequestBody Person person) {
System.out.println(person);
return "result";
}
body 내용을 직접 변환하는 것이기 때문에 setter가 필요없다.
그렇기 때문에 Body가 없는 GET방식에서는 일반적으로 사용할 수 없다.
GET방식에서는 RequestBody를 찾을 수 없다는 에러가 발생한다.
POST의 경우 Content type 'application/x-www-form-urlencoded;charset=UTF-8'
not supported 라는 에러가 발생한다.
기본적으로 요청의 content type은 application/x-www-form-urlencoded 인데
이는 url?key1=val1 & key2=val2 의 형식으로 요청이 간다.
그런데 MappingJacksonHttpMessageConverter는 이러한 형태를
처리할 수 없기 때문에 에러가 발생한다.
AJAX에서의 요청
@ModelAttribute
@RequestMapping("/result")
public String post(@ModelAttribute Person person) {
System.out.println(person);
return "result";
}
contentType이 "application/x-www-form-urlencoded" 인 경우는
<form>태그 요청과 같은 결과가 나온다.
<script>
$("#ajax").on("click" , function (){
let data = { name : "김필자" , age : 30};
$.ajax({
url : "/result"
,type: "GET" // POST
// ,contentType : "application/x-www-form-urlencoded"
,data : data
, success : function (data){
console.log(data);
}
,error : function (err){
console.log(err);
}
});
});
</script>
contentType을 "application/json"으로 변경하고
JSON.stringify(data)를 하면 요청자체는 GET방식에서는 에러가 나고
POST방식에서는 에러는 안 나지만 값이 세팅되지 않는다.
<script>
$("#ajax").on("click" , function (){
let data = { name : "김필자" , age : 30};
console.log(JSON.stringify(data));
$.ajax({
url : "/result"
,type: "POST"
,contentType : "application/json"
,data : JSON.stringify(data)
, success : function (data){
console.log(data);
}
,error : function (err){
console.log(err);
}
});
});
</script>
@RequestBody
@RequestMapping("/result")
public String post(@RequestBody Person person) {
System.out.println(person);
return "result";
}
GET, POST방식 모두 그냥 <form>와 비슷하다.
다음과 같이 contentType을 "application/json"으로 변경하고
body의 값이 key1=value1 & key2=value2 형태가 아닌 json형태로 바꿔주면
(JSON.stringify(data)) 객체 매핑이 제대로 된다.
MappingJacksonHttpMessageConverter는 요청 body의 값을 자바의 객체로 변환해주기 때문.
body의 값을 읽는 것이기 때문에 GET방식은 안된다.
<script>
$("#ajax").on("click" , function (){
let data = { name : "김필자" , age : 30};
console.log(JSON.stringify(data));
$.ajax({
url : "/result"
,type: "POST"
,contentType : "application/json"
,data : JSON.stringify(data)
, success : function (data){
console.log(data);
}
,error : function (err){
console.log(err);
}
});
});
</script>
결론
일반적인 <form>태그나 ajax요청에서는 굳이 @RequestBody를 사용하지 않아도 된다.
물론 클라이언트 측에서 application/json타입으로 보낸다면
@RequestBody를 사용하면 된다.
또 다음과 같이 복잡한 json형태를 다루는 ajax요청을 처리할 때
@RequestBody가 유용하게 사용된다
<script>
$("#ajax").on("click" , function (){
let array=[1,2,3];
let map={name : "김필자" , age : 30};
let data = { arrayData: array , mapData : map};
console.log(JSON.stringify(data));
$.ajax({
url : "/result"
,type: "POST"
,contentType : "application/json"
,data : JSON.stringify(data)
, success : function (data){
console.log(data);
}
,error : function (err){
console.log(err);
}
});
});
</script>
@RequestBody Map<String, Object> map으로 받아
ObjectMapper등을 이용해 parsing하면 된다.
@RequestMapping("/result")
public String post(@RequestBody Map<String,Object> map) throws JsonProcessingException {
System.out.println(map); //data1 array, data2 map
ObjectMapper mapper = new ObjectMapper();
String arrayStr= map.get("arrayInJson").toString();
List<Integer> list= mapper.readValue(arrayStr, new TypeReference<ArrayList<Integer>>(){} ); // 자바스크립트에서 array였지만, list로도 가능
System.out.println(list);
String personStr = mapper.writeValueAsString( map.get("personInJson") );
// name=김필자 => name:김필자 , ObjectMapper는 문자열이 name:김필자 형태이어야만 자바객체로 변환가능
Map<String , Object> person= mapper.readValue(personStr, new TypeReference< HashMap<String, Object>>() {}); //name,age는 String,int니까
System.out.println(person);
return "result";
}
그외 기타 객체 매핑
자바스크립트배열 값을 컨트롤러에서 List<String>으로 받고 싶을 때
1. 배열을 그대로 전달
AJAX
var genre = [ "드라마", "액션", "무협" ];
$.ajax({
url : "/ajax" ,
data : {"searchGenre" : genre },
success : function(data) {
}
});//ajax
Controller
@ResponseBody
@RequestMapping(value = "/ajax")
public String ajax(@RequestParam(value="searchGenre[]")List<String> searchGenre) {
System.out.println(searchGenre);
return "";
}
2. 배열을 String으로 전달
AJAX
var genre = [ "드라마", "액션", "무협" ];
$.ajax({
url : "ajax" ,//상대경로,절대경로는 알아서
data : {"searchGenre" : genre.toString() },
//data : {"searchGenre" : "드라마,액션,무협" }, 이라고쓴거랑같다.
success : function(data) {
}
});//ajax
Controller
@ResponseBody
@RequestMapping(value = "/ajax")
public String ajax( List<String> searchGenre) {
return "";
}