Validator 인터페이스에는 두 가지 메소드가 정의되어있다.
boolean supports(Class<?> clazz) { // 인스턴스가 검증 대상 타입인지 확인 } void validate(Object target, Errors errors) { // 실질적인 검증 작업 }
프로젝트에서의 유효성 검사 도입
- 해당 아래의 코드는 DTO에서의 Valid의 어노테이션의 검증이 끝난 뒤(예를들어 @NotBlank,@Email 같은 형식) 이미 존재하는 email인지 nickName인지 유효성 검사하는 Validator 인터페이스를 overide한 것 이다.
- 아래의 Validator는 spring의 bean을 주입 받아야하기 때문에 bean으로 만들어줘야한다. -> 따라서 @Component를 붙여주자! 빈과 빈들만 주입을 받을 수 있다.
@Component
@RequiredArgsConstructor
public class SignUpFormValidator implements Validator {
private AccountRepository accountRepository;
@Override
public boolean supports(Class<?> aClass) {
return aClass.isAssignableFrom(SignUpForm.class);
}
@Override
public void validate(Object object, Errors errors) {
SignUpForm signUpForm = (SignUpForm) object;
/**이메일 중복 일 시**/
if (accountRepository.existsByEmail(signUpForm.getEmail())) {
errors.rejectValue("email","invalid.email",new Object[]{signUpForm.getEmail()},"이미 사용중인 이메일 입니다.");
}
if (accountRepository.existsByNickname(signUpForm.getNickname())) {
errors.rejectValue("nickname", "invalid.nickname", new Object[]{signUpForm.getEmail()}, "이미 사용중인 닉네임입니다.");
}
}
}
public interface AccountRepository extends JpaRepository<Account,Long> {
boolean existsByEmail(String email);
boolean existsByNickname(String nickname);
}
위의 SignUpFormValidator를 이제 Controller 에서 사용할 것 이다. 그전에 우선 @InitBinder에 대해서 알아보자.
@InitBinder
- Spring Validator를 사용 시 @Valid 어노테이션으로 검증이 필요한 객체를 가져오기 전에 수행할 method를 지정해주는 어노테이션이다
- 예를 들어 @InitBinder("특정 객체 이름")으로 지정해주면 "특정 객체"에만 해당 @InitBinder 가 적용된다.
- 이점 : 해당 valid를 사용할 controller의 코드 길이가 줄고 다른 곳에서도 재사용이 가능하다.
UserController
@Controller
@RequiredArgsConstructor
public class AccountController {
private final SignUpFormValidator signUpFormValidator;
private final AccountRepository accountRepository;
private final ConsoleMailSender mailSender;
@InitBinder("signUpForm")
public void initBinder(WebDataBinder webDataBinder) {
webDataBinder.addValidators(signUpFormValidator);
}
@GetMapping("/sign-up")
public String signUpForm(Model model) {
model.addAttribute("signUpForm", new SignUpForm());
return "account/sign-up";
}
@PostMapping("/sign-up")
public String signUpSubmit(@Valid SignUpForm signUpForm, BindingResult result) {
if (result.hasErrors()) {
return "account/sign-up";
}
Account account = signUpForm.of();
Account newAccount = accountRepository.save(account);
//저장 완료 시 이메일 체크에 필요한 토큰 발급
newAccount.generateEmailCheckToken();
// TODO 이메일 보내기
SimpleMailMessage mail = new SimpleMailMessage();
mail.setSubject("스터디히어, 회원 가입 인증");
//
mail.setText("/check-email-token?token=" + newAccount.getEmailCheckToken()
+ "&email=" + newAccount.getEmail());
//수신 이메일
mail.setTo(newAccount.getEmail());
mailSender.send(mail);
return "redirect:/";
}
}
위와 같이 @InitBinder("signUpForm")를 사용하면 카멜케이스로 바인딩을 설정해주어서 SignUpForm 객체를 validator에 추가해서 유효성 검사를 할 수 있다.