spring

[Spring] 데이터의 유효성 검사 @Valid 어노테이션

허몽구 2023. 4. 19. 01:58

스프링 프레임워크에서 @Valid 어노테이션은 데이터의 유효성을 검사하기 위해 사용된다.

주로 API 요청의 입력값을 검증하는 데 적용한다.

@Valid 어노테이션은 자바 표준 검증 API인 JSR-303/JSR-380의 일부로, 이 어노테이션을 사용하기 위해서는 다음과 같은 라이브러리를 추가해야 한다. 

implementation 'org.springframework.boot:spring-boot-starter-validation'

@Valid 어노테이션은 주로 컨트롤러의 메소드 파라미터 앞에 사용되어 스프링이 해당 파라미터 객체의 유효성을 검사하고, 유효하지 않을 경우 검증 오류를 처리할 수 있다. 

 

@Valid 어노테이션의 장점은 다음과 같다.

  1. 데이터 유효성 검사 : 데이터 형식, null 여부, 필드의 길이 등 사용자로부터 받은 입력값이나 외부로부터 받은 데이터가 정상적인 조건과 형식을 갖추고 있는지 검사할 수 있다. 이를 통해 잘못된 데이터가 입력되는 것을 방지하고, 데이터의 일관성과 정확성을 유지할 수 있다.
  2. 코드 재사용과 중복 제거 : 데이터 유효성 검사를 수행하는 코드를 일일이 작성하지 않아도 된다. 대신 검증 규칙을 어노테이션으로 지정하고, 스프링이 자동으로 검증을 수행한다. 이를 통해 코드의 재사용성을 높이고, 중복 코드를 제거할 수 있다.
  3. 애플리케이션의 안정성과 신뢰성 강화 : 데이터의 유효성을 검사함으로서 잘못된 데이터가 시스템에 들어오는 것을 방지한다. 이는 애플리케이션의 안정성과 신뢰성을 높이는 데 도움이 된다. 잘못된 데이터가 처리되는 것을 방지함으로써 예상치 못한 동작, 오류, 보안 취약점 등을 예방할 수 있다.

그렇다면, 유효성 검사란 정확히 무엇일까?

유효성 검사(Validation)란, 데이터가 주어진 규칙이나 조건을 충족하는지 확인하는 과정이다.

데이터의 유효성을 검사함으로써 잘못된 데이터나 부적절한 입력을 방지하고, 시스템의 안정성을 높일 수 있다. 

데이터의 유효성을 확인하는 규칙이나 조건을 정의하고 이를 기준으로 데이터를 검사하는 과정으로 이뤄지는데, 일반적으로 데이터는 특정 형식, 범위, 제약 조건 등에 맞아야 유효하다고 판단한다.

예시로 이메일 주소를 입력하는 경우, 유효성 검사는 이메일 주소 형식에 맞는지 확인하는 것이다. 이때 이메일 주소 형식에는 특정 패턴( 1234@example.com)이 주어지며 이를 충족하지 않는 경우 유효하지 않은 이메일 주소로 판단한다.

그렇기에 유효성 검사는 사용자의 입력값을 검증하는 데 많이 사용된다.

사용자가 폼을 작성하거나 API 요청을 보낼 때, 입력값이 정확한 형식이거나 특정한 조건을 만족하는지 확인하기 위해 유효성 검사가 수행된다.

이를 통해 잘못된 데이터가 시스템에 저장되는 것을 방지하고, 사용자에게 오류 메시지를 표시하여 올바른 데이터 입력을 유도할 수 있다.

 

valid 어노테이션은 다음과 같은 종류가 있다.

Anotation 제약조건
@NotNull Null 불가
@Null Null만 입력 가능
@NotEmpty Null, 빈 문자열 불가
@NotBlank Null, 빈 문자열, 스페이스만 있는 문자열 불가
@Size(min=,max=) 문자열, 배열등의 크기가 만족하는가?
@Pattern(regex=) 정규식을 만족하는가?
@Max(숫자) 지정 값 이하인가?
@Min(숫자) 지정 값 이상인가
@Future 현재 보다 미래인가?
@Past 현재 보다 과거인가?
@Positive 양수만 가능
@PositiveOrZero 양수와 0만 가능
@Negative 음수만 가능
@NegativeOrZero 음수와 0만 가능
@Email 이메일 형식만 가능
@Digits(integer=, fraction = ) 대상 수가 지정된 정수와 소수 자리 수 보다 작은가?
@DecimalMax(value=)  지정된 값(실수) 이하인가?
@DecimalMin(value=) 지정된 값(실수) 이상인가?
@AssertFalse false 인가?
@AssertTrue true 인가?

 

유효성 검사가 무엇인지도 알았고, @Valid 어노테이션에 대해서도 알아봤다.

그렇다면 실제 코드는 어떻게 작성해야 할까? 다음과 같은 간단한 클래스가 있다.

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

public class User {
    @NotNull(message = "이름을 적어주세요")
    @Size(min = 2, max = 10)
    private String name;

    @NotNull
    @Pattern(regexp = "[a-zA-z0-9]+@[a-zA-z]+[.]+[a-zA-z.]+")
    private String email;
}

User 클래스의 name필드는 @NotNull로 null값이 들어올 수 없게 지정했고, 크기는 2~10으로 지정해줬다.

email필드는 @NotNull로 null값이 들어올 수 없게 지정했고, @Pattern을 사용하여 해당 필드의 값이 정규식 패턴과 일치해야 함을 나타냈다. 

 

위와 같이 클래스를 지정해주면, 컨트롤러는 어떻게 작성해야 할까?

import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;

@RestController
public class UserController {
    @PostMapping("/users")
    public String createUser(@Valid @RequestBody User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "error"; // 유효성 검증 오류
        }
        return "success"; // User 생성
    }
}

createUser라는 메소드의 파라미터로 @Valid와 @RequestBody, BindingResult를 받아줬다.

@Valid는 위에서 계속 설명했듯, User 객체에 대한 유효성 검사를 수행한다.

@RequestBody는 Post요청의 본문에 있는 데이터를 User 객체로 변환한다. 

BindingResult는 유효성 검사 결과를 저장하고 관리하는 인터페이스이다. 컨트롤러 메소드에서 @Valid 어노테이션과 함께 사용되며, 유효성 검사 결과를 받아오는 역할을 한다.

BindingResult를 활용하면 유효성 검사 결과를 확인하고 오류 처리   있다예를 들어오류 메시지를 사용자에게 표시하거나 로그에 오류 정보를 기록하는 등의 작업을 수행할  있다.

 

BindingResult 인터페이스의 주요 메소드와 기능은 다음과 같다.

1) hasErrors() : 유효성 검사 결과에 오류가 있는지 여부를 확인한다. 오류가 있다면 true, 없다면 false를 반환한다.

2) getFieldErrors() : 필드별 검증 오류 정보를 가져온다. 각 필드별로 발생한 오류에 대한 정보를 리스트로 반환한다.

3) getGlobalErrors() : 전역 검증 오류 정보를 가져온다. 필드와 관련되지 않고 전체 객체에 대한 검증 오류에 대한 정보를 리스트로 반환한다.

4) addError(FieldError error) : 검증 오류를 직접 추가한다. 이는 프로그래밍적으로 검증 오류를 생성하고 처리할 때 유용하다.

 

이처럼 유효성 검사에서 오류가 발생하면 bindingResult.hasErrors()를 통해 검증 오류가 있는지 확인한다. 오류가 있다면 "error"을 반환하고 오류가 없다면 "success"를 반환하여 User를 생성한다.

 

만약 컨트롤러가 아닌 다른 계층에서도 유효성 검사를 하고 싶으면 어떻게 사용해아 할까?

import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;

@Service
@Validated // 컨트롤러가 아닌 곳에서는 @Validated 사용
public class UserService {
    public void createUser(@Valid User user){
        
    }
}

위의 예시처럼 @Validated를 사용하면 된다. AOP 기반으로 메소드의 요청을 가로채 유효성 검사를 진행한다.  

 

마지막으로 @Valid와 @Validated의 차이점을 알아보자.

@Valid @Validated
JSR-303 자바 표준 스펙 스프링 프레임워크 제공
컨트롤러만 유효성 검사 가능 컨트롤러 이외의 계층도 유효성 검사 가능
유효성 검증에 실패할 경우
MethodArgumentNotValidException이 발생
유효성 검증에 실패할 경우
ConstraintViolationException이 발생