프로젝트/게시판
[SpringBoot] 게시판 9. 게시글 검색 기능 - 비동기 구현
qkzkdo
2023. 8. 10. 23:12
728x90
구현 순서
1. index.html
2. board-search.html
3. BoardSearchDTO
4. BoardController
5. BoardService
6. BoardServiceProcess
7. BoardMapper
8. board-mapper.xml
9. 게시글 검색 구현 화면
1-1. index.html
index에 검색 영역 코드 작성
<div>
<form id="form-search">
<select name="columnName">
<option value="1">제목</option>
<option value="2">내용</option>
<option value="3">제목+내용</option>
</select>
<input type="search" name="query" placeholder="검색어를 입력하세요">
<button type="button" id="btn-search">검색</button>
</form>
</div>
위 코드를 작성하면 뷰에서 아래 사진처럼 검색할 수 있는 화면이 보인다.
1-2. index <head>에 자바스크립트 코드 추가
<script type="text/javascript">
$(function(){
boardList();
$("form").submit(function(event){
event.preventDafault();
});
$("#btn-search").click(function(){
btnSearchClicked(1);
});
});
function boardList(){
$.ajax({
url: "/board-list",
type: "GET",
success:function(result){
$("#board-list").html(result);
}
});
};
function btnSearchClicked(page){
var data=$("#form-search").serialize()+"&page="+page;
$.ajax({
url:"/board/search",
type: "PATCH",
data:data,
success:function(result){
$("#board-list").html(result);
}
});
}
</script>
- $("form").submit(function(event){ ... })는 페이지 내의 모든 <form> 요소가 제출될 때의 기본 동작인 페이지 새로고침을 막는다. 즉, 폼 제출 시 페이지가 새로고침되지 않도록 한다.
- $("#btn-search").click(function(){ ... })는 페이지 내의 id가 btn-search인 요소가 클릭되었을 때의 동작을 설정한다. 클릭 시 btnSearchClicked 함수를 호출하며, 페이지 번호를 1로 전달하여 검색 결과를 가져온다.
- btnSearchClicked 함수는 매개변수 page를 받아와서 실행된다.
- $("#form-search").serialize()는 HTML 폼의 내용을 직렬화하여 URL 쿼리 문자열 형태로 반환한다.
- "&page="+page는 페이지 번호를 URL 쿼리 문자열에 추가한다.
- success:function(result) : result를 사용하여 #board-list 요소의 내용을 변경한다.
2. board-search.html
검색된 리스트들을 보여줄 html 작성
<body>
<a href="/">HOME</a>
<a href="/board/new">글쓰기</a>
<p>검색게시판</p>
<table border="1">
<tr>
<td>글번호</td>
<td>제목</td>
<td>조회수</td>
<td>수정일</td>
<td>삭제</td>
</tr>
<tr th:each="dto : ${list}">
<td th:text="${dto.no}">글번호</td>
<td>
<a th:href="|/board/${dto.no}|" th:text="${dto.title}">제목</a>
</td>
<td th:text="${dto.readCount}">조회수</td>
<td th:text="${#temporals.format(dto.updateDate, 'yyyy-MM-dd HH:mm:ss')}">수정일</td>
<td>
<form th:action="|/board/delete/${dto.no}|" method="post" onsubmit="return delete_event()">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">삭제</button>
<script type="text/javascript">
function delete_event(){
if(confirm("정말 삭제하시겠습니까?") == true){
return true;
}else{
return false;
}
}
</script>
</form>
</td>
</tr>
</table>
<div id="page-wrap" style="display: flex">
<div th:if="${from>1}">
<p th:value="${from-1}">
<a class="page" href="#" ><</a>
</p>
</div>
<div style="display: flex">
<p th:each="pno:${#numbers.sequence(from,to)}" th:value="${pno}" >
<a class="page" href="#" th:text="${pno}"></a>
</p>
</div>
<div th:if="${to < tot}">
<p th:value="${to+1}">
<a class="page" href="#" >></a>
</p>
</div>
</div>
<script type="text/javascript">
$("#board-list").find(".page").click(function(event){
event.preventDefault();
btnSearchClicked($(this).parent().attr("value"));
});
</script>
</body>
- $("#board-list")는 id가 board-list인 요소를 선택한다. 이 요소 내에서 .page 클래스를 가진 요소를 찾는다.
- .page 클래스를 가진 요소는 페이지 번호를 클릭할 때 작동한다.
- 클릭 이벤트가 발생하면 event.preventDefault()를 호출하여 기본 동작(링크 이동)을 차단한다.
- btnSearchClicked($(this).parent().attr("value"))는 클릭한 페이지 번호의 값을 btnSearchClicked 함수에 전달하여 해당 페이지의 검색 결과를 가져온다.
3. BoardSearchDTO
@Getter
@Setter
public class BoardSearchDTO {
private int columnName;
private String query;
private int page=1;
}
4. BoardController
PATCH 메서드 URL과 매핑할 메서드 작성
//게시글 검색
@ResponseBody
@PatchMapping("/board/search")
public ModelAndView boardSearchList(@RequestParam(defaultValue = "1") int page, BoardSearchDTO dto) {
return service.boardSearchProcess(page, dto);
}
5. BoardService
BoardService 인터페이스에 메서드 선언
//게시글 검색
ModelAndView boardSearchProcess(int page, BoardSearchDTO dto);
6. BoardServiceProcess
BoardService 인터페이스의 메서드 구현
//게시글 검색
@Override
public ModelAndView boardSearchProcess(int page, BoardSearchDTO dto) {
ModelAndView mv = new ModelAndView("board/board-search");
if(page<1) page = 1;
int limit = 5;
int offset = (page-1)*limit;
List<BoardDTO> result = mapper.findAllBySearch(dto, limit, offset);
mv.addObject("list", result);
int rowCount = mapper.countAllBySearch(dto); // 총 게시글 수
int tot = rowCount/limit; //총 페이지 수
if(rowCount % limit > 0) tot++; //나머지가 존재하면 1 증가
int RANGE = 5; //한 페이지에 보여질 페이지 번호 개수
int rangeNo = page/RANGE; //출력되는 페이지 번호 범위를 나타냄
if(page % RANGE > 0)rangeNo++; // 나머지 페이지가 있으면 페이지 번호를 증가시킨다
int to = RANGE * rangeNo; //한 페이지의 마지막 번호
int from = to - RANGE+1; //한 페이지의 시작 번호
//1~5 6~10 11~15 16~20
if(to > tot) to = tot;
mv.addObject("from", from); //한 페이지의 시작 번호를 "from" 이름으로 mv에 저장
mv.addObject("to", to); //한 페이지의 마지막 번호를 "to" 이름으로 mv에 저장
mv.addObject("tot", tot); //총 페이지 개수를 "tot" 이름으로 mv에 저장
return mv;
}
- RowBounds 객체를 생성하여 조회할 범위를 설정한다. 검색 결과를 페이징 처리하기 위해 사용된다.
- ModelAndView 객체를 생성하여 "board/board-search" 뷰를 사용하도록 설정한다.
7. BoardMapper
XML 파일에 정의된 쿼리를 호출할 메서드 선언
//검색
List<BoardDTO> findAllBySearch(@Param("dto") BoardSearchDTO dto, @Param("limit") int limit, @Param("offset") int offset);
int countAllBySearch(BoardSerachDTO dto);
8. board-mapper.xml
동적 쿼리 작성
<select id="findAllBySearch" resultType="com.pha.project.dto.BoardDTO">
select * from board
<where>
<if test="dto.columnName != 0 and dto.columnName == 1 or dto.columnName == 3">
title like '%${dto.query}%'
</if>
<if test="dto.columnName != 0 and dto.columnName == 2 or dto.columnName == 3">
or content like '%${dto.query}%'
</if>
</where>
order by no desc
limit #{limit} offset #{offset}
</select>
<select id="countAllBySearch" resultType="int">
select count(*) from board
<where>
<if test="columnName != 0 and columnName == 1 or columnName == 3">
title like '%${query}%'
</if>
<if test="columnName != 0 and columnName == 2 or columnName == 3">
or content like '%${query}%'
</if>
</where>
</select>
- <where> 절은 쿼리 내에서 조건문을 묶어주는 역할. 조건이 하나 이상일 경우에만 조건문을 추가한다.
- <if> : 조건문을 적용하는 요소. test 속성에 지정된 조건식이 참일 경우 내부의 SQL 코드가 적용된다.
- dto.columnName != 0 and dto.columnName == 1 or dto.columnName == 3 : 검색 조건이 제목(columnName == 1)이거나 제목과 내용(columnName == 3)에 해당하는 경우에 해당한다.
- title like '%${dto.query}%': 제목에 검색어가 포함된 경우를 찾는다.
- content like '%${dto.query}%': 내용에 검색어가 포함된 경우를 찾는다.
9. 게시글 검색 구현 화면
728x90