Spring/MVC
Model과 @ModelAttribute
by o3oppp
2024. 6. 8.
Model
- Model은 객체
- Spring이 지원하는 기능으로, Key와 Value로 이루어진 HashMap
- Controller에서 addAttribute("key", value)를 통해 View에 전달할 데이터를 저장할 수 있음
- Servlet의 request.setAttribute()와 비슷한 역할
- 따로 반환이 필요 없음
사용 예시
@Controller
public class ModelTest {
// 파라미터를 통한 전달
@GetMapping("/test")
public String testMethod(@RequestParam name, Model model){
model.addAttribute("name", name);
return "Ok";
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>model Test</title>
</head>
<body>
<h1>Test</h1>
<h2>${name}</h2> <!-- ${name}을 통해 변수 name에 접근-->
</body>
</html>
@ModelAttribute
- HTTP 요청 파마리터나 multipart/form-data 형태의 파라미터를 받아 객체로 사용하고 싶을 때 사용
- @RequestParam을 이용하여 값을 받고, set을 사용해서 값을 넣어주는 과정을 자동화 시켜줌
- @ModelAttribute는 가장 적절한 생성자를 찾아 객체를 생성 및 초기화
- 객체 생성 및 초기화 -> Data Binding -> Validation 순으로 진행
- Data Binding은 getter/setter가 존재하는 변수에 한해서 이루어짐
- @RequestMapping이 선언된 Controller 클래스만 지원
사용 예시
1. @ModelAttribute 미사용
@Controller
public class TestController {
@PostMapping("/test")
public void item(@RequestParam String name, @RequestParam int price, Model model){
Item item = new Item();
item.setName(name);
item.setPrice(price);
model.addAttribute("item", item);
...
}
}
- String이나 int 같은 단순 타입 사용 시 @RequestParam 생략 가능
2. @ModelAttribute를 사용
public class Item {
private String name;
private Integer price;
}
@Controller
public class TestController {
@PostMapping("/test")
public String test(@ModelAttribute Item item) {
log.info("name={}, price={}",Item.getName(), Item.getPrice());
...
}
@GetMapping("/test2")
public String test2(@ModelAttribute Item item, Model model) {
model.addAttribute("item, item);
...
}
}
- 객체 타입 사용 시 @ModelAttribute 생략 가능
지원 방식
1. parameter 레벨
@PostMapping("/test")
public String test(@ModelAttribute Item item, BindingResult result) {
if(bindingResult.hasErrors()) {
bindingResult.getAllErrors().forEach(e -> {
System.out.println(e.toString());
});
}
log.info("name={}, price={}",Item.getName(), Item.getPrice());
...
}
- 단순 데이터 타입을 복합 타입의 객체로 받아오거나 해당 객체를 새로 만들 때 사용할 수 있음
- 값을 바인딩할 수 없다면 BindExeption이 발생하여 400 error 반환
- 바인딩 에러를 핸들링하려면 해당 메소드에 BindingResult 파라미터 추가
2. method 레벨
@ModelAttribute
public void test(Model model) {
model.addAttribute("message", "hello");
...
}
@ModelAttribute("item")
public void test2(Model model) {
//model.addAttribute("message", "hello"); // 생략 가능
}
- 하나 이상의 속성을 Model로 추가하는 경우 method 레벨에서 @ModelAttribute 추가
- Spring MVC는 요청 핸들러 메소드를 호출하기 전에 항상 @ModelAttribute가 붙은 method를 먼저 호출
- Contoller method 내에서 처리가 시작되기 전에 모델 개체를 만들어야 한다는 것
ModelAttributeMethodProcessor
- @ModelAttribute가 붙여진 method의 반환값을 처리
1. 가장 적절한 생성자 찾기
- createAttribute 클래스에서 ReflectionUtils를 사용하여 적절한 생성자를 찾아 바인딩할 객체를 생성
- BeanUtils.getResolvableConstructor() 실행으로 대상이 되는 클래스의 사용 가능한 생성자를 찾음
- public으로 선언된 생성자를 찾음
- 없다면 생성자 중 매개변수 개수가 제일 적은 생성자를 선택(보통 기본 생성자)
- 찾은 생성자가 고유하다면 해당 생성자를 선택
- 찾은 생성자가 여러개라면 매개변수가 제일 적은 생성자를 선택
2. 지정된 생성자로 인스턴스 생성
- constructAttribute 클래스 안에서 생성자 생성
- 선택한 생성자를 이용해 인스턴스를 생성할 때 생성자 인수 이름과 요청 파라미터의 이름이 같다면 값을 바인딩
- 생성자 인수의 이름과 클라이언트가 요청한 파라미터의 이름이 같을때만 바인딩
3. 바인딩
- resolveArgument method에서 파라미터 바인딩 진행
- 요청 파라미터를 기준으로 setter method를 통하여 빈 속성을 바인딩