본문 바로가기
백엔드(Back-End)/Spring Boot

[Spring Boot] 13. Searching 검색 기능(1)

by 기딩 2023. 12. 6.
728x90

2023.12.06 - [백엔드(Back-End)/Spring Boot] - [sts4-Spring Boot] 12. Paging 페이지 블록 및 페이지 이동하기

 

[sts4-Spring Boot] 12. Paging 페이지 블록 및 페이지 이동하기

2023.12.05 - [백엔드(Back-End)/Spring Boot] - [sts4-Spring Boot] 11. Alert Message 메시지 전달 [sts4-Spring Boot] 11. Alert Message 메시지 전달 2023.12.05 - [백엔드(Back-End)/Spring Boot] - [sts4-Spring Boot] 10. 게시글 삭제하기 [sts

silvow94.tistory.com

페이징 기능을 대략 구현했으니, 검색 기능을 구현해보자.

 

검색 키워드와 검색 유형(전체, 작성자, 내용)을 이용해서 구현한다.

 

그 전에, 기존에 그냥 만들어뒀던 검색 스타일을 바꾸려고 한다.

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>

 

공통 부분이므로 인클루드 하는 형태로 변경

 

 

 

728x90