개요
이번에 스크랩 프로젝트 개발을 진행하다가 DTO를 검증해야 하는 경우가 있었습니다. 이를 if문을 이용해서 validation을 진행하였습니다. 이를 별도의 검증 클래스로 만들어 사용할 수 있지만 간단한 검증의 경우에는 JSR 표준을 이용해 간결하게 처리할 수 있습니다.
// 요청한 값에 대한 validation 처리 필요
if(postJoinReq.getUsername() == null || postJoinReq.getUsername().isEmpty()){
return new BaseResponse(JOIN_USERNAME_EMPTY);
}
if(postJoinReq.getPassword() == null || postJoinReq.getPassword().isEmpty()){
return new BaseResponse(JOIN_PASSWORD_EMPTY);
}
if(postJoinReq.getName() == null || postJoinReq.getName().isEmpty()){
return new BaseResponse(JOIN_NAME_EMPTY);
}
// 형식 확인
if(!RegexService.checkUsername(postJoinReq.getUsername())){
return new BaseResponse(JOIN_USERNAME_INVALID);
}
if(!RegexService.checkPw(postJoinReq.getPassword())){
return new BaseResponse(JOIN_PASSWORD_INVALID);
}
if(!RegexService.checkName(postJoinReq.getName())){
return new BaseResponse(JOIN_NAME_INVALID);
}
implementation 추가
Spring에서는 일종의 어댑터인 LocalValidatorFactoryBean가 제약 조건 검증을 처리한다. 이를 이용하려면 LocalValidatorFactoryBean을 빈으로 등록해야 하는데, SpringBoot에서는 아래의 의존성만 추가해주면 해당 기능들이 자동 설정된다.
// validation 검사
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '2.7.1'
@Valid과 @Validated 차이
Spring에서는 메서드 레벨 유효성 검증을 위해 JSR-303의 @Valid 어노테이션을 사용합니다. 또한 유효성 검사를 위해 멤버 속성을 표시하는 데에도 이를 사용합니다. 그러나 @Valid 어노테이션은 그룹 유효성 검사를 지원하지 않습니다.
그룹은 유효성 검사 중에 적용되는 제약 조건을 제한하는 데 도움이 됩니다. 한 가지 특정 사용 사례는 UI 마법사입니다. 여기에서 첫 번째 단계에서 필드의 특정 하위 그룹이 있을 수 있습니다. 다음 단계에서 동일한 Bean에 속하는 다른 그룹이 있을 수 있습니다. 따라서 각 단계에서 이러한 제한된 필드에 제약 조건을 적용해야 하지만 @Valid는 이를 지원하지 않습니다.
이 경우 그룹 수준 의 경우 이 JSR-303의 @Valid 의 변형인 Spring의 @Validated 를 사용해야 합니다. 이것은 메서드 수준에서 사용됩니다. 그리고 멤버 속성을 표시하기 위해 @Valid 어노테이션을 계속 사용합니다.
또한 @Valid 어노테이션은 Controller 단에서만 유효성 검사가 가능하다. 일반적으로 파라미터에 대한 유효성 검증은 Controller 단에서 최대한 처리하는 것이 좋지만, 경우에 따라 다른 곳에서도 파라미터에 대한 검증이 필요합니다. 이런 경우에 AOP 기반으로 메서드의 요청을 가로채서 유효성 검증을 진행할 수 있는 것이 바로 @Validated 어노테이션이다.
@Valid 작성
@PostMapping("/join")
public BaseResponse join(@Validated(ValidationSequence.class) @RequestBody PostJoinReq postJoinReq){
userService.join(postJoinReq);
return new BaseResponse("회원가입에 성공했습니다");
}
@RequestBody 어노테이션 옆에 @Valid를 작성하면, RequestBody로 들어오는 객체에 대한 검증을 수행한다. 이 검증의 세부적인 사항은 객체 안에 정의를 해두어야 한다.
Request class 생성
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class PostJoinReq {
@NotEmpty(message = "이메일을 입력해주세요", groups = NotEmptyGroup.class)
@Email(message = "이메일 형식이 일치하지 않습니다", groups = EmailGroup.class)
private String email; // 아이디
@NotEmpty(message = "비밀번호를 입력해주세요", groups = NotEmptyGroup.class)
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\\\d)[A-Za-z\\\\d!?@#$%&*]{5,15}$", message = "비밀번호 형식이 일치하지 않습니다", groups = PatternGroup.class)
private String password; // 비밀번호
@NotEmpty(message = "이름을 입력해주세요", groups = NotEmptyGroup.class)
@Pattern(regexp = "^(?=. *[a-zA-Z가-힣])[A-Za-z가-힣]{1,30}$", message = "이름 형식이 일치하지 않습니다", groups = PatternGroup.class)
private String name; // 이름
}
위와 같이 PostJoinReq 객체를 정의한 후 각 필드에 맞는 어노테이션을 사용하면 된다.
ValidationGroup class 생성
public class ValidationGroup {
// 인터페이스 선언. 이 인터페이스를 @NotEmpty같은 어노테이션에 적용시켜주면 됨.
public interface NotEmptyGroup{}
public interface NotNullGroup{}
public interface PatternGroup{}
public interface EmailGroup{}
}
ValidationSequence interface 생성
// 왼 -> 오 순서로 적용됨
@GroupSequence({Default.class, NotNullGroup.class, NotEmptyGroup.class, PatternGroup.class, EmailGroup.class})
public interface ValidationSequence {
}
@GroupSequence 어노테이션에 적용되어있는 왼쪽에서 오른쪽 순서로 적용된다.
validation 어노테이션 종류
@AssertTrue | Boolean, boolean | 값이 항상 True 여야 한다. | |
@DecimalMax | 실수 제외 숫자 클래스. | 지정된 최대 값보다 작거나 같아야 하는 숫자이다. | String : value (max 값을 지정한다.) |
@DecimalMin | 실수 제외 숫자 클래스. | 지정된 최소 값보다 크거나 같아야하는 숫자이다. | String : value (min 값을 지정한다.) |
@Digits | BigDecimalBigIntegerCharSequencebyte, short, int, long, 이에 대응하는 Wrapper 클래스 | 허용된 범위 내의 숫자이다. | int : integer (이 숫자에 허용되는 최대 정수 자릿수)int : fraction (이 숫자에 허용되는 최대 소수 자릿수) |
null도 valid로 간주된다. | 올바른 형식의 이메일 주소여야한다. | ||
@Future | 시간 클래스 | Now 보다 미래의 날짜, 시간 | |
@FutureOrPresent | 시간 클래스 | Now의 시간이거나 미래의 날짜, 시간 | |
@Max | 실수 제외 숫자 클래스. | 지정된 최대 값보다 작거나 같은 숫자이다. | long : value (max 값을 지정한다) |
@Min | 실수 제외 숫자 클래스. | 지정된 최소 값보다 크거나 같은 숫자이다. | long : value (min 값을 지정한다) |
@Negative | 숫자 클래스 | 음수인 값이다. | |
@NegativeOrZero | 숫자 클래스 | 0이거나 음수인 값이다 | |
@NotBlank | null 이 아닌 값이다.공백이 아닌 문자를 하나 이상 포함한다 | ||
@NotEmpty | CharSequence,Collection, Map, Array | null이거나 empty(빈 문자열)가 아니어야 한다. | |
@NotNull | 어떤 타입이든 수용한다. | null 이 아닌 값이다. | |
@Null | 어떤 타입이든 수용한다. | null 값이다. | |
@Past | 시간 클래스 | Now보다 과거의 날짜, 시간 | |
@PastOrPresent | 시간클래스 | Now의 시간이거나 과거의 날짜, 시간 | |
@Pattern | 문자열 | 지정한 정규식과 대응되는 문자열이어야한다. Java의 Pattern 패키지의 컨벤션을 따른다 | String : regexp (정규식 문자열을 지정한다) |
@Positive | 숫자 클래스 | 양수인 값이다 | |
@PositiveOrZero | 숫자 클래스 | 0이거나 양수인 값이다. | |
@Size | CharSequence,Collection, Map, Array | 이 크기가 지정된 경계(포함) 사이에 있어야한다. | int : max (element의 크기가 작거나 같다)int : min (element의 크기가 크거나 같다) |
@NotNull, @NotEmpty, @NotBlank 차이
null | "" | " " |
@NotNull | Invalid | Valid |
@NotEmpty | Invalid | Invalid |
@NotBlank | Invalid | Invalid |
참조
https://mangkyu.tistory.com/174
'프로젝트 > 스크랩' 카테고리의 다른 글
스크랩 Exception 방법과 흐름 (2) | 2022.10.25 |
---|---|
YML 설정 파일 암호화에서 Github Repository secrets으로 변경 (0) | 2022.10.23 |
스크랩 프로젝트를 진행하면서 현재 고민하고 있는 문제 (0) | 2022.10.21 |
서버와 로컬에서 특정 URL의 테스트 값이 다름 (0) | 2022.10.17 |
YML 설정 파일 암호화 (0) | 2022.10.12 |