1. 직접 검증
2. BindingResult
3. 애노테이션 사용
직접검증
- HashMap을 이용하여, error를 담고 ModelAttribute로 반환
- thymeleaf의 조건문과 삼항문을 이용하여, error 인 경우와 정상인경우 렌더링
//[Controller]
Map<String,String> errors = new HashMap<>();
if ( member.getPasswd() != null && member.getPasswd().length() >= 8 ){
if (member.getPasswd().matches("[a-zA-Z]+")){
errors.put("passwd", "비밀번호는 숫자와 특수문자를 포함해야합니다.");
}else if (member.getPasswd().matches("[0-9]+")) {
errors.put("passwd", "비밀번호는 문자와 특수문자를 포함해야합니다.");
}else if (member.getPasswd().matches("[a-zA-Z0-9]+")){
errors.put("passwd", "비밀번호는 특수문자를 포함해야합니다.");
}
}else{
errors.put("passwd", "비밀번호는 문자,숫자,특수문자를 포함하여 8자이상이어야합니다.");
}
if(!errors.isEmpty()){
model.addAttribute("errors",errors);
return "member/member-form";
}
[Thymeleaf]
<input 태그>
th:class="${errors?.containsKey('[모델attribute값]')} ? '[오류형태]' : '[정상형태]'"
<div 태그>
th:if = "${errors?.containsKey('[모델 attribute값]')}" th:text="${errors'[모델 attribute값]'}"
(* th:if 는 model.addAttribute로 값이 넘어왔는지 확인)
(* th:text는 값이 넘어왔을경우, 해당 MAP에 기록되어있는 오류메시지)
<!-- [html] -->
<div class="form-group mb-3">
<label for="passwd">Password:</label>
<input type="password" id="passwd" th:field="*{passwd}" th:class="${errors?.containsKey('passwd')}? 'form-control field-error':'form-control'", class="form-control">
<div class="field-error" th:if="${errors?.containsKey('passwd')}" th:text="${errors['passwd']}"> 비밀번호 입력 오류 </div>
</div>
BindingResult
- BindingResult는 @ModelAttribute 뒤에 바로 인자값으로 들어와야함 (spring 인지 가능)
- BindingResult의 경우, Model에 자동으로 포함됨
- FieldError를 통해서, 에러값 넣기 (HashMap역할)
- FieldError(String ObjectName, String Field, String DefaultMessage) {}
- objectName : @ModelAttribute 이름
- field : 오류가 발생한 필드 이름
- defaultMessage : 오류 기본 메시지
- FieldError(String ObjectName, String field, Object rejectedValue, boolean bindingFailure, @Nullable String[] codes, @Nullable Object[] argument, @Nullable String defaultMessage(
- ObjectName : 오류가 발생한 객체 이름
- field : 오류 필드
- rejectedValue : 사용자가 입력한 값
- bindingFailure : 타입 오류 같은 바인딩 실패인지 검증실패인지 구분값
- codes : 메시지 코드
- argument : 메시지에서 사용하는 인자
- defaultMessage : 기본 오류 메시지
- 추가) ObjectError(String objectName, String DefaultMessage){}
- objectName : @ModelAttribute 이름
- defaultMessage : 오류기본메시지
- 특정필드 이외의 오류가 필요한경우 사용
- thymeleaf의 th:errorclass 및 th:errors를 이용하여 표현
public String registryMember(@ModelAttribute Member member, BindingResult bindingResult){
//...
if ( member.getPasswd() != null && member.getPasswd().length() >= 8 ){
if (member.getPasswd().matches("[a-zA-Z]+")){
bindingResult.addError(new FieldError("member","passwd", "비밀번호는 숫자와 특수문자를 포함해야합니다."));
}else if (member.getPasswd().matches("[0-9]+")) {
bindingResult.addError(new FieldError("member", "passwd", "비밀번호는 문자와 특수문자를 포함해야합니다"));
}else if (member.getPasswd().matches("[a-zA-Z0-9]+")){
bindingResult.addError(new FieldError("member","passwd", "비밀번호는 특수문자를 포함해야합니다 "));
}
}else{
bindingResult.addError(new FieldError("member","passwd", "비밀번호는 문자,숫자,특수문자를 포함하여 8자이상이어야합니다."));
}
if (bindingResult.hasErrors()){
return "member/member-form";
}
//... 성공시나리오
}
th:errors : 해당 필드에 오류가 있는경우 태그 출력 (th:if의 편의 버전)
th:errorclass : th:field에서 지정한 필드에 오류가 있으면 class 정보를 추가함
<div class="form-group mb-3">
<label for="passwd">Password:</label>
<input type="password" id="passwd" th:field="*{passwd}" th:errorclass="field-error" class="form-control">
<div class="field-error" th:errors="*{passwd}"> 비밀번호 입력 오류 </div>
</div>
애노테이션 사용
public class BindigResultValidate implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Member.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
Member member = (Member) target;
BindingResult bindingResult = (BindingResult) errors;
//로그인ID
if ( !StringUtils.hasText(member.getLoginId())){
bindingResult.addError(new FieldError("member", "loginId","로그인 아이디는 필수입력입니다."));
}
//이름
if (!StringUtils.hasText(member.getName())){
bindingResult.addError(new FieldError("member", "name", "이름은 필수 입력값입니다."));
}
//비밀번호
if ( member.getPasswd() != null && member.getPasswd().length() >= 8 ){
if (member.getPasswd().matches("[a-zA-Z]+")){
bindingResult.addError(new FieldError("member","passwd", "비밀번호는 숫자와 특수문자를 포함해야합니다."));
}else if (member.getPasswd().matches("[0-9]+")) {
bindingResult.addError(new FieldError("member", "passwd", "비밀번호는 문자와 특수문자를 포함해야합니다"));
}else if (member.getPasswd().matches("[a-zA-Z0-9]+")){
bindingResult.addError(new FieldError("member","passwd", "비밀번호는 특수문자를 포함해야합니다 "));
}
}else{
bindingResult.addError(new FieldError("member","passwd", "비밀번호는 문자,숫자,특수문자를 포함하여 8자이상이어야합니다."));
}
}
}
@PostMapping("/form")
public String registryMember(@ModelAttribute Member member, BindingResult bindingResult, Model model){
//생성자 주입으로 넣기
bindingResultValidate.validate(member,bindingResult);
if (bindingResult.hasErrors()){
return "member/member-form";
}
memberRepository.save(member);
return "redirect:/members/login";
}
- @Validated로 적용
- bindingResultValidate 미리 등록
- @InitBinder -> WebDataBinder로 validate 등록
- @Validated 를 @ModelAttribute앞에 추가
@InitBinder
private void init(WebDataBinder webDataBinder){
webDataBinder.addValidators(bindingResultValidate);
}
@PostMapping("/form")
public String registryMember(@Validated @ModelAttribute Member member, BindingResult bindingResult, Model model){
if (bindingResult.hasErrors()){
return "member/member-form";
}
memberRepository.save(member);
return "redirect:/members/login";
}