이전 guestbook 예제의 템플릿을 먼저 가져오겠습니다.
그리고 BoardController를 생성한 뒤, 목록화면에 접근을 처리하는 메서드까지 생성해두겠습니다.
BoardController
package org.zerock.board.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.zerock.board.dto.PageRequestDTO;
import org.zerock.board.service.BoardService;
@Controller
@Log4j2
@RequestMapping("/board")
@RequiredArgsConstructor
public class BoardController {
@Autowired
private final BoardService boardService;
@GetMapping("/list")
public void list(PageRequestDTO pageRequestDTO, Model model) {
log.info("list......." + pageRequestDTO);
model.addAttribute("result", boardService.getList(pageRequestDTO));
}
}
다음으로는 list 페이지를 생성하겠습니다.
list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic::setContent(~{this::content})}">
<th:block th:fragment="content">
<h1 class="mt-4">Board List Page
<span>
<a th:href="@{/board/register}">
<button type="button" class="btn btn-outline-primary">REGISTER</button>
</a>
</span>
</h1>
<form action="/board/list" method="get" id="searchForm">
<div class="input-group">
<input type="hidden" name="page" value="1">
<div class="input-group-prepend">
<select class="custom-select" name="type">
<option th:selected="${pageRequestDTO.type == null}">------</option>
<option value="t" th:selected="${pageRequestDTO.type == 't'}">제목</option>
<option value="c" th:selected="${pageRequestDTO.type == 'c'}">내용</option>
<option value="w" th:selected="${pageRequestDTO.type == 'w'}">작성자</option>
<option value="tc" th:selected="${pageRequestDTO.type == 'tc'}">제목 + 내용</option>
<option value="tcw" th:selected="${pageRequestDTO.type == 'tcw'}">제목 + 내용 + 작성자</option>
</select>
</div>
<input class="form-control" name="keyword" th:value="${pageRequestDTO.keyword}">
<div class="input-group-append" id="button-addon4">
<button class="btn btn-outline-secondary btn-search" type="button">Search</button>
<button class="btn btn-outline-secondary btn-clear" type="button">Clear</button>
</div>
</div>
</form>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">Content</th>
<th scope="col">Regdate</th>
</tr>
</thead>
<tbody>
<tr th:each="dto : ${result.dtoList}">
<th scope="row">
<a th:href="@{/board/read(bno=${dto.bno}, page=${result.page}, type=${pageRequestDTO.type}, keyword=${pageRequestDTO.keyword})}">
[[${dto.bno}]]
</a>
</th>
//수정
<td>[[${dto.title}]] ----------- [<b th:text="${dto.replyCount}"></b>]</td>
<td>[[${dto.writerName}]] <small>[[${dto.writerEmail}]]</small></td>
<td>[[${#temporals.format(dto.regDate, 'yyyy/MM/dd')}]]</td>
</tr>
</tbody>
</table>
<ul class="pagination h-100 justify-content-center align-items-center">
<li class="page-item" th:if="${result.prev}">
<a class="page-link" th:href="@{/board/list(page=${result.start - 1}, type=${pageRequestDTO.type}, keyword=${pageRequestDTO.keyword})}" tabindex="-1">Previous</a>
</li>
<li th:class=" 'page-item ' + ${result.page == page ? 'active' : ''} " th:each="page: ${result.pageList}">
<a class="page-link" th:href="@{/board/list(page=${page}, type=${pageRequestDTO.type}, keyword=${pageRequestDTO.keyword})}">
[[${page}]]
</a>
</li>
<li class="page-item" th:if="${result.next}">
<a class="page-link" th:href="@{/board/list(page=${result.end + 1}, type=${pageRequestDTO.type}, keyword=${pageRequestDTO.keyword})}">Next</a>
</li>
</ul>
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-secondary close" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script th:inline="javascript">
var msg = [[${msg}]];
console.log(msg);
const $modal = $(".modal")
if(msg) {
$('.modal').show();
}
$(".close").on("click", () => {
$modal.hide()
});
var searchForm = $("#searchForm");
$('.btn-search').click(function(e) {
searchForm.submit();
});
$('.btn-clear').click(function(e) {
searchForm.empty().submit();
});
</script>
</th:block>
</th:block>
guestbook 예제의 list.html
에서 Ctrl + F로 guestbook
을 찾아 board
로 바꾸고, gno
를 bno
로만 바꿉니다. 그리고 리스트의 내용에 해당하는 table을 위와 같이 수정하면 동작합니다.
제목 옆에 댓글 개수가 보입니다.
다음으로는 등록 화면을 만들겠습니다. 컨트롤러에 GET, POST 메서드를 추가합니다.
BoardController
@GetMapping("/register")
public void register() {
log.info("register get.........");
}
@PostMapping("/register")
public String registerPost(BoardDTO dto, RedirectAttributes redirectAttributes) {
log.info("dto....." + dto);
Long bno = boardService.register(dto);
log.info("BNO: " + bno);
redirectAttributes.addFlashAttribute("msg", bno);
return "redirect:/board/list";
}
register 페이지도 guestbook 예제와 거의 동일하고, guestbook
을 board
로, writer
부분을 writer email
을 입력받을 수 있도록 변경합니다.
register.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content})}">
<th:block th:fragment="content">
<h1 class="mt-4">Board Register Page</h1>
<form th:action="@{/board/register}" th:method="post">
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" name="title" placeholder="Enter Title">
</div>
<div class="form-group">
<label>Content</label>
<textarea class="form-control" rows="5" name="content"></textarea>
</div>
<div class="form-group">
<label>Writer Email</label>
<input type="email" class="form-control" name="writerEmail" placeholder="Enter Writer Email">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</th:block>
</th:block>
이제 생성하면 정상적으로 등록됩니다. 여기서 Writer의 이메일이 데이터베이스에 존재하지 않는다면 에러가 발생하므로, 존재하는 이메일로 등록해야 합니다.
마찬가지로 게시물 조회 페이지도 처리해줍니다.
read.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content})}">
<th:block th:fragment="content">
<h1 class="mt-4">Board Read Page</h1>
<div class="form-group">
<label>Bno</label>
<input type="text" class="form-control" name="bno" th:value="${dto.bno}" readonly>
</div>
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" name="title" th:value="${dto.title}" readonly>
</div>
<div class="form-group">
<label>Content</label>
<textarea class="form-control" name="content" rows="5" readonly>[[${dto.content}]]</textarea>
</div>
<div class="form-group">
<label>Writer</label>
<input type="text" class="form-control" name="writer" th:value="${dto.writerName}" readonly>
</div>
<div class="form-group">
<label>RegDate</label>
<input type="text" class="form-control" name="regDate" th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<div class="form-group">
<label>ModDate</label>
<input type="text" class="form-control" name="modDate" th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<a th:href="@{/board/modify(bno=${dto.bno}, page=${requestDTO.page}, type=${requestDTO.type}, keyword=${requestDTO.keyword})}">
<button type="button" class="btn btn-primary">Modify</button>
</a>
<a th:href="@{/board/list(page=${requestDTO.page}, type=${requestDTO.page}, keyword=${requestDTO.keyword})}">
<button type="button" class="btn btn-info">List</button>
</a>
</th:block>
</th:block>
마찬가지로 read 페이지도 이전 예제에서 몇가지만 수정해서 적용합니다.
BoardController
@GetMapping("/read")
public void read(@ModelAttribute("requestDTO") PageRequestDTO pageRequestDTO, Long bno, Model model) {
log.info("bno: " + bno);
BoardDTO boardDTO = boardService.get(bno);
log.info(boardDTO);
model.addAttribute("dto", boardDTO);
}
수정 페이지에 삭제 버튼까지 있으므로 둘을 한번에 처리하겠습니다. 수정의 GET은 read와 동일하므로 @GetMapping
에 /modify
를 추가합니다. POST는 아래와 같이 작성합니다.
BoardController
@PostMapping("/modify")
public String modify(BoardDTO dto, @ModelAttribute("requestDTO") PageRequestDTO pageRequestDTO, RedirectAttributes redirectAttributes) {
log.info("post modify..........");
log.info("dto: " + dto);
boardService.modify(dto);
redirectAttributes.addAttribute("page", pageRequestDTO.getPage());
redirectAttributes.addAttribute("type", pageRequestDTO.getType());
redirectAttributes.addAttribute("keyword", pageRequestDTO.getKeyword());
redirectAttributes.addAttribute("bno", dto.getBno());
return "redirect:/board/read";
}
@PostMapping("/delete")
public String delete(long bno, RedirectAttributes redirectAttributes) {
log.info("bno: " + bno);
boardService.removeWithReplies(bno);
redirectAttributes.addFlashAttribute("msg", bno);
return "redirect:/board/list";
}
modify.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content})}">
<th:block th:fragment="content">
<h1 class="mt-4">Board Modify Page</h1>
<form action="/board/modify" method="post">
<input type="hidden" name="page" th:value="${requestDTO.page}">
<input type="hidden" name="type" th:value="${requestDTO.type}">
<input type="hidden" name="keyword" th:value="${requestDTO.keyword}">
<div class="form-group">
<label>Bno</label>
<input type="text" class="form-control" name="bno" th:value="${dto.bno}" readonly>
</div>
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" name="title" th:value="${dto.title}">
</div>
<div class="form-group">
<label>Content</label>
<textarea class="form-control" name="content" rows="5">[[${dto.content}]]</textarea>
</div>
<div class="form-group">
<label>Writer</label>
<input type="text" class="form-control" name="writer" th:value="${dto.writerName}" readonly>
</div>
<div class="form-group">
<label>RegDate</label>
<input type="text" class="form-control" th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<div class="form-group">
<label>ModDate</label>
<input type="text" class="form-control" th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
</form>
<button type="button" class="btn btn-primary modifyBtn">Modify</button>
<button type="button" class="btn btn-info listBtn">List</button>
<button type="button" class="btn btn-danger removeBtn">Remove</button>
<script th:inline="javascript">
var actionForm = $("form");
$(".removeBtn").click(function(){
actionForm
.attr("action", "/board/remove")
.attr("method","post");
actionForm.submit();
});
$(".modifyBtn").click(function() {
if(!confirm("수정하시겠습니까?")) {
return;
}
actionForm
.attr("action", "/board/modify")
.attr("method", "post")
.submit();
});
$(".listBtn").click(function() {
var page = $("input[name='page']");
var type = $("input[name='type']");
var keyword = $("input[name='keyword']");
actionForm.empty();
actionForm.append(page);
actionForm.append(type);
actionForm.append(keyword);
actionForm
.attr("action", "/board/list")
.attr("method", "get");
actionForm.submit();
})
</script>
</th:block>
</th:block>