본문 바로가기

Backend/Spring

Spring Boot Validation Custom (ConstraintValidator)

스프링 부트에서 Validation 체크할 때 커스텀한 어노테이션을 만들어서 사용해볼게요.

자주 사용할만한 체크를 미리 만들어두면 좋아요.

 

예제에서는 성별을 Validation 체크하는 어노테이션을 만들어볼거에요.

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
</dependencies>

build.gradle

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-validation'
}

Gender @interface

GenderValidator 클래스를 추가하고 ContraintValidator의 구현 로직을 작성해주면 됩니다.

gender라는 필드에 @Gender 어노테이션만 추가해서 체크해볼거에요.

DB에 성별 컬럼이 char(1) 타입이고 남자:M, 여자:W로 저장하고 있어요.

M이나 W 외의 필드는 들어오면 안되겠죠?

특정 경우에 성별을 입력안해도 될 수 있으므로 @Gender만 추가하면 null일 경우는 허용할거에요.

 

@Documented
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = GenderValidator.class)
public @interface Gender {

	String message() default "성별이 유효하지 않습니다. 남자:M,여자:W";

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

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

}

class GenderValidator implements ConstraintValidator<Gender, Character> {

	@Override
	public boolean isValid(Character value, ConstraintValidatorContext context) {
		if (value == null) {
			return true;
		} else {
			if (value == 'M' || value == 'W') {
				return true;
			} else {
				return false;
			}
		}
	}

}

Model

null 체크가 필요하다면 @NotNull을 별도로 추가해주면 됩니다.

 

@Getter
@Setter
public class SignupParam {

	@NotNull
	@Email
	String email;

	@NotNull
	String password;

	@NotNull
	String nickname;

	@NotNull
	@Gender
	Character gender;

}

Controller

컨트롤러에서 이렇게 쓰고 있어요.

 

@PostMapping("signup")
public SignupResult signup(@RequestBody @Valid SignupParam param) {
	// TODO
}

Controller 테스트

gender를 A로 설정해서 보내고 컨트롤러에서 체크할 경우 아래 오류를 확인할 수 있습니다.

 

{
    "timestamp": "2019-11-19T08:24:14.163+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "Gender.signupParam.gender",
                "Gender.gender",
                "Gender.java.lang.Character",
                "Gender"
            ],
            "arguments": [
                {
                    "codes": [
                        "signupParam.gender",
                        "gender"
                    ],
                    "arguments": null,
                    "defaultMessage": "gender",
                    "code": "gender"
                }
            ],
            "defaultMessage": "성별이 유효하지 않습니다. 남자:M,여자:W",
            "objectName": "signupParam",
            "field": "gender",
            "rejectedValue": "A",
            "bindingFailure": false,
            "code": "Gender"
        }
    ],
    "message": "Validation failed for object='signupParam'. Error count: 1",
    "path": "/user/signup"
}

Entity 테스트

어노테이션을 Entity의 필드에 붙여서 Validation 체크를 할 수 있어요.

그럼 아래와 같은 오류를 받을 수 있습니다.

 

{
    "timestamp": "2019-11-19T08:25:47.789+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Validation failed for classes [com.example.demo.jpa.entity.User] during persist time for groups [javax.validation.groups.Default, ]\nList of constraint violations:[\n\tConstraintViolationImpl{interpolatedMessage='성별이 유효하지 않습니다. 남자:M,여자:W', propertyPath=gender, rootBeanClass=class com.example.demo.jpa.entity.User, messageTemplate='성별이 유효하지 않습니다. 남자:M,여자:W'}\n]",
    "path": "/user/signup"
}