Spring MVC/02.Rest

[spring]@ModelAttribute vs @RequestBody

  • -
반응형

스프링 강의를 열심히 하고 난 후 의외로 많은 경우에 @ModelAttribute와 @RequestBody를 언제 써야하는지 잘 모르는 경우가 있어서 차이를 정확히 알아보기로 한다.

(돌이켜보면 시간상, 설명의 편의상 form에서 넘어온 데이터를 처리하기 위해서는 @ModelAttribute를 사용하고 ajax를 통해서 넘어온 post, put 데이터를 처리하기 위해서는 @RequestBody를 쓰라고 이야기 했는데 한 70%만 맞는 말이다.)

 

@ModelAttribute vs @RequestBody

사실 둘의 비교는 간단하다. @ModelAttribute는 파라미터로 전달된 데이터를 처리한다. 그리고 @RequestBody는 request body를 통해서 전달된 내용을 처리한다. 그럼 언제 그런 것들을 사용 되는가?

동작을 테스트 해보기 위해 간단한 controller와 화면을 만들어보자.

Controller

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SimplePairDto {
	private String name;
	private int age;
}

@PostMapping("/free/simpleparam")
public String simpleModelAttribute(@ModelAttribute SimplePairDto dto) {
 	log.debug("model attribute 1: {}", dto);
   	return "tests/parambodytest";
}

@PostMapping("/free/simplebody")
public @ResponseBody SimplePairDto simpleRequestBody(@RequestBody SimplePairDto dto) {
   	log.debug("request body 1: {}", dto);
  	return dto;
}

 

simpleparam은 @ModelAttribute로 처리하고 simplebody는 @ResponseBody를 사용한다. return은 중요하지 않고 log를 통해서 출력되는 DTO의 property가 잘 출력되는지만 확인하면 된다.

 

화면 구성

<form action="/free/simpleparam" method="post" id="myform">
	<input type="text" name="name" id="name" value="홍길동">
	<input type="text" name="age" id="age" value="10">
</form>
<br>
<input type="button" value="param" id="form">				<br>
<input type="button" value="ajax - body" id="ajax1">			<br>
<input type="button" value="ajax - param" id="ajax2">			<br>
<input type="button" value="ajax - formData" id="ajax3">		<br>

이제 각각의 버튼을 동작시켜보면서 simpleparam 또는 simplebody를 호출해보자.

 

form 버튼을 이용한 처리

document.querySelector("#form").addEventListener("click", function(){
	document.querySelector("#myform").submit();
})

먼저 form 버튼을 이용한 처리를 살펴보자. 특별한것은 없고 이런 요청이 어떻게 날아가는지 살펴보자. 개발자 도구의 payload를 살펴보면 양식 데이터에 name, age가 잘 전달되고 있다. 이렇게 전달되는 경우가 parameter이다. (소스보기를 클릭 해보면 name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=10 처럼 구성된 것을 볼 수 있다.

 

당연히 서버에서도 데이터는 잘 확인할 수 있다.

[DEBUG] [x.q.m.c.c.CommonController.simpleModelAttribute- 67] 
model attribute 1: SimplePairDto(name=홍길동, age=10)

 

일반적인 ajax의 post 요청 처리

먼저 ajax1 버튼을 이용한 처리를 살펴보자. ajax 처리를 위해서는 axios 라이브러리를 사용했다.

document.querySelector("#ajax1").addEventListener("click", async function(){
	const user = {
			name: document.querySelector("#name").value,
			age: document.querySelector("#age").value,
	};
	const response = await axios.post("/free/simplebody", user);
	console.log(response)
})

위 요청을 처리하고 다시 payload를 살펴보자.

페이로드에 표시된 값은 form에서의 그것과 사뭇 다르다. 바로 json 형태로 표시되어있다. 따라서 파라미터 기반으로 가져오는 @ModelAttribute나 @RequestParam은 저 요청을 처리할 수가 없는 것이다.!!

이때 ajax 요청은 data 속성을 통해서 객체를 전달한다.

 

ajax 요청을 @ModelAttribute에서 처리하려면?

그럼 ajax 요청은 언제나 @RequestBody로만 처리해야할까? 일관되게 @ModelAttribute로 처리하려면 어떻게 하면 될까? 물론 가능하다!! 정답은 바로 위에 썼던 것처럼 @ModelAttribute는 파라미터 기반의 요청을 처리하므로 데이터를 파라미터로 넘겨주면 된다.

ajax2 버튼에서 관련 동작을 처리해보자. url이 simpleparam 이라는 점을 확인한다.!

document.querySelector("#ajax2").addEventListener("click", async function(){
	const user = {
			name: document.querySelector("#name").value,
			age: document.querySelector("#age").value,
	};
	const response = await axios({
			method: "post",
			url:"/free/simpleparam",
			params:user
	});
	console.log(response)
})

또 하나 중요한 일은 ajax 요청을 날리면서 params 속성을 통해서 user를 전달하고 있다. 이제 페이로드를 살펴보면 form 요청에서처럼 표현되는 것을 볼 수 있다. 이름 즉 url 도 마치 get 방식처럼 표현되고 있다.

당연히 이 경우는 @RequestBody는 처리하지 못하고 @ModelAttribute에서 처리해야 한다.

하지만 굳이 이렇게는 하지 말자!! 

다른 개발자들은 다 @RequestBody로 쓰는데 혼자 헷갈린다고 @ModelAttribute를 쓴다면 웃긴 일이다.ㅜㅜ

 

FormData 활용

좀 더 궁극적인 방법은 FormData라는 객체를 활용하는 방법이 있다.

FormData - Web API | MDN (mozilla.org)

 

FormData - Web API | MDN

FormData 인터페이스는 form 필드와 그 값을 나타내는 일련의 key/value 쌍을 쉽게 생성할 수 있는 방법을 제공합니다. 또한 XMLHttpRequest.send() (en-US) 메서드를 사용하여 쉽게 전송할 수 있습니다. 인코

developer.mozilla.org

 

ajax3 버튼이 클릭될 때 동작을 처리해보자. 이번에도 simpleparam을 요청하고 있는데 전달하는 데이터가 FormData라는 녀석이다.!

document.querySelector("#ajax3").addEventListener("click", async function(){
	const formData = new FormData();
	formData.append("name", document.querySelector("#name").value)
	formData.append("age", document.querySelector("#age").value)
	
	const response = await axios.post("/free/simpleparam", formData);
	console.log(response)
})

이 요청의 payload를 살펴보면 다시 처음의 form 요청과 동일하다. 따라서 @ModelAttribute에서 처리 가능해야 한다.

그런데 여기서는 중요한 내용이 하나 더 있다. 이번에는 이 요청의 머리글>요청헤더 부분을 살펴보자.

Content-Type을 살펴보면 multipart/form-data로 되어있다.

어디서 들어본 적이 있는데.. 바로 파일 업로드 할 때 form의 enctype을 이렇게 지정해줘야 한다.

만약에 ajax를 통해서 file을 업로드 하려면 어떻게 하면 될까? ajax를 사용할 때는 form이 없기 때문에 enctype을 설정할 곳이 없다. ㅜㅜ

따라서 ajax를 통해서 file 업로드를 처리할 때에는 FormData를 통해서 처리해야 하고 서버는 @ModelAttribute로 받아줘야 한다.

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.