

<!DOCTYPE html>
<html layout:decorate="~{layout}" xmlns:layout="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div layout:fragment="content" class="container my-3">
<table class="table">
<thead class="table-dark">
<tr>
<th>번호</th>
<th>제목</th>
<th>작성일시</th>
</tr>
</thead>
<tbody>
<tr th:each="question, loop : ${questionList}">
<td th:text="${loop.count}"></td>
<td>
<a th:href="@{|detail/${question.id}|}" th:text="${question.subject}"></a>
</td>
<td th:text="${#temporals.format(question.createDate, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
</tbody>
</table>
<a th:href="@{/question/create}" class="btn btn-primary">질문 등록하기</a>
</div>
</body>
</html>
a링크를 타고 들어갈 때는 getmapping이니까 컨트롤러에
내용을 추가해주고,
//QuestionController.java
@GetMapping("/create")
public String questionCreate() {
return "question_form"; // 질문 저장후 질문목록으로 이동
}

이 폼 화면을 a링크 타고 들어갈 때 화면이 나올 수 있도록 한다.
<html layout:decorate="~{layout}" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:layout="http://www.w3.org/1999/xhtml">
<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" name="subject" id="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>
저장하기 버튼을 누르게 되면 method 방식이 post이므로, 컨트롤러에 get 방식이 아닌 post방식으로도 하나 더 추가해준다.
폼에 글을 쓰면 화면에 보이다시피 제목과 내용을 담아서 전달해주어야 한다.
@GetMapping("/create")
public String questionCreate() {
return "question_form"; // 질문 저장후 질문목록으로 이동
}
👉 차이점이 있다면 목록 화면에서 a링크 타고 넘어올 땐 get,
질문 등록 폼에 내용을 담아서 보낼 때는 (html에 post 방식이었기에) PostMapping으로 연결해준다.
💡 메서드명이 같아도 매개변수의 형태 다를 경우에 사용하는 메서드 오버로딩
@PostMapping("/create")
public String questionCreate(@RequestParam String subject, @RequestParam String content){
// TODO: 질문을 저장한다.
return "redirect:/question/list"; //질문 저장 후 질문 목록으로 이동
}
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);
}
해당 기능을 만들어준다.
@PostMapping("/create")
public String questionCreate(@RequestParam String subject, @RequestParam String content){
// TODO: 질문을 저장한다.
👉 this.questionService.create(subject, content);
return "redirect:/question/list"; //질문 저장 후 질문 목록으로 이동
}




//build.gradle
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.projectlombok:lombok:1.18.26'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
✅ implementation 'org.springframework.boot:spring-boot-starter-validation'
}

출처 : https://wikidocs.net/161873
package com.mysite.sbb.question;
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;
}
빈값 막기, 최대 길이를 확인하기 위해서는 질문 폼 클래스가 필요하다.
✅ @Size(max=200)은 최대 길이가 200 바이트를 넘으면 안된다는 의미이다.

@PostMapping("/create")
public String questionCreate(👉@Valid QuestionForm questionForm, BindingResult bindingResult){
// TODO: 질문을 저장한다.
if (bindingResult.hasErrors()) {
return "question_form";
}
this.questionService.create(questionForm.getSubject(), questionForm.getContent());
return "redirect:/question/list"; //질문 저장 후 질문 목록으로 이동
}
기존에 @RequestParam String subject, @RequestParam String content 받던 것을
👉@Valid QuestionForm questionForm, BindingResult bindingResult 로 변경했다.
subject, content 항목을 지닌 폼이 전송되면 QuestionForm 의 subject, content 속성이 자동으로 바인됭 된다.