Java 표준(JSR-380)에 따른 Bean Validation 어노테이션으로, 요청 객체(DTO)의 필드 값 유효성을 검증할 때 사용한다
javax.validation.Valid
@Valid annotation을 사용해서 요청 DTO에 있는 필드의 유효성을 자동으로 검사한다. 이때 유효성 조건은 DTO 클래스 내 필드에 부여된 annotation에 따라 달라진다.
public class MemberCreateReqDto {
@NotBlank(message = "이름은 필수입니다.")
private String name;
@Email(message = "올바른 이메일 형식이어야 합니다.")
private String email;
}
@PostMapping("/members")
public ResponseEntity<String> createMember(@RequestBody @Valid MemberCreateReqDto dto) {
return ResponseEntity.ok("생성 완료");
}
@PostMapping("/members")
public ResponseEntity<String> createMember(@RequestBody @Valid MemberCreateDto Dto) {
// ...
}
Spring은 유효성 검사에 실패하면 MethodArgumentNotValidException 예외를 자동으로 발생시킨다.
Spring MVC는 기본적으로 예외를 잡아서 400 Bad Request로 응답해주고,예외 내용을 더 구체적으로 커스터마이징하고 싶다면 @ControllerAdvice로 처리 가능하다.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<CommonErrorDto> MethodArgumentNotValidExceptionHandler (MethodArgumentNotValidException e) {
e.printStackTrace();
return new ResponseEntity<>(new CommonErrorDto(HttpStatus.BAD_REQUEST, e.getFieldError().getDefaultMessage()),HttpStatus.BAD_REQUEST);
}
}
Spring Framework에서 제공하는 유효성 검사용 annotation이다. @Valid와 비슷하지만, 그룹별 유효성 검사를 지원하며, Spring AOP를 활용한 메서드 레벨 검증에도 사용된다.
org.springframework.validation.annotation.Validated
public interface CreateGroup {}
public interface UpdateGroup {}
유효성 검사를 상황별로 나누기 위해 위와 같이 그룹 인터페이스를 만들 수 있다.
public class MemberCreateDto {
@NotBlank(groups = CreateGroup.class, message = "이름은 필수입니다.")
private String name;
@Email(groups = {CreateGroup.class, UpdateGroup.class})
private String email;
}
여러 개 그룹도 가능하다.
@PostMapping("/members")
public ResponseEntity<String> createMember(@RequestBody @Validated(CreateGroup.class) MemberCreateDto dto) {
// CreateGroup에 해당하는 제약만 검증
return ResponseEntity.ok("생성 완료");
}
@Service
@Validated
public class MemberService {
public void create(@NotBlank String name) {
// name이 null 또는 빈 문자열이면 ConstraintViolationException이 발생한다.
}
}
@Validated를 클래스에 붙여야 메서드 파라미터 검증이 활성화된다.
@Valid와 비슷한 과정이다.
유효성 검사에 실패할 경우 어디서 발생했는지에 따라 다른 예외가 자동으로 발생된다.
Spring MVC가 기본적으로 예외를 잡아서 400 Bad Request로 응답해주고,예외 내용을 더 구체적으로 커스터마이징하고 싶다면 @ControllerAdvice로 처리 가능하다.
@PostMapping("/members")
public ResponseEntity<String> createMember(@RequestBody @Valid MemberCreateDto dto) {
// 필드 유효성 검사 자동 수행됨
}
Controller 단에서는 @Valid, @Validated 둘 다 사용 가능하고,
그룹 검사 필요 없으면 @Valid만 사용해도 충분하다.
@PostMapping("/members")
public ResponseEntity<?> create(@RequestBody @Validated(CreateGroup.class) MemberCreateDto dto) {
// CreateGroup에 해당하는 조건만 검사됨
}
그룹 검사를 하고 싶다면 @Validated를 사용하고, 그룹 인터페이스를 명시하여 사용하면 된다.
@Service
@Validated
public class MemberService {
public void createByName(@NotBlank String name) {
// name이 null 또는 공백이면 ConstraintViolationException 발생
}
public void createByDto(@Valid MemberCreateDto dto) {
// DTO 내 제약 조건 검사됨 (ex. @NotBlank, @Email)
}
}
Service 단에서는 반드시 클래스 위에 @Validated가 있어야 메서드 파라미터 유효성 검사 가능하다. (createByName 예시)
이때 파라미터에 @Valid를 붙이면 DTO의 필드 제약까지 검사할 수 있다. @Validated가 있을 경우에만 가능하다.(createByDto 예시)
@Service
public class MemberService {
public void createByDto(@Valid MemberCreateDto dto) {
// 아무 일도 일어나지 않는다.
}
}
클래스에 @Validated가 없으면 Spring AOP 기반 검사기가 작동하지 않기 때문에 유효성 검사가 동작하지 않는다.
Bean Validation
@NotNull: 해당 값이 null이 아닌지 검증한다.
@NotEmpty: 해당 값이 null이 아니고, 빈 스트링("") 아닌지 검증한다.(빈칸 허용)
@NotBlank: 해당 값이 null이 아니고, 공백이 아닌지 검증한다.(빈칸 허용 X)
@Size: 해당 값이 주어진 값 사이에 해당하는지 검증한다.(String, Collection, Map, Array에도 적용 가능)
@Min: 해당 값이 주어진 값보다 작지 않은지 검증한다.
@Max: 해당 값이 주어진 값보다 크지 않은지 검증한다.
@Pattern: 해당 값이 주어진 패턴과 일치하는지 검증한다.
https://javaee.github.io/javaee-spec/javadocs/javax/validation/constraints/package-summary.html
MethodArgumentNotValidException 예외 발생 ConstraintViolationException 예외 발생implementation 'org.springframework.boot:spring-boot-starter-validation'
build.gradle에 위와 같이 의존성을 추가해야 한다.
@Service
@Transactional
@RequiredArgsConstructor
public class PlanService{
private final PlanRepository planRepository;
public PlanResDto planCreate(@Valid PlanCreateReqDto planCreateReqDto) {
// ...
}
}
공부하고 나서 다시 프로젝트를 보니 정말 엉뚱한 곳에 @Valid를 추가한 상태였다..

title에 @NotEmpty을 추가한 상태에서 실행해보니깐
잘 생성된다는 오류가 있다.
Service단에 있던 @Valid를 삭제하고
Controller단에 @Valid를 추가했다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/plan")
public class PlanController {
private final PlanService planService;
@PostMapping
public ResponseEntity<CommonResDto> planCreate(@Valid @RequestBody PlanCreateReqDto planCreateReqDto) {
PlanResDto dto = planService.planCreate(planCreateReqDto);
return new ResponseEntity<>(new CommonResDto(HttpStatus.CREATED, "계획생성이 성공적으로 되었습니다.", dto), HttpStatus.CREATED);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PlanCreateReqDto {
@NotEmpty(message = "title은 필수입니다.")
private String title;

짠!
title에 @NotEmpty을 붙여주면
에러가 잘 실행된다.