Bean Validation은 Java에서 객체(Bean)의 데이터가 올바른 값인지 검증하는 표준 방식(JSR-303, JSR-380)입니다.
스프링에서는 이를 활용하여 입력 데이터(예: DTO) 검증을 간편하게 수행할 수 있도록 지원합니다.
사용자가 API에 잘못된 데이터를 보내면 백엔드에서 직접 검증하는 로직을 구현해야 합니다.
하지만, 매번 수동으로 검증 로직을 작성하는 것은 비효율적입니다.
👉 Bean Validation을 사용하면 간단한 애너테이션만으로 자동 검증이 가능합니다.
1) 의존성 추가 (스프링 부트는 기본 포함됨)
Spring Boot에는 기본적으로 spring-boot-starter-validation이 포함되어 있어서, 별도의 추가 설치 없이 사용 가능합니다.
만약 직접 추가해야 한다면, Maven에서는 다음과 같이 설정합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2) 유효성 검사 어노테이션 적용
DTO에 검증 애너테이션 추가
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Data
public class UserDto {
@NotBlank(message = "이름을 입력해주세요.")
private String name;
@Email(message = "올바른 이메일 형식이어야 합니다.")
private String email;
@Size(min = 6, message = "비밀번호는 최소 6자 이상이어야 합니다.")
private String password;
}
@NotBlank, @Email, @Size 등의 애너테이션을 추가하면 자동으로 해당 필드를 검증합니다.
3) 컨트롤러에서 검증 적용
컨트롤러에서 @Valid 또는 @Validated를 사용하여 DTO 검증
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping("/signup")
public ResponseEntity<String> signUp(@Valid @RequestBody UserDto userDto) {
return ResponseEntity.ok("회원가입 성공");
}
}
@Valid를 사용하면 자동으로 DTO의 필드 유효성을 검증하고, 오류가 발생하면 예외(MethodArgumentNotValidException)가 발생합니다.
4) 에러 메시지 커스터마이징
@NotBlank(message = "이름을 입력해주세요.")
private String name;
NotBlank.userDto.name = 이름을 입력해주세요.
Email.userDto.email = 올바른 이메일 형식이어야 합니다.
Size.userDto.password = 비밀번호는 최소 6자 이상이어야 합니다.
@Configuration
public class MessageConfig {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
이렇게 하면 messages.properties 파일에서 에러 메시지를 자동으로 불러올 수 있음!
@NotNull null을 허용하지 않음
@NotBlank 공백을 허용하지 않음 ("", " ", null 불가)
@NotEmpty 빈 문자열 허용하지 않음 ("" 불가, null 가능)
@Size(min, max) 문자열 또는 컬렉션 크기 제한
@Email 이메일 형식 체크
@Pattern(regex) 정규식 패턴 일치 여부 검사
@Min(value) 숫자의 최소값 설정
@Max(value) 숫자의 최대값 설정
@Positive 양수만 허용
@Negative 음수만 허용
@Past 과거 날짜만 허용
@Future 미래 날짜만 허용
1) 검증 그룹 인터페이스 생성
// 저장용 검증 그룹
public interface SaveCheck {}
// 수정용 검증 그룹
public interface UpdateCheck {}
인터페이스만 선언하고, 내용은 비워 둡니다. → 이름만으로 검증 그룹을 구분하는 용도!
2) DTO에 groups 적용
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Range;
import lombok.Data;
@Data
public class ProductRequestDtoV2 {
// 저장 & 수정 시 `name` 필드는 필수
@NotBlank(groups = {SaveCheck.class, UpdateCheck.class})
private String name;
// `price`는 모든 상황에서 `null`이 아니어야 함
@NotNull
// 저장할 때만 `price` 범위 제한 적용
@Range(min = 10, max = 10000, groups = SaveCheck.class)
private Integer price;
// `count`는 항상 검증 (별도 groups 설정 없이 기본값)
@NotNull
@Range(min = 1, max = 999)
private Integer count;
}
@NotBlank(groups = {SaveCheck.class, UpdateCheck.class})
→ 저장, 수정할 때 모두 name 필드를 검증
@Range(min = 10, max = 10000, groups = SaveCheck.class)
→ 저장할 때만(POST) price 범위(10~10000) 검증
@NotNull과 @Range(min = 1, max = 999)
→ 모든 경우에 count 필드 검증
3) 컨트롤러에서 @Validated(그룹) 적용
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
public class ProductController {
// ✅ 저장 API (SaveCheck 그룹 적용)
@PostMapping("/v2/product")
public String save(
@Validated(SaveCheck.class) @ModelAttribute ProductRequestDtoV2 requestDtoV2
) {
log.info("생성 API가 호출되었습니다.");
return "상품 생성이 완료되었습니다.";
}
// ✅ 수정 API (UpdateCheck 그룹 적용)
@PutMapping("/v2/product/{id}")
public String update(
@PathVariable Long id,
@Validated(UpdateCheck.class) @ModelAttribute ProductRequestDtoV2 requestDtoV2
) {
log.info("수정 API가 호출되었습니다.");
return "상품 수정이 완료되었습니다.";
}
}
설정된 그룹에 따라 검증이 다르게 적용됨!
저장 요청(POST)
name 필수 (@NotBlank)
price는 10~10000 사이 값이어야 함 (@Range)
count 필수 (@NotNull)
수정 요청(PUT)
name 필수 (@NotBlank)
price는 검증 안 함 (@Range 미적용)