본문 바로가기

Dot Programming/Spring Clone

[스프링 웹앱 프로젝트 #5]회원 가입 폼 서브밋 검증

회원 가입  뷰

> 회원 가입 폼 검증

 >> JSR 303 어노테이션 검증
   >>> 값의 길이, 필수 값

>> 커스텀 검증
  >>> 중복 이메일, 닉네임 여부 확인
>> 폼 에러 읶을 시, 폼 다시 보여주기

> 회원 가입 처리
 >> 회원 정보 저장
 >> 인증 이메일 발송
 >> 처리 후 첫 페이지로 리다이렉트(Post-Redirect-Get 패턴)


>실습
 >> 시작 커밋 : fbf1423165272310274e6975d779f690ee3bdc8f
 >> 완료 커밋 : 5ff6d7bdca59f1ca7de4ec0bf4135184cc18bb28

 

 

회원가입 후 이동할 경로를 설정

AccountController.class

    @PostMapping("/sign-up")
    public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors){
        if (errors.hasErrors()){
            return "account/sign-up";
        }

        // TODO : 회원가입 처리
        return "redirect:/";

    }

 

 

SignUpForm.class

/**
 * 회원가입할 때 받아올 데이터
 */
@Data
public class SignUpForm {

    @NotBlank
    @Length(min = 3, max = 20)
    @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-z0-9_-]{3,20}$")
    private String nickname;

    @Email
    @NotBlank
    private String email;

    @NotBlank
    @Length(min = 8, max = 50)
    private String password;
}

 

프론트에도 제약을 걸어도 백엔드에서도 무조건 제약을 걸어줘야 안전하다. 아래는 서버에서 특수문자를 제약하여 발생한 오류처리 문구이다.

 

 

 

Spring IoC 컨테이너에 의한 의존성 주입은 빈(Bean) 끼리만 가능하다.
@RequiredArgsConstructor를 설정하면 private final ~ ~:로 선언한 객체들은 자동으로 생성자 만들어줌

 

initBinder()와 signUpSubmit()추가

@InitBinder란? 특정 컨트롤러에서 바인딩 또는 검증 설정을 변경하고 싶을 때 사용

 

AccountController.class

@Controller
@RequiredArgsConstructor
public class AccountController {

    private final SignUpFormValidator signUpFormValidator;


    /**
     * SignUpForm데이터를 받을때 바인더를 설정
     * public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors)
     * @Valid SignUpForm이랑 매핑
     */
    @InitBinder("signUpForm")
    public void initBinder(WebDataBinder webDataBinder){
        webDataBinder.addValidators(signUpFormValidator);
    }


    @GetMapping("/sign-up")
    public String signUpForm(Model model){
        model.addAttribute(new SignUpForm());
        return "account/sign-up";
    }

    @PostMapping("/sign-up")
    public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors){
        if (errors.hasErrors()){
            return "account/sign-up";
        }
        

        // TODO : 회원가입 처리
        return "redirect:/";

    }

}

 

signUpFormValidator.class

@Component
@RequiredArgsConstructor
public class SignUpFormValidator implements Validator {

    private final AccountRepository accountRepository;

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

    @Override
    public void validate(Object target, Errors errors) {
        SignUpForm signUpForm = (SignUpForm) errors;

        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.getNickname()}, "이미 사용중인 닉네임입니다.");
        }
    }
}

 

rejectValue() 메소드 형태

errors.rejectValue ( String field, String errorCode, String, @Nullable Object[] errorArgs, @Nullable String defaultMessage);

 

AccountRepository.interface

@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {
    boolean existsByEmail(String email);

    boolean existsByNickname(String nickname);
}

 

 


참고

인프런 강의 - 스프링과 JPA 기반 웹 애플리케이션 개발