Bean Validation

Bean Validation 은, JSR 303 에서 처음으로 제안된 어노테이션 기반의 Java Bean 검증 명세이다.
대표적인 구현체로 Hibernate Validator 가 있다.
spring-boot-starter-validation 의존성을 명시하면, hibernate-validator 의존성이 추가된다.

Bean Validation

Bean Validation 은 JSR 303 에서 처음으로 제안되었다.
최초에 제안된 명세의 내용은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
Validating data is a common task that is copied in many different layers of an application, from the presentation tier to the persistence layer.
Many times the exact same validations will have to be implemented in each separate validation framework, proving time consuming and error-prone.
To prevent having to re-implement these validations at each layer, many developers will bundle validations directly into their classes, cluttering them with copied validation code that is, in fact, meta-data about the class itself.

This JSR will define a meta-data model and API for JavaBean validation.
The default meta-data source will be annotations, with the ability to override and extend the meta-data through the use of XML validation descriptors.
It is expected that the common cases will be easily accomplished using the annotations, while more complex validations or context-aware validation configuration will be available in the XML validation descriptors.
The validation API developed by this JSR will not be specific to any one tier or programming model.
It will specifically not be tied to either the web tier or the persistence tier, and will be available for both server-side application programming, as well as rich client Swing application developers.
This API is seen as a general extension to the JavaBeans object model, and as such is expected to be used as a core component in other specifications, such as JSF, JPA, and Bean Binding.

위 내용을 정리하면 다음과 같다.

  • 데이터 검증은, 어플리케이션의 서로 다른 레이어에서 (from the presentation tier to the persistence layer) 반복적으로 수행되는 작업이다.
  • 각 계층에서 이러한 데이터 검증이 재구현되는 것을 피하기 위해, 많은 개발자들은 검증 코드를 클래스 안에 포함시킨다.
  • 이 검증 코드는 해당 클래스의 meta-data (데이터에 대한 정보를 제공하는 데이터) 이다.
  • JSR 303은, JavaBean validation 을 위한 meta-data 모델과 API 를 정의한다.
  • 기본적인 meta-data 는 오버라이드 및 확장이 가능한 어노테이션 기반이다.

Hibernate Validator

Bean Validation 스팩은 현재 2.0 까지 제안되었다.

  1. JSR 303 (Bean Validation)
  2. JSR 349 (Bean Validation 1.1)
  3. JSR 380 (Bean Validation 2.0)

그런데 위 JSR 은 스팩일 뿐, 구현체는 아니다.
검증된 구현체로는 Hibernate Validator 가 있다.
각 JSR 에 따라, Hibernate Validator 의 버젼은 다음과 같다.

  1. JSR 303 (Bean Validation) - 4.3.1.final
  2. JSR 349 (Bean Validation 1.1) - 5.1.1.final
  3. JSR 380 (Bean Validation 2.0) - 6.0.1.final

Spring Boot with Hibernate Validator

Spring Boot 에서 bean validation 을 활용하는 간단한 예를 보자.
다음과 같이 spring-boot-starter-validation 의존성을 추가하자.

그러면, 아래처럼 hibernate-validator 가 추가되는 것이 확인된다.

다음과 같이 사용자를 등록하기 위한 end-point 를 작성해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping
class UserController {

@PostMapping("/users")
fun createUser(@RequestBody userSignUp: UserSignUp): String {

return "success"
}
}

data class UserSignUp(
val name: String,
val age: Long
)

다음과 같이 나이를 10000 으로 입력해도, 정상적으로 “success” 가 응답이 된다.

아래와 같이, 최대 나이를 100 살로 제한해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import javax.validation.Valid
import javax.validation.constraints.Max

@RestController
@RequestMapping
class UserController {

@PostMapping("/users")
fun createUser(@Valid @RequestBody userSignUp: UserSignUp): String {

return "success"
}
}

data class UserSignUp(
val name: String,

@Max(value = 100)
val age: Long
)

여전희 정상적으로 “success” 가 응답이 된다.
“@Max(value = 100)” 를 추가했는데, 왜 validation 이 동작하지 않는걸까 ?

아래 문서에 정의되어 있는 것처럼, bean constraints 는 네 가지 타입이 있다.
https://docs.jboss.org/hibernate/validator/8.0/reference/en-US/html_single/#section-declaring-bean-constraints

  • field constraints
  • property constraints
  • container element constraints
  • class constraints

그런데, 위 kotlin 코드의 bytecode 를 decompile 한 결과를 보면 아래와 같이 생성자의 파라미터에 “@Max(value = 100)” 가 추가되어있다.

따라서, field constraints 를 사용하기 위해, kotlin 의 @field 를 사용하자.

1
2
3
4
5
6
data class UserSignUp(
val name: String,

@field:Max(value = 100)
val age: Long
)

다시 kotlin 코드의 bytecode 를 decompile 한 결과를 보면 아래와 같이 필드에 “@Max(value = 100)” 가 추가되어있다.

그리고 다시 요청을 하게 되면, 다음과 같이 400 error 를 응답받게 된다.

validation 이 실패했고, age 가 100 이하이어야 한다고 WARN 로깅이 되고 있다.



Comments