스프링 부트에서 손쉽게 Request 파라미터의 유효성 검사를 하는 라이브러리를 소개합니다.
Validation 라이브러리는 dto에 어노테이션만 붙임으로써, Request 파라미터의 유효성 검사를 합니다.
또한 커스덤해서 유효성 검사를 할 수 있고 유효성에 맞지않으면 Response Message도 커스덤할 수 있기에 꾀나 유용하게 사용할 수 있습니다.
Validation 라이브러리를 사용하기 위해서 다음과 같은 의존성을 추가 합니다.(Gradle 기준)
implementation("org.springframework.boot:spring-boot-starter-validation")
Validation 주요 어노테이션
Validation 사용 예제
@RestController
@RequestMapping("/api")
public class ApiController {
@PostMapping("/user")
public ResponseEntity user(@Valid @RequestBody User user, BindingResult bindingResult){
System.out.println(bindingResult);
System.out.println(bindingResult.hasErrors());
System.out.println(bindingResult.getAllErrors());
if(bindingResult.hasErrors()){
StringBuilder sb = new StringBuilder();
bindingResult.getAllErrors().forEach(objectError -> {
FieldError fieldError = (FieldError) objectError;
String message = objectError.getDefaultMessage();
System.out.println("fieldError : "+fieldError.getField());
System.out.println(message);
sb.append("fieldError : "+fieldError.getField());
sb.append("message : "+message);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString());
}
return ResponseEntity.ok(user);
}
}
중요!! Validation 라이브러리를 적용하기 위해서는 꼭 @Valid 어노테이션을 파라미터를 받는 곳에 붙여야 합니다!!!!
BindingResult 클래스를 사용하여, 에러 여부를 체크합니다. (다음 포스팅에 exception을 활용해서 예외 처리로 유효성 검사를 처리하겠습니다.)
BindingResult.hasErrors() 메소드로 에러 여부(boolean)을 체크하고,
bindingResult.getAllErrors() 배열을 FieldError 객체로 받을 수 있습니다.
FieldError.getField() 메소드로 에러가 나는 dto 속성(필드)명을 확인할 수 있습니다.
또한 FieldError.getDefaultMessage() 메소드로 에러 return 메세지를 출력할 수 있습니다.
참고 StringBuilder 클래스
StringBuilder 메소드는 긴 문자열들을 더해야 할 경유 효과적으로 사용할 수 있습니다.
문자열을 추가할 때마다 새로운 객체를 생성하는 것이 아니라 기존의 데이터에 더하는 방식을 사용하기 때문에 속도가 빠르고 부하가 적습니다.
StringBuilder.append() 메소드를 사용해서 문자열을 추가하면 됩니다.
// User.java DTO 예시
public class User {
@NotBlank
private String name;
@Max(value = 90)
private int age;
@Email
private String email;
@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "핸드폰 번호의 양식과 맞지 않습니다. 01x-xxx(x)-xxxx")
private String phoneNumber;
//@YearMonth(pattern = "yyyyMMdd", message = "yyyyMM 형식에 맞지 않습니다")
private String reqYearMonth; // yyyyMM
@Valid
private List<Car> cars;
...
}
@Pattern 어노테이션
정규식을 사용해서 유효성 검사를 실시할 수 있습니다.
regexp 속성에 정규식을 기술하면 됩니다.
message 속성은 유효성 검사 오류 시 반환할 메세지를 커스덤으로 입력할 수 있습니다.
message 속성은 @Pattern 어노테이션 외에 모든 Validation 라이브러리 유효성 검사의 어노테이션에 적용가능합니다.
@Valid 어노테이션
배열이 아닌 속성들은 컨트롤러에서 @Valid를 붙이면 유효성 검사가 적용됩니다.
하지만 배열 객체 속성들은 dto에서 @Valid를 붙여야 배열 내부 객체 속성들이 유효성 체크할 수 있습니다.
유효성 규칙은 해당 배열 객체에 해당하는 dto class에서 적용하면 됩니다.
사용자 정의 Validation
@AssertTrue/@AssertFalse를 활용한 DTO 내부 사용자 정의 유효성검사
// User.java DTO 예시
public class User {
...
private String reqYearMonth; // yyyyMM
@AssertTrue(message = "yyyyMM 의 형식에 맞지 않습니다!!!")
public boolean isReqYearMonthValidation() {
System.out.println("호출");
try {
LocalDate localDate = LocalDate.parse(getReqYearMonth()+"01", DateTimeFormatter.ofPattern("yyyyMMdd"));
} catch (Exception e){
return false;
}
return true;
}
...
}
dto 내부에 @AssertTrue 어노테이션을 붙인 메소드를 선언해줍니다.
해당 메소드를 통해서 유효성 검사를 할 수 있습니다.
getter() 메소드를 통해 유효성에 필요한 데이터를 호출하여, 유효성을 체크한 후 true/false로 반환할 수 있습니다.
@AssertTrue 속성에 message 값으로 유효성 오류 시 리턴할 커스덤 메세지를 작성할 수 있습니다.
@AssertFalse 어노테이션으로도 @AssertTrue 거의 유사한 방식으로 사용자 정의 validation을 작성할 수 있습니다.
사용자 어노테이션을 통한 사용자 정의 유효성 검사
// YearMonth annotation
package com.example.validation.annotation;
import com.example.validation.validator.YearMonthValidator;
...
@Constraint(validatedBy = {YearMonthValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface YearMonth {
String message() default "yyyyMM 형식에 맞지 않습니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String pattern() default "yyyyMMdd";
}
Validation 라이브러리에 있는 유효성검사 어노테이션을 카피해서 사용자 정의 어노테이션을 만듭니다.
생성하여 pattern 메소드와 같이 인자로 받을 어노테이션 속성을 정의 합니다.
가장 중요한 것은 사용자 정의 어노테이션 위에 기술한 @Constraint(validatedBy = {YearMonthValidator.class})을 주목해야합니다.
@Constraint 어노테이션을 통해서 유효성 검사를 진행할 타겟 클래스를 지정하는 것이 핵심입니다.
// YearMonthValidator.java
package com.example.validation.validator;
import com.example.validation.annotation.YearMonth;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class YearMonthValidator implements ConstraintValidator<YearMonth, String> {
private String pattern;
@Override
public void initialize(YearMonth constraintAnnotation) {
this.pattern = constraintAnnotation.pattern();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
// yyyyMM 뒤에 01를 붙이는 이유는 임의의 DD값을 주기 위해서임
try {
LocalDate localDate = LocalDate.parse(value+"01", DateTimeFormatter.ofPattern(this.pattern));
} catch (Exception e){
return false;
}
return true;
}
}
유효성 검사를 위한 클래스를 만들기 위해서는 반드시 ConstraintValidator 인터페이스를 상속받아야합니다.
ConstraintValidator 인터페이스의 메소드를 오버라이드합니다.
initialize 메소드를 통해서 YearMonth 사용자 정의 어노테이션의 pattern 속성에 정의한 값을 가져와 세팅합니다.
isValid 메소드에서 유효성 검사에 필요한 로직을 코딩해서 boolean 타입으로 반환하면 됩니다.
어노테이션으로 사용자 정의 유효성 검사를 하는 방식은 dto 내부에서 하는 방식과 달리 재사용이 가능합니다.
사용자 정의 어노테이션을 유효성 검사가 필요한 dto 속성에 붙여주면 여러 dto에서 재사용을 할 수 있습니다.
Validation과 같이 보기 좋은 Exception 편
https://getthismoment.tistory.com/85
유효성 검사 어노테이션 적용 방법
단건인 경우
@RequestBody @Valid dto
다건(리스트)인 경우
- 컨트롤러에 어노테이션 추가
@Validated
- api 파라미터에 어노테이션 추가
@RequestBody @Valid List<dto>
'Spring Boot' 카테고리의 다른 글
Spring Boot - Filter (0) | 2021.09.12 |
---|---|
Spring Boot - Exception 개괄 (0) | 2021.09.10 |
Spring Boot - Annotation 참고자료 (0) | 2021.09.07 |
Spring Boot - AOP (Aspect Oriented Programming) (0) | 2021.09.03 |
Spring Boot - IoC (Inversion of Control) (0) | 2021.09.03 |
댓글