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 |