Back-End

DTO 에서 회원정보 유효성 검증하기 (spring boot, @Valid)

gyu-won 2024. 12. 24. 14:51

회원가입에서 유저 정보를 받아올때, 아이디,패스워드,이메일,성별 등의 유효성 검증은 필수적입니다.

아이디의 최소/최대 글자 수는 충족하였는지, 패스워드에서 특수문자와 영문자는 포함되었는지 등을 검증하는 로직이 필요합니다.

 

이에 대한 검증 로직을 모두 추가해주어야 하는 두려움이 발생하였지만, 그 두려움도 잠시

스프링에서 유효성 검증을 해결해주는 dependency를 찾았습니다!!

제가 구현한 검증 로직은 아래와 같습니다.

 

 

1. validation dependency 추가하기

	implementation 'org.springframework.boot:spring-boot-starter-validation'

 

이전에는 spring-boot-starter-web 의존성 내부에 validation이 있었지만,

spring boot 2.3 version 이상부터는 아예 모듈로 빠져 validation 의존성을 따로 추가해줘야 사용할 수 있다고 합니다.

 

2. 어노테이션 추가하기

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class MemberDTO {
    private Long id;

    @NotBlank(message = "아이디는 필수 입력 값입니다.")
    @Size(min= 4, max= 10, message = "아이디는 최소 4자 이상 최대 10자 이하입니다.")
    private String username;

    @NotBlank(message = "비밀번호는 필수 입력 값입니다.")
    @Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,20}", message = "비밀번호는 8~20자 영문 대 소문자, 숫자, 특수문자를 사용하세요.")
    private String password;
 
 }

 

유효성 검증은 어노테이션을 추가하여 메세지를 전달하거나, 정규식을 통해 입력값을 검증하는 방식으로 이루어집니다.

 

만약, Gender 와 같이 enum클래스를 정의하여 유저정보를 받는 경우,

@NotBlank 와 같이 String을 다루는 어노테이션을 사용할 수 없으니, 서버 측에서 따로 예외처리를 해주거나,

다른 적절한 어노테이션을 찾아서 사용하셔야 합니다.

(클래스를 검증할 수 있는 어노테이션이 없음... 보통 String이나 int를 검증합니다)

 

회원가입으로 다양한 유저정보가 입력되는 만큼, 어노테이션의 종류도 다양합니다.

그 어노테이션 종류는 아래와 같습니다.

 

더보기

1. 필드 값의 null 여부

 @NotNull: 필드가 null이 아닌 값을 반드시 가져야 함.

@NotBlank: 필드가 null이 아니고, 최소한 하나 이상의 공백이 아닌 문자를 포함해야 함.

@NotEmpty: 컬렉션, 배열, 문자열 등의 필드가 비어 있지 않음을 보장.

2. 숫자 값 제한

@Min(value): 필드 값이 지정된 최소값 이상이어야 함.

@Max(value): 필드 값이 지정된 최대값 이하여야 함.

@Digits(integer, fraction): 필드 값이 지정된 정수 자리수와 소수 자리수를 가져야 함.

3. 문자열 길이 및 패턴

@Size(min, max): 문자열 또는 컬렉션의 길이나 크기가 지정된 범위 내에 있어야 함.

@Pattern(regex): 필드 값이 제공된 정규 표현식과 일치해야 함.

@Email: 필드 값이 올바른 이메일 형식이어야 함.

4. 날짜와 시간

@Past: 필드 값이 과거의 날짜/시간이어야 함.

@Future: 필드 값이 미래의 날짜/시간이어야 함.

5. 논리값 검사

@AssertTrue: 필드 값이 true여야 함.

@AssertFalse: 필드 값이 false여야 함.

6. 특별한 값 검사

@CreditCardNumber: 필드 값이 올바른 신용카드 번호 형식이어야 함.

7. 객체 및 클래스 레벨 검사

@Valid: 중첩 객체나 프로퍼티에 대해 유효성 검사를 트리거.

@Validated: 클래스 또는 메서드 레벨에서 특정 유효성 검사 그룹을 지정하여 검사.

 

 

3. Controller에서 예외처리해주기

@PostMapping("/api/user")
public ResponseEntity<ResponseMsg> join(@RequestBody @Valid MemberDTO memberDTO, Errors errors){
    //회원가입 유효성 검사 실패 시
    if(errors.hasErrors()){
        Map<String, String> validatorResult = memberService.validateHandling(errors);

        ResponseMsg errorResponse = new ResponseMsg(400, "Validation Failed", validatorResult);
        return ResponseEntity.badRequest().body(errorResponse);
    }

    memberService.save(memberDTO);
    ResponseMsg responseMsg = new ResponseMsg(200, "ok", null);

    return ResponseEntity.ok(responseMsg);
}

 

@Valid 어노테이션 설정하는건 잊지마시고!

저는 에러메세지를 던져주기 위한 클래스인 ResponseMsg를 생성해서 에러를 던져주고 있습니다!

 

그러고 에러메세지를 핸들링하는 로직은 서비스 단에서 따로 구현하여 아래와 같이 사용하고 있습니다.

에러메세지를 해시값으로 저장하여 관리하였습니다

// 회원가입 시, 유효성 체크 
public Map<String, String> validateHandling(Errors errors) {
    if (!errors.hasErrors()) {
        // Errors가 없을 경우 빈 Map 반환
        //외부에서 수정못하도록 불변객체로 변환
        return Map.of();
    }

    Map<String, String> validatorResult = new HashMap<>();
    for (FieldError error : errors.getFieldErrors()) {
        validatorResult.put(error.getField(), error.getDefaultMessage());
    }
    //외부에서 수정못하도록 불변 객체로 변환
    return Collections.unmodifiableMap(validatorResult);
}

 

 

아이디 값을 3자리로 주고 회원가입 요청을 보내면,

아래와 같은 에러 메세지를 확인할 수 있습니다!

/api/user 회원가입 요청 결과화면