본문 바로가기
Back-End/Spring

Validator 인터페이스 사용

by 어렵다어려웡 2021. 5. 20.

Spring boot 프로젝트 만드는 과정에서 Validator를 한번 써보기 위해서 적용을 시켜봤습니다..

 

보통 특정 객체에 대한 검증을 하기 위해서 사용된다.

 

Validator 인터페이스를 사용하기 위해서는 2가지 메서드를 오버라이딩해서 사용합니다.

 @Override
    public boolean supports(Class<?> clazz) {
       
    }

    @Override
    public void validate(Object target, Errors errors) {
   
   }

supports -> 어떤 클래스의 객체의 검증을 지원할 것인지에 대한 코드를 작성해야한다.

validate -> 매개변수 target의 객체에 대하여 검증을 실행 한 후 문제가 있을 경우 errors 를 통해서

확인합니다.

 

예시코드

@Setter
@Getter
public class SignUpRequest {
    // 이메일, 이름, 비밀번호, 주소( 3가지 )
    @Email
    @NotBlank
    private String email;

    @Length(min = 2)
    @NotBlank
    private String name;

    @Length(min = 8, message = "최소 8자 이상 입력해주세요.")
    @NotBlank
    private String password;

    @Length(min = 8, message = "최소 8자 이상 입력해주세요.")
    @NotBlank
    private String confirmPassword;

    @NotEmpty
    private String zipcode;

    @NotEmpty
    private String extraAddress;

    @NotEmpty
    private String detailsAddress;

    private boolean verifyPassword;
    ... 생략
   }

회원가입을 위해서 사용할 DTO를 만들었고 해당 DTO의 객체들에 대한 검증을 위해서 Validation 타입의 

어노테이션을 적용시켜뒀습니다.

 

@RequiredArgsConstructor
@Component
public class SignUpValidator implements Validator {

    private final MemberRepository memberRepository;

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.isAssignableFrom(SignUpRequest.class);
    }

    @Override
    public void validate(Object target, Errors errors) {
        SignUpRequest signupRequest = (SignUpRequest) target;

        if(!verifyPassword(signupRequest)) {
            errors.rejectValue("password", "NOT_CORRECT_PASSWORD","비밀번호가 일치하지 않습니다.");
        } else if(existEmail(signupRequest)) {
            errors.rejectValue("email", "EXIST_EMAIL","이미 존재하는 이메일입니다.");
        }
    }

    private boolean verifyPassword(SignUpRequest signupRequest) {
        return signupRequest.isVerifyPassword();
    }

    private boolean existEmail(SignUpRequest signupRequest) {
       return memberRepository.existsByEmail(signupRequest.getEmail());
    }
}

supports 메서드의 반환값 코드의 경우 한가지가 더 있다고 하는데 둘 중 하나인 isAssignableFrom을 사용했습니다.

기본적으로 회원가입할 때 비밀번호를 2번 입력하게 하는 로직이 대부분이기 때문에

동일한 비밀번호 입력 여부와 간단하게 이미 가입된 이메일주소인지에 대한 여부만 검증을 했습니다.

 

errors.rejectValue를 통해서 해당 검증에 대한 에러의 자세한 내용을 다루면됩니다.

 

Controller

@RestController
@RequiredArgsConstructor
@Log4j2
public class MemberApiController {

    private final MemberService memberService;
    private final SignUpValidator signUpValidator;

    private final static String API_URI = "/api/member";

    @InitBinder("signUpRequest") // 네이밍 주의
    public void signupInitBinder(WebDataBinder webDataBinder) {
        webDataBinder.addValidators(signUpValidator);
    }

    // 회원등록
    @PostMapping(API_URI)
    public ResponseEntity<String> signUpRequest(@RequestBody @Valid SignUpRequest signup,
                                              Errors errors) {
        if(errors.hasErrors()) {
            return new ResponseEntity<>(errors.getFieldError().getDefaultMessage(), HttpStatus.BAD_REQUEST);
        }

        // 회원가입 진행
        // save 반환타입 추후 변경
        Long memberId =  memberService.signUp(signup);
        
       return new ResponseEntity<>("success", HttpStatus.OK);
    }

}

@InitBinder를 통해서 WebDataBinder로 Validator를 설정하시면 됩니다.

@InitBinder의 값을 넣을 떄 검증하려는 객체의 클래스명칭을 넣으면 되는데 이때 네이밍을 주의해야합니다.

해당 클래스의 변수명을 지정할때와 같은 공식으로 MemberDto라면 "memberDto" 이런식으로 

입력해주셔야 정상적으로 작동을 합니다. 

테스트 코드로 진행했을 때 작동을 하지않아서 꽤 애먹었던 부분이기도 합니다.

 

  public ResponseEntity<String> signUpRequest(@RequestBody @Valid SignUpRequest signup,
                                              Errors errors) {
        if(errors.hasErrors()) {
            return new ResponseEntity<>(errors.getFieldError().getDefaultMessage(), HttpStatus.BAD_REQUEST);
        }
  }

상단의 회원가입 메서드 부분을 발췌했습니다.

 

여기서 @Valid를 지정해주시고 Errors를 매개변수로 받으시면 되겠습니다.

SignUpValidator에서 validate 메서드를 통해서 검증실패로 인해 에러가 발생하게 되면 Errors 타입 객체를 통해서

에러가 저장되고 해당 매개변수를 통해서 그에 따른 처리를 하시면 되겠습니다.

 

Errors 가 아닌 BindingResult 타입 파라미터를 사용하셔도 무방합니다.