BoardController
package hello.board.controller.board;
import hello.board.controller.SessionConst;
import hello.board.entity.Board;
import hello.board.entity.User;
import hello.board.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
@Controller
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/board")
public class BoardController {
private final BoardService boardService;
@GetMapping
public String postList(Model model) {
model.addAttribute("list", boardService.findAll());
return "board/postList";
}
@GetMapping("/{postId}")
public String postView(@PathVariable Long postId, Model model) {
log.info("postView");
Board post = boardService.findOne(postId).orElseThrow();
model.addAttribute("post", post);
return "board/post";
}
@GetMapping("/register")
public String registerForm(@ModelAttribute PostForm postForm) {
return "board/registerForm";
}
@PostMapping("/register")
public String register(@Valid @ModelAttribute PostForm postForm, BindingResult bindingResult, @SessionAttribute(name = SessionConst.LOGIN_USER, required = false) User loginUser, RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
log.info("errors = {}", bindingResult);
return "board/registerForm";
}
Long registerId = boardService.register(postForm.getTitle(), postForm.getContent(), loginUser.getId());
redirectAttributes.addAttribute("postId", registerId);
return "redirect:/board/{postId}";
}
@GetMapping("/{postId}/edit")
public String editForm(@PathVariable Long postId, Model model) {
Board post = boardService.findOne(postId).orElseThrow();
PostForm postForm = new PostForm();
postForm.setTitle(post.getTitle());
postForm.setContent(post.getContent());
model.addAttribute("postForm", postForm);
model.addAttribute("postId", postId);
return "board/editForm";
}
@PostMapping("/{postId}/edit")
public String edit(@PathVariable Long postId, @Valid @ModelAttribute PostForm postForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
log.info("errors = {}", bindingResult);
return "board/editForm";
}
boardService.updateBoard(postId, postForm.getTitle(), postForm.getContent());
return "redirect:/board/{postId}";
}
@PostMapping("/{postId}/delete")
public String delete(@PathVariable Long postId) {
boardService.deleteById(postId);
return "redirect:/board";
}
}
register()
게시글 등록 메서드 수정RedirectAttributes
를 사용하여 새로 만들어진 게시글의 상세 조회 화면으로 redirect하도록 한다.editForm()
: 게시글 수정 폼@PathVariable
로 URL에서 게시글 id(postId
)를 가져온다.boardService.findOne(postId).orElseThrow()
로 해당 게시글을 가져온다.PostForm
객체를 생성 후, 제목과 내용을 설정하고, model
에 postForm
, postId
를 넣고 뷰 화면으로 넘긴다.edit()
: 게시글 수정postForm
객체를 검증한다. 에러가 있으면 board/editForm
뷰 화면을 다시 보여준다.boardService.updateBoard()
를 호출하여 게시글을 수정하고, 게시글 상세 조회 화면으로 redirect한다.delete()
: 게시글 삭제@PathVariable
로 URL에서 게시글 id(postId
)를 가져온다.boardService.deleteById(postId);
로 해당 게시글을 삭제한다.RedirectAttributes
RedirectAttributes
를 사용하면 URL 인코딩도 해주고, pathVarible, 쿼리 파라미터까지 처리해준다.
- 예시)
redirectAttributes.addAttribute("postId", registerId);
redirectAttributes.addAttribute("status", true);
redirect:/board/{postId}
- pathVariable 바인딩: {postId}
- 나머지는 쿼리 파라미터로 처리:
?status=true
=> 자동으로 pathVariable 바인딩을하며, 바인딩이 안되는 것은 쿼리 파라미터로 넘어간다.
board/editForm.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container{
max-width: 700px;
}
.field-error {
border-color: #dc3545;
color: #dc3545;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>게시글 수정</h2>
</div>
<form th:action th:object="${postForm}" method="post">
<div>
<label for="title">제목</label>
<input type="text" id="title" th:field="*{title}" class="form-control"
th:errorclass="field-error">
<div class="field-error" th:errors="*{title}" />
</div>
<div class="mb-3">
<label for="content">내용</label>
<textarea class="form-control" rows="5"
id="content" name="content"
th:value="*{content}" th:text="*{content}"></textarea>
</div>
<hr class="my-4">
<button class="btn btn-primary" type="submit">
수정 완료
</button>
<button class="btn btn-secondary"
th:onclick="|location.href='@{/board/{postId}(postId=${postId})}'|"
type="button">취소
</button>
</form>
</div>
</body>
DB에도 반영된 것을 확인할 수 있다.
DB에도 반영된 것을 확인할 수 있다.
board/post.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container{
max-width: 700px;
}
.field-error {
border-color: #dc3545;
color: #dc3545;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>게시글</h2>
</div>
<button class="btn btn-dark pull-right"
onclick="location.href='board/postList.html'"
th:onclick="|location.href='@{/board}'|"
type="button">목록으로
</button>
<div>
<label for="postId">게시판번호</label>
<input type="text" id="postId" name="postId" class="form-control"
value="1" th:value="${post.id}" readonly>
</div>
<div>
<label for="postTitle">제목</label>
<input type="text" id="postTitle" name="postTitle" class="form-control"
value="제목" th:value="${post.title}" readonly>
</div>
<div>
<label for="postContent">내용</label>
<textarea class="form-control" rows="5"
id="postContent" name="postContent"
th:value="${post.content}" th:text="${post.content}" readonly></textarea>
</div>
<div>
<label for="writer">작성자</label>
<input type="text" id="writer" name="writer" class="form-control"
value="작성자" th:value="${post.user.loginId}" readonly>
</div>
<div>
<label for="registerDate">작성일</label>
<input type="text" id="registerDate" name="registerDate" class="form-control"
value="작성자" th:value="${post.registerDate}" readonly>
</div>
<hr class="my-4">
<form th:action="@{/board/{postId}/delete(postId=${post.id})}" method="post">
<button class="btn btn-primary"
th:onclick="|location.href='@{/board/{postId}/edit(postId=${post.id})}'|"
type="button">수정</button>
<button class="btn btn-danger" type="submit">삭제</button>
</form>
</div> <!-- /container -->
</body>
<form th:action="@{/board/{postId}/delete(postId=${post.id})}" method="post">
<button class="btn btn-danger" type="submit">삭제</button>