스프링 MVC 2편 - 검증1 Validation

서은경·2023년 1월 9일
0

Spring

목록 보기
39/43

BindingResult

  • 스프링이 제공하는 검증 오류를 보관하는 객체, 겆믕 오류가 발생하면 여기에 보관하면 된다.
  • BindingResult가 있으면 @ModelAttribute에 데이터 바인딩 시 오류가 발생해도 컨트롤러가 호출된다.
    • BindingResult가 없으면 400에러, 있으면 스프링이 바인딩하면서 생긴 문제의 결과를(=FieldError) BindingResult에 담아줌
  • 순서는 @ModelAttribute 바로 뒤!

BindingResult에 검증 오류를 적용하는 방법

  1. @ModelAttribute의 객체에 타입 오류 등으로 바인딩이 실패하는 경우 스프링이 FieldError 생성해서 BindingResult에 넣어줌
  2. 개발자가 직접 넣음
  3. Validator 사용

🚫 주의!
BindingResult는 순서가 매우 중요
Model에 자동으로 포함

BindingResult와 Errors

  • BindingResult는 인터페이스이고 Error 인터페이스를 상속받음
  • 실제 넘어오는 구현체는 BeanPropertyResult라는 것인데 둘다 구현하고 있으므로 BindingResult 대신 Errors를 사용해도 된다.
  • Errors 인터페이스는 단순한 오류 저장과 조회 기능을 제공, BindingResult는 몇 가지 기능을 추가 제공

FieldError, ObjectError

사용자 입력 오류 값을 필드에 그대로 남기려면 ⁉️

  • BindingResult.addError의 rejectedValue 생성자 활용
    objectName : 오류가 발생한 객체 이름
    field : 오류 필드
    rejectedValue : 사용자가 입력한 값(거절된 값)
    bindingFailure : 타입 오류 같은 바인딩 실패인지 검증 실패인지 구분 값
    code : 메시지 코드
    arguments : 메시지에서 사용하는 인자
    defaultMessage : 기본 오류 메시지

바인딩 실패값 유지

사용자의 입력 데이터가 컨트롤러의 @ModelAttribute에 바인딩되는 시점에 오류가 발생하면 모델 객체에 사용자 입력값을 유지하기 어렵다.(ex 숫자 필드에 문자열)

스프링은 HTTP 요청이 넘어올 때 requestParameter 값을 꺼내고 바인딩이 실패했을 경우 FieldError를 만듦 -> 바인딩 실패 값을 rejectedValue에 값을 넣음, 바인딩 실패 구분값은 true -> BindingResult에 담아 컨트롤러 호출

오류코드와 메세지 처리

BindingResult는 객체 뒤에 바로 붙기 때문에 어떤 객체를 대상으로 검사하는지 이미 알고 있음.

  • rejectValue(), reject()
    field: 오류 필드명
    errorCode:오류 코드(addError의 code와는 다름, messageResolever를 위한 오류 코드)
    errorArgs: 오류 메시지에서 {0}을 치환하기 위한 값
    defaultMessage: 오류 메시지를 찾을 수 없을 때 기본 메시지

errorCode

오류 코드를 단순하게 만들면 범용성이 좋아서 여러곳에서 사용할 수 있지만 세밀하게 작성하긴 어렵다. 반대로 너무 자세하게 만들면 범용성이 떨어진다.

#Level1
required.item.itemName=상품 이름은 필수입니다.

#Level2
required=필수 값입니다.

가장 좋은 방법은 💡범용성으로 사용하다가, 세밀하게 작성해야하는 경우 세밀한 내용이 적용되도록 단계를 두는 방법이다. (객체와 필드명을 조합한 에러코드가 있으면 그게 1순위, 그냥 에러코드가 2순위)

스프링은 MessageCodesResolver라는 것으로 이런 기능을 지원함 ✨

MessageCodesResolver

  • 검증 오류 코드로 메시지 코드들을 생성
  • MessageCodesResolver 인터페이스이고 DefaultMessageCodesResolver는 기본 구현체이다.

BindingResult가 rejectValue 실행시 내부에서 MessageCodesResolver를 사용 -> 생성된 에러코드 배열을 FieldError에 생성자로 넘김

생성 순서

  • 객체 오류의 경우
    1. code.객체명
    2. code
  • 필드 오류의 경우
    1. code.객체.필드명
    2. code.필드명
    3. code.클래스타입
    4. code

ValidationUtils

  • null이나 공백처리 ValidationUtils로 처리
    ValidationUtils.rejectIfEmptyOrWhitepace(bindingResult, "필드명", "에러코드");

스프링이 직접 만든 오류 메시지 처리

  • 개발자가 직접 설정한 오류 코드 -> rejectValue() 직접 호출
  • 스프링이 직접 검증 오류에 추가한 경우(주로 타입 정보가 맞지 않음)

typeMismatch 추가

Validator 분리

스프링은 Validator라는 인터페이스를 제공

  • supports() : 해당 검증기를ㅈ ㅣ원하는 여부 확인
  • validate(Object target, Error error) : 검증 대상 객체와 BindingResult

스프링 인터페이스인 Validator를 쓰는 이유는 ?!

  • 체계적으로 검증 기능을 도입하기 위해서!

WebDataBinder 사용하기 : 스프링 파라미터 바인딩의 역할을 해주고 검증기능도 내부에 포함된다

@InitBinder
public void init(WebDataBinder dataBinder) {
	dataBinder.addValidators(itemValidator);
}

컨트롤러 실행 시 항상 검증기를 적용할 수 있음

public String test(@Validated @ModelAttribute Item item, BindingResult bindingResult)

@Validated 어노테이션을 넣으면 Item이라는 객체에 대해 자동으로 검증기 수행, bindingResult에 결과 담김

동작

@Validated : 검증기를 실행하라는 어노테이션
이 어노테이션이 붙으면 앞서 WebDataBinder에 등록한 검증기를 찾아서 실행한다. 만약 여러 검증기를 등록한다면 어떤 검증기를 실행해야할지 구분이 필요하기 때문에 이때 supports() 함수를 사용한다! (이 과정은 스프링이 자동화해줌)

검증기 글로벌 설정

메인 클래스에 WebMvcConfigurer를 상속받아 getValidator() 오버라이딩
(@InitBinder를 제거해도 검증기 정상 동작)

🙋‍♀️참고
@Valid는 자바 표준 검증 어노테이션
@Validator는 스프링 전용 검증 어노테이션

0개의 댓글