boardList.html
<form th:action="@{{boardCode}(boardCode=${boardCode})}" method="get" id="boardSearch">
<select name="key" id="searchKey">
<option value="t">제목</option>
<option value="c">내용</option>
<option value="tc">제목+내용</option>
<option value="w">작성자</option>
</select>
<input type="text" name="query" id="searchQuery" placeholder="검색어를 입력해주세요.">
<button>검색</button>
</form>
select 태그 쿼리스트링에 ?key=c 이런 형태로 감
input 태그 쿼리스트링에 &query=짱구 이런 형태로 붙음
현재 : /board/1
요청 주소 : /board/1?key=w&query=짱구
앞에 요청 주소는 똑같음(쿼리스트링만 다름)
BoardController
기존에 있던 selectBoardList 메서드에 매개변수 추가
@RequestParam Map<String, Object> paramMap
검색 시 key, query 담겨있음
@GetMapping("{boardCode:[0-9]+}")
public String selectBoardList(@PathVariable("boardCode") int boardCode,
@RequestParam(value="cp", required=false, defaultValue = "1") int cp,
Model model,
@RequestParam Map<String, Object> paramMap
) {
검색이 아닌 경우, 검색인 경우 나눠줄 거
Map<String, Object> map = null;
// 검색이 아닌 경우 (paramMap 이 {} 이렇게 나옴)
if(paramMap.get("key") == null) {
// 게시글 목록 조회 서비스 호출
map = service.selectBoardList(boardCode,cp);
} else {
// 검색인 경우 -> paramMap 은 {key=t, query=검색어}
// 검색 서비스 호출
// boardCode 를 paramMap 에 추가
paramMap.put("boardCode", boardCode);
// -> paramMap 은 {key=t, query=검색어, boardCode=1}
map = service.searchList(paramMap, cp);
}
BoardServiceImpl
// 1. 지정된 게시판(boardCode)에서
// 검색 조건에 맞으면서
// 삭제되지 않은 게시글 수를 조회
int listCount = mapper.getSearchCount(paramMap);
작성자 검색인 경우 Board 테이블에 없고 Member 테이블에 있어서 테이블 join 해줘야함
검색 조건에 따라 sql이 달라짐
동적 SQL : <choose>, <when>, <otherwise>
<choose> : 조건문을 작성할 영역 지정
내부에 <when>, <otherwise> 태그 작성 가능
<when> : if, else if 역할의 태그
test 속성 : 조건식을 작성하는 속성
<otherwise> : else 역할의 태그
<!-- 검색 조건이 맞는 게시글 수 조회 -->
<select id="getSearchCount">
SELECT COUNT(*)
FROM "BOARD"
<!-- 작성자 검색인 경우 -->
<if test='key == "w"'>
JOIN "MEMBER" USING(MEMBER_NO)
</if>
WHERE BOARD_DEL_FL = 'N'
AND BOARD_CODE = #{boardCode}
<choose>
<!-- 제목 검색 (key 값이 "t" 인 경우) -->
<when test='key == "t"'>
AND BOARD_TITLE LIKE '%' || #{query} || '%'
</when>
<!-- 내용 검색 (key 값이 "c" 인 경우) -->
<when test='key == "t"'>
AND BOARD_CONTENT LIKE '%' || #{query} || '%'
</when>
<!-- 제목 검색 (key 값이 "tc" 인 경우) -->
<when test='key == "t"'>
AND (
BOARD_TITLE LIKE '%' || #{query} || '%'
OR
BOARD_CONTENT LIKE '%' || #{query} || '%'
)
</when>
<!-- 작성자 검색 (key 값이 "w" 인 경우) -->
<otherwise>
AND MEMBER_NICKNAME LIKE '%' || #{query} || '%'
</otherwise>
</choose>
</select>
BoardServiceImpl
// 2. 1번의 결과 + cp 를 이용해서
// Pagination 객체를 생성
// * Pagination 객체 : 게시글 목록 구성에 필요한 값을 저장한 객체
Pagination pagination = new Pagination(cp,listCount);
// 3. 특정 게시판의 지정된 페이지 목록 조회
/* ROWBOUNDS 객체 (Mybatis 제공 객체)
* - 지정된 크기(offset) 만큼 건너뛰고
* 제한된 크기(limit) 만큼의 행을 조회하는 객체
*
* --> 페이징 처리가 굉장히 간단해짐
* */
int limit = pagination.getLimit();
int offset = (cp - 1) * limit;
RowBounds rowBounds = new RowBounds(offset, limit);
/* Mapper 메서드 호출 시
* - 첫 번째 매개변수 -> SQL에 전달할 파라미터
* - 두 번째 매개변수 -> RowBounds 객체만 전달 가능
* */
List<Board> boardList = mapper.selectSearchList(paramMap, rowBounds);
board-mapper.xml
<!-- 검색 결과 목록 조회 -->
<select id="selectSearchList">
SELECT BOARD_NO, BOARD_TITLE, MEMBER_NICKNAME, READ_COUNT,
(SELECT COUNT(*)
FROM "COMMENT" C
WHERE C.BOARD_NO = B.BOARD_NO) COMMENT_COUNT,
(SELECT COUNT(*)
FROM BOARD_LIKE L
WHERE L.BOARD_NO = B.BOARD_NO) LIKE_COUNT,
<![CDATA[
CASE
WHEN SYSDATE - BOARD_WRITE_DATE < 1 / 24 / 60
THEN FLOOR((SYSDATE - BOARD_WRITE_DATE) * 24 * 60 * 60) || '초 전'
WHEN SYSDATE - BOARD_WRITE_DATE < 1 / 24
THEN FLOOR((SYSDATE - BOARD_WRITE_DATE) * 24 * 60) || '분 전'
WHEN SYSDATE - BOARD_WRITE_DATE < 1
THEN FLOOR((SYSDATE - BOARD_WRITE_DATE) * 24) || '시간 전'
ELSE TO_CHAR(BOARD_WRITE_DATE, 'YYYY-MM-DD')
END BOARD_WRITE_DATE
]]>
FROM BOARD B
JOIN "MEMBER" USING(MEMBER_NO)
WHERE BOARD_DEL_FL = 'N'
AND BOARD_CODE = #{boardCode}
<choose>
<!-- 제목 검색 (key 값이 "t" 인 경우) -->
<when test='key == "t"'>
AND BOARD_TITLE LIKE '%' || #{query} || '%'
</when>
<!-- 내용 검색 (key 값이 "c" 인 경우) -->
<when test='key == "t"'>
AND BOARD_CONTENT LIKE '%' || #{query} || '%'
</when>
<!-- 제목 검색 (key 값이 "tc" 인 경우) -->
<when test='key == "t"'>
AND (
BOARD_TITLE LIKE '%' || #{query} || '%'
OR
BOARD_CONTENT LIKE '%' || #{query} || '%'
)
</when>
<!-- 작성자 검색 (key 값이 "w" 인 경우) -->
<otherwise>
AND MEMBER_NICKNAME LIKE '%' || #{query} || '%'
</otherwise>
</choose>
ORDER BY BOARD_NO DESC
</select>
ServiceImpl
// 4. 목록 조회 결과 + Pagination 객체를 Map 으로 묶음
Map<String, Object> map = new HashMap<>();
map.put("pagination", pagination);
map.put("boardList", boardList);
// 5. 결과 반환
return map;
Controller
model.addAttribute("pagination", map.get("pagination"));
model.addAttribute("boardList", map.get("boardList"));
return "board/boardList"; // boardList.html 로 forward
<!-- 게시글이 있을 때만 페이지네이션이 보이게 할 예정 -->
<div class="pagination-area">
<!-- 검색이 아닌 경우 (일반 목록 조회) -->
<ul class="pagination" th:unless="${param.key}" th:object="${pagination}">
<!-- 첫 페이지로 이동 -->
<!-- < == < // /board/{boardCode}cp = 1 -->
<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=1)}"><<</a></li>
<!-- 이전 목록 마지막 번호로 이동 -->
<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{prevPage})}"><</a></li>
<!-- 특정 페이지로 이동 -->
<th:block th:each="i : *{#numbers.sequence(startPage, endPage)}">
<!-- 현재 보고있는 페이지 -->
<li th:if="${i} == *{currentPage}">
<a class="current" th:text="${i}">현재페이지</a>
</li>
<!-- 보고있지 않은 페이지 -->
<li th:unless="${i} == *{currentPage}">
<a th:text="${i}"
th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=${i})}">이동할 페이지</a>
</li>
</th:block>
<!-- 다음 목록 시작 번호로 이동 -->
<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{nextPage})}">></a></li>
<!-- 끝 페이지로 이동 -->
<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{maxPage})}">>></a></li>
</ul>
<!-- 검색인 경우 -->
<ul class="pagination" th:if="${param.key}" th:object="${pagination}">
<!-- 첫 페이지로 이동 -->
<!-- < == < // /board/{boardCode}cp = 1 -->
<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=1, key=${param.key}, query=${param.query})}"><<</a></li>
<!-- 이전 목록 마지막 번호로 이동 -->
<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{prevPage}, key=${param.key}, query=${param.query})}"><</a></li>
<!-- 특정 페이지로 이동 -->
<th:block th:if="*{startPage lt endPage}" th:each="i : *{#numbers.sequence(startPage, endPage)}">
<!-- 현재 보고있는 페이지 -->
<li th:if="${i} == *{currentPage}">
<a class="current" th:text="${i}">현재페이지</a>
</li>
<!-- 보고있지 않은 페이지 -->
<li th:unless="${i} == *{currentPage}">
<a th:text="${i}"
th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=${i}, key=${param.key}, query=${param.query})}">이동할 페이지</a>
</li>
</th:block>
<!-- 일치하는 게시글이 없을 경우 -->
<th:block th:unless="*{startPage lt endPage}">
<li>
<a class="current">1</a>
</li>
</th:block>
<!-- 다음 목록 시작 번호로 이동 -->
<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{nextPage}, key=${param.key}, query=${param.query})}">></a></li>
<!-- 끝 페이지로 이동 -->
<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{maxPage}, key=${param.key}, query=${param.query})}">>></a></li>
</ul>
</div>