2023.12.06 - [백엔드(Back-End)/Spring Boot] - [sts4-Spring Boot] 12. Paging 페이지 블록 및 페이지 이동하기
페이징 기능을 대략 구현했으니, 검색 기능을 구현해보자.
검색 키워드와 검색 유형(전체, 작성자, 내용)을 이용해서 구현한다.
그 전에, 기존에 그냥 만들어뒀던 검색 스타일을 바꾸려고 한다.
src/main/resources > static-css 디렉터리에 style.css 파일의 가장 아래에 코드를 작성한다.
.dropdown.dropdown-lg .dropdown-menu {
margin-top: -1px;
padding: 6px 20px;
}
.input-group-btn .btn-group {
display: flex !important;
}
.btn-group .btn {
border-radius: 0;
margin-left: -1px;
}
.btn-group .btn:last-child {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.btn-group .form-horizontal .btn[type="submit"] {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.form-horizontal .form-group {
margin-left: 0;
margin-right: 0;
}
.form-group .form-control:last-child {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
@media screen and (min-width: 768px) {
#adv-search {
width: 500px;
margin: 0 auto;
}
.dropdown.dropdown-lg {
position: static !important;
}
.dropdown.dropdown-lg .dropdown-menu {
min-width: 500px;
이제 템플릿의 fragments 패키지의 common.html로 가서,
pagination fragment 뒤에 search fragment를 작성하자.
<div th:fragment="search" id="adv-search" class="input-group">
<input type="text" id="mainSearchKeyword" class="form-control" th:value="${params.searchKeyword}" placeholder="키워드를 입력해 주세요." />
<div class="input-group-btn">
<div class="btn-group" role="group">
<div class="dropdown dropdown-lg">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<!--/* 검색 form */-->
<form id="searchForm" th:action="@{/board/list.do}" method="get" th:onsubmit="return searchBoard(this)" class="form-horizontal" role="form">
<!--/* 현재 페이지 번호, 페이지당 출력할 데이터 개수, 페이지 하단에 출력할 페이지 개수 Hidden 파라미터 */-->
<input type="hidden" name="currentPageNo" value="1" />
<input type="hidden" name="recordsPerPage" th:value="${params.recordsPerPage}" />
<input type="hidden" name="pageSize" th:value="${params.pageSize}" />
<div class="form-group">
<label for="filter">검색 유형</label>
<select name="searchType" class="form-control">
<option value="" th:selected="${#strings.isEmpty( params.searchType )}">전체</option>
<option value="title" th:selected="${#strings.equals( params.searchType, 'title' )}">제목</option>
<option value="content" th:selected="${#strings.equals( params.searchType, 'content' )}">내용</option>
<option value="writer" th:selected="${#strings.equals( params.searchType, 'writer' )}">작성자</option>
</select>
</div>
<div class="form-group">
<label for="contain">키워드</label>
<input type="text" name="searchKeyword" class="form-control" th:value="${params.searchKeyword}" />
</div>
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></button>
</form>
</div>
</div>
<button type="button" class="btn btn-primary" onclick="searchBoard(null)"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></button>
</div>
</div>
</div>
<form> 태그에서 get방식으로 th:action을 사용하여 list.do로 요청을 보내고,
th:onsubmit을 사용하여 해당 폼이 제출될 때 searchBoard() 자바스크립트 함수를 실행한다.
- 검색을 하면 현재 페이지는 항상 1로 시작해야 하므로, currentPage value는 1
- 드롭다운 안에서의 검색은 버튼 타입 "submit" : 컨트롤러로 폼 데이터 전송
list.html에서 search fragment 인클루드
<th:block layout:fragment="search">
<div th:replace="~{board/fragments/common :: search}"></div>
</th:block>
글의 제목을 클릭했을 때, 게시글 내용 페이지로 이동하는 a 태그에 th:href 속성 값을 변경한다.
<td class="text-left">
<a th:href="|@{/board/view.do}${params.makeQueryString(params.currentPageNo)}&idx=${row.idx}|" th:text="${row.title}"></a>
</td>
페이지에 이동하기 전인 이전 페이지 정보를 유지하기 위해, currentPageNo를 전달해야 한다.
타임리프에서 문자열을 연결하려면 수직선 ( | )으로 감싸야 한다.
자바스크립트 영역에
메인 검색과 드롭다운 검색을 처리하는 searchBoard 함수를 추가한다.
<th:block layout:fragment="script">
<script th:inline="javascript">
/* <![CDATA[ */
function movePage(uri, queryString) {
location.href = uri + queryString;
}
function searchBoard(form) {
/*[- 드롭다운이 아닌 메인 검색 키워드로 검색했을 때 -]*/
if (isEmpty(form) == true) {
var searchKeyword = document.getElementById("mainSearchKeyword");
if (isEmpty(searchKeyword.value) == true) {
alert("키워드를 입력해 주세요.");
searchKeyword.focus();
return false;
}
form = document.getElementById("searchForm");
form.searchKeyword.value = searchKeyword.value;
form.submit();
}
if (isEmpty(form.searchKeyword.value) == true) {
alert("키워드를 입력해 주세요.");
form.searchKeyword.focus();
return false;
}
}
/* ]]> */
</script>
</th:block>
if (isEmpty(from) == ture)
메인에서 입력한 키워드 통해 검색 시 실행
검색 영역에서 인클루드 한 ( common.html ) search fragment에서
"searchForm" 안의 searchKeyword value에 입력 키워드 저장 -> 컨트롤러로 폼 데이터 전송
if (isEmpty(form.searchKeyword.value) == true )
드롭다운을 통한 검색 시 실행
드롭다운 안의 검색 버튼 타입이 submit로 지정
이제 키워드 검색과 페이징 기능을 위해 쿼리 메서드를 만들어야 한다.
공통 Mapper XML 을 생성하자.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="CommonMapper">
<sql id="paging">
LIMIT
#{paginationInfo.firstRecordIndex}, #{recordsPerPage}
</sql>
<sql id="search">
<!-- 검색 키워드 있을 때 -->
<if test="searchKeyword != null and searchKeyword != ''">
<choose>
<!-- 검색 유형 있을 때 -->
<when test="searchType != null and searchType != ''">
<choose>
<when test="'title'.equals(searchType)">
AND title LIKE CONCAT('%', #{searchKeyword}, '%')
</when>
<when test="'content'.equals(searchType)">
AND content LIKE CONCAT('%', #{searchKeyword}, '%')
</when>
<when test="'writer'.equals(searchType)">
AND writer LIKE CONCAT('%', #{searchKeyword}, '%')
</when>
</choose>
</when>
<!-- 검색 유형 없을 때 -->
<otherwise>
AND
(
title LIKE CONCAT('%', #{searchKeyword}, '%')
OR content LIKE CONCAT('%', #{searchKeyword}, '%')
OR writer LIKE CONCAT('%', #{searchKeyword}, '%')
)
</otherwise>
</choose>
</if>
</sql>
</mapper>
search
SQL에서 '가' 라는 키워드를 찾으려면, 속성명 LIKE '%가%' 이런 식으로 써야 한다.
<otherwise> : 검색 유형이 없을 때 = 제목, 작성자, 내용 전체에서 검색
paging
보드매퍼 XML에서 selectBoardList의 LIMIT과 동일
공통 기능이므로 해당 SQL 조각을 인클루드해서 사용
=> 보드매퍼 XML 변경 필요
<select id="selectBoardList" parameterType="BoardDTO" resultType="BoardDTO">
SELECT
<include refid="boardColumns" />
FROM
board
WHERE
delete_yn = 'N'
<include refid="CommonMapper.search" />
ORDER BY
notice_yn ASC,
idx DESC,
insert_time DESC
<include refid="CommonMapper.paging" />
</select>
<select id="selectBoardTotalCount" parameterType="BoardDTO" resultType="int">
SELECT
COUNT(*)
FROM
board
WHERE
delete_yn = 'N'
<include refid="CommonMapper.search" />
</select>
공통 부분이므로 인클루드 하는 형태로 변경
'백엔드(Back-End) > Spring Boot' 카테고리의 다른 글
[sts4-Spring Boot] 15. REST API 사용해보기 (0) | 2023.12.07 |
---|---|
[sts4-Spring Boot] 14. 이전 페이지 정보 유지하기 (0) | 2023.12.07 |
[sts4-Spring Boot] 12. Paging 페이지 블록 및 페이지 이동하기 (0) | 2023.12.06 |
[sts4-Spring Boot] 11. Alert Message 메시지 전달 (1) | 2023.12.05 |
[sts4-Spring Boot] 10. 게시글 삭제하기 (0) | 2023.12.05 |