작성
·
8.9K
0
== Controller ==
@Controller
public class MainController {
@RequestMapping(value = "/test", method = RequestMethod.POST)
@ResponseBody
public int addTest(@ModelAttribute TestVo _testVo) {
TestVo testVo = _testVo;
System.out.printf("%s", testVo.getName());
// null 출력
return 0;
}
}
=== Model ===
public class TestVo {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
이렇게 작성한 후에 PostMan 프로그램을 통해서 아래 와 같이 전달 하였는데 전달 _testVo 객체의 값이 모두 비어 있습니다. 강좌를 이해하기로는 변수명이 맞으면 바인딩이 되는 것처럼 보였는데 왜 바인딩이 되지 않는 것인지 궁금합니다.
{
"id": 10
, "name": "cho"
}
@RequestBody 어노테이션을 이용하면 값이 매핑 되는데 무슨 차이가 있는지 궁금합니다.
spring-webmvc : 4.3 버전을 사용하고 있습니다.
강좌 너무 감사히 잘 보고 있습니다.
고맙습니다.
답변 7
5
폼 데이터는 요청의 본문(body)을 통해 전달 되는 데이터가 맞습니다. 그런데 헤더에 해당 요청이 폼 데이터를 전송한다고 알려주고 있고, 그 데이터를 스프링이 받아서 RequestAttribute라는 걸로 파싱을 하고 그 안에 있는 걸 @RequestAttribute나 @ModelAttribute로 받을 수 있습니다. 쿼리 스트링은 바디나 헤더가 아니라 요청 라인이라는 부분이고요. 그쪽으로 들어오는 데이터도 마찬가지로 처리됩니다.
그런데 요청 헤더에 현재 요청 본문이 폼 데이터라는 헤더가 없는 경우, 그 경우에 바디에 들어있는 데이터(보통 JSON이거나 XML)를 파싱 하려면 그때는 @RequestBody가 필요하도 MessageConverter를 사용하는 겁니다.
2
POST 본문 데이터가 폼 데이터가 아닌 경우에도 @RB를 사용해서 바인딩 받을 수가 있기는 한데 그걸 처리할 수 있는 적절한 메시지 컨버터가 필요합니다. 그 부분에 대해서는 뒤에 어딘가 관련 수업이 있을겁니다.
@MA에 대해서는 이 수업에서 설명 드렸으니 다시 수업을 들어보시기 바랍니다.
@RB에 대해서는 메시지 컨버터를 다룰 때 설명이 나올겁니다.
관련 스프링 문서도 읽어보시기 바랍니다.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html
간단히 말해서, 폼 데이터가 아닌 경우에 요청 본문이 XML인지 JSON인지 뭔지 알고 그걸 어떻게 바인딩을 하나요?? 요청을 만들어 보내신 조남철님이야 그게 JSON이고 name이 뭐구나 하고 알겠지만 스프링 입장에서 @RB가 없으면 본문을 바인딩 하라는 건줄도 모르고 (바인딩이 안되는 이유), @RB가 있더라도 그 본문을 바인딩할 때 사용할 메시지 컨버터가 없으면 바인딩을 못해서 에러가 나는겁니다. (415 에러가 난 이유)
1
요청 본문에 있는 값을 바인딩 받으려면 @RequestBody 애노테이션도 같이 사용해야합니다. @RB가 없을 때는 reuqest attriute에 들어있는 값들로 바인딩 하는데, 보통 화면에서 Form 서브밋 하거나, URL 뒤에 쿼리 매개변수로 (?name=cho)넘겨주면 그쪽으로 들어가죠.
0
HTTP 메시지 컨버터 2부 JSON 강좌 영상 7:50 부분이 지금 제가 문의 드렸던 내용과 같은 이유라고 생각 됩니다. (강좌를 처음부터 보지 않은 덕택에 더 많이 공부하고 있음에 감사합니다 ^^;)
그래서 HTTP Message 구조를 찾아 보았습니다.
https://developer.mozilla.org/ko/docs/Web/HTTP/Messages
HTTP Message는 시작줄, Header, Body으로 이루어 지는데 리소스를 가져오는 요청은 본문이 필요가 없고, 업데이트를 하기 위한 요청 정보에 본문을 이용한다.
그래서 POST 요청을 보내는 정보는 Body에 들어가고@RequestBody 어노테이션은 그 HTTP Message Body 값을 읽는다. 라고 이해 했습니다.
그런데 이해가 안 되는 부분이 있습니다.
HTML 에서 Submit 할 때의 Form data와 브라우저 주소창에 입력하는 Query String은 Http Message에서 Body에 들어가지 않으면 Header 부분에 들어 가게 되는건가요?
PostMan 프로그램데 Body 탭에 form-data, x-www-form-urlencoded, raw, binary가 함께 있는데 실제 Post Man에서 요청을 보낼 때는 다른 구조의 Http Message가 생성되는 건가요?
오늘도 좋은 하루 보내세요~
0
Http Request 어떻게 이루어져 있는지 정확히 몰라서 이해가 안 되었나 봅니다.
요청을 보내면 Header와 Body로만 이루어져 있다고 생각 했었습니다.
말씀주신대로 다시 강좌도 다시 보고Form data와 Request Body (Payload)에 대해서도 찾아봐야 겠습니다.
예전에는 그냥 그려려니 했던 Expressjs Request Parser 가 두 가지 다른 타입을 처리하는 이유도 말씀 주신 내용과 동일한 이유일 것이라고 생각되는군요.
app.use(bodyParser.json()) // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
매일밤 답변 달아주셔서 정말 감사합니다.
행복한 하루 보내세요.
0
저는 그냥 스프링 부트 프로젝트를 사용하지 않고 Spring 4.3.13 Release를 사용한 팀 프로젝트에서 기선님 강좌를 따라해도 큰 문제는 없을 것이다 라고 생각을 갖고
@ModelAttribute를 사용해 보려고 한 것인데, 원하는 결과를 얻지 못해서 질문을 드렸습니다.
하지만 어제부터 하루 종일 이것 저것 고쳐 보아도 원하는 결과를 얻지 못해서 (ContentType에 문제가 있어서 바인딩을 하지 못 하는 것인가 생각 했습니다)
오늘 아침 "핸들러 메소드 4부 폼 서브밋" 강좌와 동일하게 스프링 부트 프로젝트를 새로 생성해서 테스트 해보았습니다.
1. 스프링 부트 프로젝트 생성
web, thymeleaf 참조
2. Person Vo class 생성 (getter, setter 생성)
private int age;
private String firstName;
private String lastName;
private boolean gender;
3. MainController 생성
@Controller
public class MainController {
@GetMapping("/person")
public String getPerson(@ModelAttribute("person") Person person, Model model) {
return "/person";
}
@PostMapping("/person")
@ResponseBody
public Person person(@ModelAttribute("person") Person person, Model model) {
return person;
}
}
4. templates 하위에 person.html 생성
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="#" th:action="@{/person}" method="post" th:object="${person}">
<input type="text" th:field="*{age}" />
<input type="text" th:field="*{firstName}" />
<input type="text" th:field="*{lastName}" />
<input type="text" th:field="*{gender}" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
이렇게 준비해서 실제 프로젝트 Run 후에 localhost:8080/person 접속 후 값을 채워 submit을 하면 person 객체가 json 형식으로 브라우저에 표시 되었습니다.
그리고 나서 강좌에서 보여주신대로 postman을 실행해서
- Get 방식으로 호출해서 html 결과가 표시 되는 것을 확인 (정상 동작)
- POST 방식
- form-data 방식 Header 추가 되지 않았음
> 정상 동작
- x-www-form-urlencoded 방식 Header에 Content-Type = application/x-www-form-urlencoded 자동 추가 되었음
> 정상 동작
- raw 방식 Header에 Content-Type = application/json 자동 추가 되었음
{ // 요청 값
"age": 99
, "firstName": "Nathan"
, "lastName": "Cho"
, "gender": true
}
> 값이 초기화 상태인 Person 객체가 전달 됨.
왜 이 경우에 값이 바인딩 되지 않는 것인가요? (원하지 않은 결과입니다. age, gender에 올바르지 않은 값을 보내도 Bad Request 에러가 발생하지 않습니다.)
> 이 경우에는 @PostMapping 어노테이션을 사용한 컨트롤러 함수에서 @ModelAttribute 대신 @RequestBody 어노테이션을 이용하는 것이 올바른 방법 인가요? (@RB를 사용했더니 원하는 결과를 얻을 수 있었습니다.)
> @RequestBody와 @ModelAttribute 어노테이션이 정확하게 어떠한 상황에서 사용을 해야 맞는것인지 궁금합니다.
긴~ 질문 읽어주셔서 감사합니다.
PS - 추가 테스트
@ModelAttribute 붙였을 때
form-data 방식: 바인딩 정상
x-www-form-urlencoded 방식: 바인딩 정상
raw - json 방식: 바인딩 되지 않음
@RequestBody 붙였을 때
form-data 방식: 415 Unsupported Media Type
x-www-form-urlencoded 방식: 415 Unsupported Media Type
raw - json 방식: 바인딩 정상
0
답변 주신 내용을 제가 잘 이해하지 못 하고 있는 것 같습니다. 이해부탁 드립니다.
1. 요청 본문에 있는 값을 바인딩 받으려면 @RequestBody도 같이 사용해야 합니다.
> @ModelAttribute @RequestBody TestVo testVo 이렇게 써야 2개의 어노테이션을 함께 사용 한다는 말씀이신가요? 아니면 Post 요청을 받을 때는 @ModelAttribute 대신 @RequestBody 어노테이션을 사용해야 한다는 말씀이신가요?
2. @RequestBody가 없을 때는 request attribute에 들어 있는 값들로 바인딩을 하는데, 보통 화면에서 form 서브밋하거나, URL 뒤에 쿼리 매개변수로 넘겨주면 그쪽으로 들어가죠.
> Post 요청일 때만 Http Message 에서 Request Body (요청 본문) 이 생성되어 전달 되는데 @ModelAttribute로는 그 값을 읽을 수 없다는 말씀이신가요?
Http Message에 관련해서는 아래 링크 참고했습니다.
https://developer.mozilla.org/ko/docs/Web/HTTP/Messages
###
본문은 요청의 마지막 부분에 들어갑니다. 모든 요청에 본문이 들어가지는 않습니다. GET
, HEAD
, DELETE
, OPTIONS
처럼 리소스를 가져오는 요청은 보통 본문이 필요가 없습니다. 일부 요청은 업데이트를 하기 위해 서버에 데이터를 전송합니다. 보통 (HTML 폼 데이터를 포함하는) POST
요청일 경우에 그렇습니다.
###
method = RequestMethod.GET 과 (@ModelAttribute MyType myType)를 사용하면 myType에 값이 채워지는데.
method = RequestMethod.POST를 했을 때는 채워지지가 않습니다.
강좌에서 @Test postEvent() 테스트에서 post 방식으로도 받을 수 있는 것을 보여 주셨는데. 왜 저는 RequestMethod.POST 방식으로 @ModelAttribute 어노테이션을 사용했을 때 값이 바인딩 되지 못하는지 너무 궁금합니다.
ps
유투브 영상에서 테스트 코드 작성 관련 내용을 말씀 하신 것을 들었는데... 저는 작성을 하지 않아서 부끄럽습니다...