이전에 서비스 계층에서 register
를 만들어두었기 때문에 컨트롤러에서 구현만 하면 등록은 완료됩니다.
GuestbookController
...생략
@GetMapping("/register")
public void register() {
log.info("register get...");
}
@PostMapping("/register")
public String registerPost(GuestbookDTO dto, RedirectAttributes redirectAttributes) {
log.info("dto..." + dto);
Long gno = service.register(dto);
redirectAttributes.addFlashAttribute("msg", gno);
return "redirect:/guestbook/list";
}
등록 화면은 HTML Form형태이므로 Get과 Post방식의 통신을 이용합니다. Post에서 redirectAttributes에 flashAttribute를 추가하는데, 이는 redirect시에 단 한번만 msg를 전달하기 위해 사용합니다. 뒤에서 Modal(팝업창)을 만들기 위해 사용할 예정입니다.
register.html
파일을 생성하고, 아래의 코드를 작성했습니다.
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">GuestBook Register</h1>
<form th:action="@{/guestbook/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</label>
<input type="text" class="form-control" name="writer" placeholder="Enter Writer">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</th:block>
</th:block>
또한 list.html
에 리스트 아래에 코드와 스크립트를 추가하였습니다.
list.html
<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()
})
</script>
list에 추가한 코드는 등록 후 list화면으로 되돌아왔을 때(redirect) Modal을 띄우기 위한 코드입니다. 컨트롤러에서 FlashAttribute로 redirect화면에 전달한 msg
에는 gno가 담겨있습니다. 하지만, 단순히 list페이지에 접근했을 때는(register후 redirect방식이 아닌 다른 방식) msg값은 null입니다.
if(msg) {
$('.modal').show();
}
$(".close").on("click", () => {
$modal.hide()
})
이를 이용해 script에서 msg값이 null이 아닌 값, 즉 gno값이 담겨있으면 위의 html에서 modal이 활성화되도록 구성하였습니다.
<div class="modal" tabindex="-1" role="dialog">
...
마지막으로 h1태그 내부에 등록 버튼을 만들어 등록창에 접근할 수 있도록 합니다.
<h1 class="mt-4">GuestBook List Page
<span>
<a th:href="@{/guestbook/register}">
<button type="button" class="btn btn-outline-primary">REGISTER</button>
</a>
</span>
</h1>
<table class="table table-striped">
...
다음으로는 조회 화면을 만들겠습니다. 각 엔티티의 gno값을 하이퍼링크로 설정해두고, url이 /guestbook/read?gno=xxx&page=xxx
형태에서 각각을 조회할 수 있도록 합니다.
...
<tbody>
<tr th:each="dto : ${result.dtoList}">
//gno 가리키는 th태그 내부 변경
<th scope="row">
<a th:href="@{/guestbook/read(gno=${dto.gno}, page=${result.page})}">
[[${dto.gno}]]
</a>
</th>
...
서비스 계층에 조회 기능을 추가하겠습니다.
GuestbookService
public interface GuestbookService {
Long register(GuestbookDTO dto);
PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestDTO);
//추가
GuestbookDTO read(Long gno);
GuestbookServiceImpl
@Override
public GuestbookDTO read(Long gno) {
Optional<Guestbook> result = repository.findById(gno);
return result.isPresent() ? entityToDto(result.get()) : null;
}
컨트롤러에서도 GetMapping을 통해 dto를 Model에 담아 반환하겠습니다.
@GetMapping("/read")
public void read(long gno, @ModelAttribute("requestDTO") PageRequestDTO requestDTO, Model model) {
log.info("gno:" + gno);
GuestbookDTO dto = service.read(gno);
model.addAttribute("dto", dto);
}
requestDTO
에는 다시 목록 페이지로 돌아갈 수 있도록 조회에 오기 직전의 page번호와 size를 저장해두고 있습니다. gno
를 통해 서비스 계층에서 DTO를 조회하고, 결과를 model
에 담아 반환합니다.
requestDTO는 @ModelAttribute로 선언되었기 때문에 Model 객체안에 자동으로 주입됩니다. 따라서 뷰로 돌아가는 데이터는 {"requestDTO":requestDTO}, {"dto":dto}입니다.
다음으로는 조회 페이지인 read.html
을 작성합니다.
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">GuestBook Read Page</h1>
<div class="form-group">
<label>Gno</label>
<input type="text" class="form-control" name="gno" th:value="${dto.gno}" 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.writer}" 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="@{/guestbook/modify(gno=${dto.gno}, page=${requestDTO.page})}">
<button type="button" class="btn btn-primary">Modify</button>
</a>
<a th:href="@{/guestbook/list(page=${requestDTO.page})}">
<button type="button" class="btn btn-info">List</button>
</a>
</th:block>
</th:block>