@ModelAttribute를 통해 요청값을 입력받는 DTO를 만들었다.@Getter
@Setter
public class RequestDTO {
private ShareType type;
private List<MultipartFile> images;
private int price;
public RequestDTO(ShareType type, List<MultipartFile> images, int price) {
this.type = type;
this.images = images;
this.price = price;
}
}
type, price 필드는 클라이언트가 입력하지 않으면 null 값이 할당되어 @NotNull 어노테이션을 통해 Bean Validation이 동작하도록 구현했다.price 필드는 Primitive Type이기 때문에 null 값이 들어갈 수 없으므로 Wrapper Class인 Integer로 타입을 변경해줬다.@Getter
@Setter
public class RequestDTO {
@NotNull(message = "요청한 쉐어정보 값이 비어있습니다.")
private ShareType type;
@NotEmpty(message = "요청한 쉐어정보 값이 비어있습니다.")
private List<MultipartFile> images;
@NotNull(message = "요청한 쉐어정보 값이 비어있습니다.")
private Integer price;
public RequestDTO(ShareType type, List<MultipartFile> images, int price) {
this.type = type;
this.images = images;
this.price = price;
}
}
price 필드의 값을 넣지 않고 테스트를 실행해보니 아래와 같은 예외(TypeMismatchException)가 발생했다.
int 타입인 price 필드에 null 값을 할당할 수 없다는 뜻으로 이해했다.@ModelAttribute의 동작 방식이 자바빈 프로퍼티 방식으로만 필드에 값을 할당해주는 걸로 알고 있었기 때문에 이 에러가 발생한게 조금 의아했다. RequestDTO 클래스는 Lombok의 @Setter를 통해서 setPrice 메서드를 만들어 줄 것이고 setPrice 메서드의 매개변수 타입은 price 필드의 타입과 같은 Integer라고 생각했기 때문에 도대체 저 int 타입은 어디서 나온거지…? 라는 궁금증이 생겼다.TypeMismatchException이 발생하는지 확인해봤다.
ModelAttributeMethodProcessor 클래스에서 createAttribute 메서드를 호출하고 BindException이 발생한 것을 확인할 수 있었다.createAttribute 메서드의 시작 부분에 break point를 찍고 다시 요청해봤다.
createAttribute 메서드의 219번째 줄에서 getResolvableConstructor 메서드를 통해 ModelAttribute로 생성할 객체의 생성자를 찾아서 ctor 변수에 할당한다.
ModelAttribute로 생성할 객체의 생성자 정보를 담고 있는 변수 ctor의 정보를 디버거로 확인해봤다.int인 것을 확인할 수 있다.price 필드 타입은 분명 Integer로 변경했는데 생각해보니까 생성자의 매개변수 타입은 따로 변경해주지 않아서 int 타입이었던 것이다.null 값으로 매개변수를 채워준 것일까?
int 타입인 생성자의 매개변수 이름(price)와 동일한 파라메터를 클라이언트가 입력하지 않는다면 스프링이 null 값으로 매개변수를 넣어줘서 TypeMissMatchException이 발생한 것이다.price도 Integer 타입으로 변경해줬다.@Getter
@Setter
public class RequestDTO {
@NotNull(message = "요청한 쉐어정보 값이 비어있습니다.")
private ShareType type;
@NotEmpty(message = "요청한 쉐어정보 값이 비어있습니다.")
private List<MultipartFile> images;
@NotNull(message = "요청한 쉐어정보 값이 비어있습니다.")
private Integer price;
public RequestDTO(ShareType type, List<MultipartFile> images, Integer price) {
this.type = type;
this.images = images;
this.price = price;
}
}

price 필드를 입력하지 않아도 TypeMismatchException이 발생하지 않고 예외 메시지를 보내줄 수 있다!기본 생성자와 setter를 통해서 ModelAttribute가 동작한다고 생각했지만 스프링은 생성자의 매개변수 이름과 같은 파라메터 값을 ModelAttribute의 대상인 객체에게 할당해 주는 것을 알 수 있었다.Primitive Type이여서 TypeMismatchException이 발생했던 것이다.@AllArgsConstructor만 있어도 필드에 값이 제대로 들어가는 경우도 있었고 기본 생성자의 접근 제어자와 setter의 유무에 따라서 필드에 값이 제대로 들어가지 않는 경우도 있었다.ModelAttribute의 동작 방식에 대해 제대로 학습해봐야겠다.