GuestBook 프로젝트 (3) (목록처리, 페이징, Controller)
GuestBook 프로젝트 (3) (목록처리, 페이징, Controller)
GuestBook 프로젝트 (2) (Querydsl Test Code, DTO, Service, ServiceImpl) GuestBook 프로젝트 (2) (Querydsl Test Code, DTO, Service, ServiceImpl) GuestBook 프로젝트 (1) (프로젝트 구조, gradle, application, querydsl 설정) GuestBook 프로젝
soohykeee.tistory.com
Guestbook 등록
등록의 경우는 앞서 GuestbookService 쪽에 register() 메소드를 통해 구현을 해놓았다. 해당 서비스를 컨트롤러를 통해 보여주기만 하면 된다.
등록의 경우 주의할 점은, 처리하는 작업은 PostMapping을 통해서 해야한다. 우선적으로 GuestbookController에 register를 매핑하고, 아래와 같이 코드를 추가해줘야한다.
@GetMapping("/register")
public void register() {
log.info("Get - register--------------r");
}
@PostMapping("/register")
public String registerPost(GuestbookDTO dto, RedirectAttributes redirectAttributes) {
log.info("registerPost Method ---- DTO : " + dto);
Long gno = service.register(dto); // dto를 통해 입력한 정보를 등록하고, 등록한 gno를 가져온다.
//추후에 modal 창을 통해 등록된 gno 정보를 뿌려주기 위해서
//post 방식에서 한번 데이터를 전달하는 용도로 사용하기 위해 작성
redirectAttributes.addFlashAttribute("msg", gno);
return "redirect:/guestbook/list";
}
register의 경우, GET 방식에서는 화면을 보여주고 POST방식에서는 처리 후에 목록페이지로 redirect 하도록 설계했다. 코드에 주석부분에 작성한 것 처럼 POST방식에서 데이터를 전달해서 modal 창에 띄울것이므로 addFlashAttribut를 사용해서 등록되는 gno 값을 넘겨준다.
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 Page</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" placeholder="Enter 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>
</html>
추가적으로 /gusetbook/list , 목록 부분에서 바로 등록할 수 있는 register 버튼을 추가해주고 만약 register에서 넘겨준 등록된 gno의 값이 msg로 넘어온다면, list로 redirect 했을 때 modal 창이 보일 수 있도록 코드를 추가해줬다.
<!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 List Page
<!-- 추가한 부분 -->
<span>
<a th:href="@{/guestbook/register}">
<button type="button" class="btn btn-outline-primary">REGISTER</button>
</a>
</span>
</h1>
<!-- 생략 -->
<!-- 추가한 부분 -->
<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-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save Changes</button>
</div>
</div>
</div>
</div>
<!-- 추가한 부분 -->
<script th:inline="javascript">
var msg = [[${msg}]];
console.log(msg);
if (msg) {
$(".modal").modal();
}
</script>
</th:block>
</th:block>
위와 같이 정상적으로 등록이 되는 것을 확인 할 수 있다.
Guestbook 조회
조회의 경우는 앞서 Service에 작성한 코드가 없기에 Service 쪽부터 작성을 해줘야한다. 조회시에는 키값인 gno만 받아오면 respository를 통해 db에 접근해서 값을 가져올 수 있다.
만약 database에 값이 존재하는 gno라면, 해당 entity 객체를 dto로 변환을 한후 return 해줘야한다.
//GuestbookService
GuestbookDTO read(Long gno);
------------------------------------
//GusetbookServiceImpl
@Override
public GuestbookDTO read(Long gno) {
Optional<Guestbook> result = repository.findById(gno);
return result.isPresent() ? entityToDto(result.get()) : null;
}
service쪽을 작성했으니, controller를 연결해줘야한다.
@GetMapping("/read")
@ModelAttribute("requestDTO")
public void read(long gno, PageRequestDTO requestDTO, Model model) {
log.info("Read - gno : " + gno);
GuestbookDTO dto = service.read(gno);
model.addAttribute("dto", dto);
}
requestDTO 파라미터를 작성해준 이유는, 조회페이지 -> 목록페이지로 다시 되돌아가기 위해서이다.
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" rows="5" name="content" 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})}">
<buton type="button" class="btn btn-info">List</buton>
</a>
</th:block>
</th:block>
위와 같이 방금 등록했던 정보가 정상적으로 조회가 되는 것을 확인 할 수 있다.
Guestbook 수정 / 삭제
guestbook 수정의 경우는 POST 방식으로 처리하고, 수정된 결과를 확인 할 수 있는 read 조회 화면으로 redirect 한다.
guestbook 삭제의 경우는 POST 방식으로 처리하고, 목록 화면으로 이동한다.
수정하는 화면에서 수정하지 않고, 목록으로 이동하는 작업은 GET 방식으로 처리하고, 이때 다시 목록으로 돌아갈 때 기존의 페이지 번호로 이동해야 하기 때문에 페이지 번호를 유지해서 넘겨주고 받아야한다.
GuestbookService와 Impl에 삭제, 수정서비스가 구현을 먼저 해야한다. 삭제의 경우는 repository를 통해 database에 값을 가져온 후 삭제하면 되고, 수정의 경우는 repository에 gno로 값을 찾은 후, 값이 존재 한다면 entity 객체에 넣어주고 entity객체에 있는 제목, 내용 수정 메소드를 통해 수정후 다시 저장을 해주면 된다.
//GuestbookService
void remove(Long gno);
void modify(GuestbookDTO dto);
---------------------------------
//GuestbookServiceImpl
@Override
public void remove(Long gno) {
repository.deleteById(gno);
}
@Override
public void modify(GuestbookDTO dto) {
Optional<Guestbook> result = repository.findById(dto.getGno());
if (result.isPresent()) {
Guestbook entity = result.get();
entity.changeTitle(dto.getTitle());
entity.changeContent(dto.getContent());
repository.save(entity);
}
}
Controller에 modify도 read와 동일하게 화면을 뿌려주기 위해 같은 GET 방식으로 받는다.
수정과 삭제모두 POST 방식으로 처리해야한다.
@GetMapping({"/read", "/modify"})
public void read(long gno, @ModelAttribute("requestDTO") PageRequestDTO requestDTO, Model model) {
log.info("Read - gno : " + gno);
GuestbookDTO dto = service.read(gno);
model.addAttribute("dto", dto);
}
@PostMapping("/modify")
public String modify(GuestbookDTO dto, @ModelAttribute("requestDTO") PageRequestDTO requestDTO, RedirectAttributes redirectAttributes) {
log.info("Post Modify..............");
log.info("Modify - dto : " + dto);
service.modify(dto);
redirectAttributes.addAttribute("page", requestDTO.getPage());
redirectAttributes.addAttribute("gno", dto.getGno());
return "redirect:/guestbook/read";
}
@PostMapping("/remove")
public String remove(long gno, RedirectAttributes redirectAttributes) {
log.info("remove - gno : " + gno);
service.remove(gno);
redirectAttributes.addFlashAttribute("msg", gno);
return "redirect:/guestbook/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">GuestBook Modify Page</h1>
<form action="/guestbook/modify" method="post">
<input type="hidden" name="page" th:value="${requestDTO.page}">
<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}">
</div>
<div class="form-group">
<labeL>Content</labeL>
<textarea class="form-control" rows="5" name="content">[[${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"
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", "/guestbook/remove")
.attr("method", "post")
actionForm.submit();
});
$(".modifyBtn").click(function () {
if (!confirm("수정하시겠습니까?")) {
return;
}
actionForm
.attr("action", "/guestbook/modify")
.attr("method", "post")
.submit();
});
$(".btn-list").click(function () {
var pageInfo = $("input[name='page']");
actionForm.empty();
actionForm.append(pageInfo);
actionForm
.attr("action", "/guestbook/list")
.attr("method", "get")
console.log(actionForm.html());
actionForm.submit();
});
</script>
</th:block>
</th:block>
</html>
위와 같이 정상적으로 제목, 내용만 수정이 가능하고 해당 modify 페이지에서 수정, 삭제, 목록으로 돌아가기 전부 가능하다.