강의

멘토링

커뮤니티

Cộng đồng Hỏi & Đáp của Inflearn

Hình ảnh hồ sơ của nclovehs8572
nclovehs8572

câu hỏi đã được viết

MVC mùa xuân

Phương thức Handler Phần 5 @ModelAttribute

@ModelAttribute 바인딩이 되지 않습니다.

Viết

·

9.2K

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 버전을 사용하고 있습니다.

강좌 너무 감사히 잘 보고 있습니다.

고맙습니다.

MVCspringjava

Câu trả lời 7

5

whiteship님의 프로필 이미지
whiteship
Người chia sẻ kiến thức

폼 데이터는 요청의 본문(body)을 통해 전달 되는 데이터가 맞습니다. 그런데 헤더에 해당 요청이 폼 데이터를 전송한다고 알려주고 있고, 그 데이터를 스프링이 받아서 RequestAttribute라는 걸로 파싱을 하고 그 안에 있는 걸 @RequestAttribute나 @ModelAttribute로 받을 수 있습니다. 쿼리 스트링은 바디나 헤더가 아니라 요청 라인이라는 부분이고요. 그쪽으로 들어오는 데이터도 마찬가지로 처리됩니다.

그런데 요청 헤더에 현재 요청 본문이 폼 데이터라는 헤더가 없는 경우, 그 경우에 바디에 들어있는 데이터(보통 JSON이거나 XML)를 파싱 하려면 그때는 @RequestBody가 필요하도 MessageConverter를 사용하는 겁니다.

2

whiteship님의 프로필 이미지
whiteship
Người chia sẻ kiến thức

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

whiteship님의 프로필 이미지
whiteship
Người chia sẻ kiến thức

요청 본문에 있는 값을 바인딩 받으려면 @RequestBody 애노테이션도 같이 사용해야합니다. @RB가 없을 때는 reuqest attriute에 들어있는 값들로 바인딩 하는데, 보통 화면에서 Form 서브밋 하거나, URL 뒤에 쿼리 매개변수로 (?name=cho)넘겨주면 그쪽으로 들어가죠.

0

nclovehs8572님의 프로필 이미지
nclovehs8572
Người đặt câu hỏi

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

nclovehs8572님의 프로필 이미지
nclovehs8572
Người đặt câu hỏi

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

nclovehs8572님의 프로필 이미지
nclovehs8572
Người đặt câu hỏi

저는 그냥 스프링 부트 프로젝트를 사용하지 않고 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

nclovehs8572님의 프로필 이미지
nclovehs8572
Người đặt câu hỏi

답변 주신 내용을 제가 잘 이해하지 못 하고 있는 것 같습니다. 이해부탁 드립니다.

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

###

본문은 요청의 마지막 부분에 들어갑니다. 모든 요청에 본문이 들어가지는 않습니다. GETHEADDELETE , OPTIONS처럼 리소스를 가져오는 요청은 보통 본문이 필요가 없습니다. 일부 요청은 업데이트를 하기 위해 서버에 데이터를 전송합니다. 보통 (HTML 폼 데이터를 포함하는) POST 요청일 경우에 그렇습니다.

### 

method = RequestMethod.GET 과 (@ModelAttribute MyType myType)를 사용하면 myType에 값이 채워지는데.

method = RequestMethod.POST를 했을 때는 채워지지가 않습니다.

강좌에서 @Test postEvent() 테스트에서 post 방식으로도 받을 수 있는 것을 보여 주셨는데. 왜 저는 RequestMethod.POST 방식으로 @ModelAttribute 어노테이션을 사용했을 때 값이 바인딩 되지 못하는지 너무 궁금합니다.

ps

유투브 영상에서 테스트 코드 작성 관련 내용을 말씀 하신 것을 들었는데... 저는 작성을 하지 않아서 부끄럽습니다...

Hình ảnh hồ sơ của nclovehs8572
nclovehs8572

câu hỏi đã được viết

Đặt câu hỏi