/service/BoardService.java search() 메서드 추가
import org.springframework.data.jpa.domain.specification; // 복합쿼리 생성용
public Specification<Board> searchBoard(String keyword) {
return new Specification<Board>() {
private static final long serialVersionUID = 1L; // 필요한 값이라서 추가
@Override
@Nullable
public Predicate toPredicate(Root<Board> b, CriteriaQuery<?> query, CriteriaBuilder cb) {
// query를 JPA로 생성
query.distinct(true); // 중복 제거
Join<Board, Reply> r = b.join("replyList", JoinType.LEFT);
return cb.or(cb.like(b.get("title"), "%" + keyword + "%"), // 게시글 제목에서 검색
cb.like(b.get("content"), "%" + keyword + "%"), // 게시글 내용에서 검색
cb.like(r.get("content"), "%" + keyword + "%") // 댓글 내용에서 검색
);
}
};
}
더보기
SELECT DISTINCT
b.BNO
, b.TITLE
, b.CONTENT
, b.CREATE_DATE
, b.MODIFY_DATE
, b.WRITER_MID
FROM BOARD b
LEFT OUTER JOIN REPLY r ON b.BNO = r.BOARD_BNO
WHERE b.title LIKE '%사람%'
OR b.content LIKE '%사람%'
OR r.content LIKE '%사람%';
- 위와 같은 쿼리문을 jpa가 대신 해줌
/repository/BoardRepository.java findAll() 메서드 추가
Page<Board> findAll(Specification<Board> spec, Pageable pageable);
/service/BoardService.java getList() 메서드 추가 생성
public Page<Board> getList(int page, String keyword) {
List<Sort.Order> sorts = new ArrayList<>();
sorts.add(Sort.Order.desc("createDate"));
Pageable pageable = PageRequest.of(page, 10, Sort.by(sorts)); // pageSize를 동적으로도 변경할 수 있음. 나중에...
Specification<Board> spec = searchBoard(keyword);
return this.boardRepository.findAll(spec, pageable);
}
/controller/BoardController.java list() 메서드 추가
@GetMapping("/list")
public String list(Model model, @RequestParam(value="page", defaultValue = "0") int page,
@RequestParam(value = "kw", defaultValue = "") String keyword) {
Page<Board> paging = this.boardService.getList(page, keyword); // 검색 추가
model.addAttribute("paging", paging);
model.addAttribute("kw", keyword);
return "board/list";
}
🚫 기존의 @GetMapping("/list") 메서드는 주석처리한 후 작성해야 함! (주석처리 안하면 동일하기 때문에 오류 발생)
/templates/board/list.html 검색창 추가, searchForm 폼 영역 추가
- 검색창 추가
<div class="row my-3 align-items-center">
<!-- 등록 버튼 -->
<div class="col-8">
<a th:href="@{/board/create}" class="btn btn-sm btn-primary my-2">게시글 등록</a>
</div>
<!-- 검색창 영역 -->
<div class="col-4">
<div class="input-group">
<input type="text" id="search_kw" class="form-control" th:value="${kw}">
<button id="btn_search" type="button" class="btn btn-sm btn-outline-secondary">찾기</button>
</div>
</div>
</div>
- searchForm 폼 영역 추가
<form th:action="@{}" method="get" id="searchForm">
<input type="hidden" id="kw" name="kw" th:value="${kw}">
<input type="hidden" id="page" name="page" th:value="${paging.number}">
</form>
- 페이징 부분 <a> 태그 수정
<!-- 페이징 시작 -->
<div th:if="${!paging.isEmpty()}">
<ul class="pagination justify-content-center">
<!-- 이전버튼 -->
<li th:classappend="${!paging.hasPrevious} ? disabled" class="page-item">
<a th:data-page="${0}" class="page-link" href="javascript:void(0)">《</a>
</li>
<li th:classappend="${!paging.hasPrevious} ? disabled" class="page-item">
<a th:data-page="${paging.number-1}" class="page-link" href="javascript:void(0)">〈</a>
</li>
<!-- 페이지번호버튼 -->
<li th:each="page : ${#numbers.sequence(0, paging.totalPages-1)}"
th:if="${page >= paging.number-5 and page <= paging.number+5}"
th:classappend="${page == paging.number} ? active" class="page-item">
<a th:data-page="${page}" th:text="${page+1}" class="page-link" href="javascript:void(0)">〈</a>
</li>
<!-- 다음버튼 -->
<li th:classappend="${!paging.hasNext} ? disabled" class="page-item">
<a th:data-page="${paging.number+1}" class="page-link" href="javascript:void(0)">〈</a>
</li>
<li th:classappend="${!paging.hasNext} ? disabled" class="page-item">
<a th:data-page="${paging.totalPages-1}" class="page-link" href="javascript:void(0)">《</a>
</li>
</ul>
</div>
<!-- 페이징 끝 -->
- 가장 아래에 script 추가하기
<script layout:fragment="sub-script" type="text/javascript">
const page_elements = document.getElementsByClassName("page-link");
Array.from(page_elements).forEach(function(elements) {
elements.addEventListener('click', function(){
document.getElementById('page').value = this.dataset.page;
document.getElementById('searchForm').submit(); // 서브밋 발동
});
});
const btn_search = document.getElementById("btn_search");
btn_search.addEventListener('click', function() {
document.getElementById('kw').value = document.getElementById('search_kw').value;
document.getElementById('page').value = 0; // 검색할 경우 0 페이지부터
document.getElementById('searchForm').submit(); // 서브밋 발동
});
// 검색창을 검색하고 엔터치면 검색버튼 클릭 발생
var search_kw = document.getElementById('search_kw');
search_kw.addEventListener('keypress', function(event) {
if(event.key == 'Enter') {
event.preventDefault(); // html은 부모, 자식 관계로 구성되어 있으므로 자식에서는 해당 이벤트가 발생하면 안됨. 이를 막기 위한 preventDefault();
document.getElementById('btn_search').click();
}
});
</script>
- 검색하면 검색 문자열이 포함된 게시물 리스트가 뜨는 걸 확인할 수 있음
'Spring Boot > STUDY' 카테고리의 다른 글
[Spring Boot] JPA 프로젝트 - 마크다운 적용 (1) | 2024.06.25 |
---|---|
[Spring Boot] JPA 프로젝트 - @Query를 사용한 검색 기능 구현(2) (0) | 2024.06.25 |
[Spring Boot] JPA 프로젝트 - 앵커 기능 구현 (0) | 2024.06.24 |
[Spring Boot] JPA 프로젝트 - 수정 기능 구현 (0) | 2024.06.24 |
[Spring Boot] JPA 프로젝트 - 게시글 등록 시 작성자 추가 (0) | 2024.06.21 |