개발공부/Spring Boot

[Spring Boot] Custom Validation

환타몬 2022. 2. 16. 09:24

Custom Validation

 

1. AssertTure / False와 같은 method 지정을 통해서 Custom Logic 적용 가능

2. ConstraintValidator를 적용하여 재사용이 가능한 Custom Logic 적용 가능

 

@AssertTrue 를 통한 날짜 Validation

- User.java

@AssertTrue
public boolean reqYearMonthValidation(){
    try{
        LocalDate localDate = LocalDate.parse(getReqYearMonth()+"01",
                DateTimeFormatter.ofPattern("yyyyMMdd"));

    }catch (Exception e){
        return false;
    }
    return true;
}

 

Talend API를 통해서 확인을 해본 결과, 잘 실행이 되지 않음을 확인할 수 있었다.

그렇기에 우리는 해당 메서드가 실행이 되는지를 확인하기 위하여 호출이 되는지를 확인해본다.

 

그래도 되지 않아서, 확인해본 결과 boolean 값을 받기 위해서는 메서드 명이 is로 시작되어야 한다는 것을 확인했고, 메서드의 명칭을 재명명 후 다시 실행했을 때는 assert true call이 되는 것을 확인할 수 있었다.

 

마찬가지로 AssertTure annotation 또한 message 값을 받을 수 있다.

@AssertTrue(message = "yyyyMM의 형식에 맞지 않습니다.")
public boolean isReqYearMonthValidation(){
    System.out.println("Assert True Call");
    try{
        LocalDate localDate = LocalDate.parse(getReqYearMonth()+"01",
                DateTimeFormatter.ofPattern("yyyyMMdd"));

    }catch (Exception e){
        return false;
    }
    return true;
}

 

 

재활용이 가능한 Annotation 만들기 

 

 

-YearMonth.java

package com.example.validation.annotation;


import com.example.validation.validator.YearMonthValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Constraint(validatedBy = {YearMonthValidator.class}) // YearMonthValidator class를 통해서 검사가 이루어짐
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {

    String message() default "yyyyMM 형식에 맞지 않습니다.";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    String pattern() default "yyyyMMdd";
}

 

@Constraint( validatedBy = { } ) 안쪽에 class 명을 올바르게 설정해주어야 한다.

 

 

- ApiController.java

package com.example.validation.controller;

import com.example.validation.dto.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequestMapping("/api")
public class ApiController {

    @PostMapping("/user") // @Valid annotation이 객체 안에서 @Email과 같은 Annotation을 검사하게 됨.
    public Object user(@Valid @RequestBody User user, BindingResult bindingResult){

        if(bindingResult.hasErrors()){
            StringBuilder sb = new StringBuilder();
            bindingResult.getAllErrors().forEach(objectError -> {
                FieldError field = (FieldError)objectError;
                String message = objectError.getDefaultMessage();

                System.out.println("field: "+field.getField());
                System.out.println(message);

                sb.append("field : "+field.getField());
                sb.append("message :" +message );
            });

            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString());
        }
        System.out.println(user);
        //logic
        return user;
    }


}

 

 

 

- YearMonthValidator

package com.example.validation.validator;

import com.example.validation.annotation.YearMonth;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraints.AssertTrue;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class YearMonthValidator implements ConstraintValidator<YearMonth, String > {

    private String pattern;

    @Override
    public void initialize(YearMonth constraintAnnotation) {
        this.pattern = constraintAnnotation.pattern();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        //yyyyMM // default가 yyyyMMdd 인데, 우리는 Year,Month까지만 받을 것이기 때문에 01를 붙이는 것

            try{
                LocalDate localDate = LocalDate.parse(value+"01",
                        DateTimeFormatter.ofPattern(this.pattern));

            }catch (Exception e){
                return false;
            }

        return false;
    }
}