du.study기록공간

Customizing Validation Tasks 본문

스프링

Customizing Validation Tasks

du.study 2019. 11. 16. 16:26
728x90

저번 포스팅에서 @Validated and BindingResult에 대해서 기록한적이 있습니다.

https://duooo-story.tistory.com/11

 

[Spring MVC] @Validated(@Valid) and BindingResult

이번에는 모델의 검증작업, 모델 바인딩 과정에서 발생되는 에러의 결과를 저장해주는 BindingResult에 대해서 기록하려 합니다. 만약 이런 해당사항의 컨트롤러,도메인이 있다고 가정하겠습니다. @GetMapping("/s..

duooo-story.tistory.com

개발을 하다보면  @Validated에서 기본적으로 제공해주는 기능 외에도 커스텀하여 사용해야하는 경우가 많습니다. 

이번에는 Validate 검증작업을 직접 구현하여 사용하는 방법에 기록해보려 합니다.

 

먼저 크게 Custom Validation 방법으로 3가지를 기록해보려 합니다.

1. @InitBinder에 Validator 인터페이스를 구현한 클레스 등록

2. Validator를 빈으로 등록하여 사용.

3. ConstraintValidator<> 구현한 클레스 사용하기.

 

1. @InitBinder 사용하기

1. 먼저 InitBinder 를 사용하여 Validator를 구현하는 법을 기록하겠습니다.

먼저 Controller에 @InitBinder를 구현 해 줍니다.

@Controller
@RequestMapping("/valid")
public class ValidateTestController {

    @InitBinder
    public void initValidator(WebDataBinder webDataBinder) {
        webDataBinder.addValidators(new ClassInfoValidator());
    }

    @GetMapping("/binder")
    @ResponseBody
    public ClassInfo binderTest(@Validated ClassInfo classInfo , BindingResult bindingResult) {
        // 보통 Service 라면 classInfo 값을 받아 뭔가를 조회하겠지만..
        if(bindingResult.hasErrors()){
            return new ClassInfo(ErrorType.INVALID_VALUE);
        }
        return classInfo;
    }
}



public class ClassInfoValidator implements Validator {
    @Override
    public boolean supports(Class<?> aClass) {
        return ClassInfo.class.isAssignableFrom(aClass);
    }
    @Override
    public void validate(Object o, Errors errors) {
        ClassInfo classinfo = (ClassInfo) o;

        if(classinfo.getClassId() < 0){
            errors.reject("id");
            errors.rejectValue("classId","Invalid id","classId must be greater than 0 ");
        }
    }
}

@InitBinder에서 사용되는 WebDataBinder에는 Validator외에도 많은 기능을 제공해주고 있어서 추후에 기록하려 합니다. 해당 코드를 통해서 컨트롤러 에서는 현재는 ClassInfo 값을 리턴했으나, BindResult의 값을 front에서 받아 처리하거나, 직접 BindResult의 값을 꺼내어 결과를 내려줄 수 있습니다.

 

2. Validator를 빈으로 등록하여 사용.

@Component
public class ClassInfoValidator{
    public ClassInfo validate(ClassInfo info) {
        if(classinfo.getClassId() < 0){
        	//throws로 던지고 에러를 캐치해잡는게 더 좋을것같다.
            return new ClassInfo(ErrorType.INVALID_VALUE);
        }
        return classInfo;
    }
}


@Autowired
ClassInfoValidator classInfoValidator;

@GetMapping("/binder")
@ResponseBody
public ClassInfo binderTest(@Validated ClassInfo classInfo , BindingResult bindingResult) {
	//validator를 먼저 쓰겠지만 예시니까..
    return classInfoValidator.validate(classInfo);
}

직접 빈으로 등록하여, 관련 Validator에서 throws 던져서 다른곳에서 캐치하거나, 관련 값을 가공하는 등의 방법으로 Validator 작업을 진행할 수 있습니다.

 

 

3. ConstraintValidator<> 구현한 클레스 사용하기.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
@Constraint(validatedBy = {ClassInfoConstraintValidator.class})
public @interface ClassInfoCheck {
    String message() default "Invalid Parameter";
    Class[] groups() default {};
    Class[] payload() default {};
}

public class ClassInfoConstraintValidator implements ConstraintValidator<ClassInfoCheck,String> {
    @Override
    public void initialize(ClassInfoCheck constraintAnnotation) {}
    @Override
    public boolean isValid(String name, ConstraintValidatorContext constraintValidatorContext) {
        if(name.equals("test")){
            return false;
        }
        return true;
    }
}


// Controller에서 받는 도메인 

@ClassInfoCheck
private String name;

 

먼저 Valid를 적용할 @Interface를 구현해야합니다. 구현시 @Constraint를 선언하게 되는데, message(), groups(), payload() 를 같이 구현해줘야합니다.

미구현시 에러, 참고자료는 https://docs.oracle.com/javaee/7/api/javax/validation/Constraint.html여기서 확인할 수 있습니다.

 

그 다음, ConstraintValidator를 구현한 클레스를 생성하여, valid 작업을 구현하고,Controller에서 받아오는 도메인에서  @Valid 작업을 진행할 Field에 붙여주면, 에러 결과를 BindResult에 받을 수 있습니다. 만약 에러메시지를 따로 지정하려면  @ClassInfoCheck(message = "error") 해당 방법으로 붙여주면 defaultMessage 대신 적용이 됩니다.

 

 

728x90
Comments