스프링수업 19일차

하파타카·2022년 4월 20일
0

SpringBoot수업

목록 보기
19/23

한 일

  • 페이지네이션 (2)
  • 제목 검색

페이지네이션 (2)

페이지 이동, 포인트컬러

- list.html -
페이지 숫자를 클릭하면 해당 페이지로 이동, 현재 페이지 숫자에 포인트컬러를 주는 기능을 추가.

<li class="page-item" th:classappend="${pmk.cri.pageNum == number} ? 'active' : '' " th:each="number : ${#numbers.sequence(pmk.startPage, pmk.endPage)}">
  <a class="page-link" th:href="@{/board/list} + '?pageNum=__${number}__'" th:text="${number}"></a>
</li>

th:href="@{/board/list} + '?pageNum=__${number}__'": 현재 페이지 넘버가 동적으로 들어감.
parameter로 넘겨야 하므로 ?pageNum=뒤에 값이 들어와야하는데, 동적데이터 내부에 동적데이터가 들어가기때문에 __${number}__의 형태로 넣어야 한다.

th:classappend="${pmk.cri.pageNum == number} ? 'active' : '' ": 현재 페이지(pageNum)가 반복문의 number와 같으면 클래스에 active를 추가하고, 다르면 공백을 주는 삼항 연산자
=> 페이지네이션의 현재 페이지에만 active클래스를 줌으로써 포인트컬러를 넣음.

화살표로 이동

- list.html -
좌우 화살표를 눌렀을 때

<ul class="pagination pagination-primary justify-content-center mt-5">
  <li class="page-item" th:if="${pmk.prev}">
    <a class="page-link" th:href="@{/board/list} + '?pageNum=__${pmk.startPage - 1}__'" aria-label="이전">
      <span class="material-icons"> keyboard_arrow_left </span>
      <span class="sr-only">이전</span>
    </a>
  </li>
  <li class="page-item" th:classappend="${pmk.cri.pageNum == number} ? 'active' : '' " th:each="number : ${#numbers.sequence(pmk.startPage, pmk.endPage)}">
    <a class="page-link" th:href="@{/board/list} + '?pageNum=__${number}__'" th:text="${number}"></a>
  </li>
  <li class="page-item" th:if="${pmk.next}">
    <a class="page-link" th:href="@{/board/list} + '?pageNum=__${pmk.endPage + 1}__'" aria-label="다음">
      <span class="material-icons"> keyboard_arrow_right </span>
      <span class="sr-only">다음</span>
    </a>
  </li>
</ul>

startPage: 시작 페이지 (현재 페이지네이션바의 첫번째 숫자)
endPage: 끝 페이지 (현재 페이지네이션바의 마지막 숫자)

#numbers.sequence(시작숫자, 끝숫자): Thymeleaf의 문법으로 시퀀스를 사용하지 않고 시작숫자부터 끝숫자까지 임의의 수를 반복할 때 사용함. #numbers까지 모두 문법에 포함이니 주의.
Tymeleaf의 #numbers.sequence

th:if="${pmk.prev}"로 pmk.prev가 있을때만 li태그가 보이도록 함.
=> model패키지의 PageMakerDTO클래스에서 prev, next를 구할때 각각 startPage가 1과 같거나 작을때, endPagerealEnd보다 같거나 크면 false가 반환되므로 if문에의해 li태그가 나타나지 않음.


제목 검색

검색 keyword 추가

새 메서드를 생성하지 않고 기존의 메서드에 키워드를 추가하여 사용.

- Criteria -

private String keyword;	// 검색어 키워드
// get/set, toString 메서드 자동생성

- BoardMapper.xml -

<!-- 게시판 목록 (페이징) : skip과 amount는 Criteria 객체에서 입력됨 -->
<select id="getListPaging" resultType="BoardVO">
	SELECT * FROM (
			SELECT bno, title, writer, regdate, updatedate
			FROM board
			<if test="keyword != null">
				WHERE title LIKE concat('%', #{keyword}, '%')
			</if> 
			 ORDER BY bno DESC) as T1
	LIMIT #{skip}, #{amount}
</select>

<!-- 게시글 총 갯수 -->
<select id="getTotal" resultType="int">
	SELECT count(*) FROM board
		<if test="keyword != null">
			WHERE title LIKE concat('%', #{keyword}, '%')
		</if> 
</select>

페이징을 할 때 검색한 값이 있다면 검색한 결과를 가지고 페이징을 해야함.
게시글의 총 갯수를 구할때도 마찬가지로 검색한 결과가 있으면 그 결과만을 가지고 총 갯수를 세야함.

<if test="keyword != null"> WHERE title LIKE concat('%', #{keyword}, '%') </if>: 입력된 keyword에 값이 있을 때만 WHERE절로 조건을 부여함.

concat('%', #{keyword}, '%')은 문자열을 합쳐주는 함수. 입력한 키워드가 제목의 처음, 중간, 끝 중 어디에 포함되어있더라도 검색결과에 걸리도록 함.
결과 예시- %길%, %부산%

- BoardMapper.interface -

public int getTotal(Criteria cri);	// 게시글 총 갯수

- BoardService.interface -

public int getTotal(Criteria cri);	// 게시글 총 갯수

- BoardServiceImpl -

@Override
public int getTotal(Criteria cri) {
	return boardMapper.getTotal(cri);
}

- BoardController -

// 게시글 목록 페이지 (페이징 적용)
@GetMapping("/list")
public String boardListGet(Criteria cri, Model model) {
	// boardList에 페이징 된 모든 게시글을 전달
	model.addAttribute("boardList", boardService.getListPaging(cri));
	
	int total = boardService.getTotal(cri);
	PageMakerDTO pmk = new PageMakerDTO(total, cri);	// 객체 생성 시 모든 변수 계산됨
	
	model.addAttribute("pmk", pmk);		// 페이지네이션을 위한 pmk객체 전달
	
	return "list";
}

검색한 keyword가 있으면 넘겨주기 위해 페이징이 필요한 부분에 모두 Criteria객체를 넘겨줌

뷰에 검색창 생성

- list.html-
table태그 끝, 페이지네이션 바로 앞에 검색창을 추가

<!-- 검색창 -->
<div class="d-flex justify-content-center mt-3">
  <div class="w-md-25 w-sm-50 input-group input-group-outline is-filled">
    <label class="form-label">search here...</label>
    <input type="text" id="searchKeyword" th:value="${pmk.cri.keyword}" class="form-control" />
    <button id="searchButton" class="btn btn-primary mb-0">검색</button>
  </div>
</div>

th:value="${pmk.cri.keyword}"는 검색결과가 나온 후 에도 검색한 키워드가 그대로 남아있도록 하기 위해 추가함.

검색했을때 parameter로 넘겨주는 기능을 자바스크립트로 구현
- list.html-

<script>
  document.getElementById('pageName').textContent = '게시판 목록';

  const searchKeyword = document.getElementById('searchKeyword'); // 키워드 입력창의 값(내용)
  const searchButton = document.getElementById('searchButton'); // 검색버튼

  // 검색버튼을 눌렀을 때(이벤트 'click') 키워드를 url에 추가하여 보내면 됨
  searchButton.addEventListener('click', function () {
    let keyword = ''; // 키워드가 있을 경우 키워드 추가
    let currentKeyword = searchKeyword.value.trim();
    if (currentKeyword) keyword = '&keyword=' + currentKeyword; // 키워드가 있을 경우 '&keyword=키워드'
    location.href = '/board/list?pageNum=1' + keyword; // 처음 키워드로 검색 시 무조건 1페이지를 보여줌
  });
</script>

let currentKeyword = searchKeyword.value.trim();으로 입력한 키워드의 값을 구하되 trim을 해줌으로써 공백의 입력을 제외함. 이 값이 중복되므로 변수로 지정하여 사용.
공백을 제거한 키워드 currentKeyword의 값이 있을 경우 &keyword=키워드를 keyword에 저장.

location.href는 자바스크립트에서 주소를 통한 페이지이동을 할 때 사용하는 함수.

최종주소의 예 - http://localhost:8080/board/list?pageNum=1&keyword=10


여기까지 마치면 검색기능이 동작은 하나, 검색결과에서 페이지네이션 숫자를 클릭하면 전체 리스트에서 해당숫자를 누른 것과 같이 동작함.
예 - 검색 후 2페이지 클릭 > 전체 2페이지로 이동

페이지네이션 수정

페이지네이션 버튼을 통해 페이지를 이동했을때 keyword값이 유지되려면 페이지네이션에도 keyword를 추가해야 함.
이번에도 자바스크립트로 처리.

- list.html-

// 페이지네이션의 a태그들을 전부 자바스크립트 요청으로 바꾸기 (키워드 추가)
const pageLinks = document.querySelectorAll('ul.pagination .page-link');
pageLinks.forEach(function (link) {
  // 각각의 a태그를 클릭했을 때 함수 실행
  link.addEventListener('click', function (e) {
    e.preventDefault(); // a태그의 이동 요청이 취소됨
    let keyword = ''; // 키워드가 있을 경우 키워드 추가
    let currentKeyword = searchKeyword.value.trim();
    if (currentKeyword) keyword = '&keyword=' + currentKeyword; // 키워드가 있을 경우 '&keyword=키워드'
    location.href = this.getAttribute('href') + keyword; // 클릭한 a태그의 주소에 + keyword를 해준 후 요청함
  });
});

const pageLinks = document.querySelectorAll('ul.pagination .page-link');: 페이지네이션의 모든 버튼(화살표 포함)을 선택

function(e)e.preventDefault();로 a태그의 이벤트객체 e의 실행을 멈춰줌.
a태그의 기능인 주소이동을 멈춰야 원하는 주소로 이동하도록 변경할 수 있기때문.

this.getAttribute('href'): 현재 클릭한 a태그의 주소를 가져옴
location.href = this.getAttribute('href') + keyword;: 클릭한 a태그의 주소 + keyword 를 한 주소를 요청함


10을 검색했을 때

페이지네이션바에서 2페이지를 클릭해도 keyword가 유지되며 정상적인 주소 http://localhost:8080/board/list?pageNum=2&keyword=10 로 이동함을 확인가능.

상세페이지에서 키워드 유지

게시글 상세보기페이지(get.html페이지)에서 목록 버튼을 클릭하면 키워드검색이 풀려버림.
고로 keyword를 상세보기페이지에도 넘겨주는데 이때 keyword만 넘겨주면 page가 무조건 1로 돌아오게 됨.
이를 해결하기 위해 keyword(검색어)와 함께 pageNum(현재 페이지번호)를 넘겨줘야 정상적으로 동작함.

tbody태그내 제목을 출력하는 a태그에 class="title" 을 부여함.

- list.html -

// 제목 a태그들을 선택해서 a태그이동을 취소하고 keyword를 추가하여 요청
const getLinks = document.querySelectorAll('table .title');
getLinks.forEach(function (link) {
  // 각각의 a태그를 클릭했을 때 함수 실행
  link.addEventListener('click', function (e) {
    e.preventDefault(); // a태그의 이동 요청이 취소됨
    let keyword = ''; // 키워드가 있을 경우 키워드 추가
    let currentKeyword = searchKeyword.value.trim();
    if (currentKeyword) keyword = '&keyword=' + currentKeyword; // 키워드가 있을 경우 '&keyword=키워드'
    let pageNum = document.querySelector('.active a').textContent;
    location.href = this.getAttribute('href') + keyword + '&pageNum=' + pageNum;
  });
});

[[${pmk.cri.pageNum}]]로 가져와도 됨

- get.html-

<a th:href="@{/board/list} + '?pageNum=__${cri.pageNum}__'" class="btn btn-success">목록</a>
<a th:href="@{/board/modify(bno=${board.bno})} + '&pageNum=__${cri.pageNum}__'" class="btn btn-secondary">수정</a>

get페이지에서도 keyword와 pageNum을 넘길 수 있도록 수정.

- BoardController -

@GetMapping("/get")
public String boardPageGet(@RequestParam("bno") int bno, Model model, Criteria cri) {
	model.addAttribute("board", boardService.getPage(bno));
	model.addAttribute("cri", cri);
	return "get";
}

// 수정페이지 출력
@GetMapping("/modify")
public String boardModifyGet(@RequestParam("bno") int bno, Model model, Criteria cri) {
	model.addAttribute("board", boardService.getPage(bno));
	model.addAttribute("cri", cri);
	return "modify";
}

하나의 게시글을 띄울 때와 수정하기페이지로 이동할때도 Criteria객체를 가지고 이동할 수 있도록 매개변수에 추가.
이후 model객체에 cri객체를 추가해 리턴받을 페이지에 함께 이동시킴.

- get.html-

// a태그들에 keyword를 추가해서 자바스크립트로 요청함
  const links = document.querySelectorAll('a.page');
  links.forEach(function (link) {
    // 각각의 a태그를 클릭했을 때 함수 실행
    link.addEventListener('click', function (e) {
      e.preventDefault(); // a태그의 이동 요청이 취소됨
      let keyword = '[[${cri.keyword}]]';
      location.href = this.getAttribute('href') + '&keyword=' + keyword; // 클릭한 a태그의 주소에 + keyword를 해준 후 요청함
    });
  });

테스트

  1. 결과가 2페이지 이상 나오는 키워드를 검색하여 1페이지 이외 페이지의 게시글을 하나 클릭

  2. 게시글 상세페이지(get.html)에서 목록 버튼을 클릭

  3. 리스트페이지(list.html)에서 앞서 검색한 keyword가 유지되며, 1페이지로 돌아가지않고 원래 있던 페이지에 그대로 머무르면 완료

profile
천 리 길도 가나다라부터

0개의 댓글