@ModelAttribute
먼저 @ModelAttribute 어노테이션이 붙은 매개변수를 client에게서 요청받아서 처리하려는 경우 아래와 같은 특징이있다.
- 객체에 데이터를 바인딩할 수 있는 생성자혹은 Setter메서드가 하나는 필요하다.
- Query String (w-xxx-url-encoded)방식이나 Form 형식 데이터가 아니면 null을 반환한다.
스프링 내부적으로 @ModelAttribute 어노테이션이 붙은 Controller에 전달될 매개변수 RequestDto 객체에 에 client의 요청 데이터 값을 bind하기 위해서 ModelAttributeMethodProcessor 클래스가 RequestDto객체를 어떻게 생성하는지 살짝만 보면,
ModelAttributeMethodProcessor 가 HandlerMethodArgumentResolver 인터페이스를 구현한 함수인 resolveArgument에서 요청한 데이터의 값을 검증하고 binding하여 Java 객체에 요청한 값을 담아서 처리하도록한다.
resolveArgument() → createAttribute() 메소드를 통해서 RequestDto 객체를 생성하는데,
NoArgsContructor를 기본으로,
적절한 변수를 매개변수로 받는 생성자가 있고 사용가능하다면 해당 생성자를 통해 객체를 생성한다.
이후 bind되지 않은 변수와 데이터에 대해서 setter method를 통해서 값을 bind한다.
따라서 위 과정을 통해 @ModelAttribute 어노테이션이 붙은 매개변수의 객체가 완성이 되려면 아래의 2가지를 만족해야한다.
1. 모든 매개변수를 포함하는 생성자가 있다. (AllArgsConstructor)
2. 모든 매개변수를 포함하는 생성자 대신 기본 생성자 혹은 선택적으로 변수를 포함한 생성자가 있다면, 생성자 함수를 이용해서 값을 할당하지 못한 필드에 대해서 Setter 메소드가 존재해야한다.(NoArgsConstructor + Setter)
@RequestBody
내가 헷갈렸던 이유는 평소에 @ModelAttribute 보다 @RequestBody를 압도적으로 많이 사용하기도 하고
@RequestBody 를 사용할 때는 Dto에 보통 @Getter + @NoArgsConstructor 를 거의 고정으로 사용했던 터라 @Setter가 언제 왜 필요한지 전혀 몰랐었다.
@RequestBody를 이용하여 Json 데이터를 Dto로 변환하는 방식은, 내부적으로 ObjectMapper가 기본생성자로 객체를 생성하고,
Setter 혹은 Getter를 사용하여 Dto의 필드를 가져와 reflection을 사용해서 필드 값에 httpRequest 속 client의 요청 데이터를 넣어준다.
reflection을 사용해서 동적으로 값을 주입하니 필드에 대한 접근 권한을 변경할 수 있었던 터라 꼭 Setter가 필요하진 않았던 것이다.
물론 Getter 메소드는 비즈니스로직이나 테스트, 여러 방면으로 사용할 데가 있고, Setter 메소드의 존재로 인해 언제 어디서 해당 객체가 수정되었을지 모른다는 가능성을 갖고 갈 이유가 없으므로 종합적으로 Getter만 사용하게 된 것.