Spring Boot Board Project_25 검색

송지윤·2024년 5월 1일
0

Spring Framework

목록 보기
58/65

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=짱구
앞에 요청 주소는 똑같음(쿼리스트링만 다름)

boardList 에서 보낸 요청 받아줄 Controller

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);
		}

Pagination 만들기 위해서 Mapper 호출

BoardServiceImpl

		// 1. 지정된 게시판(boardCode)에서
		//    검색 조건에 맞으면서
		//    삭제되지 않은 게시글 수를 조회
		int listCount = mapper.getSearchCount(paramMap);

mapper.xml

작성자 검색인 경우 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>

Pagination 만들어서 rowBounds 와 함께 전달

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);

mapper 에서 검색 결과 목록 조회하는 sql 작성

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 에서 map 에 세팅 후 다시 Controller로 돌려주기

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

html 에서 돌려받은 값 보여주기

<!-- 게시글이 있을 때만 페이지네이션이 보이게 할 예정 -->
			<div class="pagination-area">

				<!-- 검색이 아닌 경우 (일반 목록 조회) -->
				<ul class="pagination" th:unless="${param.key}" th:object="${pagination}">

					<!-- 첫 페이지로 이동 -->
					<!-- &lt; == < // /board/{boardCode}cp = 1 -->
					<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=1)}">&lt;&lt;</a></li>

					<!-- 이전 목록 마지막 번호로 이동 -->
					<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{prevPage})}">&lt;</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})}">&gt;</a></li>

					<!-- 끝 페이지로 이동 -->
					<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{maxPage})}">&gt;&gt;</a></li>
				</ul>

				<!-- 검색인 경우 -->
				<ul class="pagination" th:if="${param.key}" th:object="${pagination}">

					<!-- 첫 페이지로 이동 -->
					<!-- &lt; == < // /board/{boardCode}cp = 1 -->
					<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=1, key=${param.key}, query=${param.query})}">&lt;&lt;</a></li>

					<!-- 이전 목록 마지막 번호로 이동 -->
					<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{prevPage}, key=${param.key}, query=${param.query})}">&lt;</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})}">&gt;</a></li>

					<!-- 끝 페이지로 이동 -->
					<li><a th:href="@{/board/{boardCode}(boardCode=${boardCode}, cp=*{maxPage}, key=${param.key}, query=${param.query})}">&gt;&gt;</a></li>
				</ul>
			</div>

0개의 댓글