@RequestBody / @ResponseBody 어노테이션 이란?
스프링에서 비동기 처리를 하는 경우 @RequestBody , @ResponseBody를 사용한다.
비동기 처리를 위해 이 어노테이션들은 어떻게 작동할까?
클라이언트와 서버의 비동기 통신
클라이언트에서 서버로 통신하는 메시지를 요청(request) 메시지라고 하며, 서버에서 클라이언트로 통신하는 메시지를 응답(response) 메시지라고 한다.
웹에서 화면전환(새로고침) 없이 이루어지는 동작들은 대부분 비동기 통신으로 이루어진다.
비동기통신을 하기위해서는 클라이언트에서 서버로 요청 메세지를 보낼 때, 본문에 데이터를 담아서 보내야 하고, 서버에서 클라이언트로 응답을 보낼때에도 본문에 데이터를 담아서 보내야 한다.
이 본문이 바로 body 이다.
즉, 요청본문 requestBody, 응답본문 responseBody 을 담아서 보내야 한다.
이때 본문에 담기는 데이터 형식은 여러가지 형태가 있겠지만 가장 대표적으로 사용되는 것이 JSON 이다.
즉, 비동기식 클라-서버 통신을 위해 JSON 형식의 데이터를 주고받는 것이다.
* JSON 제이슨이란?
스프링 MVC에서도 클라이언트에서 전송한 xml데이터나 json 등등 데이터를 컨트롤러에서 DOM객체나 자바객체로 변환해서 송수신할 수 있다.
@RequestBody 어노테이션과 @ResponseBody 어노테이션이 각각 HTTP요청 바디를 자바객체로 변환하고 자바객체를 다시 HTTP 응답 바디로 변환해준다.
요청 본문(request body)에 담긴 값을 자바객체로 변환.
@RequestBody를 통해서 자바객체로 conversion을 하는데, 이때 HttpMessageConverter를 사용한다.
@ResponseBody 가 붙은 파라미터에는 HTTP 요청의 분문 body 부분이 그대로 전달된다.
RequestMappingHandlerAdpter 에는 HttpMessageConverter 타입의 메세지 변환기가 여러개 등록되어 있다.
@RequestBody
이 어노테이션이 붙은 파라미터에는 http요청의 본문(body)이 그대로 전달된다.
일반적인 GET/POST의 요청 파라미터라면 @RequestBody를 사용할 일이 없을 것이다.
반면에 xml이나 json기반의 메시지를 사용하는 요청의 경우에 이 방법이 매우 유용하다.
HTTP 요청의 바디내용을 통째로 자바객체로 변환해서 매핑된 메소드 파라미터로 전달해준다.
@RequestMapping(value = "/ajaxTest.do")
public String ajaxTest(@RequestBody UserVO getUserVO) throws Exception {
System.out.println(getUserVO.getId());
return "test/login.tiles";
}
@ResponseBody
자바객체를 HTTP요청의 바디내용으로 매핑하여 클라이언트로 전송한다.
@ResponseBody 가 붙은 파라미터가 있으면 HTTP요청의 미디어타입과 파라미터의 타입을 먼저 확인한다.
* dispatcher-servlet.xml 의 <annotation-drvien> 태그 내에 선언하는 <message-converter> 에서 확인.
메세지 변환기 중에서 해당 미디어타입과 파라미터 타입을 처리할 수 있다면, HTTP요청의 본문 부분을 통째로 변환해서 지정된 메소드 파라미터로 전달해준다.
@ResponseBody
@RequestMapping(value = "/ajaxTest.do")
public UserVO ajaxTest() throws Exception {
UserVO userVO = new UserVO();
userVO.setId("테스트");
return userVO;
}
즉, @Responsebody 어노테이션을 사용하면 http요청 body를 자바 객체로 전달받을 수 있다.
* @RestController
@Controller와는 다르게 @RestController는 리턴값에 자동으로 @ResponseBody가 붙게되어 별도 어노테이션을 명시해주지 않아도 HTTP 응답데이터(body)에 자바 객체가 매핑되어 전달 된다.
@Controller인 경우에 바디를 자바객체로 받기 위해서는 @ResponseBody 어노테이션을 반드시 명시해주어야한다.
@RequestBody / @ResponseBody 정리.
클라이언트에서 서버로 필요한 데이터를 요청하기 위해 JSON 데이터를 요청 본문에 담아서 서버로 보내면, 서버에서는 @RequestBody 어노테이션을 사용하여 HTTP 요청 본문에 담긴 값들을 자바객체로 변환시켜, 객체에 저장한다.
서버에서 클라이언트로 응답 데이터를 전송하기 위해 @ResponseBody 어노테이션을 사용하여 자바 객체를 HTTP 응답 본문의 객체로 변환하여 클라이언트로 전송한다.
출처: https://cheershennah.tistory.com/179 [Today I Learned. @cheers_hena 치얼스헤나:티스토리]
클라이언트에서 서버로 HTTP 요청(Request) 메시지를 보내면 서버에서는 클라이언트로 HTTP 응답(Response) 메시지를 보냅니다. HTTP Request와 Response 메시지에는 body, 즉 본문이 존재하는데요, 스프링이 제공하는 @RequestBody, @ResponseBody 어노테이션을 이용하면 body에 JSON 데이터를 담아 데이터를 손쉽게 주고 받을 수 있습니다.
라이브러리 추가
자바에서 JSON 데이터에 대한 처리를 하기 위해 라이브러리를 추가합니다. 대표적으로 Jackson, Gson, SimpleJSON 등의 라이브러리가 있는데, 저는 Jackson 라이브러리를 사용하겠습니다.
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4</version>
</dependency>
@RequestBody
@RequestBody 어노테이션을 컨트롤러 메소드의 파라미터로 설정하면, HttpMessageConverter가 HTTP Request body를 읽고 이를 역직렬화(deserialization)하여 자바 객체로 변환해 줍니다. 이때 @RequestBody로 선언한 파라미터 타입과 클라이언트에서 보낸 데이터가 일치해야 합니다.
서버측에 ajax 비동기로 JSON 데이터를 전송한 후, 200 상태값과 함께 전송했던 데이터를 문자열로 응답받아 출력해 보겠습니다. 전송할 데이터를 JSON 객체로 작성한 후 문자열로 변환하여 전송하되, 해당 데이터가 JSON 형태임을 알려주기 위해 Content-Type을 application/json으로 지정합니다.
<button id="aaa">aaa</button><br>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
$(function () {
$('#aaa').on('click', function () {
$.ajax({
url:'aaa',
type:'post', //post방식전송 - request의 body부분에 데이터가 전달됨
dataType:'text', //응답 데이터 타입
headers: {
"Content-Type":"application/json" //전달하는 데이터가 JSON형태임을 알려줌
},
data: JSON.stringify( {'userid':'amy', 'passwd':'1234'} ), //String으로 변환하여 전달 (JSON.stringify: JSON->String / JSON.parse: String->JSON)
success: function (data, status, xhr) {
console.log(data)
},
error: function (xhr, status, error) {
console.log(error)
}
})
})
})
</script>
@RequestMapping("/aaa")
public ResponseEntity<String> aaa(@RequestBody LoginDTO login) {
String info = login.getUserid()+" "+login.getPasswd();
return ResponseEntity.status(HttpStatus.OK).body(info);
}
@RequestBody 어노테이션을 통해 요청 본문에 담긴 JSON 데이터를 자동으로 파싱하여 해당하는 DTO클래스로 담아주는 것을 확인할 수 있습니다.
JSON 객체의 배열로 데이터를 전송하면 자동으로 데이터를 파싱하여 List에 담아줍니다.
<button id="aaa2">aaa2</button><br>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
$(function () {
$('#aaa2').on('click', function () {
let json1 = {'userid':'amy', 'passwd':'1234'}
let json2 = {'userid':'jack', 'passwd':'5678'}
$.ajax({
url:'aaa2',
type:'post', //post방식전송 - request의 body부분에 데이터가 전달됨
dataType:'text', //응답 데이터 타입
headers: {
"Content-Type":"application/json" //전달하는 데이터가 JSON형태임을 알려줌
},
data: JSON.stringify([json1, json2]), //JSON의 배열을 문자열로 변형시켜서 전송
success: function (data, status, xhr) {
console.log(data)
},
error: function (xhr, status, error) {
console.log(error)
}
})
})
})
</script>
@RequestMapping("/aaa2")
public ResponseEntity<Integer> aaa2(@RequestBody ArrayList<LoginDTO> list) {
return ResponseEntity.status(HttpStatus.OK).body(list.size());
}
@ResponseBody
@ResponseBody 어노테이션을 메소드 레벨이나 메소드의 리턴 타입으로 붙이면, 메소드가 리턴하는 값을 HttpMessageConverter를 통해 Response body로 직렬화(serialization)하게 됩니다.
이번에는 서버측으로부터 JSON 데이터를 응답받아 보겠습니다. 응답받는 데이터의 타입을 json으로 지정합니다.
<button id="bbb">bbb</button><br>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
$(function () {
$('#bbb').on('click', function () {
$.ajax({
url:'bbb',
type:'post', //post방식전송 - request의 body부분에 데이터가 전달됨
dataType:'json', //응답 데이터 타입
success: function (data, status, xhr) {
console.log(data)
},
error: function (xhr, status, error) {
console.log(error)
}
})
})
})
</script>
@RequestMapping("/bbb")
@ResponseBody
public LoginDTO bbb() {
LoginDTO login = new LoginDTO("amy", "1234");
return login;
}
자바 객체가 자동으로 JSON 형태로 변환되어 응답된 것을 확인할 수 있습니다.
HashMap을 사용하여 JSON 데이터를 응답받을 수도 있습니다.
<button id="bbb2">bbb2</button><br>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
$(function () {
$('#bbb2').on('click', function () {
$.ajax({
url:'bbb2',
type:'post', //post방식전송 - request의 body부분에 데이터가 전달됨
dataType:'json', //응답 데이터 타입
success: function (data, status, xhr) {
console.log(data)
},
error: function (xhr, status, error) {
console.log(error)
}
})
})
})
</script>
@RequestMapping("/bbb2")
@ResponseBody
public HashMap<String, String> bbb2() {
HashMap<String, String> map = new HashMap<String, String>();
map.put("userid", "amy");
map.put("address", "seoul");
return map;
}
마찬가지로 여러 개의 데이터를 List에 담아 JSON으로 응답받을 수도 있습니다.
<button id="bbb3">bbb3</button><br>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
$(function () {
$('#bbb3').on('click', function () {
$.ajax({
url:'bbb3',
type:'post', //post방식전송 - request의 body부분에 데이터가 전달됨
dataType:'json', //응답 데이터 타입
success: function (data, status, xhr) {
console.log(data)
},
error: function (xhr, status, error) {
console.log(error)
}
})
})
})
</script>
@RequestMapping("/bbb3")
@ResponseBody
public ArrayList<LoginDTO> bbb3() {
ArrayList<LoginDTO> list = new ArrayList<LoginDTO>();
list.add(new LoginDTO("amy", "1234"));
list.add(new LoginDTO("jack", "5678"));
return list;
}
@RestController
스프링 4에서부터는 @Controller 외에 @RestController라는 어노테이션을 추가해서 해당 Controller의 모든 메소드 리턴 타입을 @ResponseBody로 처리한다는 것을 명시할 수 있습니다. 기존에 @Controller와 @ResponseBody 어노테이션을 함께 사용했던 것과 동일한 기능으로서 @ResponseBody 어노테이션을 생략할 수 있게 됩니다.
@RestController
public class TestController {
@RequestMapping("/bbb")
public LoginDTO bbb() {
return new LoginDTO("amy", "1234");
}
}
@JsonIgnore
보안상 노출되면 안 되는 데이터의 경우(ex. 비밀번호) 필드에 @JsonIgnore 어노테이션을 붙이면 해당 값을 무시합니다.
public class LoginDTO {
private String userid;
@JsonIgnore
private String passwd;
//Constructors, Getters and Setters
}
@RestController
public class TestController {
@RequestMapping("/bbb")
public LoginDTO bbb() {
return new LoginDTO("amy", "1234");
}
}
참고
- https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-requestbody
- https://www.baeldung.com/spring-request-response-body
- https://admm.tistory.com/100