본문 바로가기
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으로 선언된 생성자를 찾음
    • 없다면 생성자 중 매개변수 개수가 제일 적은 생성자를 선택(보통 기본 생성자)
      1. 찾은 생성자가 고유하다면 해당 생성자를 선택
      2. 찾은 생성자가 여러개라면 매개변수가 제일 적은 생성자를 선택

2. 지정된 생성자로 인스턴스 생성

  • constructAttribute 클래스 안에서 생성자 생성
  • 선택한 생성자를 이용해 인스턴스를 생성할 때 생성자 인수 이름과 요청 파라미터의 이름이 같다면 값을 바인딩
    • 생성자 인수의 이름과 클라이언트가 요청한 파라미터의 이름이 같을때만 바인딩

3. 바인딩

  • resolveArgument method에서 파라미터 바인딩 진행
    • 요청 파라미터를 기준으로 setter method를 통하여 빈 속성을 바인딩

'Spring > MVC' 카테고리의 다른 글

Validator  (0) 2024.06.28
BindingResult  (0) 2024.06.27
@Controller와 @RestController  (1) 2024.06.06
@RequestBody와 @ResponseBody  (0) 2024.06.06
애노테이션(Annotation)  (0) 2024.05.23