국비 74 - 글 검색 (페이지네이션 포함)

냐아암·2023년 8월 9일
0

국비

목록 보기
101/114

글 검색(option O)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 

<c:set var="boardName" value="map.boardName"/><c:setvar="pagination"value="{map.boardName}" /> <c:set var="pagination" value="{map.pagination}" />
<c:set var="boardList" value="${map.boardList}" />

${boardName}
<link rel="stylesheet" href="${contextPath}/resources/css/boardList-style.css">

<link rel="stylesheet" href="${contextPath}/resources/css/main-style.css">

<script src="https://kit.fontawesome.com/8f020b2fa9.js" crossorigin="anonymous"></script>
   <%-- 검색을 진행한 경우 key, query를 쿼리스트링 형태로 저장한 변수 생성 --%>
   <c:if test="${!empty param.key}">
        <c:set var="sURL" value="&key=${param.key}&query=${param.query}" />
   </c:if>

   

    <section class="board-list">

        <h1 class="board-name">${boardName}</h1>

        <c:if test="${!empty param.key}">
            <h3 style="margin-left: 30px;">"${param.query}" 검색 결과</h3>
        </c:if>

        <div class="list-wrapper">

            <table class="list-table">
                <thead>
                    <tr>
                        <th>글번호</th>
                        <th>제목</th>
                        <th>작성자</th>
                        <th>작성일</th>
                        <th>조회수</th>
                    </tr>
                </thead>

                <tbody>

                    <c:choose>

                        <c:when test="${empty boardList}">
                            <!-- 게시글 목록 조회 결과가 비어있다면 -->
                            <tr>
                                <th colspan="5">게시글이 존재하지 않습니다.</th>
                            </tr>
                            
                        </c:when>

                        <c:otherwise>
                            <!-- 게시글 목록 조회 결과가 비어있지 않다면 -->

                            <!-- 향상된 for문처럼 사용 -->
                            <c:forEach var="board" items="${boardList}">

                                <tr>
                                    <td>${board.boardNo}</td>
                                    <td>
                                        <c:if test="${!empty board.thumbnail}">
                                            <img src="${contextPath}${board.thumbnail}" class="list-thumbnail">
                                        </c:if>
                                    
                                        <a href="detail?no=${board.boardNo}&cp=${pagination.currentPage}&type=${param.type}${sURL}">${board.boardTitle}</a>
                                    </td>
                                    <td>${board.memberNickname}</td>
                                    <td>${board.createDate}</td>
                                    <td>${board.readCount}</td>
                                </tr>

                            </c:forEach>

                        </c:otherwise>

                    </c:choose>

                </tbody>

            </table>

        </div>

        <div class="btn-area">

            <c:if test="${!empty loginMember}">
                <!-- /community/board/write -->
                <button id="insertBtn" onclick="location.href='write?mode=insert&type=${param.type}&cp=${param.cp}'">글쓰기</button>
            </c:if>

        </div>

       

        <div class="pagination-area">

            <!-- 페이지네이션 a태그에 사용될 공통 주소를 저장한 변수 선언 -->
            <c:set var="url" value="list?type=${param.type}&cp="></c:set>


            <ul class="pagination">

                <!-- 첫 페이지로 이동 -->
                <li><a href="${url}1${sURL}">&lt;&lt;</a></li>

                <!-- 이전 목록 마지막 번호로 이동 -->
                <li><a href="${url}${pagination.prevPage}${sURL}">&lt;</a></li>

                
                <!-- 범위가 정해진 일반 for문 사용 -->
                <c:forEach var="i" begin="${pagination.startPage}" end="${pagination.endPage}" step="1">

                <c:choose>
                    <c:when test="${i == pagination.currentPage}">
                        <li><a class="current">${i}</a></li>
                    </c:when>

                    <c:otherwise>
                        <li><a href="${url}${i}${sURL}">${i}</a></li>
                    </c:otherwise>

                </c:choose>

                </c:forEach>
              

                <!-- 다음 목록 시작 번호로 이동 -->
                <li><a href="${url}${pagination.nextPage}${sURL}">&gt;</a></li>

                <!-- 끝 페이지로 이동 -->
                <li><a href="${url}${pagination.maxPage}${sURL}">&gt;&gt;</a></li>
            </ul>
        </div>

        <!-- /board/list?type=1&cp=3 -->

        <!-- /board/list?type=1&key=t&query=안녕 -->
        <form action="list" method="get" id="boardSearch" onsubmit="return searchValidate()">
            <input type="hidden" name="type" value="${param.type}">

            <select name="key" id="search-key">
                <option value="t">제목</option>
                <option value="c">내용</option>
                <option value="tc">제목 + 내용</option>
                <option value="w">작성자</option>

            </select>
            
            <input type="text" name="query" id="search-query" placeholder="검색어를 입력해주세요.">

            <button>검색</button>
        </form>

    </section>
</main>

<div class="modal">
    <span id="modal-close">&times;</span>
    <img id="modal-image" src="${contextPath}/resources/images/user.png">
</div>

<jsp:include page="/WEB-INF/views/common/footer.jsp" />

<script src="${contextPath}/resources/js/board/board.js"></script>
``` ```js // 검색창에 이전 검색 기록 반영하기 (function(){ const select = document.getElementById("search-key");
const input = document.getElementById("search-query");

// const option = select.children;
const option = document.querySelectorAll("#search-key>option");

if(select != null) { // 검색창 화면이 존재할 때만 코드 적용
    // 현재 주소에서 쿼리스트링(파라미터) 얻어오기
    const params = new URL(location.href).searchParams;

    // 얻어온 파라미터 중 key, query만 변수에 저장
    const key = params.get("key");
    const query = params.get("query");

    // input에 query값 대입
    input.value=query;

    // option을 반복 접근해서 value와 key와 같으면 selected 속성 추가
    for(let op of option){
        if(op.value == key){
            op.selected = true;
        }
    }
}

})();

// 검색 유효성 검사(검색어를 입력했는지 확인)
function searchValidate(){

const query =  document.getElementById("search-query");

if(query.value.trim().length == 0) {

    alert("검색어를 입력해주세요");
    query.value="";
    query.focus();
    return false;
} 

return true;

}


----------------------

### Servlet
```java
package edu.kh.community.board.controller;

import java.io.IOException;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import edu.kh.community.board.model.service.BoardService;

@WebServlet("/board/list")
public class BoardListServlet extends HttpServlet{
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			
			// 쿼리스트링 얻어오기 == 파라미터 얻어오기
			
			int type = Integer.parseInt(req.getParameter("type"));
			
			// nav 메뉴(공지사항, 자유게시판, 질문게시판) 선택 시
			// 쿼리스트링에 cp가 없음 --> cp = 1 고정
			int cp = 1;
			
			// 페이지네이션 번호 선택 시
			// 쿼리스트링에 cp가 있음 --> cp = 쿼리스트링 cp값
			if(req.getParameter("cp") != null) { // 쿼리스트링에 "cp"가 존재한다면
				cp = Integer.parseInt(req.getParameter("cp"));
			}
					
			
			BoardService service = new BoardService();
			
			// 게시판 이름, 페이지네이션 객체, 게시글 리스트를 한 번에 반환하는 service 호출
			Map<String, Object> map = null;
			
			if(req.getParameter("key") == null) { // 일반 목록 조회
				map = service.selectBoardList(type,cp);
			} else {
				String key = req.getParameter("key");
				String query = req.getParameter("query");
				
				map = service.searchBoardList(type, cp, key, query);
				
			}
			
			// request 범위로 map 세팅
			req.setAttribute("map", map);
			
			String path = "/WEB-INF/views/board/boardList.jsp";
			
			RequestDispatcher dispatcher = req.getRequestDispatcher(path);
			
			dispatcher.forward(req, resp);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

}

🔑 검색 조건에 따라 switch문 작성

Service

/** 검색 목록 조회 service
	 * @param type
	 * @param cp
	 * @param key
	 * @param query
	 * @return map
	 * @throws Exception
	 */
	public Map<String, Object> searchBoardList(int type, int cp, String key, String query) throws Exception {
		
		Connection conn = getConnection();
		
		// 기존 목록 조회 Service, DAO, SQL을 참고하면서 진행
		// 1. 게시판 이름 조회 DAO 호출
		String boardName = dao.selectBoardName(conn, type);
		
		// 2. SQL 조건절에 추가될 구문 가공(key, query)
		String condition = null; // 조건
		
		switch(key) {
		case "t" 	: condition = " AND BOARD_TITLE LIKE '%" + query + "%' "; break;
		case "c" 	: condition = " AND BOARD_CONTENT LIKE '%" + query + "%' "; break;
		case "tc" 	: condition = " AND (BOARD_TITLE LIKE '%"+ query +"%' OR BOARD_CONTENT LIKE '%" + query  +"%') "; break;
		case "w"	: condition = " AND MEMBER_NICK LIKE '%" + query + "%' "; break;
		}
		
		// 3-1 특정 게시판에서 조건을 만족하는 게시글 수 조회
		int listCount = dao.searchList(conn, type, condition);
		
		// 3-2 listCount 게시글 수 + 현재 페이지(cp)를 이용해 페이지네이션 객체 생성
		Pagination pagination = new Pagination(cp, listCount);
		
		// 4. 특정 게시판에서 조건을 만족하는 게시글 목록 조회
		List<Board> boardList = dao.searhBoardList(conn, pagination, type, condition);
		
		// 5. 결과값을 하나의 Map에 모아서 반환
		Map<String, Object> map = new HashMap<String, Object>();

		map.put("boardName", boardName);
		map.put("pagination", pagination);
		map.put("boardList", boardList);

		close(conn);

		return map; // Map 객체 반환
		
	}

DAO

/** 특정 게시판에서 조건을 만족하는 게시글 수 조회하는 DAO
	 * @param conn
	 * @param type
	 * @param condition
	 * @return listCount
	 * @throws Exception
	 */
	public int searchList(Connection conn, int type, String condition) throws Exception {
		
		int listCount = 0;
		
		try {
			
			String sql = prop.getProperty("searchListCount") + condition;
			
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setInt(1, type);
			
			rs = pstmt.executeQuery();
			
			if(rs.next()) {
				listCount = rs.getInt(1);
			}
			
		} finally {
			close(rs);
			close(pstmt);
		}
		
		return  listCount;
	}

sql

<!-- 특정 조건을 만족하는 게시글 수 조회 -->
	<entry key="searchListCount">
		SELECT COUNT(*) FROM BOARD
		JOIN MEMBER USING (MEMBER_NO)
		WHERE BOARD_ST = 'N'
		AND BOARD_CD = ?
	</entry>

DAO

🔑 두 개의 sql문 결합

/** 특정 게시판에서 조건을 만족하는 게시글 목록 조회 DAO
	 * @param conn
	 * @param pagination
	 * @param type
	 * @param condition
	 * @return boardList
	 * @throws Exception
	 */
	public List<Board> searhBoardList(Connection conn, Pagination pagination, int type, String condition) throws Exception {
		
		
		List<Board> boardList = new ArrayList<Board>();
		
		try {
			
			String sql = prop.getProperty("searchBoardList1")
					   + condition
					   + prop.getProperty("searchBoardList2");
			
			// BETWEEN 구문에 들어갈 범위 계산
			int start = (pagination.getCurrentPage() - 1) * pagination.getLimit() + 1;

			int end = start + pagination.getLimit() - 1;

			pstmt = conn.prepareStatement(sql);

			pstmt.setInt(1, type);
			pstmt.setInt(2, start);
			pstmt.setInt(3, end);

			rs = pstmt.executeQuery();

			while (rs.next()) {
				Board board = new Board();

				board.setBoardNo(rs.getInt("BOARD_NO"));
				board.setBoardTitle(rs.getString("BOARD_TITLE"));
				board.setMemberNickname(rs.getString("MEMBER_NICK"));
				board.setCreateDate(rs.getString("CREATE_DT"));
				board.setReadCount(rs.getInt("READ_COUNT"));
				board.setThumbnail(rs.getString("THUMBNAIL"));

				boardList.add(board);
			}
			
			
		} finally {
			
			close(rs);
			close(pstmt);
			
		}
		
		return boardList;
	}

sql

<!-- 특정 게시판에서 조건을 만족하는 게시글 목록 조회 -->
	<entry key="searchBoardList1">
		SELECT * FROM(
		    SELECT ROWNUM RNUM, A.* FROM(
		        SELECT BOARD_NO, BOARD_TITLE, MEMBER_NICK, 
		                TO_CHAR(CREATE_DT,'YYYY-MM-DD') AS CREATE_DT,
		                READ_COUNT,
                        (SELECT IMG_RENAME FROM BOARD_IMG
                        WHERE IMG_LEVEL = 0
                        AND BOARD_IMG.BOARD_NO = BOARD.BOARD_NO) THUMBNAIL
		        FROM BOARD
		        JOIN MEMBER USING (MEMBER_NO)
		        WHERE BOARD_CD = ?
		        AND BOARD_ST = 'N'
	</entry>
<entry key="searchBoardList2">
		ORDER BY BOARD_NO DESC
		    ) A
		)
		WHERE RNUM BETWEEN ? AND ?
	</entry>
profile
개발 일지

0개의 댓글