질문들의 List는 출력하지만, 질문을 등록할 수 있는 방법은 DB에서 직접 적어 넣을 수 있는 방법밖에는 없다.
하지만, <form>을 사용해 질문을 직접 web application에서 만들 수 있다.
1. 질문 등록하기 버튼 생성
question_list.html 파일 가장 아래 button을 넣을 곳에 아래 코드를 넣어준다.
<a th:href="@{/question/create}" class="btn btn-primary">질문 등록하기</a>
bootstrap을 사용한 버튼이 생성되고, question/create로 Link되었다.
2. URL Mapping
우리는 question/create란 URL로 이동할 수 없다. 왜냐하면 Controller에서 Mapping을 해주지 않았기 때문이다.
Mapping을 위해 QuestionController에 아래 코드를 적어주자.
@GetMapping("/create")
public String questionCreate() {
return "question_form";
}
@GetMapping은 GET요청만 받을 수 있고, URL에 /question을 넣지 않은 이유는 class의 URL Prefix로 /question을 넣어주었기 때문이다.
이 questionCreate메서드는 question_form을 랜더링 해준다.
그러기 위해서는
3. question_form.html 파일 생성
layout template을 상속해주고, 질문을 등록할 수 있는 form을 생성한다.
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container">
<h5 class="my-3 border-bottom pb-2">질문등록</h5>
<form th:action="@{/question/create}" method="post">
<div class="mb-3">
<label for="subject" class="form-label">제목</label>
<input type="text" id="subject" name="subject" class="form-control">
</div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea name="content" id="content" class="form-control" rows="10"></textarea>
</div>
<input type="submit" value="저장하기" class="btn btn-primary my-2">
</form>
</div>
</html>
여기서 label의 for는 label클릭 시 label의 for과 같은 이름의 id를 활성화 시킬 수 있다.
하지만, submit을 할 경우 저장이 되지 않고 오류가 발생하는데, GET방식을 Mapping했지만, 보내는 것은 POST방식으로 보내주기 때문이다.
고로 GET으로 받아오는 것이 없어 오류가 발생하는 것이다.
4. POST 요청 받을 수 있도록 수정
POST요청을 받을 수 있도록 수정해보자.
POST요청을 받기 위해 Controller에서 PostMapping을 진행해보자.
그리고, 위와 다르게 <form>에서 받아온 파라미터를 받아온다.
@PostMapping("/create")
public String questionCreate(@RequestParam String subject, @RequestParam String content) {
// TODO 질문을 저장한다.
return "redirect:/question/list"; // 질문 저장후 질문목록으로 이동
}
이제 질문 저장의 기능을 동작하게 하는 시간이다.
5. 서비스에 기능 추가
질문 엔티티에 객체를 새로 추가할 수 있도록 설정자를 사용한다.
public void create(String subject, String content) {
Question q = new Question();
q.setSubject(subject);
q.setContent(content);
q.setCreateDate(LocalDateTime.now());
this.questionRepository.save(q);
}
그 후 Controller에서 service로 만든 기능을 추가한다.
@PostMapping("/create")
public String questionCreate(@RequestParam String subject, @RequestParam String content) {
this.questionService.create(subject, content);
return "redirect:/question/list";
}
++ <form>의 빈 값을 보내지 않도록 하기위한 방법
spring boot에 validation이라는 라이브러리가 있다. 이것을 이용해 검증해보자.
implementation 'org.springframework.boot:spring-boot-starter-validation'
이 라이브러리를 이용하면 다음과 같은 어노테이션을 사용할 수 있다.
이제 화면에서 전달되는 입력 값을 검증하기 위해 form class가 새로 필요하다.
package com.mysite.sbb.question;
import javax.validation.constraints.NotEmpty;
import javax.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;
}
QuestionForm을 Controller에서 동작할 수 있도록 수정한다.
@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";
}
Controller에 매개변수를 파라미터로 보낸 subject와 content가 아닌 QuestionForm 객체로 보내면, subject와 content를 가진 폼이 전송될 경우 QuestionForm의 subject와 content 속성이 자동으로 바인딩이 된다.
QuestionForm 앞에 @Valid 어노테이션을 적용하면 QuestionForm 내 @NotEmpty와 @Size 어노테이션이 검증해준다.
BindingResult 객체는 @Valid 어노테이션으로 인해 검증이 수행된 결과를 의미한다.
그러면 폼이 있는 웹에서도 오류메시지를 출력시킬 수 있도록 하면 어떨까?
그러기 위해서 아래 코드를 입력한다.
<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>
.....
...
th:object속성을 사용하여 폼의 속성들이 QuestionForm 속성으로 구성된다는 것을 thymeleaf 엔진에 알리고 시작한다.
th:if="${#fields.hasAnyErrors()}" 는 true가 검증에 실패한 경우, false는 검증에 성공한 경우를 의미한다. 만약 검증에 실패할 경우 해당 영역에 표시될 수 있도록 했다.
th:each="err : ${#fields.allErrors()}" th:text="${err}" 는 검증에 실패한 모든 오류를 출력시키는 것을 의미한다.
GET요청을 받았을 경우에도 th:object 속성으로 인해 QuestionForm 객체가 필요하여 파라미터로 넣어주어야 한다.
+++ 오류 발생 시 값 지워지는 것 방지하기
오류가 발생하면 이미 입력한 값들이 다 지워지는 현상이 발생한다.
그러기 위해서는 어떻게 해야할까?
간단한 template 수정을 통해 해결할 수 있다.
name 속성을 th:field 속성으로 변경하여 속성들이 모두 자동으로 생성되고, 기존 값을 채워 넣어 유지할 수 있다.
'Spring' 카테고리의 다른 글
[Spring]Spring 시작해보기(14) - 에러메시지 출력 창 중복 제거 (0) | 2022.08.17 |
---|---|
[Spring]Spring 시작해보기(13) - 답변 등록 오류 (0) | 2022.08.17 |
[Spring]Spring 시작해보기(11) - Template 상속 (0) | 2022.08.16 |
[Spring]Spring 시작해보기(10) - BootStrap, Font 적용 (0) | 2022.08.16 |
[Spring]Spring 시작해보기(9) - 답글 달기 form 생성 및 출력 (0) | 2022.08.16 |