[Spring] How - validation을 이용해서 BindingResult 활용하기.

하쮸·2025년 9월 9일

Error, Why, What, How

목록 보기
28/68

1. validation

implementation 'org.springframework.boot:spring-boot-starter-validation'
  • 스프링 부트에서 유효성 검증(Validation)을 지원하는 라이브러리.
    • 에노테이션 기반으로 유효성을 검사함.
      • Ex) @NotBlank, @Size, @Email, @Min, @Max 등 다양한 에노테이션을 사용하여 필드에 대한 유효성 검사 규칙을 정의할 수 있음.

Ex

@Getter
@Setter
public class ArticleDto {
    @NotBlank(message = "제목은 필수 입력 항목입니다.")
    @Size(max = 100)
    private String title;

    @NotBlank(message = "내용은 필수 입력 항목입니다.")
    private String content;
}

2. BindingResult.

    @PostMapping("/create")
    public String createArticle(@Valid ArticleDto articleDto, BindingResult bindingResult) {
        // BindingResult : @Valid 에노테이션의 검증 결과를 담고 있는 객체.
        if (bindingResult.hasErrors()) {
            return "/article_form";
        }
        articleService.createArticle(articleDto.getTitle(), articleDto.getContent());
        return "redirect:/article/list";
    }
  • 컨트롤러 메서드의 매개변수에 @ValidBindingResult를 추가했음.
    • 이때 BindingResult는 항상 @Valid 매개변수 바로 뒤에 위치해야됨.
      즉, 검증할 객체 바로 뒤에 위치해야됨.
  • 분기문을 통해 bindingResult.hasErrors(), 즉 BindingResult에 오류가 담겨있을 경우에는
    article_form 뷰 템플릿을 return해서 글쓰기 화면으로 돌아감.

3. 오류 메시지를 클라이언트에 출력.

<!-- layout.html을 상속 받도록 설정. -->
<html layout:decorate="~{layout}">
    <!-- 부모 템플릿(layout.html)의 layout:fragment="content" 부분에 삽입되도록 설정. -->
    <div layout:fragment="content" class="container my-3">
        <h5 class="my-3 border-bottom pb-2">게시판 글쓰기</h5>
        <form th:action="@{/article/create}" method="post" th:object="${articleDto}">
            <div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
                <div th:each="error : ${#fields.allErrors()}" th:text="${error}"></div>
            </div>
            <div class="mb-3">
                <label for="title" class="form-label">제목</label>
                <input type="text" placeholder="제목을 입력해 주세요." name="title" id="title" class="form-control">
            </div>
            <div class="mb-3">
                <label for="content" class="form-label">내용</label>
                <textarea name="content" placeholder="내용을 입력해 주세요." id="content" class="form-control" rows="10" style="resize: none"></textarea>
            </div>
            <button type="submit" class="btn btn-primary my-2">등록하기</button>
        </form>
    </div>
</html>
- 아무것도 입력하지 않고 하단에 등록하기 버튼을 누르면 우측처럼 출력됨.- 검증 메시지가 출력된 모습.

3-1. 보완하기.


  • 제목에만 'ㅇㅇㅇㅇ'을 입력하고 등록하기 버튼을 누르면 에러 메시지가 정상적으로 출력되지만
    기존에 입력해놨던 값들이 초기화 되어버림.

3-1-1. 기존 코드

<!-- layout.html을 상속 받도록 설정. -->
<html layout:decorate="~{layout}">
    <!-- 부모 템플릿(layout.html)의 layout:fragment="content" 부분에 삽입되도록 설정. -->
    <div layout:fragment="content" class="container my-3">
        <h5 class="my-3 border-bottom pb-2">게시판 글쓰기</h5>
        <form th:action="@{/article/create}" method="post" th:object="${articleDto}">
            <div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
                <div th:each="error : ${#fields.allErrors()}" th:text="${error}"></div>
            </div>
            <div class="mb-3">
                <label for="title" class="form-label">제목</label>
                <input type="text" placeholder="제목을 입력해 주세요." name="title" id="title" class="form-control">
            </div>
            <div class="mb-3">
                <label for="content" class="form-label">내용</label>
                <textarea name="content" placeholder="내용을 입력해 주세요." id="content" class="form-control" rows="10" style="resize: none"></textarea>
            </div>
            <button type="submit" class="btn btn-primary my-2">등록하기</button>
        </form>
    </div>
</html>

3-1-2. 변경된 코드.

<!-- layout.html을 상속 받도록 설정. -->
<html layout:decorate="~{layout}">
    <!-- 부모 템플릿(layout.html)의 layout:fragment="content" 부분에 삽입되도록 설정. -->
    <div layout:fragment="content" class="container my-3">
        <h5 class="my-3 border-bottom pb-2">게시판 글쓰기</h5>
        <form th:action="@{/article/create}" method="post" th:object="${articleDto}">
            <div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
                <div th:each="error : ${#fields.allErrors()}" th:text="${error}"></div>
            </div>
            <div class="mb-3">
                <label for="title" class="form-label">제목</label>
                <input type="text" placeholder="제목을 입력해 주세요." th:field="*{title}" class="form-control">
            </div>
            <div class="mb-3">
                <label for="content" class="form-label">내용</label>
                <textarea placeholder="내용을 입력해 주세요." th:field="*{content}" class="form-control" rows="10" style="resize: none"></textarea>
            </div>
            <button type="submit" class="btn btn-primary my-2">등록하기</button>
        </form>
    </div>
</html>

  • 수정된 코드로 테스트해보면 기존에 입력해 놨던 값이 그대로 유지되는 모습을 확인할 수 있음.

4. 참고

profile
Every cloud has a silver lining.

0개의 댓글