[Spring] 24.SpringProject-검색 기능(1)




검색 기능



이번 장에서는 저희 게시물 프로젝트에 검색기능을 추가하겠습니다!


image



위에서 보듯이 검색기능을 구현하기 위해 두 가지 속성을 이용합니다!


  • 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 박스는 다음과 같이 구현할 것입니다!


image




<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를 수정해서 검색이 제대로 되나 확인해보겠습니다~!