점프 투 스프링부트를 참고하여 학습,작성하였습니다.
1.Validation 의존성 추가
폼 클래스로 입력값을 검증받기 위해 데이터 검증을 위한 유효성 검사를 수행하기 위한 라이브러리인 Validation 의존성을 추가한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- @NotNull:해당 필드가 null이 아니어야한다.
- @Size: 문자열, 컬렉션, 배열 등의 크기를 제한
- @Min / @Max : 숫자 값의 최소/최대 값을 제한
- @Pattern : 정규 표현식으로 문자열 값을 제한
- @Email :이메일 형식을 검사
- @Future / @Past : 날짜가 미래 또는 과거인지 검사
- @NotBlank :문자열이 null이 아니고, 길이가 0이 아니며, 공백 문자가 아닌지 검사
- @NotEmpty :문자열, 컬렉션 등이 null이 아니고, 비어있지 않은지 검사
- @Positive / @Negative / @PositiveOrZero / @NegativeOrZero :숫자가 양수, 음수, 양수 또는 0, 음수 또는 0인지 검사
2.폼클래스 제작
애너테이션을 사용하여 질문 등록의 subject와 content에 데이터 검증을 추가한다.
다음은 NotEmpty(문자열, 컬렉션 등이 null이 아니고, 비어있지 않은지 검사), 최대 길이를 200으로 제한했다.
package com.example.form;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class QuestionForm {
@NotEmpty(message="제목은 필수항목입니다.")
@Size(max=200)
private String subject;
@NotEmpty(message="내용은 필수항목입니다.")
private String content;
}
3.컨트롤러 수정
다음 questionCreate 클래스의 매개변수는 QuestionForm 객체를 매개변수로 받아 @Valid 애너테이션으로 Spring에게 questionForm 객체의 유효성을 검사하도록 지시한뒤 BindingResult 객체에 유효성 검사 결과를 담는다.
그후 BindingResult의 에러발생을 검사(유효한지)하고 실패시 다시 question_form으로 돌아간다.
성공시 questionForm의 Subject, Content를 매개변수로 하여 questionService에 전달하여 새로운 질문을 생성한다.
@PostMapping("/create")
public String questionCreate(@Valid QuestionForm questionForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "question_form";
}
this.questionService.create(questionForm.getSubject(), questionForm.getContent());
return "redirect:/question/list"; // 질문 저장후 질문목록으로 이동
}
4.템플릿 수정
th:object="${questionForm}으로 폼에서 사용할 데이터는 questionForm이라는 객체에 저장하게한다.
그후 반복문을 통해 유효성 검사를 한 에러 메세지를 출력하는 부분을 추가한다.
<html layout:decorate="~{base}">
<div layout:fragment="content">
<h5>질문등록</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>
<label for="subject" class="form-label">제목</label>
<input type="text" name="subject" id="subject" class="form-control">
<p></p>
<label for="content" class="form-label">내용</label>
<textarea name="content" id="content" class="form-control" rows="10"></textarea>
<input type="submit" value="저장하기" class="btn btn-primary my-2">
</form>
</div>
5.GetMapping 수정
th:object="${questionForm} 를 사용했기때문에 GET 방식으로 URL이 요청되면, 이 요청은 컨트롤러의 @GetMapping 메서드에 의해 처리된다. 이때, 템플릿이 렌더링되기 전에 모델에 questionForm 객체가 추가되어 있어야 한다.
@GetMapping("/create")
public String questionCreate(QuestionForm questionForm) {
return "question_form";
}
6.문자 그대로 남아있게
name="subject", name="content" 대신 th:field를 사용하여 오류 발생시에도 문자가 남아있게 한다.
<input type="text" th:field="*{subject}" class="form-control">
<p></p>
<label for="content" class="form-label">내용</label>
<textarea th:field="*{content}" class="form-control" rows="10"></textarea>
7.답변도 똑같이
7-1.폼
package com.example.form;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AnswerForm {
@NotEmpty(message = "내용은 필수항목입니다.")
private String content;
}
7-2.컨트롤러
public class AnswerController {
private final AnswerService answerService;
private final QuestionService questionService;
@PostMapping("/create/{id}")
public String createAnswer(Model model, @PathVariable("id") Integer id, @Valid AnswerForm answerForm, BindingResult bindingResult) {
Question question = this.questionService.getQuestion(id);
if (bindingResult.hasErrors()) {
model.addAttribute("question", question);
return "question_detail";
}
this.answerService.create(question, answerForm.getContent());
return String.format("redirect:/question/detail/%s", id);
}
}
7-3.템플릿
<form th:action="@{|/answer/create/${question.id}|}" th:object="${answerForm}" method="post">
<div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}"></div>
<div th:each="err : ${#fields.allErrors()}" th:text="${err}" /></div>
<textarea th:field="*{content}" rows="15"></textarea>
<input type="submit" value="답변등록">
</form>
7-4.템플릿수정에 따른 컨트롤러 수정
@GetMapping(value = "/detail/{id}")
public String detail(Model model, @PathVariable("id") Integer id, AnswerForm answerFor) {
Question question = this.questionService.getQuestion(id);
model.addAttribute("question", question);
return "question_detail";
}
8.오류 템플릿 분리
공통되는 오류 템플릿을 분리하여 사용한다.
<div th:fragment="formErrorsFragment" class="alert alert-danger"
role="alert" th:if="${#fields.hasAnyErrors()}">
<div th:each="err : ${#fields.allErrors()}" th:text="${err}" />
</div>
공통 템플릿을 적용한다.
<form th:action="@{|/answer/create/${question.id}|}" th:object="${answerForm}" method="post">
<div th:replace="~{form_errors :: formErrorsFragment}"></div>
<textarea th:field="*{content}" rows="15"></textarea>
<input type="submit" value="답변등록">
</form>
<form th:action="@{/question/create}" th:object="${questionForm}" method="post">
<div th:replace="~{form_errors :: formErrorsFragment}"></div>
</div>
<label for="subject" class="form-label">제목</label>
<input type="text" th:field="*{subject}" class="form-control">
<p></p>
<label for="content" class="form-label">내용</label>
<textarea th:field="*{content}" class="form-control" rows="10"></textarea>
<input type="submit" value="저장하기" class="btn btn-primary my-2">
</form>
'BackEnd > SpringBoot' 카테고리의 다른 글
[Spring Boot] 부트스트랩, 대량 테스트 데이터, 꾸미기 (0) | 2024.07.04 |
---|---|
[Spring Boot] 질문등록 (0) | 2024.07.04 |
[Spring Boot] 표준 HTML 구조 상속 (0) | 2024.07.03 |