[Spring] Bean Validation (1)

이연우·2025년 7월 26일

TIL

목록 보기
50/100

📋 Bean Validation이란?

  • 웹 애플리케이션을 만들 때, 사용자가 입력한 값이 유효한지 검증(Validation)이 필요
  • 이 검증을 Annotation 기반으로 간단하게 처리할 수 있도록 도와주는 표준 기술

🛠️ Bean Validation 등장 배경

⚠️ 기존 방식의 문제

if (request.getAge() == null || request.getAge() < 0) {
        // BindingResult FieldError 추가
        bindingResult.addError(
                new FieldError("request", "age", "age 필드는 필수이며 0 이상의 값이어야 합니다.")
        );
    }
  • 이렇게 매번 직접 조건문으로 검증 코드를 작성했어야 함
    → 번거롭고, Controller가 비대해지며, 단일 책임 원칙 위반
    → 그래서 등장한 것이 Bean Validation

📦 Bean Validation 핵심 요소

구성 요소설명
jakarta.validation-apiBean Validation의 인터페이스, 즉 표준
hibernate-validator이를 실제로 구현한 구현체
@Validated or @Valid컨트롤러에서 유효성 검증을 실행하게 하는 어노테이션
다양한 Annotation들@NotBlank, @NotNull, @Range

→ Bean Validation(인터페이스) 구현체인 Hibernate Validator 사용

💡 Hibernate Validator 공식 문서
> Hibernate Validator → 모든 기술의 정확한 사용법은 공식 문서에 존재

🔍 Bean Validation 적용 예시

✍ DTO 클래스

@Getter
public class SignUpRequestDto {
	
	@NotBlank
	private String name;

	@NotNull
	@Range(min = 1, max = 120)
	private Integer age;

}

💻 Controller

@Controller
public class BeanValidationController {

		@PostMapping("/model-attribute")
    public String beanValidationV1(
            @Validated @ModelAttribute SignUpRequestDto dto
    ) {
        // 로직
        
        // ViewName
        return "complete";
    }

}

@RestController
public class BeanValidationRestController {

		@PostMapping("/request-body")
    public String beanValidationV2(
            @Validated @RequestBody SignUpRequestDto dto
    ) {
        // 로직
        
				// 문자 Data 반환
        return "회원가입 완료";
    }

}

@ModelAttribute 방식은 HTML form에서
@RequestBody 방식은 JSON 형태 요청에서 사용


📏 Field Error

  • 특정 필드 검증의 경우, 빈 값, 길이, 크기, 형식과 같은 간단한 로직
  • 이러한 로직들을 모든 프로젝트에 적용할 수 있도록 표준화한 것이 Bean Validation

🧩 Bean Validation 적용

  • 📦 Gradle 의존성
implementation 'org.springframework.boot:spring-boot-starter-validation'
  • Spring Boot에서는 별도 버전 명시 없이 자동 설정

  • 외부 라이브러리로 아래 파일이 추가됨:
    - jakarta.validation-api
    - hibernate-validator

  • 사용된 Annotation 정리

  • @NotBlank

    1. null을 허용하지 않음
    2. 공백(” “)을 허용하지 않고, 하나 이상의 문자를 포함해야 함
    3. 빈 값(””)을 허용하지 않음
    4. CharSequence 타입 허용
    • String은 CharSequence(Interface)의 구현체
  • @NotNull

    1. null을 허용하지 않음
    2. 모든 타입 허용
  • @NotEmpty

    1. null을 허용하지 않음
    2. 빈 값(””)을 허용하지 않음
    3. CharSequence, Collection, Map, Array 허용

🧪 테스트 코드

@Data
public class TestDto {
	// 테스트 하고싶은 Annotation으로 변경 가능
	@NotBlank
	private String stringField;
	
	@NotNull
	@Range(min = 1, max = 9999)
	private Integer integerField;
}

import static org.assertj.core.api.Assertions.assertThat;

public class BeanValidationTest {
	
	@Test
	void beanValidation() {
		
		// Spring과 통합하면 아래 두줄의 코드는 사용하지 않는다.
		ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
		Validator validator = factory.getValidator();
		
		// Test 하고싶은 상황을 만들어서 검증 가능
		TestDto dto = new TestDto();
		dto.setStringField(" ");
		dto.setIntegerField(1);
		
		// DTO를 검증
		Set<ConstraintViolation<TestDto>> violations = validator.validate(dto);

		// 검증 결과가 예상대로 발생했는지 확인
		// 검증에 걸린 필드가 있어야 함
    assertThat(violations).isNotEmpty();
    // 2개의 제약 위반 발생
    assertThat(violations.size()).isEqualTo(2);
		
		// Validation에 걸린 내역을 출력
		for(ConstraintViolation<TestDto> violation : violations) {
			// 아래의 결과에 Message가 있으면 Validation에 걸린것.
			// Default Message가 있기 때문에 출력됨
			// Message를 수정하고싶다면 Annotation 속성값(message="입력")으로 설정할 수 있다.
			// 결과가 비어있으면 Validation에 걸리지 않은것.
			System.out.println("violation = " + violation.getMessage());
		}
		
	}

}

> 검증 결과 출력 예시:

violation = 공백일 수 없습니다

violation.getMessage()는 어노테이션의 기본 메시지
→ 메시지는 (message="직접 작성")으로 커스터마이징 가능

🏷️ Annotation 총정리

어노테이션설명
@NotBlanknull ❌, 빈 문자열 ❌, 공백만 ❌ (문자가 반드시 있어야 함)
@NotNullnull ❌ (빈 문자열은 허용)
@NotEmptynull ❌, 빈값 ❌ (컬렉션, 배열 포함)
@Range숫자 범위를 지정 (Hibernate 전용)

🛡️ Validator

  • 단순히 Annotation을 선언해 주면 검증이 완료되는 이유는
    Validator(Validation을 사용하는 것)가 존재하기 때문
  • 자동으로 Bean Validator를 Spring에 통합되도록 설정

🧠 Validator 동작 원리

  1. Spring Boot가 실행되면 LocalValidatorFactoryBean이 글로벌 Validator로 자동 등록됨

  2. @Validated@Valid를 붙이기만 하면 됨

  3. 필드 바인딩 성공 후, 해당 필드에 Validation Annotation이 붙어 있으면 검증 수행

  4. 오류 발생 시 FieldError, ObjectErrorBindingResult에 담김

🔎 @Valid vs @Validated 차이점

항목@Valid@Validated
소속Java 표준 (jakarta.validation)Spring Framework
그룹 검증❌ 지원 안 함✅ 그룹 지정 가능
발생 예외MethodArgumentNotValidExceptionConstraintViolationException
사용 범위주로 ControllerController 외 Service 등도 가능

⚠️ 바인딩 실패 시 Validation 제외

  • 예: Integer 필드에 문자열이 들어오면 변환 자체가 실패하여 null 처리
    → Bean Validation은 해당 필드 무시 (적용 안 됨)

🧠 검증은 바인딩(변환)에 성공한 필드에만 의미가 있음

0개의 댓글