-
코드로 배우는 스프링부트 웹 프로젝트 Day 10Spring/코드로 배우는 스프링부트 웹 프로젝트 2022. 8. 25. 21:28
/* Annotation들은 스프링부트 프로젝트의 Annotation 정리 페이지에 따로 정리해두었습니다. */
[방명록의 수정/삭제 처리]
계획
- Guestbook의 수정은 Post 방식으로 처리하고 다시 수정된 결과를 확인할 수 있는 조회 화면으로 이동
- 삭제는 Post방식으로 처리하고 목록 화면으로 이동
- 목록을 이동하는 작업은 Get 방식으로 처리. 기존에 사용하던 페이지 번호 등을 유지해서 이동!
수정과 삭제는 모두 '수정 및 삭제가 가능한 페이지'로 이동한 상태에서 수정과 삭제 중 선택을 해서 작업이 이루어짐
이를 구현하기 위해 GuestbookController에서는 조회와 비슷하게 Get방식으로 진입하는 'gusetbook/modify'를 기존의 read()에 어노테이션의 값을 변경해서 처리
@GetMapping({"/read", "/modify"}) public void read(long gno, @ModelAttribute("requestDTO") PageRequestDTO requestDTO, Model model) { log.info("gno: " + gno); GuestbookDTO dto = service.read(gno); model.addAttribute("dto" ,dto); }
read.html을 modify.html을 만들어서 복사.붙여넣기 한다.
<h1 class="mt-4">GuestBook Modify Page</h1> <form action="/guestbook/modify" method="post">
<h1>태그의 내용을 modify로 바꾸고, form 태그로 수정된 내용을 감싸 Post 방식으로 처리하도록 함
<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, 'yyy/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>
Title과 내용만 수정 가능하도록 한다. (readonly삭제)
regDate와 modDate는 수정이 불가능할 뿐더러, JPA에서 자동 처리되므로, name속성을 없앰
</form> <button type="button" class="btn btn-primary">Modify</button> <button type="button" class="btn btn-info">List</button> <button type="button" class="btn btn-danger">Remove</button>
화면에 표시할 수정, List, 삭제용 버튼 추가
위와 같이 Title과 Content만 수정 가능하도록 처리된다.
수정, 삭제, 목록의 각 버튼을 클릭할 때 마다 다른 이벤트를 처리해야 한다.
이는 <form>태그의 action속성을 통해 처리 가능하다. 해당 처리는 조금 뒤쪽에 한다.
[서비스 계층에서의 수정과 삭제]
수정과 삭제 관련 기능 추가
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); } }
remove()와 modify()를 구현
[컨트롤러의 게시물 삭제]
삭제 기능은 Post 방식으로 gno값을 전달하고, 삭제 후에는 다시 목록의 첫 페이지로 이동하는 방식이 가장 보편적임
GuestbookController 클래스
@PostMapping("/remove") public String remove(long gno, RedirectAttributes redirectAttributes) { log.info("gno: " + gno); service.remove(gno); redirectAttributes.addFlashAttribute("msg", gno); return "redirect:/guestbook/list"; }
[modify.html 삭제 처리]
삭제 작업은 Get 방식으로 수정 페이지에 들어가서 '삭제' 버튼을 클릭하는 방식으로 제작
modify.html 수정
<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"); //form 태그 객체 $(".removeBtn").click(function() { actionForm .attr("action", "/guestbook/remove") .attr("method", "post"); actionForm.submit(); }); </script>
자바스크립트 인라인 기능을 사용해 구현
<form> 태그의 action 속성에 값으로 /guestbook/remove 를 지정
<form> 태그의 method 속성에 값으로 post를 지정
<form> 태그는 사용자가 입력한 정보를 서버로 전달하는 역할 ↴
<form> 태그 내에는 <input> 태그로 gno가 있기 때문에 컨트롤러에서는 여러 파라미터 중에서 gno를 추출해서 삭제 시에 이용함
[POST 방식의 수정 처리]
수정 처리는 POST 방식으로 이루어져야 함
고려해야 할 점
- 수정 시에 수정해야하는 내용(제목, 내용, 글 번호)이 전달되어야 함
- 수정 후에는 목록 페이지로 이동하거나 조회 페이지로 이동해야 함 (이때, 가능하면 기존의 페이지 번호를 유지하는 것이 좋음)
modify.html에는 /guestbook/read 로 이동할 때 페이지 번호가 파라미터로 전달되고 있고, 수정 페이지로 이동할 경우에도 같이 전달됨
이를 이용하여 수정이 완료된 후에도 동일한 목록 및 조회 페이지를 유지할 수 있도록 page값도 <form> 태그에 추가해서 전달하도록 함
modify.html
<form action="/guestbook/modify" method="post"> <!-- 페이지 번호 --> <input type="hidden" name="page" th:value="${requestDTO.page}">
[컨트롤러의 수정 처리]
GuestbookController에서는 Guestbook 자체의 수정과 페이징 관련 데이터 처리를 같이 진행해주어야 함
GuestbookController 클래스
@PostMapping("/modify") public String modify(GuestbookDTO dto, @ModelAttribute("requestDTO") PageRequestDTO requestDTO, RedirectAttributes redirectAttributes) { log.info("post modify.................................................."); log.info("dto: " + dto); service.modify(dto); service.modify(dto); redirectAttributes.addAttribute("page", requestDTO.getPage()); redirectAttributes.addAttribute("gno" ,dto.getGno()); return "redirect:/guestbook/read"; }
수정해야하는 글의 정보를 가지는 GuestbookDTO, 기존의 페이지 정보를 유지하기 위한 PageRequestDTO, 리다이렉트로 이동하기 위한 RedirectAttributes 세 가지가 파라미터로 필요함
수정을 완료하면, read 페이지로 이동 (기존 페이지 정보도 같이 유지해서 조회 페이지에서 다시 목록 페이지로 이동할 수 있도록 함)
[수정 화면에서의 이벤트 처리]
modify.html
$(".modifyBtn").click(function() { if(!confirm("수정하시겠습니까?")) { return; } actionForm .attr("action", "/guestbook/modify") .attr("method", "post") .submit(); });
confirm을 통해 정말 수정할 것인지 재확인 후, Post 방식으로 서버에 전송
이후, 다시 조회 페이지(read)로 이동
[수정 화면에서 다시 목록 페이지로]
목록 페이지로 이동하는 버튼 처리
목록 페이지로 이동할 때에는 page 파라미터 외에는 별도로 필요하지 않음
=> page를 제외한 파라미터들을 지운 상태로 처리
$(".listBtn").click(function () { var pageInfo = $("input[name='page']"); actionForm.empty(); //form 태그의 모든 내용을 지움 actionForm.append(pageInfo); //목록 페이지 이동에 필요한 내용을 다시 추가 actionForm .attr("action", "/guestbook/list") .attr("method", "get"); console.log(actionForm.html()); actionForm.submit(); });
[검색 처리]
검색 처리는 크게 서버 사이드 처리와 화면 쪽의 처리로 나눌 수 있음
서버 사이드 처리
- PageRequestDTO에 검색 타입(type)과 키워드(keyword)를 추가
- 이하 서비스 계층에서 Querydsl을 이용해서 검색 처리
검색 항목 분류
- 제목, 내용, 작성자
- 제목 or 내용
- 제목 or 내용 or 작성자
[서버측 검색 처리]
PageRequestDTO에 검색 타입과 검색 키워드 추가
PageRequestDTO 클래스
private String type; private String keyword;
type과 keyword 추가
[서비스 계층의 검색 구현과 테스트]
동적으로 검색 조건이 처리되는 경우의 실제 코딩은 Querydsl을 통해서 BooleanBuilder를 작성
GuestbookRepository는 Querydsl로 작성된 BooleanBuilder를 findAll() 처리하는 용도로 사용
BooleanBuilder는 별도의 클래스 등을 작성하여 처리할 수 있지만, 간단히 처리하기 위해 GuestbookServiceImpl에 메서드를 작성해서 처리
GuestbookServiceImpl 클래스에 추가
private BooleanBuilder getSearch(PageRequestDTO requestDTO) { //Querydsl 처리 String type = requestDTO.getType(); BooleanBuilder booleanBuilder = new BooleanBuilder(); QGuestbook qGuestbook = QGuestbook.guestbook; String keyword = requestDTO.getKeyword(); BooleanExpression expression = qGuestbook.gno.gt(0L); //gno > 0 조건만 생성 booleanBuilder.and(expression); if(type == null || type.trim().length() == 0) { //검색 조건이 없는 경우 return booleanBuilder; } //검색 조건 작성 BooleanBuilder conditionBuilder = new BooleanBuilder(); if (type.contains("t")) { conditionBuilder.or(qGuestbook.title.contains(keyword)); } if (type.contains("c")) { conditionBuilder.or(qGuestbook.content.contains(keyword)); } if (type.contains("w")) { conditionBuilder.or(qGuestbook.writer.contains(keyword)); } //모든 조건 통합 booleanBuilder.and(conditionBuilder); return booleanBuilder; }
검색 조건이 있는 경우, conditionBuilder 변수를 통해 검색 조건들을 or로 연결하여 return
검색 조건이 없는 경우, gno > 0 으로만 return
'Spring > 코드로 배우는 스프링부트 웹 프로젝트' 카테고리의 다른 글
코드로 배우는 스프링부트 웹 프로젝트 Day 12 (0) 2022.09.07 코드로 배우는 스프링부트 웹 프로젝트 Day 11 (0) 2022.08.26 코드로 배우는 스프링부트 웹 프로젝트 Day 9 (0) 2022.08.24 코드로 배우는 스프링부트 웹 프로젝트 Day 8 (0) 2022.08.21 코드로 배우는 스프링부트 웹 프로젝트 Day 7 (0) 2022.08.20