GuestBook 프로젝트 (4) (guestbook 등록, 조회, 수정, 삭제)
GuestBook 프로젝트 (4) (guestbook 등록, 조회, 수정, 삭제)
GuestBook 프로젝트 (3) (목록처리, 페이징, Controller) GuestBook 프로젝트 (3) (목록처리, 페이징, Controller) GuestBook 프로젝트 (2) (Querydsl Test Code, DTO, Service, ServiceImpl) GuestBook 프로젝트 (2) (Querydsl Test Code, DTO
soohykeee.tistory.com
검색처리
검색처리는 크게 서버 사이드 처리, 화면 쪽의 처리로 나눠진다. 순차적으로 처리하는 방식을 작성할것이다.
서버 사이드 계층에서의 처리는 아래와 같다.
- PageRequestDTO에 검색 타입(type)과 키워드(keyword)를 추가
- 이하 서비스 계층에서 Querydsl을 이용하여 검색 처리
검색항목의 경우는 제목, 내용, 작성자를 추가 할 것이다.
- '제목(t), 내용(c), 작성자(w)' 로 검색하는 경우
- '제목 또는 내용' (tc) 로 검색하는 경우
- '제목 또는 내용 또는 작성자' (twc)로 검색하는 경우
위에 작성한것처럼 type과 keyword를 추가해줘야하므로 PageRequestDTO에 아래와 같이 선언을 해준다.
private String type;
private String keyword;
우리가 구현하는 것 처럼 동적으로 검색 조건이 처리되는 경우, 실제 코딩은 Querydsl을 사용하여 BooleanBuilder를 작성하고, 해당 repository는 앞서 Querydsl로 작성된 BooleanBuilder를 findAll() 처리하는 용도로 사용한다.
BooleanBuilder 작성의 경우에는 별도의 클래스를 추가하여 작성해주기도 하지만, 해당 ServcieImpl에 구현해 놓아도 된다. 따라서 GuestbookServiceImpl에 Querydsl을 통해 BooleanBuilder를 작성 해줘야 한다.
package com.example.guestbookdemo.service;
import com.example.guestbookdemo.dto.GuestbookDTO;
import com.example.guestbookdemo.dto.PageRequestDTO;
import com.example.guestbookdemo.dto.PageResultDTO;
import com.example.guestbookdemo.entity.Guestbook;
import com.example.guestbookdemo.entity.QGuestbook;
import com.example.guestbookdemo.repository.GuestbookRepository;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.function.Function;
@Service
@Log4j2
@RequiredArgsConstructor
public class GuestbookServiceImpl implements GuestbookService {
.... 생략
// 수정
@Override
public PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestDTO) {
Pageable pageable = requestDTO.getPageable(Sort.by("gno").descending());
BooleanBuilder booleanBuilder = getSearch(requestDTO);
Page<Guestbook> result = repository.findAll(booleanBuilder, pageable);
Function<Guestbook, GuestbookDTO> fn = (entity -> entityToDto(entity));
return new PageResultDTO<>(result, fn);
}
// 추가
private BooleanBuilder getSearch(PageRequestDTO requestDTO) {
String type = requestDTO.getType();
String keyword = requestDTO.getKeyword();
QGuestbook qGuestbook = QGuestbook.guestbook;
BooleanBuilder booleanBuilder = new BooleanBuilder();
BooleanExpression expression = qGuestbook.gno.gt(0L); //gno > 0 조건 생성
booleanBuilder.and(expression);
if (type == null || type.trim().length() == 0) { // 검색 type이 없는 경우
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;
}
}
위의 코드를 살펴보면,
getSearch() 메소드는 앞서 작성한 PageRequestDTO에서 type, keyword 값을 받아온 후 type이 없다면 keyword도 의미가 없으니 type이 있는 경우만 conditionBuilder로 받아와서 or로 연결하여 검색 조건을 추가해준다.
만약 type이 없다면 gno>0인 조건만 실행하여 검색을 보여준다.
기존에 작성했었던 page처리만 되어 있었던 getList() 메소드에 검색조건을 추가해준다.
BooleanBuilder를 통해 요청 DTO에서 keyword, type을 받아온 후, repository에서 해당 검색 조건을 가진 list를 가져올 수 있도록 추가해준다.
이제 해당 검색 기능과, 조회, 수정, 삭제 후에도 그 전 페이지에서의 type, keyword 값을 가져올 수 있도록 list.html, modify.html, read.html을 수정해줘야한다. 수정할 코드는 아래와 같다.
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">
... 생략
<form action="/guestbook/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>
... 생략
<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="@{/guestbook/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="@{/guestbook/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="@{/guestbook/list(page=${result.end +1}, type=${pageRequestDTO.type},keyword=${pageRequestDTO.keyword})}">Next</a>
</li>
</ul>
... 생략
<script th:inline="javascript">
var msg = [[${msg}]];
console.log(msg);
if (msg) {
$(".modal").modal();
}
var searchForm = $("#searchForm");
$('.btn-search').click(function (e) {
searchForm.submit();
});
$('.btn-clear').click(function (e) {
searchForm.empty().submit();
});
</script>
</th:block>
</th:block>
</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>
... 생략
<a th:href="@{/guestbook/modify(gno=${dto.gno}, page=${requestDTO.page}, type=${requestDTO.type},keyword=${requestDTO.keyword})}">
<button type="button" class="btn btn-primary">Modify</button>
</a>
<a th:href="@{/guestbook/list(page=${requestDTO.page}, type=${requestDTO.type},keyword=${requestDTO.keyword})}">
<buton type="button" class="btn btn-info">List</buton>
</a>
</th:block>
</th:block>
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}">
<input type="hidden" name="type" th:value="${requestDTO.type}">
<input type="hidden" name="keyword" th:value="${requestDTO.keyword}">
... 생략
</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">
... 생략
$(".listBtn").click(function () {
//var pageInfo = $("input[name='page']");
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", "/guestbook/list")
.attr("method", "get")
console.log(actionForm.html());
actionForm.submit();
});
</script>
</th:block>
</th:block>
</html>
GuestbookController
@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("type", requestDTO.getType());
redirectAttributes.addAttribute("keyword", requestDTO.getKeyword());
redirectAttributes.addAttribute("gno", dto.getGno());
return "redirect:/guestbook/read";
}
위와 같이 정상적으로 검색이 되고 clear 버튼도 오류없이 잘 실행이 된다.
해당 게시물 조회, 삭제, 수정 후 에도 type, keyword에 정상적으로 파라미터가 전달되어 그전에 있던 페이지로 오류없이 돌아가게 된다.