728x90

개요

이번에 스크랩 프로젝트 개발을 진행하다가 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 (이 숫자에 허용되는 최대 소수 자릿수)
@Email 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

 

[Spring] @Valid와 @Validated를 이용한 유효성 검증의 동작 원리 및 사용법 예시 - (1/2)

Spring으로 개발을 하다 보면 DTO 또는 객체를 검증해야 하는 경우가 있습니다. 이를 별도의 검증 클래스로 만들어 사용할 수 있지만 간단한 검증의 경우에는 JSR 표준을 이용해 간결하게 처리할 수

mangkyu.tistory.com

https://jyami.tistory.com/55

 

@Valid 를 이용해 @RequestBody 객체 검증하기

Springboot를 이용해서 어노테이션을 이용한 validation을 하는 방법을 적으려 한다. RestController를 이용하여 @RequestBody 객체를 사용자로부터 가져올 때, 들어오는 값들을 검증할 수 있는 방법을 소개한

jyami.tistory.com

 

728x90

+ Recent posts