Bean Validation 2.0(JSR-380)에는 검증과 관련해서 간지러운 점을 긁어주는 애노테이션이 몇 개 추가되었다. 예를 들어 2.0에 추가된 @Email 애노테이션을 사용하면 이메일 형식을 검사하기 위해 정규 표현식을 사용하지 않아도 되고, @Positive 애노테이션을 사용하면 값이 양수인지 검사할 수 있다.
스프링 5에서 Bean Validation 2.0 설정
스프링 5 버전에서 Bean Validation 2.0을 사용하려면 다음과 같이 2.0 API와 관련 프로바이더를 의존에 추가해주면 된다. hibernate-validator 6.0 의존을 추가하면 validationa-api 2.0 의존도 함께 추가되므로 validation-api 의존을 생략해도 된다.
<!-- validation-api는 생략 가능 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.7.Final</version>
</dependency>
스프링 관련 설정
@EnableWebMvc 애노테이션을 사용하면 된다. 이 애노테이션을 사용하면 Bean Validation 애노테이션에 대한 검증 기능을 제공하는 OptionalValidatorFactoryBean를 글로벌 Validator로 등록한다.
@Configuration
@EnableWebMvc
public class SpringConfig implements WebMvcConfigurer {
...
}
애노테이션 사용 예
다음은 Bean Validation 애노테이션을 사용한 예이다.
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.PastOrPresent;
import java.time.LocalDate;
public class FormData {
@NotBlank
private String email;
@NotBlank
private String name;
@NotEmpty
private String password;
@DateTimeFormat(pattern = "yyyyMMdd")
@PastOrPresent
private LocalDate birthday;
컨트롤러는 다음과 같이 @Valid를 이용해서 검증을 적용할 수 있다.
@PostMapping
public String submit(@ModelAttribute @Valid FormData formData, Errors errors) {
if (errors.hasErrors()) return "form";
return "submit";
}
실제 예제 코드는 https://github.com/madvirus/spring5-bv2 리포지토리에서 확인할 수 있다. 리포지토리에서 코드를 clone하고 아래 순서대로 실행한다.
- git clone https://github.com/madvirus/spring5-bv2.git
- cd spring5-bv2
- cd sp5-bv2
- mvnw jetty:run
이 예제는 JSP를 뷰 기술로 사용했다. 관련 폼을 보여주는 JSP 코드는 다음과 같다.
<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<title>가입</title>
</head>
<body>
<h2>가입</h2>
<form:form modelAttribute="formData">
<p>
<label>이메일:<br>
<form:input path="email" />
<form:errors path="email"/>
</label>
</p>
<p>
<label>이름:<br>
<form:input path="name" />
<form:errors path="name"/>
</label>
</p>
<p>
<label>암호:<br>
<form:password path="password" />
<form:errors path="password"/>
</label>
</p>
<p>
<label>생일:<br>
<form:input path="birthday" />
<form:errors path="birthday" />
</label>
</p>
<input type="submit" value="가입">
</form:form>
</body>
</html>
브라우저에서 http://localhost:8080/register로 연결한 뒤 폼에 아무 값도 입력하지 않고 [가입] 버튼을 누르면 검증 결과를 확인할 수 있다. 실제 결과는 다음과 같다.
스프링 부트 2 사용
스프링 부트 2 버전은 기본적으로 Bean Validation 2.0을 사용하므로 추가 설정이 필요없다. 그냥 스프링 부트 2 프로젝트를 만들어 사용하면 된다. 관련 예제 코드는 같은 리포지토리의 boot2-bv2 폴더에 있다. 아래 순서대로 실행한 뒤 브라우저에서 http://localhost:8080/register 주소로 연결해서 확인할 수 있다.
- git clone https://github.com/madvirus/spring5-bv2.git
- cd spring5-bv2
- cd boot2-bv2
- mvnw spring-boot:run
부트 예제는 Thymeleaf를 뷰로 사용했다. 폼을 보여주는 Themeleaf 템플릿 코드는 다음과 같다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>가입</title>
</head>
<body>
<h2>가입</h2>
<form th:object="${formData}" method="POST">
<p>
<label>이메일:<br>
<input type="text" th:field="*{email}" />
<span th:each="err : ${#fields.errors('email')}" th:text="${err}" />
</label>
</p>
<p>
<label>이름:<br>
<input type="text" th:field="*{name}" />
<span th:each="err : ${#fields.errors('name')}" th:text="${err}" />
</label>
</p>
<p>
<label>암호:<br>
<input type="password" th:field="*{password}" />
<span th:each="err : ${#fields.errors('password')}" th:text="${err}" />
</label>
</p>
<p>
<label>생일:<br>
<input type="text" th:field="*{birthday}" />
<span th:each="err : ${#fields.errors('birthday')}" th:text="${err}" />
</label>
</p>
<input type="submit" value="가입">
</form>
</body>
</html>
실행 결과는 앞서 본 그림과 동일하다.
Bean Validation 2.0 제공 애노테이션
Bean Validation 2.0이 제공하는 애노테이션 목록은 다음과 같다. 2.0에 추가된 애노테이션에는 (2)를 표시했다. 아래 표의 지원 타입에서 CharSequence는 문자열 관련 인터페이스로 이 인터페이스를 구현한 대표적인 클래스가 String이다.
애노테이션 |
주요 속성 |
설명 | 지원 타입 |
@AssertTrue @AssertFalse |
|
값이 true인지 또는 false인지 검사한다. null은 유효하다고 판단한다. | boolean Boolean |
@DecimalMax @DecimalMin | String value - 최댓값 또는 최솟값 boolean inclusive - 지정값 포함 여부 - 기본 값 true | 지정한 값보다 작거나 같은지 또는 크거나 같은지 검사한다. inclusive가 false면 value로 지정한 값은 포함하지 않는다. null은 유효하다고 판단한다. | BigDecimal BigInteger CharSequence byte, short, int, long 및 각 래퍼 타입 |
@Max @Min | long value | 지정한 값보다 작거나 같은지 또는 크거나 같은지 검사한다. null은 유효하다고 판단한다. | BigDecimal |
@Digits | int integer - 허용 가능한 정수 자릿수 int fraction - 허용 가능한 소수점 이하 자릿수 | 자릿수가 지정한 크기를 넘지 않는지 검사한다. null은 유효하다고 판단한다. | BigDecimal BigInteger CharSequence byte, short, int, long 및 관련 래퍼 타입 |
@Size | int min - 최소 크기 - 기본 값 0 int max - 최대 크기 - 기본 값 | 길이나 크기가 지정한 값 범위에 있는지 검사한다. null은 유효하다고 판단한다. | CharSequence Collection Map 배열 |
@Null @NotNull | 값이 null인지 또는 null이 아닌지 검사한다. | ||
@Pattern | String regexp - 정규표현식 | 값이 정규표현식에 일치하는지 검사한다. null은 유효하다고 판단한다. | CharSequence |
@NotEmpty (2) | 문자열나 배열의 경우 null이 아니고 길이가 0이 아닌지 검사한다. 콜렉션의 경우 null이 아니고 크기가 0이 아닌지 검사한다. | CharSequence Collection Map 배열 | |
@NotBlank (2) | null이 아니고 최소한 한 개 이상의 공백아닌 문자를 포함하는지 검사한다. | CharSequence | |
@Positive (2) @PositiveOrZero (2) | 양수인지 검사한다. OrZero가 붙은 것은 0 또는 양수인지 검사한다. null은 유효하다고 판단한다. | BigDecimal BigInteger byte, short, int, long 및 관련 래퍼 타입 | |
@Negative (2) @NegativeOrZero (2) | 음수인지 검사한다. null은 유효하다고 판단한다. | BigDecimal | |
@Email (2) |
| 이메일 주소가 유효한지 검사한다. null은 유효하다고 판단한다. | CharSequence |
@Future (2) @FutureOrPresent (2) | 해당 시간이 미래 시간인지 검사한다. OrPresent가 붙은 것은 현재 또는 미래 시간인지 검사한다. null은 유효하다고 판단한다. | 시간 관련 타입 | |
@Past (2) @PastOrPresent (2) | 해당 시간이 과거 시간인지 검사한다. OrPresent가 붙은 것은 현재 또는 과거 시간인지 검사한다. | 시간 관련 타입 |
* 애노테이션이 속한 패키지: javax.validation.constraints
* 시간 관련 타입: Date, Calendar, Instant, LocalDate, LocalDateTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime 등
관련 링크
- 예제 코드: https://github.com/madvirus/spring5-bv2
- Bean Validation: http://beanvalidation.org/