Spring Boot - Validation
데이터에 대한 유효성 검증을 효과적으로 도와줄 수 있다.
bean-validation? hibernate-validator?
Bean-validation: JSR-380, 애노테이션을 이용하여 bean 유효성 검사를 위한 Java API 스펙
Hibernate-validator: Bean Validation을 구현한 Java API
Dependency
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-validation')
compile('org.projectlombok:lombok')
runtime('com.h2database:h2')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Entity
애노테이션을 사용하면 쉽게 Entity에 대한 유효성 검사를 할 수 있다. 해당 애노테이션은 javax.validation.constraints패키지에 정의되어 있으며, 이를 아래와 같이 활용할 수 있다.
@Data
public class Member {
private Long idx;
@NotNull(message="name null")
private String name;
@Min(value=14, message="min 14")
private Integer age;
@NotNull(message="tel null")
private String tel;
}
@NotNull
: null 검증
@Min
, @Max
: 최소값, 최대값 검증
@Size
: 범위 검증
@Email
: e-mail 검증
@AssertTrue
: true 검증
@NotEmpty
: null이나 size가 0 검증 (String, Collection)
@NotBlank
: null이나 whitespace 검증 (String)
@Positive
, @PositiveOrZero
: 숫자 검증
@Negative
, @NegativeOrZero
: 숫자 검증
@Past
, @PastOrPresent
: 날짜 검증
@Future
, @FutureOrPresent
: 날짜 검증
Controller
검증하고자 하는 Entity에 @Valid를 붙이며, 이에 대한 결과를 받기 위해 BindingResult를 추가하여 사용할 수 있다.
@RestController
@RequestMapping("member")
public class MemberController {
private final static Logger logger = Logger.getLogger(MemberController.class);
private final static int ZERO = 0;
//...
@PostMapping
public ResponseEntity<?> add(@Valid @RequestBody Member member, BindingResult bindingResult){
if(bindingResult.hasErrors()){
String errorMessage = bindingResult.getAllErrors().get(ZERO).getDefaultMessage();
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(member, HttpStatus.OK);
}
}
Custom Validation
제공해주는 애노테이션도 많지만 사용자 정의 검증 애노테이션을 만들어야 하는 상황이 있을 수 있다.
@interface 정의
@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {
String message() default "Invalid phone number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
ConstraintValidator 구현
public class PhoneValidator implements ConstraintValidator<Phone, String> {
@Override
public void initialize(Phone phone) {
}
@Override
public boolean isValid(String field, ConstraintValidatorContext cxt) {
return field != null && field.matches("[0-9]+")
&& (field.length() > 8) && (field.length() < 14);
}
}
활용
@Phone
private String phone;
Test Code
자세한 코드는 GitHub을 참고
@RunWith(SpringRunner.class)
@WebMvcTest(MemberController.class)
public class MemberControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
// ...
@Test
public void test_success() throws Exception {
Member member = new Member(TEST_NAME, TEST_AGE, TEST_PHONE);
String memberToJson = objectMapper.writeValueAsString(member);
mockRequest(memberToJson, status().isOk(), memberToJson);
}
// ...
private void mockRequest(String memberToJson, ResultMatcher matcher, String result) throws Exception {
mvc.perform(post(TEST_END_POINT)
.content(memberToJson)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8))
.andExpect(matcher)
.andExpect(content().json(result));
}
}
참고
SpringBootSample / SpringBootValidator Baeldung / spring-mvc-custom-validator