2024-10-31,   이현지


이번 포스트에서는 Spring Framework에서 데이터 유효성 검사 수행 시 사용되는 @Valid, @Validate 에 대해 알아보겠습니다.


@Valid

  • 자바 표준 스펙 (Bean Validation 사양(JRS-303) 에 정의됨)
  • 자바의 표준 유효성 검사 기능을 활용하여 객체의 필드에 대한 유효성을 검증
  • 주로 RequestBody를 검증할 때 사용함
  • 검증 오류 시 MethodArgumentNotValidException 을 발생시킴


유효성 검사 어노테이션

@NotNull

필드가 null이 아닌지 검사
예: @NotNull(message = “이름은 필수입니다.”) private String name;

@NotEmpty

문자열이 null이 아니고 비어 있지 않은지 검사합니다.
예: @NotEmpty(message = “이메일은 필수입니다.”) private String email;

@NotBlank

문자열이 null이 아니고 공백이 아닌지 검사합니다.
예: @NotBlank(message = “비밀번호는 필수입니다.”) private String password;

@Size

문자열, 배열, 컬렉션의 크기를 제한합니다.
예: @Size(min = 3, max = 15, message = “이름은 3자 이상 15자 이하입니다.”) private String name;

@Min

숫자가 지정된 최소값 이상인지 검사합니다.
예: @Min(value = 18, message = “최소 나이는 18세입니다.”) private int age;

@Max

숫자가 지정된 최대값 이하인지 검사합니다.
예: @Max(value = 100, message = “최대 나이는 100세입니다.”) private int age;

@Email

유효한 이메일 주소 형식인지 검사합니다.
예: @Email(message = “유효한 이메일 주소를 입력하세요.”) private String email;

@Pattern

정규 표현식에 따라 문자열이 특정 형식인지 검사합니다.
예: @Pattern(regexp = “^[A-Za-z0-9]{3,15}$”, message = “사용자 이름은 3자 이상 15자 이하의 영문자 또는 숫자여야 합니다.”) private String username;

@Future

날짜가 현재보다 미래인지 검사합니다.
예: @Future(message = “날짜는 미래여야 합니다.”) private LocalDate eventDate;

@Past

날짜가 현재보다 과거인지 검사합니다.
예: @Past(message = “날짜는 과거여야 합니다.”) private LocalDate birthDate;


사용 예시

DTO 내 필드에 유효성 검사 어노테이션 추가

@Getter
@Setter
public class UserDTO {

	@NotNull(message="이름을 입력하세요.")
	private String name;

	@NotBlank(message="아이디를 입력해주세요. 빈 문자열 null, 공백 입력 불가")
	private String id;

	@NotEmpty(message="비밀번호를 입력해주세요. 빈 문자열, null 입력 불가")
	private String pwd;

	@Email(message="유효한  이메일 주소를 입력해주세요.")
	private String emailAddress;
    
}

@RequestBody로 UserDTO를 받을 때 @Valid 어노테이션도 추가해야 유효성 검증이 수행됨

@RestController
pubic class Controller {
	
    @PostMapping("/register/user")
    public ResponseEntity<String> registerUser(
                                            @Valid @RequestBody UserDTO userDTO
                                            ) throws Exception {   	        
        return ResponseEntity.ok("회원 등록 성공");
    }
    
}

ExceptionHandler로 MethodArgumentNotValidException 예외 처리 가능

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseMessage<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
    return ResponseMessage.badRequest("오류 메시지");
}



@Validated

  • 스프링 프레임워크에서 제공하는 어노테이션
  • @Valid가 수행하는 기능 외에 몇 가지 추가적인 기능을 가지고 있음 1) 유효성 검사 그룹을 지정하여 특정 조건에 따라 다르게 유효성 검사 수행 가능 2) Controller 외 다른 계층에서도 유효성 검증 가능
  • 검증 오류 시 ConstraintViolationException 을 발생시킴


Request Body의 경우 @Valid 어노테이션을 추가함으로써 검증할 수 있었습니다.
그렇다면 쿼리 스트링쿼리 파라미터는 어떻게 처리할 수 있을까요?

쿼리 스트링 (Query String)

  • URL의 일부로, ? 이후에 오는 모든 키-값 쌍을 포함하는 문자열
    https://example.com/search?name=Bob&age=20 에서 name=Bob&age=20 를 가리킵니다.

    쿼리 파라미터 (Query Parameter)

  • 쿼리 스트링을 구성하는 개별적인 키-값 쌍 즉, 쿼리 스트링을 구성하는 요소
    https://example.com/search?name=Bob&age=20 에서 name=Bob, age=20 각각의 키 값 쌍이 쿼리스트링입니다.

사용 예시

Controller에서 @Validated를 사용하여 쿼리 파라미터를 검증할 경우

1) @Validated 를 클래스 레벨에 선언 2) 파라미터 앞에 검증 어노테이션 추가 (ex. @NotNull)

@RestController
@validated
public class Controller {
	
    @GetMapping("search/user/{id}")
    public RequestEntity<String> find(@PathVariable @NotNull String id) {    	
        return ResponseEntity.ok("조회 성공");
    }
}

Controller 가 아닌 Service에서 @Validated를 사용할 경우

1) 클래스 레벨에 @Validated를 선언 2) 파라미터에 @Valid 어노테이션을 추가

@Validated
@Service
public class Service {
	
    public void registerUser(@Valid UserDTO userDTO) throws Exception {
    	// 회원 등록 로직
    }
}

예외처리

@ExceptionHandler(ConstraintViolationException.class)
public ResponseMessage<String> handleMethodConstraintViolationException(ConstraintViolationException e) {
    return ResponseMessage.badRequest("");
}


이상으로 포스팅을 마치겠습니다.

Reference

  • https://medium.com/sjk5766/valid-vs-validated-%EC%A0%95%EB%A6%AC-5665043cd64b

업데이트: