Bean Validation이란?

김현정·2025년 3월 27일
0

Bean Validation이란?

Bean Validation은 Java에서 객체(Bean)의 데이터가 올바른 값인지 검증하는 표준 방식(JSR-303, JSR-380)입니다.
스프링에서는 이를 활용하여 입력 데이터(예: DTO) 검증을 간편하게 수행할 수 있도록 지원합니다.

Bean Validation이 필요한 이유

사용자가 API에 잘못된 데이터를 보내면 백엔드에서 직접 검증하는 로직을 구현해야 합니다.
하지만, 매번 수동으로 검증 로직을 작성하는 것은 비효율적입니다.
👉 Bean Validation을 사용하면 간단한 애너테이션만으로 자동 검증이 가능합니다.

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) 에러 메시지 커스터마이징

  • 어노테이션의 message 속성 사용 (가장 간단)
@NotBlank(message = "이름을 입력해주세요.")
private String name;
  • messages.properties 파일에서 메시지 관리 (국제화 지원 가능)
    src/main/resources/messages.properties 파일 생성
NotBlank.userDto.name = 이름을 입력해주세요.
Email.userDto.email = 올바른 이메일 형식이어야 합니다.
Size.userDto.password = 비밀번호는 최소 6자 이상이어야 합니다.
  • 스프링 설정에서 MessageSource를 등록하여 사용 가능
@Configuration
public class MessageConfig {

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
}

이렇게 하면 messages.properties 파일에서 에러 메시지를 자동으로 불러올 수 있음!

Bean Validation에서 자주 사용하는 애너테이션

@NotNull null을 허용하지 않음
@NotBlank 공백을 허용하지 않음 ("", " ", null 불가)
@NotEmpty 빈 문자열 허용하지 않음 ("" 불가, null 가능)
@Size(min, max) 문자열 또는 컬렉션 크기 제한
@Email 이메일 형식 체크
@Pattern(regex) 정규식 패턴 일치 여부 검사
@Min(value) 숫자의 최소값 설정
@Max(value) 숫자의 최대값 설정
@Positive 양수만 허용
@Negative 음수만 허용
@Past 과거 날짜만 허용
@Future 미래 날짜만 허용

groups 속성 적용방법

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 미적용)

정리

  • groups 속성을 사용하면, 같은 DTO에서 상황별로 다른 검증을 적용 가능
  • 인터페이스(SaveCheck, UpdateCheck)를 정의하고, DTO에서 groups 속성으로 구분
  • 컨트롤러에서 @Validated(그룹.class)를 적용하여 검증 방식 다르게 설정 가능

0개의 댓글