Spring

[Spring] 필드 에러 메세지 표출하기 ( + Thymeleaf)

walwal_ 2023. 6. 18. 20:55

 

 

에러 메세지를 웹에 표출하기 위한 여러방법 (properties 이용하기 등등) 이 존재한다.

 

그 중 가장 간편하게(내 기준) 메세지를 띄우는 방법인 Entity 클래스의 검증 어노테이션을 이용하는 방법을 살펴보자아

 

 

 

 

결과

빈 값, 공백, 아이디 또는 비밀번호가 틀렸을 때 에러 메세지 표출.

 

 

 

 

 

 Gradle 라이브러리 추가하기


implementation 'org.springframework.boot:spring-boot-starter-validation'

 

 

spring-boot-starter-validation을 추가하면 여러 검증 어노테이션을 사용할 수 있게된다.

  • @NotBlank : 공백 또는 빈값만 있는 경우 허용하지 않음
  • @NotNull : null 허용 하지 않음
  • @Range(min = num1, max = num2) : num1 ~ num2 안의 값이여야함
  • @Max( num ) : 최대 num 까지만 허용
  • @Pattern(regexp = "정규식") : 패턴에 대한 허용 또는 허용하지 않음을 나타낼 때 

 

 

 

Entity 클래스

 

Member.java

@NoArgsConstructor
@Getter
@Setter
@Entity
public class Member {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @NotBlank(message = "빈 값일 수 없습니다.")
    @NotNull
    @Pattern(regexp="^[\\S]*$", message="아이디에 공백문자를 입력할 수 없습니다.")
    private String user;
    @NotNull
    @NotBlank(message = "빈 값일 수 없습니다.")
    private String password;
}

 

null이 허용될 수 없는 필드 값에 빈 값이 들어 왔을 때 :  "빈 값일 수 없습니다."
공백이 허용될 수 없는 필드 값에 공백 값이 들어 왔을 때 :  "공백일 수 없습니다."
라는 메세지를 보여주기 위해 위 와 같이 작성해 주기

 

 

 

Controller

 

MemberController

   @GetMapping("/login")
    public String loginView(Model model, Member member){
        model.addAttribute("member", member);
        return "login";
    }


    @PostMapping("/login")
    public String login(@Validated @ModelAttribute("member") Member member,BindingResult bindingResult){

	// 에러 처리
        if(bindingResult.hasErrors()){
            return "/login";
        }

        //글로벌 에러 처리
        boolean result = memberService.login(member);
        Member user = memberService.findByUser(member.getUser());

        if(user == null || !result){
            bindingResult.reject("loginFail","아이디 또는 비밀번호가 맞지 않습니다.");
            return "/login";
        }
        
        //로그인 성공
        else{
            return "redirect:/board/list";
        }
    }

 

login메서드를 살펴보자.

 

1. 매개변수

검증 대상(Member) 앞에 붙는 @Validated
@Validated 는 검증을 실행하는 어노테이션이고, Member클래스를 검증하게 된다.

검증 대상(Member) 뒤에 붙는 BindingResult
BindingResult는 @Validated에서 실행한 검증의 결과를 가지게 된다.

그렇기 때문에 꼬옥 아래 순서대로 코드를 작성해야한다..! 
 @Validated Member member BindingResult bindingResult 

 

 

2. 로직

bindingResult.hasErrors() 는 에러의 결과가 존재 하는지를 확인하는 메서드다.
만약 null값이 들어와 에러가 발생했다면, bindingResult 에 검증 결과가 담겨 bindingResult.hasErrors() 는 true가 되고 
login.html을 return하게 된다.

bindingResult.reject() 또는 bindingResult.rejectValue() 는 글로벌 에러를 추가해 준다.
에러코드에 대한 메세지를 설정하지 않았을 경우 defaultMessage를 사용해 오류 메세지를 보여주게 된다.
이렇게 생성한 글로벌 에러는 html에서
${#fields.hasGlobalErrors()} 와 같이 사용 할 수 있게 된다.

 

 

 

 

html 파일 수정하기

login.html

<form action="/login" method="post" th:object="${member}">

    <div>
        ID :
        <input placeholder="ID를 입력하세요" type="text" th:field="*{user}" th:errorclass="error-class">
        <p class="error-class" th:errors="${member.user}"></p>

        PW :
        <input type="password" placeholder="PW를 입력하세요" th:field="*{password}" th:errorclass="error-class" >
        <p class="error-class" th:errors="${member.password}"></p>

        <!-- 글로벌 에러 -->
        <div th:if="${#fields.hasGlobalErrors()}">
            <p class="error-class" th:each="err : ${#fields.globalErrors()}" th:text="${err}"/>
        </div>
    </div>
    
    <input type="submit" value="login">
</form>
<form th:action="@{/sign}" method="get">
    <input type="submit" value="sign">
</form>

 

 

th:errorclass="error-class" 는 bindingResult에 결과가 있는 경우 "error-class" 로 input 요소의 클래스 이름을 변경하게 한다. (에러가 발생할 때 html style 적용에 유용함!)

th:errors="${member.user}" bindingResult에 결과를 text로 보여줄 때 사용한다. Member 클래스의 필드에 대한 검증을 하고, 바인딩 된 객체의 오류 메세지를 표시할때 사용한다.

th:if="${#fields.hasGlobalErrors()}" 는 Controller에서 생성한  글로벌 오류(bindingResult.reject 등)가 있는지 확인한다.

th:each="err : ${#fields.globalErrors()}" th:text="${err}" 는 #fields 에서 가져온 글로벌 오류 메세지들을 순회하고 그 메세지를 text로 나타내게 한다.

 

 

 

(참고) th:field

더보기

th:field는 필드의 값과 오류 메세지를 자동으로 매칭하고 처리해준다.

( 참고로 th:field가 있으면 html에서 id="user" name="user" 코드를 생략할 수 있다. )

 

th:field 를 사용하려면 꼭 th:object 를 정의해야 한다.

th:object는 바인딩할 객체를 지정하는 데 사용된다. th:field는 th:object에 지정된 객체(member)의 필드(user, password)와 입력 요소를 연결한다.