Spring

Spring 05 - Paging

나주나주 2024. 3. 4. 11:21

20240304

Tip) 서비스에서 보드 + 멤버 +  아이템 등 여러 테이블 붙음 조합? ex) 상품 결제 페이지, 제품 상세정보 페이지

분기 Controller URL로 들어오고 JSP로 보내고 C: regist R: read (R은 두 개 list, read), U: 

http://localhost/board/list -> board 클래스, list 메서드 너낌~

<c:forEach items="리스트 갖고옴">

 

<form role="form"> 부트스트랩에 있는 속성

 

 


Index

[서론] rowNum: 가상의 일련 번호 order by: DB가 하는 일이라 힘들어

기존 SB Admin2 부트 스트랩 페이징 처리: 모든 리스트를 가져와 프론트에서 페이징 처리: 성능 저하

배운 방법: DB에서 백엔드로 10개씩 가져와 그때그때 불러옴 성능 업

Tip) 무한스크롤 - 자바스크립트, 에이젝스, 리액트 ........ 사용해야

 

 → 이미 정렬된 구조의 index(PK)를 이용해서 정렬 생략

 

hint

  • select문 처리 조건 (강제성)
  • 에러가 나도 SQL 실행에 지장을 주지 않는다
  • /*+ 로 시작, */ 로 마무리

 

FULL P280

 

INDEX_ASC

 

INDEX_DESC

 

 

ROWNUM

 

in-line view

 

 

실행 계획 execution plan

  • parsing: SQL을 실행해야 하는 대상 객체(테이블, 제약 조건, 권한 등)의 존재, SQL구문 오류 검사
  • 최적화: SQL이 실행되는 데 필요한 비용 계산

계산된 값을 기초로 해서 실행 계획을 세운다

 

2. MyBatis와 스프링에서 페이징 처리

MyBatis는 인라인뷰를 이용하는 SQL을 작성하고, 필요한 파라미터를 지정하는 방식으로 페이징 처리 한다.

이 때 필요한 파라미터는 페이지 번호 pageNum, 페이지당 보여질 데이터 amount가 있다.

 

Tip) criteria == 검색 기준

 

2-1. MyBatis 처리와 테스트 (mapper)

Criteria.java

package org.zerock.domain;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter // 게터 생성
@Setter // 세터 생성
@ToString // 객체 문자로 변환(콘솔 출력용)
public class Criteria {
	// 페이징 처리의 필요한 검색 처리 조건용 코드(검색 기준)
	
	private int pageNum; // 기본 페이지 번호(변경할 수도 있음)
	private int amount; // 기본 목록 개수(변경할 수도 있음)
	
	public Criteria() {
		this(1, 10); // 기본값: pageNum은 1, amount는 10 //밑에 생성자를 호출한다.
	}
	
	public Criteria(int pageNum, int amount) {
		this.pageNum = pageNum;
		this.amount = amount;
	}
	
}

 

BoardMapper.java (i)

package org.zerock.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Select;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

public interface BoardMapper { // 마이바티스를 이용하여 sql 처리
	
	//@Select("select * from tbl_board where bno > 0") // 단점 - 컴파일되어 쉽게 수정할 수 없음
	// where bno > 0 = bno가 pk라 index 처리가 되서 빠른 결과를 확인할 수 있다.
	public List<BoardVO> getList(); // 추상메서드(구현클래스에서 동작)
	
	public List<BoardVO> getListWithPaging(Criteria cri); // 페이징한 추상메서드(1, 10)
	
	public void insert(BoardVO board); // 추상메서드 -> board객체를 받아서 insert sql 문을 처리 // 1개의 게시물이 등록되었습니다.
	
	public void insertSelectKey(BoardVO board); // insert 전에 시퀀스 객체로 번호를 먼저 받아 insert 처리한다. // 300번 게시물이 등록되었습니다.
	
	public BoardVO read(Long bno); // bno를 받아 객체를 가져온다.
	
	public int delete(Long bno); // bno를 받아 객체를 삭제한다.
	
	public int update(BoardVO board); // board 객체를 update 처리한다.
	
}

 

BoardMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mydatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 마이바티스 용도이다. -->

<mapper namespace="org.zerock.mapper.BoardMapper">
	<resultMap type="org.zerock.domain.BoardVO" id="mapping_list">
		<result property="bno" column="bno" />
		<result property="title" column="title" />
		<result property="content" column="content" />
		<result property="writer" column="writer" />
		<result property="regdate" column="regdate" />
		<result property="updateDate" column="updatedate" />
	</resultMap>
	<!-- 맨뒤에 세미콜론 쓰지 말기! -->

	<select id="getList" resultMap="mapping_list">
   <![CDATA[ 
   select * from tbl_board where bno > 0
   ]]> <!-- CDATA : * 등을 계산하지 말고 문자열로 취급해라 -->
	</select> <!-- 페이징 처리 후 전체 게시물을 가져온다. -->
	
	<select id="getListWithPaging" resultMap="mapping_list"> <!-- resultMap으로 리스트화 -->
	<!-- rownum이 20 이하인 행 가져오기 -->
	<![CDATA[
	select bno, title, content, writer, regdate, updateDate
	from 
	(select /*+INDEX_DESC(tbl_board pk_board)*/ rownum rn, bno, title, content, writer, regdate, updateDate from tbl_board where rownum <= #{pageNum} * #{amount})
	where rn > (#{pageNum} - 1) * #{amount}
	]]> <!-- 특수 문자 처리 -->
	</select> <!-- inner select: pageNum * amount 이하 값 내림차순으로 가져옴, outer select: (pageNum - 1) * amount이상인 값 가져옴 => 11 ~ 20 -->

	<insert id="insert"> <!-- id = 추상메서드이름 -->
		insert into tbl_board(bno, title, content, writer)
		values(seq_board.nextval, #{title}, #{content}, #{writer})
	</insert> <!-- insert시 번호가 생성된다. -->

	<insert id="insertSelectKey">
		<selectKey keyProperty="bno" order="BEFORE"
			resultType="long">
			select seq_board.nextval from dual <!-- dual = 가짜테이블 -->
		</selectKey>
		insert into tbl_board(bno, title, content, writer) values(#{bno},
		#{title}, #{content}, #{writer}) <!-- before에서 만든 bno를 활용 -->
	</insert> <!-- 자동키를 생성하고 insert 처리 -->

	<select id="read" resultMap="mapping_list">
		select * from tbl_board where bno = #{bno}
	</select>

	<delete id="delete">
		delete from tbl_board where bno = #{bno}
	</delete>

	<update id="update"> <!-- 파라미터가 객체로 넘어온다. -->
		update tbl_board set title = #{title}, content = #{content}, writer =
		#{writer}, updateDate = sysdate where bno = #{bno}
	</update>
</mapper>

Tip) <![CDATA[ ]]> : 특수 문자 처리

 

2-2. BoardService 와 BoardController  수정

BoardService.java (i) : BoardService가 Criteria를 파라미터로 처리하도록

package org.zerock.service;

import java.util.List;

import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

public interface BoardService { // 서비스 계층을 담당한다. (실용영어를 사용한다.)
	// 서비스 계층 : mapper에서 넘어온 여러개의 dto나 vo 객체를 믹스한다.
	// 인터페이스로 만들었기 때문에 여기서 생성한 메서드는 추상메서드이다.
	
	public void register(BoardVO board); // register : 등록하다 // 게시물을 폼에서 받아 객체로 등록한다.
	
	public BoardVO get(Long bno); // bno를 이용해 게시물을 1개 객체(VO)로 가져온다.
	
	public boolean modify(BoardVO board); // 객체를 받아 수정한다. 리턴은 수정 성공/실패
	
	public boolean remove(Long bno); // bno를 이용해 게시물을 삭제한다. 리턴은 성공/실패
	
	// public List<BoardVO> getList(); // 전체 게시물을 받아 리스트 안에 객체로 리턴한다.
	
	public List<BoardVO> getList(Criteria cri); // 전체 게시물을 받아 리스트 안에 객체로 리턴한다. + 페이징 처리 조건 추가
}

 

BoardServiceImpl.java

	/*
	 * @Override public List<BoardVO> getList() { // 게시물의 모든 리스트를 출력한다.
	 * log.info("getList 메서드 실행중 ..."); return mapper.getList(); // mapper에 있는
	 * getList 메서드를 실행해서 결과를 리턴한다. }
	 */ // 페이징 X

	@Override
	public List<BoardVO> getList(Criteria cri) {
		log.info("getList 메서드 실행중 ... cri : " + cri);
		return mapper.getListWithPaging(cri);
	}

 

BoardController.java : 기존의 list는 매개변수 없이 처리되었기 때문에 수정합니다

	@GetMapping("/list") // http://localhost/board/list
	public void list(Criteria cri, Model model) {
		log.info("list 컨트롤러 메서드 실행........." + cri);
		model.addAttribute("list", service.getList(cri));
		model.addAttribute("pageMarker", new PageDTO(cri, 101)); //totalPage가 101이라 가정하고
		// 모델 영역에 list라는 이름으로 결과 저장 select * from 테이블 where bno > 0
	} // 리턴 타입이 void면 매핑명으로 된 jsp파일을 찾는다

Tip) Criteria클래스를 만들어 두면 위와 같이 하나의 타입만으로 매개변수나 리턴 타입을 사용할 수 있어 편리

 

3. 페이징 화면 처리

테스트가 완료되면 어떤 작업 페이지를 거치든지 페이지 번호가 유지되는지 주의 하며 연결한다

  • 브라우저 주소창에서 페이지 번호를 전달해서 결과를 확인하는 단계
  • JSP에서 페이지 번호를 출력하는 단계
  • 각 페이지 번호에 클릭 이벤트 처리
  • 전체 데이터 개수를 반영해서 페이지 번호 조절

 

Math.ceil()

  • 1페이지의 경우: Math.ceil(0.1) * 10 = 10
  • 10페이지의 경우: Math.ceil(1) * 10 = 10
  • 11페이지의 경우: Math.ceil(1.1) * 10 = 20

 

PageDTO.java

package org.zerock.domain;

import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
public class PageDTO {
	private int startPage;		// 시작 게시물 번호
	private int endPage;		// 마지막 게시물 번호
	private boolean prev, next;	// 앞으로 뒤로 유무
	private int total;			// 게시물 총 개수
	private Criteria cri;		// 블럭과 행 수(현재 페이지)
	
	public PageDTO(Criteria cri, int total) { // cri(pageNum:1, amount:10), total:101개라고 가정
		this.cri = cri;
		this.total = total;
		
		// ceil : 소수점을 올림 // 1 ~ 10
		this.endPage = (int)(Math.ceil(cri.getPageNum() / 10.0)) * 10; // ceil(1 / 10.0) = 0.1 = 1 * 10 = 10
		this.startPage = this.endPage - 9; // 10 - 9 = 1
		
		// realEnd : 마지막 페이지 번호
		int realEnd = (int)(Math.ceil((total * 1.0) / cri.getAmount())); // ceil(101 * 1.0 = 101.0 / 10 = 10.1) = 11
		if(realEnd < this.endPage) { // 11 < 10
			this.endPage = realEnd;  // endPage = 11
		}
		
		this.prev = this.startPage > 1; // 1 > 1 = false
		System.out.println("----------------------------------------");
		System.out.println(prev);
		this.next = this.endPage < realEnd; // 10 < 11 = true
		System.out.println("----------------------------------------");
		System.out.println(next);
	}	
}

 

List.jsp: 페이징 바 출력

<div class='pull-right'>
	<ul class="pagination">
		<c:if test="${ pageMarker.prev }">
			<li class="paginate_button previous"><a href="${ pageMarker.startPage - 1 }">이전</a>
			</li>
		</c:if>
		<c:forEach var="num" begin="${pageMarker.startPage}"	end="${ pageMarker.endPage }">
			<li class="paginate_button"><a href="${ num }">${ num }</a></li>
		</c:forEach>
		<c:if test="${ pageMarker.next }">
			<li class="paginate_button next"><a href="${ pageMarker.endPage + 1 }">다음</a></li>
		</c:if>
	</ul>
</div>
						
	<!-- 페이징 처리에 필요한 값을 가져와 감춘다. -->
<form id='actionForm' action="/board/list" method='get'>
	<input type='hidden' name='pageNum' value='${ pageMarker.cri.pageNum }'>
	<input type='hidden' name='amount' value='${ pageMarker.cri.amount }'>
</form>

 

List.jsp: 페이지 이벤트(클릭 시 이동)

<c:if test="${ pageMarker.prev }">
	<li class="paginate_button previous"><a href="${ pageMarker.startPage - 1 }">이전</a>
		</li>
</c:if>
	<c:forEach var="num" begin="${pageMarker.startPage}"	end="${ pageMarker.endPage }">
		<li class="paginate_button"><a href="${ num }">${ num }</a></li>
	</c:forEach>
	<c:if test="${ pageMarker.next }">
		<li class="paginate_button next"><a href="${ pageMarker.endPage + 1 }">다음</a></li>
	</c:if>

 

ㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗ 오류 및 버그 처리 ㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗㅗ

 

<a> 태그가 원래의 동작을 못하도록 JavaScript 처리

/* 페이징 처리용 링크 추가 */
var actionForm = $("#actionForm");
	$(".paginate_button a").on("click", function(e){
		e.preventDefault(); // 기본동작 사용금지
		console.log('click');
		actionForm.find("input[name='pageNum']").val($(this).attr("href"));
		actionForm.submit();
	});

 

P313

' 게시글 선택 >  get.jsp에서 목록 '  이동 시 페이지 번호 유지 X

[해결] 조회 페이지로 가는 링크 대신에 단순히 번호만이 출력

 

[해결] 실제 클릭은 JavaScript를 통해 이벤트 처리 (게시물의 제목을 클릭했을 때 이동)

 

 

 

 

검색

LIKE

AND 연산자가 OR 연산자 보다 우선 순위가 높기 때문에 

 

 

 

 

 

 

292P

BoardControllerTests: 프론트에서 값 입력 시 잘 도나?

BoardMapperTests: 매퍼 잘 도나?

 

302P

조회 페이지 - 추가/수정/삭제 페이지 - 조회 페이지 이동 시 현재 페이지 값이 전송되야

 

 

 

'Spring' 카테고리의 다른 글

Spring 07 - AOP, Transaction  (0) 2024.03.07
Spring 06 - REST방식, 댓글  (0) 2024.03.06
Spring 04  (0) 2024.02.29
기본적인 웹 게시물 관리  (0) 2024.02.28
Spring 03 - Spring MVC, 파일 업로드(기본)  (0) 2024.02.28