@Valid 어노테이션은 스프링에서 객체의 유효성 검증을 수행하기 위해 사용되는 어노테이션입니다. 이는 Java Bean Validation API의 일부로, 스프링과 통합되어 사용됩니다.
MethodArgumentNotValidException을 발생시킵니다.기본적인 사용 방법은 다음과 같습니다:
import javax.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@PostMapping("/users")
public User createUser(@Valid @RequestBody User user) {
// user 객체는 이미 유효성 검증을 통과한 상태
return userService.createUser(user);
}
}
public class User {
@NotNull(message = "Name cannot be null")
@Size(min = 2, max = 30, message = "Name must be between 2 and 30 characters")
private String name;
@Email(message = "Email should be valid")
private String email;
// getters and setters
}
@NotNull, @Size, @Min, @Max, @Email 등 다양한 내장 검증 어노테이션을 제공합니다.public class Order {
@Valid
private List<OrderItem> items;
@Valid
private Address shippingAddress;
// other fields, getters and setters
}
public class User {
@NotNull(groups = {BasicInfo.class, AdvancedInfo.class})
private String name;
@NotNull(groups = AdvancedInfo.class)
private String email;
// getters and setters
}
@PostMapping("/users/basic")
public User createBasicUser(@Validated(BasicInfo.class) @RequestBody User user) {
// ...
}
@PostMapping("/users/advanced")
public User createAdvancedUser(@Validated(AdvancedInfo.class) @RequestBody User user) {
// ...
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = AdultValidator.class)
public @interface Adult {
String message() default "Must be an adult";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class AdultValidator implements ConstraintValidator<Adult, LocalDate> {
@Override
public boolean isValid(LocalDate value, ConstraintValidatorContext context) {
return value != null && ChronoUnit.YEARS.between(value, LocalDate.now()) >= 18;
}
}
@Valid 검증 실패 시 발생하는 MethodArgumentNotValidException을 처리하는 방법:
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, List<String>>> handleValidationErrors(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult().getFieldErrors()
.stream().map(FieldError::getDefaultMessage).collect(Collectors.toList());
return new ResponseEntity<>(getErrorsMap(errors), new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
private Map<String, List<String>> getErrorsMap(List<String> errors) {
Map<String, List<String>> errorResponse = new HashMap<>();
errorResponse.put("errors", errors);
return errorResponse;
}
}
@Valid 어노테이션이 적용된 컨트롤러를 테스트할 때:
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
void whenInvalidInput_thenReturns400() throws Exception {
User user = new User(null, "invalid-email");
mockMvc.perform(post("/users")
.contentType("application/json")
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isBadRequest());
}
}
@Valid 어노테이션은 스프링 애플리케이션에서 입력 데이터의 유효성을 보장하는 강력한 도구입니다. 이를 통해 애플리케이션의 안정성을 높이고, 잘못된 데이터로 인한 오류를 사전에 방지할 수 있습니다. 선언적 방식으로 검증 규칙을 정의할 수 있어 코드의 가독성과 유지보수성을 향상시키며, 복잡한 객체 구조에 대해서도 효과적인 검증이 가능합니다. 다만, 성능과 복잡성을 고려하여 적절히 사용해야 하며, 비즈니스 로직에 특화된 복잡한 검증은 별도의 로직으로 분리하는 것이 좋습니다.
@RequestBody
@ControllerAdvice
@RestController
@ExceptionHandler
@Validated