@ModelAttribute
메소드 매개변수 주석은 요청 매개변수를 모델 객체에 바인딩합니다. 예를 들어:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { // (1)
// method logic...
}
(1) Pet
인스턴스에 바인딩합니다.
Pet
인스턴스는 다음과 같습니다.
@ModelAttribute 메소드에 의해 추가될 수 있는 모델에서 액세스됩니다.
모델 속성이 클래스 수준 @SessionAttributes
annotation에 나열된 경우 HTTP 세션에서 액세스됩니다.
모델 속성 이름이 경로 변수 또는 요청 매개변수와 같은 요청 값의 이름과 일치하는 경우 Converter
를 통해 가져옵니다(예제는 다음 참조).
기본 생성자를 통해 인스턴스화됩니다.
서블릿 요청 매개변수와 일치하는 인수를 사용하여 "primary 생성자"를 통해 인스턴스화됩니다. 인수 이름은 바이트코드의 런타임 유지 매개변수 이름을 통해 결정됩니다.
위에서 언급한 바와 같이 모델 속성 이름이 경로 변수나 요청 매개변수 등 요청 값의 이름과 일치하고 호환되는 Converter<String, T>
이 있는 경우 Converter<String, T>
를 사용하여 모델 객체를 얻을 수 있다. 아래 예에서 모델 속성 이름 account
는 URI 경로 변수 account
와 일치하며 아마도 지속성 저장소에서 이를 검색하는 등록된 Converter<String, Account>
가 있습니다.
@PutMapping("/accounts/{account}")
public String save(@ModelAttribute("account") Account account) {
// ...
}
기본적으로 생성자와 속성 데이터 바인딩이 모두 적용됩니다. 그러나 모델 객체 설계에는 세심한 고려가 필요하며 보안상의 이유로 웹 바인딩에 특화된 객체를 사용하거나 생성자 바인딩만 적용하는 것이 좋습니다. 속성 바인딩을 계속 사용해야 하는 경우 allowedFields 패턴을 설정하여 설정할 수 있는 속성을 제한해야 합니다. 이에 대한 자세한 내용과 구성 예는 모델 설계를 참조하세요.
생성자 바인딩을 사용할 때 @BindParam
annotation을 통해 요청 매개변수 이름을 사용자 정의할 수 있습니다. 예를 들어:
class Account {
private final String firstName;
public Account(@BindParam("first-name") String firstName) {
this.firstName = firstName;
}
}
[Note]
@BindParam
은 생성자 매개변수에 해당하는 필드에 배치될 수도 있습니다.@BindParam
은 기본적으로 지원되지만DataBinder
에서DataBinder.NameResolver
를 설정하여 다른 annotation을 사용할 수도 있습니다.
어떤 경우에는 데이터 바인딩 없이 모델 속성에 액세스하고 싶을 수도 있습니다. 이러한 경우 Model
을 컨트롤러에 삽입하고 직접 액세스하거나 다음 예제와 같이 @ModelAttribute(bind=false)
를 설정할 수 있습니다.
@ModelAttribute
public AccountForm setUpForm() {
return new AccountForm();
}
@ModelAttribute
public Account findAccount(@PathVariable String accountId) {
return accountRepository.findOne(accountId);
}
@PostMapping("update")
public String update(AccountForm form, BindingResult result,
@ModelAttribute(binding=false) Account account) { // (1)
// ...
}
(1) @ModelAttribute(binding=false)
를 설정합니다.
데이터 바인딩으로 인해 오류가 발생하면 기본적으로 MethodArgumentNotValidException
이 발생하지만 컨트롤러 메서드에서 이러한 오류를 처리하기 위해 @ModelAttribute
바로 옆에 BindingResult
인수를 추가할 수도 있습니다. 예를 들어:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { // (1)
if (result.hasErrors()) {
return "petForm";
}
// ...
}
(1) @ModelAttribute
옆에 BindingResult
를 추가합니다.
jakarta.validation.Valid
annotation이나 Spring의 @Validated
annotation을 추가하면 데이터 바인딩 후 자동으로 유효성 검사를 적용할 수 있습니다. Bean Validation 검사 및 Spring 유효성 검사를 참조하세요. 예를 들어:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { // (1)
if (result.hasErrors()) {
return "petForm";
}
// ...
}
(1) Pet
인스턴스를 검증합니다.
@ModelAttribute
뒤에 BindingResult
매개변수가 없으면 유효성 검사 오류와 함께 MethodArgumentNotValueException
이 발생합니다. 그러나 다른 매개변수에 @jakarta.validation.Constraint
annotation이 있어서 메서드 유효성 검사가 적용되는 경우 대신 HandlerMethodValidationException
이 발생합니다. 자세한 내용은 유효성 검사 섹션을 참조하세요.
[Tip]
@ModelAttribute
사용은 선택 사항입니다. 기본적으로 BeanUtils#isSimpleProperty에 의해 결정된 단순 값 유형이 아니고 다른 인수 resolver에 의해 resolve되지 않은 매개변수는 암시적@ModelAttribute
로 처리됩니다.
[Warning]
GraalVM을 사용하여 네이티브 이미지로 컴파일할 때 위에 설명된 암시적@ModelAttribute
지원은 관련 데이터 바인딩 반영 힌트에 대한 적절한 사전 추론(ahead-of-time inference)을 허용하지 않습니다. 결과적으로 GraalVM 네이티브 이미지에서 사용하기 위해@ModelAttribute
로 메서드 매개변수에 명시적으로 annotation을 추가하는 것이 좋습니다.