비즈니스 로직이 올바르게 동작하려면 데이터를 사전 검증하는 작업이 필요하다.
이를 검증하는 작업을 유효성 검사 혹은 데이터 검증이라고 부른다.
계층별로 진행하는 유효성 검사는 검증 로직이 클래스 별로 분산되어 있어 관리하기 어렵다.
검증 로직에 중복 코드가 많아 유사 기능 코드가 존재할 수 있다.
이를 해결하기 위해 Bean Validation이라는 데이터 유효성 검사 프레임워크를 제공함
어노테이션을 통해 다양한 데이터를 검증하는 기능을 제공하며 유효성 검사 로직을 DTO 같은 도메인 모델과 묶어서 각계층에서 사용하면서 도메인 모델에 검증을 얹는 방식으로 수행한다.
Bean Validation 명세의 구현체
유효성 검사는 각 계층으로 데이터가 넘어오는 시점에 검사를 실시한다.
스프링부트 프로젝트에서는 계층간 데이터 전송에 대체로 DTO 객체를 활용하기 때문에 DTO 객체를 대상으로 수행한다.
유효성 검사를 수행하겠다고 선언한는 어노테이션
기존에 사용한 @Valid는 자바에서 지원하는 어노테이션이다.
스프링에서는 @Validated라는 어노테이션을 지원하며 @Valid의 기능 또한 포함하고 있다.
@Validated는 유효성 검사를 그룹으로 묶어 대상을 특정할 수 있는 기능이 있다.
@Max, @Min 등 검사 어노테이션의 groups 속성을 지정한 후 @Validated 에서 그룹을 지정해 유효성 검사를 실행할 것인지 정할 수 있다.
예제
자바, Spring 유효성 검사 어노테이션에서 제공하지 않는 기능을 쓸 때도 있다.
ConstraintValidator, 커스텀 어노테이션을 조합해서 별도의 유효성 검사 어노테이션을 생성할 수 있다.
ConstraintValidator 인터페이스를 구현하는 클래스를 생성
인터페이스 선언 시 어떤 어노테이션 인터페이스인지 타입을 지정해야한다.
인터페이스 구현을 위해서는 isValid()
를 구현해야 한다.
public class TelephoneValidator implements ConstraintValidator<Telephone, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context){
if(value==null)
return false;
return value.matches("01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$");
}
}
null 값 허용 로직과 지정 정규식과 비교하는 형식을 띠는 지 검사하게 작성한다.
null 값 허용을 false로 지정 시 null 값이 들어가면 MethodArgumentNotValidException이 발생한다.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = TelephoneValidator.class)
public @interface Telephone {
String message() default "전화번호 형식이 일치하지 않음";
Class[] groups() default {};
Class[] payload() default {};
}
이 어노테이션을 어디서 선언할 수 있는지 정의한다.
설정시 ElementType을 통해 설정한다.
지금 코드에서는 필드에서 선언할 수 있게 설정되어 있다.
어노테이션이 실제로 적용되고 유지되는 범위를 설정한다.
RetetionPolicy를 통해 설정한다.
RetetionPolicy.RUNTIME
컴파일 이후에도 JVM에 의해 계속 참조
RetetionPolicy.CLASS
컴파일러가 클래스를 참조할 때까지 유지
RetetionPolicy.SOURCE
컴파일 전까지만 유지, 컴파일 이후 사라짐
Validator와 시켜주는 역할을 수행하는 어노테이션
validatedBy를 통해 매핑할 인터페이스를 지정한다.
message(), groups(), payload() 메소드를 정의해줘야 한다.
message()
유효성 검사 실패시 반환 메시지
group()
유효성 검사를 사용하는 그룹으로 설정한다.
payload
: 사용자가 추가 정보를 위해 전달하는 값