Spring MVC/Validation

Validation 검증 - BindingResult의 rejectValue와 reject

Hcode 2022. 12. 19.

 

이전 글로 Validation에 메시지를 사용하는 간단한 내용을 보았습니다.
 

Validation 검증 - Message 메시지

이전 글로 도메인 객체를 하나만 사용할 때 발생할 수 있는 문제점과 그 해결을 알아보았습니다. Validation 검증 - 도메인 객체를 하나만 사용할 경우 문제점 해결 이전 글로 Bean Validation의 글로벌

hhhhicode.tistory.com

이번 글로 불편한 FieldError와 ObjectError 생성을, BindingResult의 rejectValue와 reject로 대체하는 것을 알아봅시다.

 


 

BindingResult의 rejectValue와 reject

BindingResult는 FieldError와 ObjectError 객체를 담을 수 있습니다.

매번 new FieldError, new ObjectError를 사용해서 객체를 생성해도 되지만 번거로울 수 있습니다.

그래서 같은 결과를 보여주지만 다른 방법을 제안합니다.

 

  • BindingResult는 Target 객체를 알고 있다.
  • rejectValue()와 reject() 사용

에 대해 다룹니다.

 


 

BindingResult는 Target 객체를 알고 있다.

Handler의 파라미터에서 BindingResult는
  검증해야 할 객체인 target 객체 바로 다음에 옵니다.

따라서 BindingResult는 이미 본인이 Validation 해야 할 객체인
  target이 무엇인지 알고 있습니다.

 

그런데 FieldError나 ObjectError를 생성할때를 봅시다.

public FieldError(String objectName, String field, @Nullable Object rejectedValue, boolean bindingFailure,
		@Nullable String[] codes, @Nullable Object[] arguments, @Nullable String defaultMessage) {

	super(objectName, codes, arguments, defaultMessage);
	Assert.notNull(field, "Field must not be null");
	this.field = field;
	this.rejectedValue = rejectedValue;
	this.bindingFailure = bindingFailure;
}

우리는 FieldError나 ObjectError를 생성할 때
  objectName 파라미터에 대상 객체의 인스턴스 이름을 전달해 주어야 했습니다.

 

이것이 필요한 이유는 FieldError나 ObjectError는 target 객체가 무엇인지 모르기 때문입니다.

하지만 BindingResult는 target 객체를 알고 있습니다.

즉, BindingResult로 FieldError나 ObjectError를 생성할 수 있다면,
  target 객체의 인스턴스를 지정해 주지 않아도 됩니다.

 

그러므로 BindingResult의 rejectValue()와 reject의 사용을 제안하는 것입니다.

개발자가 직접 다루는 객체의 수도 적어질 뿐더러 더욱 편하게 다룰 수 있습니다.

FieldError와 ObjectError를 직접 생성하지 않고
  깔끔하게 Validation 오류를 다루어 봅시다.

 


 

rejectValue()와 reject() 사용

rejectValue()

메서드의 시그니처를 확인해보겠습니다.

void rejectValue(@Nullable String field, String errorCode,
			@Nullable Object[] errorArgs, @Nullable String defaultMessage);
  • field : 오류 필드 명
  • errorCode : 오류 코드(messages에 등록된 코드가 아닙니다. 후에 나올 messageResolver를 위한 오류 코드입니다.)
  • errorArgs : 오류 메시지에서 {0}, {1},... 를 치환하기 위한 값들입니다.
  • defaultMessage : 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지입니다.

앞에서 BindingResult는 어떤 객체를 target으로 Validation 하는지

  파라미터 순서를 통해 이미 알고 있다고 했습니다.

따라서 target에 대한 정보는 없어도 됩니다.

 

사용 예시를 들어보겠습니다.

@PostMapping("/add")
public String add(@Validated @ModelAttribute MemberAddDto memberAddDto, BindingResult bindingResult) {

    ...
    
    if (isUserIdDuplicate) {
        bindingResult.rejectValue("userId", "exist");
    }

    if (bindingResult.hasErrors()) {
        return "members/add";
    }

    ...
}

간단하게 field와 errorCode만 인자로 전달하여 사용하고 있는 모습입니다.

errorCode를 보시면, errors.properties의 키를 모두 입력하지 않았는데도

  자동으로 찾아서 오류 메시지를 정상 출력합니다.

[errors.properties]

exist.memberAddDto.userId=중복된 ID가 존재합니다.

간단하게 이해하자면,

  target 객체의 인스턴스가 무엇인지 알고 있으며, 해당 field 또한 전달해주기 때문에 알고 있습니다.

그래서 exist라는 동사 내용만 있어도 찾아서 출력해줄 수 있는 것입니다.

 

Spring은 매우 Configuration 한 프레임워크입니다.
장점이기도 하지만 단점이기도 합니다.
대표적인 문제 중 Configuration 문제도 있을 정도니까요.

위의 errors.properteis를 사용하시려면
application.properties에 spring.messages.basename=errors를 해주셔야 합니다.

 

해당 내용은 메시지에 대한 좀 더 자세한 이해를 필요로 합니다.

Bean Validation 메시지 찾는 순서
1. 생성된 메시지 코드 순서대로 messageSource에서 메시지 찾기
2. 애노테이션의 message 속성 사용 : @NotBlank(message="공백입니다. {0}")
3. 라이브러리가 제공하는 기본 값 사용 : 공백일 수 없습니다.

예시에서 exist를 적었을 때, 생성되는 메시지 코드 순서
1. exist.memberAddDto.userId
2. exist.userId
3. exist.java.lang.String
4. exist

생성되는 메시지 코드 순서대로 찾아가며 있으면 선택, 없으면 다음 메시지 코드로 찾습니다.
exist.memberAddDto.userId=중복된 ID가 존재합니다.​

가 있었으므로, 첫 번째 생성되는 exist.memberAddDto.userId로 바로 찾아서 메시지로 사용합니다.
구체적인 것에서 덜 구체적인 순서대로 찾아간다고 생각하시면 됩니다.

세밀한 내용이 필요하지 않은 메시지는, 기존에 정의한 범용적인 메시지를 그냥 재활용하면 됩니다.
exist=이미 존재합니다.​
이렇게 사용해도 되겠군요.

 


 

reject()

rejectValue와 내용이 같아서 생략합니다.

 

 

 

 


댓글