사이먼's 코딩노트
[SpringBoot] 유효성 체크 및 에러 처리(2) 본문
[유효성 체크 및 에러 처리]
- 질문 등록 시 제목이나 내용을 입력하지 않고 질문이 등록되는것을 방지하고자 폼 클래스를 통해 유효성 체크하는 기능을 추가하고 로컬 서버를 재실행하면 에러 메시지가 붉은 색으로 표시되는 것을 확인할 수 있다.
- 하지만 여기서 한 가지 문제를 발견했다.
- 예를 들어 제목을 입력하고 내용을 비워둔 채로 '저장하기' 버튼을 클릭하면 에러 메시지가 표시되면서 이미 입력했던 제목 텍스트가 사라진다는 점이다.
- 이번에는 입력한 제목이나 내용의 텍스트가 남아있도록 에러 처리를 해봅시다.
- 해당 에러를 처리하기 위해 아래는 question_form.html 템플릿에서 코드를 수정한 모습이다.
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container">
<h5 class="my-3 border-bottom pb-2">질문등록</h5>
<form th:action="@{/question/create}" th:object="${questionForm}" method="post">
<div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
<div th:each="err : ${#fields.allErrors()}" th:text="${err}" />
</div>
<div class="mb-3">
<label for="subject" class="form-label">제목</label>
<input type="text" th:field="*{subject}" class="form-control">
</div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea th:field="*{content}" class="form-control" rows="10"></textarea>
</div>
<input type="submit" value="저장하기" class="btn btn-primary my-2">
</form>
</div>
</html>
- 코드가 수정된 부분은 총 2군데이고, 각 제목과 내용의 입력란인 input 태그와 textarea 태그의 속성 값을 수정하였다.
- 기존에는 name="subject" id="subject" 와 name="content" id="content" 를 없애고 th:field="*{subject}" 와 th:field="*{content}를 추가하였다.
- 이렇게 하면 해당 태그의 name, id, value 속성이 모두 자동으로 생성되고 타임리프가 value 속성에 기존에 입력된 값을 채워 넣어 오류가 발생하더라도 기존에 입력한 값이 유지된다.
- 만약 th:field 속성을 사용하기 싫다하면 name, id 속성은 그대로 남겨두고, value 속성을 추가하면 된다.
- 해당 코드를 적용했다면 이제는 제목 또는 내용에 입력한 값은 계속 유지될 것이다.
[답변 등록 유효성 체크]
- 지금까지는 질문 등록 부분에만 QuestionForm.java 클래스를 생성하여 유효성 체크를 적용하였다면, 이번에는 답변 등록 부분에도 동일한 유효성 체크를 AnswerForm.java 클래스를 생성하여 적용해봅시다.
- 아래는 답변 등록하는 부분에 유효성 체크 확인을 위한 AnswerForm.java 클래스를 생성하여 아래와 같이 코드를 추가해봅시다.
package com.sbs.sbb.Answer;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AnswerForm {
@NotBlank(message="내용은 필수항목입니다.")
@Size(max=20000, message = "제목을 20,000자 이하로 입력해주세요.")
private String content;
}
- 질문 등록 부분과는 다른 점은 답변 등록에는 내용만 포함되기 때문에 content 변수 하나만을 선언하였다.
- content 속성에는 @NotBlank와 @Size 어노테이션을 적용하여 에러 발생 시 표시되는 메시지를 작성하였다.
- 다음은 AnswerForm을 컨트롤러에서 사용할 수 있도록 아래와 같이 AnswerController.java 클래스에 코드를 수정해봅시다.
@PostMapping("/create/{id}")
public String createAnswer(Model model, @PathVariable("id") Integer id, @Valid AnswerForm answerForm, BindingResult bindingResult) {
Question q = this.questionService.getQuestion(id);
if(bindingResult.hasErrors()) {
model.addAttribute("question", q);
return "question_detail";
}
Answer answer = this.answerService.create(q, answerForm.getContent());
return "redirect:/question/detail/%d".formatted(id);
}
- 유효성 체크를 위해서 @Valid 어노테이션을 적용하였고, 이를 적용함으로써 AnswerForm의 @NotBlank, @Size 등으로 설정한 검증 기능이 동작하도록 하였다.
- 검증이 수행된 결과를 의미하는 객체인 BindingResult도 @Valid 어노테이션 뒤에 매개변수로 받아오면서 bindingResult.hasErrors() 메서드를 조건문을 통해 호출하여 에러가 있는 경우 다시 입력을 하도록하고 에러가 없을 때 답변이 저장되도록 하였다.
- 화면 페이지에 에러 메시지가 잘 표시가 되도록 답변 등록하는 페이지인 question_detail.html 템플릿에 아래와 같이 코드를 수정하였다.
<form th:action="@{|/answer/create/${question.id}|}" th:object="${answerForm}" method="post" class="my-3">
<div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
<div th:each="err : ${#fields.allErrors()}" th:text="${err}" />
</div>
<textarea th:field="*{content}" rows="10" class="form-control"></textarea>
<input type="submit" value="답변등록" class="btn btn-primary my-2">
</form>
- 위 코드는 question_detail.html 중 가장 하단의 textarea 태그를 통해 내용을 입력하는 부분과 '답변등록' 버튼이 구현된 부분이다.
- 질문 작성때와 마찬가지로 답변 등록 form의 입력 항목과 AnswerForm을 타임리프에 연결하기 위해 th:object 속성을 추가하였다.
- 그리고 검증이 실패할 경우 #fields.hasAnyErrors()와 #fields.allErrors()를 사용하여 에러 메시지를 표시하도록 하였고, 작성된 내용의 텍스트가 남아있도록 th:field="*{content}" 속성 값을 추가하였다.
- 위의 나열된 모든 코드들을 각 클래스에 작성하고 로컬 서버를 다시 실행해보면 답변 등록 시에도 폼 클래스를 이용한 유효성 체크가 적용되었을 것이다.
[프론트단에서 직접 유효성 체크]
- 지금까지 적용한 것처럼 폼 클래스를 사용하여 유효성 체크를 할 수 있지만, 사실 폼 필드 속성으로 HTML인 프론트단에서 직접 유효성 체크를 할 수도 있다.
- 아래는 HTML에서 직접 유효성 체크가 가능하도록 바로 위에서 적용한 question_detail.html 템플릿에서 코드를 추가하였다.
<textarea required maxlength="20000" placeholder="내용(20,000자 이하)" th:field="*{content}" rows="10" class="form-control"></textarea>
- 위 코드와 같이 유효성을 체크하고자 하는 태그 안에서 속성 값을 required를 지정하면 프론트단에서 직접 체크를 할 수 있다.
- required maxlength="20000"은 최대 20,000자 까지라는 뜻이고, 아무것도 작성하지 않을 때도 유효성 체크를 자동으로 검사하게 된다.
- 해당 코드를 적용하여 로컬 서버를 다시 실행해보면 이제는 붉은 화면이 아닌 하나의 아이콘과 함께 경고 텍스트가 화면에 표시된다.
[에러 출력 부분 공통 템플릿 적용]
- 마지막으로 에러 메시지를 출력하는 HTML 코드는 질문, 답변 등록에서 모두 반복해서 사용되고 있어 공통 템플릿으로 만들어봅시다.
- 아래 코드는 question_form.html과 question_detail.html에서 공통적으로 사용된 에러 메시지 출력 코드이다.
<div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
<div th:each="err : ${#fields.allErrors()}" th:text="${err}" />
</div>
- 중복을 막는 것은 앞으로 코드를 구현하거나 유지보수를 할 때 굉장히 중요한 부분이다.
- 앞으로 추가로 만들 템플릿에도 이와 같은 에러를 표시하는 부분이 필요할 것이고, 더 편리하게 관리하기 위해서 에러 메시지 출력 부분의 부모 격인 템플릿을 생성해봅시다.
- 아래는 form_erros.html 라는 템플릿을 새로 생성하여 작성한 코드이다.
<div th:fragment="formErrorsFragment" class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
<div th:each="err : ${#fields.allErrors()}" th:text="${err}" />
</div>
- 상위 div 태그에 th:fragment="formErrorsFragment" 속성을 추가하여 다른 템플릿에서 해당 영역을 사용할 수 있도록 설정하였다.
- question_form.html와 question_detail.html 템플릿에 해당 부모 템플릿을 상속받으려면 에러 메시지가 출력되는 태그 자리에 아래의 코드를 대체하면된다.
<div th:replace="~{form_errors :: formErrorsFragment}"></div>
반응형
'Java > SpringBoot' 카테고리의 다른 글
[SpringBoot] 게시물 번호 정렬 / 답변 개수 표시 (0) | 2024.05.13 |
---|---|
[SpringBoot] 내비게이션 바 / 페이징 (2) | 2024.05.13 |
[SpringBoot] Question 등록 / 유효성 체크 및 에러 처리(1) (0) | 2024.05.12 |
[SpringBoot] Bootstrap 및 폰트 적용 / 공통 layout 추가 (0) | 2024.05.12 |
[SpringBoot] Answer 저장 및 출력 / 스타일시트 적용 (0) | 2024.05.09 |