Programming/Spring Boot

Spring Boot Validation 적용하는 법, @Valid 작동 안되는 이유

Jan92 2021. 8. 16. 22:04

spring-boot-starter-validation

 

 

* @Valid 어노테이션이 작동이 안 한다면?

spring boot 2.3 version 이상부터는 spring-boot-starter-web 의존성 내부에 있던 validation이 사라졌습니다.

때문에 사용하시는 spring boot version이 2.3 이상이라면 validation 의존성을 따로 추가해주셔야 사용할 수 있습니다.

 


Spring Boot Validation 적용하는 방법

- Validation을 사용하기 위해 먼저 의존성을 추가합니다.

 

// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '2.5.2'

Gradle 의존성 추가

 

 

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.5.2</version>
</dependency>

Maven 의존성 추가

 

 

    @Getter
    @Setter
    public static class Signup {

        @NotEmpty(message = "이메일은 필수 입력값입니다.")
        @Pattern(regexp = "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,6}$", message = "이메일 형식에 맞지 않습니다.")
        private String email;

        @NotEmpty(message = "비밀번호는 필수 입력값입니다.")
        @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[~!@#$%^&*()+|=])[A-Za-z\\d~!@#$%^&*()+|=]{8,16}$\n", message = "비밀번호는 8~16자 영문 대 소문자, 숫자, 특수문자를 사용하세요.")
        private String password;
    }

그리고 유효성 검사가 필요한 Request 객체에 Validation 어노테이션을 통해 필요한 유효성 검사를 적용합니다.

 

 

 

유효성 검사에 사용할 수 있는 어노테이션은 다음과 같습니다.

@Null  // null만 혀용합니다.
@NotNull  // null을 허용하지 않습니다. "", " "는 허용합니다.
@NotEmpty  // null, ""을 허용하지 않습니다. " "는 허용합니다.
@NotBlank  // null, "", " " 모두 허용하지 않습니다.

@Email  // 이메일 형식을 검사합니다. 다만 ""의 경우를 통과 시킵니다. @Email 보다 아래 나올 @Patten을 통한 정규식 검사를 더 많이 사용합니다.
@Pattern(regexp = )  // 정규식을 검사할 때 사용됩니다.
@Size(min=, max=)  // 길이를 제한할 때 사용됩니다.

@Max(value = )  // value 이하의 값을 받을 때 사용됩니다.
@Min(value = )  // value 이상의 값을 받을 때 사용됩니다.

@Positive  // 값을 양수로 제한합니다.
@PositiveOrZero  // 값을 양수와 0만 가능하도록 제한합니다.

@Negative  // 값을 음수로 제한합니다.
@NegativeOrZero  // 값을 음수와 0만 가능하도록 제한합니다.

@Future  // 현재보다 미래
@Past  // 현재보다 과거

@AssertFalse  // false 여부, null은 체크하지 않습니다.
@AssertTrue  // true 여부, null은 체크하지 않습니다.

https://docs.jboss.org/hibernate/validator/6.2/reference/en-US/html_single/#section-builtin-constraints

 

Hibernate Validator 6.2.0.Final - Jakarta Bean Validation Reference Implementation: Reference Guide

Validating data is a common task that occurs throughout all application layers, from the presentation to the persistence layer. Often the same validation logic is implemented in each layer which is time consuming and error-prone. To avoid duplication of th

docs.jboss.org

해당 기능들에 대한 더 세부적인 내용은 위 사이트를 참고하시면 됩니다.

 

 


 

    // 회원가입
    @PostMapping("/signup")
    public ResponseEntity<?> signUp(@Validated UserRequestDto.Signup userSignup, Errors errors) {
        // validation check
        if(errors.hasErrors()) {
            return response.invalidFields(common.refineErrors(errors));
        }
        return userService.signUp(userSignup);
    }

컨트롤러에서는 Request 객체 앞에 @Validated 어노테이션을 사용하고, Errors를 통해 유효성 검사 적합 여부를 확인합니다.

 

* 이때 Errors는 반드시 Request 객체 바로 뒤에 위치해야 합니다.

(두 개의 객체에 validation 검사를 한다면, 각각의 객체 뒤에 Errors를 받습니다.)

 

그리고 hasErrors() 메서드를 통해 Request 객체에 설정한 유효성 검사에 문제가 있는지 확인, 있다면  errors를 원하는 데이터 형식으로 가공해서 사용하면 됩니다.

 

 

* @Valid, @Validated 차이

@Valid는 Java 에서 지원해주는 어노테이션입니다. @Validated는 Spring에서 지원해주는 어노테이션입니다.

@Validated는 @Valid의 기능을 포함하기 때문에 @Valid를 적용한 곳이면 @Validated로 변경할 수 있습니다.

@Validated는 유효성을 검토할 그룹을 지정할 수 있는 기능을 추가로 가지고 있습니다. 

 

 

 

(errors 객체를 임시로 가공한 형태입니다. 참고를 위해 첨부합니다.)

    public LinkedList<LinkedHashMap<String, String>> refineErrors(Errors errors) {
        LinkedList errorList = new LinkedList<LinkedHashMap<String, String>>();
        errors.getFieldErrors().forEach(e-> {
            LinkedHashMap<String, String> error = new LinkedHashMap<>();
            error.put(e.getField(), e.getDefaultMessage());
            errorList.push(error);
        });
        return errorList;
    }

errors 가공

getField() 메서드로 해당 필드의 이름, getDefaultMessage() 메서드로 저장된 메세지를 가지고 와서 가공하였습니다.