본문 바로가기

Java/Spring

Custom Constraint

javax 등에 내장된 validation 애너테이션만으로 유효성 검사를 수행할 수 없는 경우 사용자가 직접 정의한 유효성 검사 애너테이션을 생성할 수 있다. 이는 Spring MVC의 @Valid를 통해 유효성 체크가 되는 다른 Validation 애너테이션과 동일한 방식으로 사용될 수 있어 편리하다.

 

애너테이션 인터페이스와 ConstraintValidator 인터페이스를 구현한 Validator를 구현하여 사용한다.

 

예시로 문자열로 된 날짜 정보가 유효한지 검증하는 사용자 정의 유효성 검사 애너테이션을 구현하였다.

 

StringDateFormat.java

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Documented
@Constraint(validatedBy = StringDateFormatValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StringDateFormat {
	String message() default "DateFormat is not valid.";
	
	String format() default "yyyyMMdd";
	
	Class<?>[] groups() default {};
	
    Class<? extends Payload>[] payload() default {};
}

 

코드에 따르면 StringDateFormat은 StringDateFormatValidator에 의해 유효성 검사를 받게 되며, 사용하는 대상은 필드에 해당하며, 런타임 환경에서 작동하는 것임을 알 수 있다. 또한 message와 format을 입력받을 수 있으며, default 값이 설정되어 있어 입력받지 않은 경우 기본값으로 처리됨을 알 수 있다.

 

StringDateFormatValidator.java

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class StringDateFormatValidator implements ConstraintValidator<StringDateFormat, String> {
	private String format;

	@Override
	public void initialize(StringDateFormat constraintAnnotation) {
		format = constraintAnnotation.format();
	}

	@Override
	public boolean isValid(String value, ConstraintValidatorContext context) {
		if (value != null) {
			try {
				LocalDate.parse(value, DateTimeFormatter.ofPattern(format));
			} catch (Exception e) {
				return false;
			}
		}
		return true;
	}
}

 

StringDateFormat의 유효성을 검사하는 StringDateFormatValidator는 isValid 메소드에서 애너테이션의 format을 사용하여 LocalDate의 포매팅을 시도함으로써 정상적인 형태의 날짜 문자열이 넘어왔는가를 확인하고 있다. 만일 정상적으로 처리되지 않았거나 그외 기타 Exception이 발생한 경우는 비정상 처리된 것으로 보고 false를 반환하고 있으며, 그렇지 않은 경우는 true를 반환하도록 되어 있다.

 

Controller 및 Controller 파라미터로 사용한 Request에서는 다음과 같이 사용이 가능하다.

 

MemberCreateRequest.java

import javax.validation.constraints.NotEmpty;
import com.demo.spring.validation.StringDateFormat;
import lombok.Setter;

@Getter
@Setter
public class MemberCreateRequest {
	@NotEmpty(message = "memberName is mandatory value.")
	private String memberName;
    
	@NotEmpty(message = "email is mandatory value.")
	private String email;
    
	@StringDateFormat // message와 format을 입력하지 않으면 기본값을 사용한다.
	private String birthDay;
}

 

MemberContorller.java

@Slf4j
@RestController
@RequiredArgsConstructor
public class MemberContorller {
	private final MemberService memberService;
	
	@PostMapping("/member")
	public String create(@Valid @RequestBody MemberCreateRequest request, Errors errors) {
		if (errors.getErrorCount() > 0) {
			final String errorMessages = errors.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(", "));
			log.warn(errorMessages);
			return errorMessages;
		}
		return memberService.create(request);
	}
}

 

'Java > Spring' 카테고리의 다른 글

Spring AOP: AOP Concepts 1  (0) 2020.07.30
Spring AOP: Aspect Oriented Programming with Spring  (0) 2020.07.15
@Transactional Attribute  (0) 2020.06.29
Spring StopWatch  (0) 2020.04.28
Spring에서 Annotation으로 정의된 Validation 확인  (0) 2020.04.12