[Spring] 24.SpringProject-검색 기능(1)
검색 기능
이번 장에서는 저희 게시물 프로젝트에 검색기능을 추가하겠습니다!
위에서 보듯이 검색기능을 구현하기 위해 두 가지 속성을 이용합니다!
- searchType : 검색타입
- keyword : 검색어
페이징에서 이미 저희가 많은 것들을 했기에 ㅋㅋㅋㅋ 약간만 추가해주면 쉽게 검색을 구현할 수 있습니다!
한 번 구현해볼까요??
Criteria, PageMaker 수정
먼저, 검색을 했을 때 검색 결과에 대한 정보를 게시물 삭제, 등록, 수정 , 조회 후에도 그대로 유지하려면 URI에 searchType, keyword를 달고 다녀야합니다!
어? 페이징에서와 뭔가 유사하지 않나요? page와 perPageNum를 달고다녀야 하는 상황과 말입니다!
그 때 저희는 page와 perPageNum를 속성으로 갖는 Criteria를 정의해서 페이징 구현을 좀 더 수월하게 했습니다!
심지어 ‘?page=3&perPageNum=15’ 와 같이 URI를 생성해주는 makeQuery() 함수도 Criteria와 PageMaker에 추가해줬습니다!
눈치채셨겠지만, searchType과 keyword 속성을 Criteria와 PageMaker 클래스에 추가해주면 좀 더 쉽게 검색기능을 구현할 수 있습니다!
수정해보겠습니다!
//Criteria.java
package com.gguri.swp.domain;
import org.springframework.web.util.UriComponentsBuilder;
public class Criteria {
private int page;
private int perPageNum;
//속성 searchType, keyword 추가
private String searchType;
private String keyword;
public Criteria() {
this.page = 1;
this.perPageNum = 10;
this.searchType = null;
this.keyword = null;
}
public int getPageStart() {
return (this.page - 1)*perPageNum;
}
//getter, setter
...
//searchType, keyword 추가
public String makeQuery() {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance()
.queryParam("page", page)
.queryParam("perPageNum", this.perPageNum);
if (searchType!=null) {
uriComponentsBuilder
.queryParam("searchType", this.searchType)
.queryParam("keyword", this.keyword);
}
return uriComponentsBuilder.build().encode().toString();
}
@Override
public String toString() {
return "Criteria [page=" + page + ", perPageNum=" + perPageNum + ", searchType=" + searchType + ", keyword="
+ keyword + "]";
}
}
위의 makeQuery를 보면 searchType이 null인지 확인하는 작업를 통해 검색을 한 경우와 하지 않은 경우의 URI를 다르게 구현한 것을 볼 수 있습니다.
검색을 하지 않았는데 굳이 searchType과 keyword를 달고 다닐 필요는 없겠죠??
PageMaker의 makeQuery도 수정해줍니다!
//PageMaker.java
package com.gguri.swp.domain;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
public class PageMaker {
private int displayPageCnt = 10; // 화면에 보여질 페이지 번호 수
private int totalDataCount; // 실제 게시물 수
private int startPage; // 현재 페이지 기준 시작 페이지 번호
private int endPage; // 현재 페이지 기준 끝 페이지 번호
private boolean prev; // 이전 버튼 활성화 여부
private boolean next; // 다음 버튼 활성화 여부
private Criteria cri; //page(현재 페이지), perPageNum(페이지 당 보여질 게시물의 수)
...
public String makeQuery(int page, boolean needSearch) {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance()
.queryParam("page", page)
.queryParam("perPageNum", this.cri.getPerPageNum());
//검색 한 경우
if (this.cri.getSearchType() != null) {
uriComponentsBuilder
.queryParam("searchType", this.cri.getSearchType())
.queryParam("keyword", this.cri.getKeyword());
}
return uriComponentsBuilder.build().encode().toString();
}
...
}
Mapper
일단 검색을 위한 select 박스는 다음과 같이 구현할 것입니다!
<div class="form-inline">
<select id="searchType" name="searchType">
<option value="">검색조건</option>
<option value="t">제목</option>
<option value="c">내용</option>
<option value="w">작성자</option>
<option value="tc">제목+내용</option>
<option value="all">전체조건</option>
</select>
<input class="form-control" type="text" id="keyword" name="keyword"
value="${pageMaker.cri.keyword}" placeholder="검색어를 입력하세요"/>
<button id="searchBtn" class="btn btn-primary">Search</button>
</div>
만약, 제목에 꾸리가 들어가는 게시물을 검색하려면 다음과 같이 쿼리문을 작성해서 mysql 서버에 있는 board 테이블에서 데이터를 조회해 와야합니다!
--만약 searchType의 option value가 "t" 라면
select *
from board
where bno > 0 and title like CONCAT ('%','꾸리','%')
order by bno desc
limit #{pageStart}, #{perPageNum}
그럼 BoardMapper에 검색타입마다 알맞는 쿼리문을 어떻게 추가해줄까요??? 막막합니다!
하지만, MyBatis 가 제공하는 <if>을 사용하면 검색 조건에 따른 동적 SQL문을 손쉽게 추가해줄 수 있습니다!
기존의 listPage 쿼리를 수정해보겠습니다!
<!-- BoardMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD 선언 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="BoardMapper">
...
<!-- 일정 페이지 씩 조회 -->
<select id="listPage" resultType="BoardVO">
select *
from board
where bno > 0
<!-- 검색 조건이 null이 아니라면 검색조건에 따른 데이터 조회-->
<if test="searchType != null">
<if test="searchType == 't'.toString()">
and title like CONCAT('%', #{keyword}, '%')
</if>
<if test="searchType == 'c'.toString()">
and content like CONCAT('%', #{keyword}, '%')
</if>
<if test="searchType == 'w'.toString()">
and writer like CONCAT('%', #{keyword}, '%')
</if>
<if test="searchType == 'tc'.toString()">
and (title like CONCAT('%', #{keyword}, '%')
or content like CONCAT('%', #{keyword}, '%'))
</if>
<if test="searchType == 'all'.toString()">
and (title like CONCAT('%', #{keyword}, '%')
or content like CONCAT('%', #{keyword}, '%')
or writer like CONCAT('%', #{keyword}, '%'))
</if>
</if>
order by bno desc, regdate desc
limit #{pageStart}, #{perPageNum}
</select>
...
</mapper>
검색된 결과에 따른 페이지 번호도 달라지기 때문에 전체 게시물 수를 구하는 gettotalcount 쿼리도 수정해줘야합니다!
하지만, 위의 listPage에 추가한 <if> 을 똑같이 추가하기에는 코드가 중복이됩니다!
이럴 때 <sql> 과 <include> 를 사용하면 됩니다!
<!-- boardMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD 선언 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="BoardMapper">
...
<sql id="searchCondition">
<if test="searchType != null">
<if test="searchType == 't'.toString()">
and title like concat('%', #{keyword}, '%')
</if>
<if test="searchType == 'c'.toString()">
and content like concat('%', #{keyword}, '%')
</if>
<if test="searchType == 'w'.toString()">
and writer like concat('%', #{keyword}, '%')
</if>
<if test="searchType == 'tc'.toString()">
and (title like concat('%', #{keyword}, '%')
or content like concat('%', #{keyword}, '%'))
</if>
<if test="searchType == 'all'.toString()">
and (title like concat('%', #{keyword}, '%')
or content like concat('%', #{keyword}, '%')
or writer like concat('%', #{keyword}, '%'))
</if>
</if>
</sql>
<!-- 검색 조건에 해당하는 일정 페이지 조회 -->
<select id="listPage" resultType="BoardVO">
select *
from board
where bno > 0
<include refid="searchCondition"></include>
order by bno desc, regdate desc
limit #{pageStart}, #{perPageNum}
</select>
<!-- 검색 조건에 해당하는 전체 게시물 수 조회 -->
<select id="gettotalcount" resultType="int">
select count(bno)
from board
where bno > 0
<include refid="searchCondition"></include>
</select>
</mapper>
아 그리고! boardDAO 의 listPage 메소드와 getTotalCount 메소드 둘다 Criteria을 매개변수로 받았었죠??
그렇기에 boardMapper까지 searchType과 keyword가 무사히 전달된 것입니다~!
다음 장에서는 listPage.jsp를 수정해서 검색이 제대로 되나 확인해보겠습니다~!