스프링 게시판 검색기능 구현
검색기능을 구현하기 앞서 만들었던 boardTest테이블의 content컬럼의 타입을 변경해 준다.
(Long type은 like가 안되어서 에러가 남..)
테이블에 데이터가 있으면 타입 변경이 안돼서 그냥 테이블을 지우고 다시 만들었다.
//테이블 삭제
drop table boardTest;
//테이블 생성
CREATE TABLE boardTest(
idx NUMBER(10,0),
writer VARCHAR2(20) NOT NULL,
title VARCHAR2(30) NOT NULL,
content VARCHAR(4000) NOT NULL,
reg_date DATE NOT NULL,
cnt int default 0
);
alter table boardTest add constraint pk_board primary key (idx);
//시퀀스 삭제
DROP SEQUENCE tmp_seq;
//시퀀스 생성
CREATE SEQUENCE tmp_seq ;
//커밋
commit;
//테스트 데이터 삽입
INSERT INTO boardtest(idx, title,writer,content,reg_date) VALUES(tmp_seq.NEXTVAL, tmp_seq.currval||'번째 글', 'test', tmp_seq.currval||'번째 글입니다.', (select sysdate from dual));
검색기능을 만들기 위하여 이전에 만들었던 PagingCriteria를 Criteria로 이름을 바꾸어 준다.
이렇게 해서 바꾸어 주면 다른 소스들을 직접 수정할 필요가 없다.
Criteria
package com.wipia.study.domain;
public class Criteria {
private int pageNum=1; //페이지 번호
private int amount=10; //페이지당 데이터 갯수
private String type;
private String keyword;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public String[] getTypeArr() {
return type == null? new String[] {}: type.split("");
}
@Override
public String toString() {
return "Criteria [pageNum=" + pageNum + ", amount=" + amount + ", type=" + type + ", keyword=" + keyword + "]";
}
}
검색 기능을 구현하기 위하여 Criteria에 type과 keyword를 추가해 주고, getter와 setter, toString을 생성해 준다.
그리고 getTypeArr이 추가되었는데, Mapper에서 사용하기 위하여 만들어 주었다.
boardList.jsp
<!-- 검색 form -->
<div id="search">
<form id="searchForm" action="getBoardList.do" methos="get">
<select name="type">
<option value="">선택</option>
<option value="T">제목</option>
<option value="C">내용</option>
<option value="W">작성자</option>
<option value="TC">제목 + 내용</option>
<option value="TW">제목 + 작성자</option>
<option value="TCW">제목 + 내용 + 작성자</option>
</select>
<input type="text" name="keyword" />
<button id="searchBtn">검색</button>
</form>
</div>
게시판 리스트에 검색할 수 있는 form을 추가해 준다.
전체소스
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>글 목록</title>
<script src="resources/jQuery/jquery-3.4.1.min.js"></script>
</head>
<body>
<a href="/">메인</a>
<table border="1" >
<tr>
<th bgcolor="" width="50">no</th>
<th bgcolor="" width="200">제목</th>
<th bgcolor="" width="150">작성자</th>
<th bgcolor="" width="150">작성일</th>
<th bgcolor="" width="100">조회수</th>
</tr>
<c:choose>
<c:when test="${!empty boardList}">
<c:forEach items="${boardList }" var="board">
<tr>
<td>${board.idx }</td>
<td align="left"><a href="${board.idx }">
${board.title }</a></td>
<td>${board.writer }</td>
<td><fmt:formatDate value="${board.regDate }" pattern="yyyy-MM-dd"/></td>
<td>${board.cnt }</td>
</tr>
</c:forEach>
</c:when>
<c:otherwise>
<tr>
<td colspan="5">등록된 글이 없습니다.</td>
</tr>
</c:otherwise>
</c:choose>
</table>
<br>
<a href="writeBoard.do">글 쓰기</a><br>
<div id="pagingDiv">
<c:if test="${paging.prev}">
<a href="${paging.startPage - 1 }">이전</a>
</c:if>
<c:forEach var="num" begin="${paging.startPage}" end="${paging.endPage }">
<a href="${num }">${num }</a>
</c:forEach>
<c:if test="${paging.next}">
<a id="next" href="${paging.endPage + 1 }">다음</a>
</c:if>
</div>
<form id="pagingFrm" name="pagingForm" action="getBoardList.do" method="get">
<input type="hidden" id="pageNum" name="pageNum" value="${paging.cri.pageNum }">
<input type="hidden" id="amount" name="amount" value="${paging.cri.amount }">
<input type="hidden" id="type" name="type" value="${paging.cri.type }">
<input type="hidden" id="keyword" name="keyword" value="${paging.cri.keyword }">
</form>
<br>
<!-- 검색 form -->
<div id="search">
<form id="searchForm" action="getBoardList.do" method="get">
<select name="type">
<option value="" <c:out value="${paging.cri.type == null?'selected':'' }" />>선택</option>
<option value="T" <c:out value="${paging.cri.type eq 'T'?'selected':'' }" />>제목</option>
<option value="C" <c:out value="${paging.cri.type eq 'C'?'selected':'' }" />>내용</option>
<option value="W" <c:out value="${paging.cri.type eq 'W'?'selected':'' }" />>작성자</option>
<option value="TC" <c:out value="${paging.cri.type eq 'TC'?'selected':'' }" />>제목 + 내용</option>
<option value="TW" <c:out value="${paging.cri.type eq 'TW'?'selected':'' }" />>제목 + 작성자</option>
<option value="TCW" <c:out value="${paging.cri.type eq 'TCW'?'selected':'' }" />>제목 + 내용 + 작성자</option>
</select>
<input type="text" name="keyword" />
<button id="searchBtn">검색</button>
</form>
</div>
</body>
<script type="text/javascript">
$(document).ready(function(){
//페이지 번호 이동
$('#pagingDiv a').click(function(e){
e.preventDefault();
$('#pageNum').val($(this).attr("href"));
pagingForm.submit();
});
//게시글에 pageNum넘기기
$('table a').click(function(e){
e.preventDefault();
var html = "<input type='hidden' name='idx' value='"+$(this).attr("href")+"'>";
$('#pagingFrm').append(html);
$('#pagingFrm').attr("action","getContent.do");
$('#pagingFrm').submit();
});
});
</script>
</html>
BoardController
//글 목록
@RequestMapping("/getBoardList.do")
public String getBoardList(Criteria cri, Model model) {
List<BoardVO> boardList = boardService.getBoardList(cri);
int total = boardService.totalCnt(cri);
// Model 정보 저장
model.addAttribute("boardList",boardList);
model.addAttribute("paging",new PageMaker(cri,total));
return "boardList"; // View 이름 리턴
}
글 목록에 total값을 받아오기 위하여 cri값을 보내준다.
보내주지 않고 전체 게시글 갯수만 가져오게 되면 검색했을때 글은 없는데 페이지는 있는 경우가 생긴다.
서비스와 다오에서 cri값을 받고 주는것만 추가해주고, mapper를 수정하면 검색기능은 끝난다.
testMapper
<?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="BoardMapper">
<sql id="criteria">
<trim prefixOverrides="OR" suffix=") AND " prefix="(">
<foreach collection="typeArr" item="type">
<trim prefix="OR">
<choose>
<when test="type == 'T'.toString()">title like '%'||#{keyword}||'%' </when>
<when test="type == 'C'.toString()">content like '%'||#{keyword}||'%' </when>
<when test="type == 'W'.toString()">writer like '%'||#{keyword}||'%' </when>
</choose>
</trim>
</foreach>
</trim>
</sql>
<select id="getBoardList" resultType="BoardVO">
<![CDATA[
SELECT idx,title,writer,reg_date,cnt
FROM (
SELECT /*+INDEX_DESC(boardTest pk_board) */
rownum rn, idx, title, writer,reg_date,cnt
from boardTest
where
]]>
<include refid="criteria"></include>
<![CDATA[
rownum <= #{pageNum} * #{amount}
)
where rn > (#{pageNum}-1) * #{amount}
]]>
</select>
<select id="getTotalCnt" resultType="int">
SELECT count(*)
FROM boardTest
where
<include refid="criteria"></include>
idx>0
</select>
</mapper>
가독성을 위하여 getBoardList와 getTotalCnt만 남겨두었다.
위에 보면 sql id="criteria"가 있는데 getBoardList와 getTotalCnt 두 곳에 사용하기때문에 소스 코드를 줄이기 위하여 sql로 만들었다.
trim과 foreach문을 이용하여 like문을 생성해 검색을 한다.
total에서도 include해주었기 때문에 검색한 데이터의 갯수만큼 반환해준다.
소스코드는 https://github.com/ming9mon/spring/tree/master/Study 에서 다운받을 수 있다.