1. Spring MVC 유효성 검증
- Spring MVC는 사용자의 입력 데이터를 검증하는 강력한 기능을 제공합니다.
- 유효성 검증은 API 안정성과 보안을 위해 필수적인 요소입니다.
- Jakarta Bean Validation과 통합되어 코드를 간결하게 유지할 수 있습니다.
2. 두 가지 유효성 검증 방식
2.1 객체 검증: @Valid/@Validated 사용하기
- 주로 JSON 요청 본문이나 폼 데이터와 같은 객체를 검증할 때 사용합니다.
- 객체 파라미터 앞에
@Valid
또는@Validated
를 붙이면 검증이 작동합니다. - 유효성 검증에 실패하면
MethodArgumentNotValidException
예외가 발생합니다.
객체 검증 예시 코드
// User 클래스 정의
public class User {
@NotBlank(message = "이름은 필수입니다")
private String name;
@Email(message = "올바른 이메일 형식이 아닙니다")
private String email;
@Min(value = 1, message = "나이는 1세 이상이어야 합니다")
private int age;
// getter, setter 생략
}
// 컨트롤러에서 유효성 검증 적용
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
// 유효성 검증을 통과하면 이 코드가 실행됩니다
return ResponseEntity.ok(userService.saveUser(user));
}
2.2 파라미터 검증: 제약 조건 직접 사용하기
- URL 경로 변수, 쿼리 파라미터, 헤더와 같은 개별 파라미터를 검증할 때 사용합니다.
- 파라미터 앞에
@Min
,@NotBlank
등의 제약 조건을 직접 붙여 사용합니다. - 유효성 검증에 실패하면
HandlerMethodValidationException
예외가 발생합니다.
파라미터 검증 예시 코드
@GetMapping("/products/{id}")
public ResponseEntity<Product> getProduct(
@PathVariable @Min(value = 1, message = "ID는 양수여야 합니다") Long id) {
// 유효성 검증을 통과하면 이 코드가 실행됩니다
return ResponseEntity.ok(productService.findById(id));
}
중요
@Valid
는 객체 내부의 중첩된 제약 조건을 검증하기 위한 것이고, @Min
, @NotBlank
등은 직접적인 제약 조건입니다. 두 가지 방식을 상황에 맞게 사용하세요.
3. 유효성 검증 예외 처리하기
- 유효성 검증 실패 시 발생하는 두 가지 예외를 적절히 처리해야 합니다.
MethodArgumentNotValidException
: 객체 검증 실패 시 발생HandlerMethodValidationException
: 파라미터 검증 실패 시 발생
3.1 기본적인 예외 처리 예시
@RestControllerAdvice
public class ValidationExceptionHandler {
// 객체 검증 실패 처리
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidationErrors(MethodArgumentNotValidException ex) {
Map<String, Object> response = new HashMap<>();
List<String> errors = ex.getBindingResult().getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
response.put("timestamp", LocalDateTime.now());
response.put("status", HttpStatus.BAD_REQUEST.value());
response.put("errors", errors);
return ResponseEntity.badRequest().body(response);
}
// 파라미터 검증 실패 처리
@ExceptionHandler(HandlerMethodValidationException.class)
public ResponseEntity<Map<String, Object>> handleMethodValidationErrors(HandlerMethodValidationException ex) {
Map<String, Object> response = new HashMap<>();
List<String> errors = new ArrayList<>();
ex.getAllValidationResults().forEach(result -> {
errors.add(result.getMethodParameter().getParameterName() + ": " +
result.getResolvableErrors().get(0).getDefaultMessage());
});
response.put("timestamp", LocalDateTime.now());
response.put("status", HttpStatus.BAD_REQUEST.value());
response.put("errors", errors);
return ResponseEntity.badRequest().body(response);
}
}