게시판 주제별로 찾고자하는 키워드를 검색하면 원하는 게시물이 출력되도록 구현하였다.
검색기능의 전체적인 흐름은 검색하고자 하는 키워드를 쿼리를 조건문으로 실행한 후 반환 결과를 서버를 거쳐서 뷰(View)에 전송되어 출력하게 되는 것이다.
1. board 클래스 변수 추가
기존 Board.java에서 searchType(검색타입)과 keyword(검색어)를 추가해주었다.
searchType은 제목 or 내용 or 작성자 or 제목,내용과 같은 검색타입을 말하고,
keyword는 실제 검색한 내용인 검색어를 말한다.
@Data
public class Board {
private int boardNo; // 게시판번호
private String boardTitle; // 제목
private String boardContent; // 내용
private String boardWriter; // 작성자
private int readCount; // 조회수
private String regDate; // 작성날짜(YYYY-MM-DD hh24:mi)
private int priority; // 고정공지사항
private String fileName; // 파일이름
private String filePath; // 첨부파일경로
private int ncCount; // 댓글수
private String searchType; // 검색타입(내용,작성자...)
private String keyword; // 검색어
}
2. 검색 form 만들기
- <form>태그를 이용하여 검색창을 만들어준다.
- <select>와 <input>태그에는 어떤게 들어갔는지 매개변수로 넘길 수 있어야 하므로 각각 name을 추가해줘야 한다.
<select name="searchType"> <input type="text" name="keyword">
- form타입을 이용하여 검색 버튼을 누르게 되면 boardSearch.do() 함수를 실행한다.
- reqPage=1은 1페이지라는 의미
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:import url="/WEB-INF/views/common/header.jsp"/>
<div class="container">
<fieldset>
<h2>자유게시판</h2>
<c:if test="${not empty sessionScope.m && (sessionScope.m.memberLevel eq 0 || sessionScope.m.memberLevel eq 1)}">
<a class="btn btn-secondary" href='/boardWriteFrm.do' style="float:right; margin-bottom:5px;">글쓰기</a>
</c:if> 
<form action="/boardSearch.do?reqPage=1" method="post">
<a class="search" style="float:left;">
<select name="searchType">
<option value="boardTitle"<c:out value="${searchType eq 'boardTitle' ? 'selected' : ''}"/>>제목</option>
<option value="boardContent"<c:out value="${searchType eq 'boardContent' ? 'selected' : ''}"/>>내용</option>
<option value="boardWriter"<c:out value="${searchType eq 'boardWriter' ? 'selected' : ''}"/>>작성자</option>
<option value="tc"<c:out value="${searchType eq 'tc' ? 'selected' : ''}"/>>제목+내용</option>
</select>
<input type="text" name="keyword"/>
<input type="submit" class="btn btn-secondary" value="검색">
</a>
</form>
<table class="table table-hover">
<tr class="table-active">
<th scope="row">번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회수</th>
</tr>
<c:forEach items="${list }" var="b" varStatus="i">
<c:choose>
<!-- 고정게시물이 아닐 때 -->
<c:when test="${b.priority == 0}">
<tr class="table-default">
<td>${start + i.index - fixPage}</td>
<td><a href='/boardView.do?boardNo=${b.boardNo}' style="text-decoration:none;">${b.boardTitle}</a></td>
<td>${b.boardWriter}</td>
<td>${b.regDate}</td>
<td>${b.readCount}</td>
</tr>
</c:when>
<c:otherwise>
<!-- 고정게시물 -->
<tr class="table-default" style="background-color:#EAEAEA;">
<td><img src="resources/img/bell.png" style="width:20px; height:20px;"></td>
<td><a href='/boardView.do?boardNo=${b.boardNo}' style="text-decoration:none;">${b.boardTitle}</a></td>
<td>${b.boardWriter}</td>
<td>${b.regDate}</td>
<td>${b.readCount}</td>
</tr>
</c:otherwise>
</c:choose>
</c:forEach>
</table><br>
<div id="pageNavi">${pageNavi }</div>
</fieldset>
</div>
<c:import url="/WEB-INF/views/common/footer.jsp"/>
</body>
</html>
3. controller, service, dao 만들기
BoardController.java
@RequestMapping(value="/boardSearch.do")
public String boardSearch(int reqPage, Board b, Model model) {
HashMap<String, Object> data = service.boardSearch(reqPage, b);
model.addAttribute("pageNavi", data.get("pageNavi"));
model.addAttribute("list", data.get("list"));
model.addAttribute("start", data.get("start"));
return "board/boardList";
}
BoardService.java
- HashMap을 이용하여 페이지 시작, 페이지 끝, 키워드, 검색타입을 한번에 dao.boardSearch와 totalSearch 로 보낸다.
ArrayList<Board> list = dao.boardSearch(map); 검색한 게시물 리스트를 구함
int totalCount = dao.totalSearch(map); 검색한 게시물 총 개수를 구함
public HashMap<String, Object> boardSearch(int reqPage, Board b) {
int numPerPage = 10; //한페이지당 게시물 수
int end = reqPage * numPerPage;
int start = end - numPerPage + 1;
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("start", start);
map.put("end", end);
map.put("keyword", b.getKeyword());
map.put("searchType", b.getSearchType());
ArrayList<Board> list = dao.boardSearch(map);
int totalCount = dao.totalSearch(map);
int totalPage = 0;
if(totalCount%numPerPage == 0) {
totalPage = totalCount/numPerPage;
}else {
totalPage = totalCount/numPerPage + 1;
}
int pageNaviSize=5; // 페이지 네비 길이(1~5, 6~10...)
int pageNo = 1;
if(reqPage>4) {
pageNo= reqPage-2;
if(totalPage - reqPage < (pageNaviSize-1)) {
pageNo = totalPage-(pageNaviSize-1);
}
}
String pageNavi = "<ul class='pagination pagination-lg'>";
if(pageNo != 1) { // 이전버튼
pageNavi += "<li class='page-item disabled'>
<a class='page-link' href='/boardSearch.do?reqPage="+(reqPage-1)+"'><</a></li>";
}
for(int i=0;i<pageNaviSize;i++) { // 페이지숫자
if(pageNo == reqPage) {
pageNavi += "<li class='page-item active'>
<a class='page-link' href='/boardSearch.do?reqPage="+pageNo+"'>"+pageNo+"</a></li>";
}else {
pageNavi += "<li class='page-item'>
<a class='page-link' href='/boardSearch.do?reqPage="+pageNo+"'>"+pageNo+"</a></li>";
}
pageNo++;
if(pageNo > totalPage) { // 최종 페이지보다 네이비게이션 시작번호가 더 클경우 break
break;
}
}
if(pageNo <= totalPage) { // 다음버튼
pageNavi += "<li class='page-item'>
<a class='page-link' href='/boardSearch.do?reqPage="+(reqPage+1)+"'>></a><li>";
}
pageNavi += "</ul>";
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("pageNavi", pageNavi);
data.put("list", list);
data.put("start", start);
return data;
}
BoardDao.java
//검색한 키워드 전체 조회
public ArrayList<Board> boardSearch(HashMap<String, Object> map) {
List<Board> list = session.selectList("board.boardSearch", map);
return (ArrayList<Board>)list;
}
//검색한 키워드 총 게시물 수 조회
public int totalSearch(HashMap<String, Object> map) {
return session.selectOne("board.totalSearch",map);
}
4. mapper 쿼리입력
- 검색한 전체 리스트 출력하는 쿼리문, 검색한 총 게시물 수 -> 두개의 쿼리문이 필요하다.
두개의 쿼리문에서 중복코드가 존재하므로 중복코드는 별도로 빼서 사용하였다.
중복코드를 빼기위해서는 <sql>, <include>가 필요하다.
<sql>은 다른 구문에서 재사용가능한 SQL구문을 정의할 때 사용한다.
주의할 점은 반드시 재사용할 쿼리의 상단에 선언되어야 한다는 점이다. 상단에 선언되어있지 않고 하단에 선언되어 있다면 불러올수 없다.
<sql id="id값 넣기">
<!-- 사용할 쿼리 -->
</sql>
같은 파일 내에 정의해둔 <sql> 태그 내 쿼리들을 불러올 수 있도록 해준다.
예를들어 select count(*) from board 다음에 <include refid=search"></search>가 있으므로
sql id값인 search를 불러와 조건절을 실행하게 된다.
<sql id="search">
<choose>
<when test="searchType != null and searchType.equals('boardTitle')">
where BOARD_TITLE like '%'||#{keyword}||'%'
</when>
</choose>
</sql>
<select id="totalSearch" parameterType="map" resultType="int">
select count(*) from board
<include refid="search"></include>
</select>
boardSQL.xml
<!-- 검색 조건에 해당하는 중복코드 -->
<sql id="search">
<choose>
<when test="searchType != null and searchType.equals('boardTitle')">
where BOARD_TITLE like '%'||#{keyword}||'%'
</when>
<when test="searchType != null and searchType.equals('boardContent')">
where BOARD_CONTENT like '%'||#{keyword}||'%'
</when>
<when test="searchType != null and searchType.equals('boardWriter')">
where BOARD_WRITER like '%'||#{keyword}||'%'
</when>
<when test="searchType != null and searchType.equals('tc')">
where (BOARD_TITLE like '%'||#{keyword}||'%' ) or (BOARD_CONTENT LIKE '%'||#{keyword}||'%')
</when>
</choose>
</sql>
<!-- 게시판 검색 -->
<select id="boardSearch" parameterType="map" resultType="b">
select
board_no as boardNo,
board_title as boardTitle,
board_content as boardContent,
board_writer as boardWriter,
readCount,
regDate,
priority,
fileName,
filePath,
ncCount
from (select rownum as rnum, b.* from (SELECT * FROM board
<include refid="search"></include>
order by 1 desc)b)
where (rnum between #{start} and #{end})
</select>
<!-- 검색 한 게시물 수 조회 -->
<select id="totalSearch" parameterType="map" resultType="int">
select count(*) from board
<include refid="search"></include>
</select>
5. 실행결과
'spring > 게시판 만들기' 카테고리의 다른 글
[spring] 스프링 게시판 checkbox 체크되었을 때 1, 안되었을 때 0 표현 (0) | 2022.02.20 |
---|---|
[spring] 게시판 이전화면/수정하기/삭제하기 (0) | 2022.02.19 |
[spring] 스프링게시판 페이징(paging) 처리 (0) | 2022.02.18 |
[spring/mybatis] 게시판 조회수 (0) | 2022.02.16 |
[spring/mybatis] 게시판 리스트 출력 (0) | 2022.02.15 |