@ModelAttirbutes 동작과정
1. 클라이언트로부터 전송된 폼 데이터가 컨트롤러에 도착
2. 스프링은 폼 데이터를 바탕으로 @ModelAttribute 붙은 클래스의 객체 생성
3. 폼 데이터의 필드 이름과 객체 필드의 이름과 일치하는 경우 폼 데이터의 필드의 값을 객체 필드에 할당
4. 객체의 생성과 필드 할당이 이뤄지면 컨트롤러 메서드의 파라미터로 전달
2~3번 과정은 ArgumentResolver에서 이뤄지며 다음의 과정을 거친다.
1. 기본 생성자를 확인하고 있다면 사용해서 객체 생성
2. 기본 생성자가 없다면 폼 데이터와 일치하는 매개변수를 가지는 다른 생성자를 찾음
3. 폼 데이터의 필드 이름과 생성자의 매개변수 이름이 일치하는 경우 해당 생성자를 사용해 객체 생성
기본생성자가 필요할 때는 ?
자바의 클래스는 디폴트로 아무 파라미터도 받지 않는 기본생성자를 갖고 있지만 임의의 생성자를 구현하면 기본생성자는 사라진다.
프로필 수정 폼은 Account에서 정보를 가져와서 이전의 정보를 보여줘야하기 때문에 다음과 같이 구현했다.
@Data
@NoArgsConstructor // 기본생성자 생성 @
public class Profile {
@Length(max = 35)
private String bio;
@Length(max = 50)
private String url;
@Length(max = 50)
private String occupation;
@Length(max = 50)
private String location;
private String profileImage;
public Profile(Account account) {
this.bio = account.getBio();
this.url = account.getUrl();
this.occupation = account.getOccupation();
this.location = account.getLocation();
this.profileImage = account.getProfileImage();
}
}
// Controller
@GetMapping(SETTINGS_PROFILE_URL)
public String updateProfileForm(@CurrentAccount Account account, Model model) {
model.addAttribute(account);
model.addAttribute(new Profile(account));
return SETTINGS_PROFILE_VIEW_NAME;
}
@PostMapping(SETTINGS_PROFILE_URL)
public String updateProfile(@CurrentAccount Account account, @Valid @ModelAttribute Profile profile,
Errors errors, Model model, RedirectAttributes attributes) {
if (errors.hasErrors()) {
model.addAttribute(account);
return SETTINGS_PROFILE_VIEW_NAME;
}
accountService.updateProfile(account.getId(), profile);
attributes.addFlashAttribute("message", "프로필 수정했습니다.");
return "redirect:" + SETTINGS_PROFILE_URL;
}
위와 같이 Profile의 생성자로 Account를 받으면 POST 수정 요청을 받으면서 @ModelAttirbute를 Profile에 붙이는데 View에서 컨트롤러로 넘어올 때는 Account 객체의 생성자를 사용할 수 없어 오류가 발생한다. 따라서 이럴 때는 기본생성자를 통해 생성하고 폼 데이터 바인딩은 setter를 통해 이뤄지도록 해야한다.
기본생성자가 없고 구현된 생성자를 통해 바인딩이 안될 때
참고
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1/dashboard