회원가입 시 사용자가 입력한 정보가 서버로 전송되기 전에 특정 규칙에 맞게 입력했는지, 이미 사용하고 있는 닉네임이나 이메일은 아닌지 확인하는 검증 단계가 필요하다.
참고
- @Validated : 스프링 전용 검증 애너테이션
- @Valid : 자바 표준 검증 애너테이션
클라이언트 검증과 서버 검증을 적절히 섞어서 사용하되, 최종적으로 서버 검증은 필수이다.
검증 로직을 모든 프로젝트에 적용할 수 있게 표준화한 것이 바로 Bean Validation이다.
✓ Validation 유효성 검사하기
1. build.gradle 설정하기
validation 의존성을 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
2. MemberDto
유효성 검사가 필요한 Dto 객체에 Validation 애너테이션을 사용한다.
public class MemberDto {
@NotBlank(message = "이름은 필수 입력 값입니다.")
private String name;
@NotBlank(message = "닉네임은 필수 입력 값입니다.")
@Pattern(regexp = "^[가-힣a-zA-Z0-9]{2,10}$" , message = "특수문자를 포함하지 않은 2자 이상 10자 이하 단어로 입력해주세요.")
private String nickname;
@NotEmpty(message = "이메일은 필수 입력 값입니다.")
@Email(message = "이메일 형식으로 입력해주세요.")
private String email;
}
3. MemberController
BindingResult는 error를 자동으로 model에 담아 전달한다.
@PostMapping(value = "/register/member")
public String registerMember(@Valid MemberDto memberDto, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "member/member-register";
}
try {
Member member = Member.createMember(memberDto, passwordEncoder);
memberService.saveMember(member);
} catch (IllegalStateException e) {
model.addAttribute("errorMessage", e.getMessage());
return "member/member-register";
}
return "member/register-complete";
}
✓ 중복 확인하기
1. MemberRepository
boolean true : 중복 O
boolean false : 중복 X
JPA는 해당 데이터가 DB에 존재하는지 확인할 때 existsBy 키워드를 사용한다. 해당 데이터가 존재하는 경우 true, 존재하지 않는 경우 false를 return 한다.
boolean existsByEmail(String email);
boolean existsByNickname(String nickname);
2. MemberService
public boolean checkEmailDuplicate(String email) {
return memberRepository.existsByEmail(email);
}
public boolean checkNicknameDuplicate(String nickname) {
return memberRepository.existsByNickname(nickname);
}
3. MemberController
@GetMapping("/email/{email}/exists")
public ResponseEntity<Boolean> checkEmailDuplicate(@PathVariable String email) {
return ResponseEntity.ok(memberService.checkEmailDuplicate(email));
}
@GetMapping("/nickname/{nickname}/exists")
public ResponseEntity<Boolean> checkNicknameDuplicate(@PathVariable String nickname) {
return ResponseEntity.ok(memberService.checkNicknameDuplicate(nickname));
}
4. Validator 구현체 AbstractValidator 생성하기
Validator 클래스를 구현한 AbstractValidator 추상 클래스를 생성한다.
@Slf4j
public abstract class AbstractValidator<T> implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return true;
}
@SuppressWarnings("unchecked")
@Override
public void validate(Object target, Errors errors) {
try {
doValidate((T) target, errors); // 유효성 검증 로직
} catch (IllegalStateException e) {
log.error("중복 검증 에러", e);
throw e;
}
}
// 유효성 검증 로직
protected abstract void doValidate(final T dto, final Errors errors);
}
- supports() :
@Validated는 검증기를 실행하라는 애너테이션이다. 이 애너테이션이 붙으면 WebDataBinder에 등록한 검증기를 찾아서 실행한다. 여러 검증기(Validator)를 등록한다면 그중 어떤 검증기로 실행되어야 할지 구분이 필요한데, 이때 supports()가 사용된다.
- doValidate() :
검증 로직이 들어갈 부분을 따로 작성하기 위해서 추가한다.
- @SuppressWarnings("unchecked") :
컴파일러에서 경고하지 않도록 하는 설정이다.
5. CheckNicknameValidator, CheckEmailValidator 클래스 작성하기
AbstractValidator 추상 클래스를 상속받은 필드 중복 검사 클래스를 작성한다. doValidate()를 오버라이딩하여 로직을 구현한다.
@RequiredArgsConstructor
@Component
public class CheckNicknameValidator extends AbstractValidator<MemberDto> {
private final MemberRepository memberRepository;
@Override
protected void doValidate(MemberDto dto, Errors errors) {
Member member = new Member();
member.setNickname(dto.getNickname());
if (memberRepository.existsByNickname(member.getNickname())) {
// 중복인 경우
errors.rejectValue("nickname", "닉네임 중복 오류", "이미 사용 중인 닉네임입니다.");
}
}
}
- rejectValue() :
깔끔하게 검증 오류를 다룰 수 있다.
void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);
6. WebDataBinder
Validator 클래스 사용을 위해 MemberController에 @InitBinder가 붙은 WebDataBinder을 인자로 받는 메서드를 작성하여 검증 Validator를 추가한다.
private final CheckNicknameValidator checkNicknameValidator;
private final CheckEmailValidator checkEmailValidator;
@InitBinder
public void validatorBinder(WebDataBinder binder) {
binder.addValidators(checkNicknameValidator);
binder.addValidators(checkEmailValidator);
}
- @InitBinder : 특정 컨트롤러에서 바인딩 또는 검증 설정을 변경하고 싶을 때 사용한다.
- WebDataBinder binder : HTTP 요청 정보를 컨트롤러 메서드의 파라미터나 모델에 바인딩할 때 사용되는 바인딩 객체이다. 스프링의 파라미터 바인딩의 역할을 해주며 검증 기능도 내부에 포함한다.
- addValidators() : addValidators() 메서드를 이용해 검증기(Validator)를 추가하면 해당 컨트롤러에서는 검증기를 자동으로 적용한다. @InitBinder에 해당하는 컨트롤러에만 영향을 준다. 즉, 컨트롤러에 요청이 올 때마다 WebDataBinder가 호출되면서 WebDataBinder에 등록한 검증기를 매번 적용하게 할 수 있다.
아래와 같이 View에서 에러 메시지를 사용자에게 알려줄 수 있다.


reference
'Dev > Spring Boot' 카테고리의 다른 글
| Spring Security 동작 원리 이해하기 (0) | 2022.11.20 |
|---|---|
| Spring Boot - MVC 패턴과 Layered Architecture (0) | 2022.10.06 |
| Spring Annotation - 스프링 어노테이션 정리 (0) | 2022.09.20 |
| Java Build Tool - Maven, Gradle (0) | 2022.08.03 |
댓글