Spring 실습

Spring 실습 16일차

choco2706 2024. 5. 14. 17:43

페이징

우선 페이징 처리를 하기 위해선 데이터가 많아야 하니 SQL에 더미데이터를 넣어주자.

DECLARE
    --변수 : 일반(SCALAR) 변수, 참조형 변수, COMPOSITE(배열) 변수
    --V_LPROD_ID NUMBER := 0;
    V_LPROD_ID LPROD.LPROD_ID%TYPE := 0;
BEGIN
    --PL/SQL에서 SELECT랑 INTO는 바늘과 실의 관계.
    SELECT MAX(LPROD_ID)+1 INTO V_LPROD_ID
    FROM    LPROD;
    
    --I : 자동 선언 정수형 변수
    FOR I IN V_LPROD_ID .. 735 LOOP   
        INSERT INTO LPROD(LPROD_ID, LPROD_GU, LPROD_NM)
        VALUES(I, 'U'||TRIM(TO_CHAR(I,'000')), '테스트'||I);
    END LOOP;
    
    COMMIT;
END;

 

lprod테이블의 마지막 번호를 v_lprod_id에 넣고 735까지 반복하려 자동으로 insert가 되게 하는 PL / SQL문이다.

 

utils패키지에 페이징을 담당할 ArticlePage클래스를 하나 만들어주자

package kr.or.ddit.utils;

import java.util.List;

public class ArticlePage<T> {
	// 전체글 수
	private int total;
	// 현재 페이지 번호
	private int currentPage;
	// 전체 페이지수
	private int totalPages;
	// 블록의 시작 페이지 번호
	private int startPage;
	// 블록의 종료 페이지 번호
	private int endPage;
	// 검색어
	private String keyword = "";
	// 요청URL
	private String url = "";
	// select 결과 데이터
	private List<T> content;
	// 페이징 처리
	private String pagingArea = "";

	// 생성자(Constructor) : 페이징 정보를 생성
	// 						753 			1		    10	   select결과10행
	public ArticlePage(int total, int currentPage, int size, List<T> content, String keyword) {
		// size : 한 화면에 보여질 목록의 행 수
		this.total = total;// 753
		this.currentPage = currentPage;// 1
		this.content = content;
		this.keyword = keyword;

		// 전체글 수가 0이면?
		if (total == 0) {
			totalPages = 0;// 전체 페이지 수
			startPage = 0;// 블록 시작번호
			endPage = 0; // 블록 종료번호
		} else {// 글이 있다면
			// 전체 페이지 수 = 전체글 수 / 한 화면에 보여질 목록의 행 수
			// 3 = 31 / 10
			totalPages = total / size;// 75

			// 나머지가 있다면, 페이지를 1 증가
			if (total % size > 0) {// 나머지3
				totalPages++;// 76
			}

			// 페이지 블록 시작번호를 구하는 공식
			// 블록시작번호 = 현재페이지 / 페이지크기 * 페이지크기 + 1
			startPage = currentPage / 5 * 5 + 1;// 1

			// 현재페이지 % 페이지크기 => 0일 때 보정
			if (currentPage % 5 == 0) {
				startPage -= 5;
			}

			// 블록종료번호 = 시작페이지번호 + (페이지크기 - 1)
			// [1][2][3][4][5][다음]
			endPage = startPage + (5 - 1);// 5

			// 종료페이지번호 > 전체페이지수
			if (endPage > totalPages) {
				endPage = totalPages;
			}
		}

		pagingArea += "<div class='col-sm-12 col-md-7'>";
		pagingArea += "<div class='dataTables_paginate paging_simple_numbers' id='example2_paginate'>";
		pagingArea += "<ul class='pagination'>";
		pagingArea += "<li class='paginate_button page-item previous ";
		if (this.startPage < 6) {
			pagingArea += "disabled ";
		}
		pagingArea += "'";
		pagingArea += "id='example2_previous'>";
		pagingArea += "<a href='" + this.url + "?currentPage=" + (this.startPage - 5) + "&keyword=" + this.keyword
				+ "' aria-controls='example2' data-dt-idx='0' tabindex='0' ";
		pagingArea += "class='page-link'>Previous</a></li>";

		for (int pNo = this.startPage; pNo <= this.endPage; pNo++) {
			pagingArea += "<li class='paginate_button page-item ";
			if (this.currentPage == pNo) {
				pagingArea += "active";
			}
			pagingArea += "'>";
			pagingArea += "<a href='" + this.url + "?currentPage=" + pNo + "&keyword=" + this.keyword
					+ "' aria-controls='example2' data-dt-idx='1' tabindex='0' ";
			pagingArea += "class='page-link'>" + pNo + "</a>";
			pagingArea += "</li>";
		}
		pagingArea += "<li class='paginate_button page-item next ";
		if (this.endPage >= this.totalPages) {
			pagingArea += "disabled";
		}
		pagingArea += "' id='example2_next'><a ";
		pagingArea += "href='" + this.url + "?currentPage=" + (this.startPage + 5) + "&keyword=" + this.keyword
				+ "' aria-controls='example2' data-dt-idx='7' ";
		pagingArea += "tabindex='0' class='page-link'>Next</a></li>";
		pagingArea += "</ul>";
		pagingArea += "</div>";
		pagingArea += "</div>";
	}// end 생성자
}

 

Mapper패키지에 LprodMapper인터페이스도 하나 만들어주고

package kr.or.ddit.mapper;

import java.util.List;
import java.util.Map;

import kr.or.ddit.vo.LprodVO;
import kr.or.ddit.vo.ProductVO;

public interface LprodMapper {

	public int getTotal();
	
	public int createPost(LprodVO lprodVO);

	public List<LprodVO> list(Map<String, Object> map);

	public LprodVO detail(LprodVO lprodVO);

	public int updatePost(LprodVO lprodVO);

	public int deletePost(LprodVO lprodVO);

	public int getLprodId();

	public int insertProduct(ProductVO productVO);

}

 

LprodController의 list메소드를 수정해주자

@RequestMapping(value="/list",method=RequestMethod.GET)
public ModelAndView list(ModelAndView mav
        ,@RequestParam(value="currentPage",required=false,defaultValue="1") int currentPage 
        ,@RequestParam(value="keyword",required=false,defaultValue="") String keyword) {
    log.info("list에 왔다");

    Map<String,Object> map = new HashMap<String,Object>();
    map.put("keyword", keyword);
    map.put("currentPage", currentPage);

    // 전체 행의 수
    int total = this.lprodService.getTotal();
    log.info("total : " + total);

    //Model(데이터)
    //상품분류 목록
    List<LprodVO> lprodVOList = this.lprodService.list(map);

    log.info("list->lprodVOList : " + lprodVOList);
    mav.addObject("lprodVOList", new ArticlePage<LprodVO>(total, currentPage, 10, lprodVOList, keyword));

    //View(jsp)
    mav.setViewName("lprod/list");

    return mav;
}

 

코드 분석

@RequestParam(value="currentPage",required=false,defaultValue="1") int currentPage

 

파람으로 currentPage를 받을껀데, requierd가 false면 값이 없어도 된다는 뜻, 만약 없다면 기본 값은 1로 설정하겠다는 내용으로 그 값을 currentPage라는 변수명에 담겠다는 말이다.

new ArticlePage<LprodVO>(total, currentPage, 10, lprodVOList, keyword));

 

total = 총 페이지수

currentPage = 페이지 위치

10 = 한 페이지 당 출력할 수

lprodVOList = 출력할 대상

keyword = 검색할 단어

 

JSP 수정

<div class="col-sm-12 col-md-7">
 <div class="dataTables_paginate paging_simple_numbers"
    id="example2_paginate">
    <ul class="pagination">
       <li class="paginate_button page-item previous
       <c:if test="${articlePage.startPage<6}">disabled</c:if>"
          id="example2_previous">
          <a href="/lprod/list?currentPage=${articlePage.startPage-5}" aria-controls="example2" data-dt-idx="0" tabindex="0" 
             class="page-link">Previous</a></li>
       <c:forEach var="pNo" begin="${articlePage.startPage}" end="${articlePage.endPage}">
          <li class="paginate_button page-item
              <c:if test="${pNo==param.currentPage}">active</c:if>
           "><a href="/lprod/list?currentPage=${pNo}"
             aria-controls="example2" data-dt-idx="1" tabindex="0"
             class="page-link">${pNo}</a></li>
       </c:forEach>
       <li class="paginate_button page-item next" id="example2_next">
       <c:if test="${articlePage.endPage==articlePage.totalPages}"></c:if>
       <a href="/lprod/list?currentPage=${articlePage.startPage+5}" aria-controls="example2" data-dt-idx="7" tabindex="0"
            class="page-link">Next</a></li>
    </ul>
 </div>
</div>

 

페이징을 위한 요소를 추가해준다. a링크를 통해 get방식으로 currentPage 데이터를 보내준다.

 

lprod_SQL.xml, lprod_SQL.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="lprod">

	<sql id="where">
		AND(
	       	   LPROD_ID LIKE '%' || #{keyword} || '%' 
	        OR LPROD_GU LIKE '%' || #{keyword} || '%' 
	        OR LPROD_NM LIKE '%' || #{keyword} || '%'            
	    	)
	</sql>

	<!-- LprodVO(lprodId=14, lprodGu=P501, lprodNm=분식류) -->
	<insert id="createPost" parameterType="lprodVO">
		INSERT INTO LPROD(LPROD_ID, LPROD_GU, LPROD_NM)
		VALUES(#{lprodId},#{lprodGu},#{lprodNm})
	</insert>
	
	<select id="list" parameterType="hashMap" resultType="lprodVO">
		WITH U AS (
			SELECT ROW_NUMBER() OVER(ORDER BY LPROD_ID DESC) RNUM, T.*
			FROM(
			    SELECT LPROD_ID, LPROD_GU, LPROD_NM
			    FROM   LPROD
			    WHERE  1 = 1
			    <include refid="where"></include>
			    ) T
			)
			SELECT U.*
			FROM U
			WHERE U.RNUM BETWEEN ((#{currentPage} * 10)-(10 - 1)) AND (#{currentPage} * 10)
	</select>	
	
	<!-- LPROD : 1 -->
	<resultMap type="lprodVO" id="lprodMap">
		<result property="lprodId" column="LPROD_ID"/>
		<result property="lprodGu" column="LPROD_GU"/>
		<result property="lprodNm" column="LPROD_NM"/>
		<collection property="productVOList" resultMap="productMap"></collection>
	</resultMap>
	
	<!-- PRODUCT : N -->
	<resultMap type="productVO" id="productMap">
		<result property="productId" column="PRODUCT_ID"/>
		<result property="pname" column="PNAME"/>
		<result property="unitPrice" column="UNIT_PRICE"/>
		<result property="description" column="DESCRIPTION"/>
		<result property="manufacturer" column="MANUFACTURER"/>
		<result property="category" column="CATEGORY"/>
		<result property="unitsInStock" column="UNITS_IN_STOCK"/>
		<result property="condition" column="CONDITION"/>
		<result property="filename" column="FILENAME"/>
		<result property="quantity" column="QUANTITY"/>
	</resultMap>
	
	<select id="detail" parameterType="lprodVO" resultMap="lprodMap">
		SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM
             , B.PRODUCT_ID, B.PNAME, B.UNIT_PRICE, B.DESCRIPTION, B.MANUFACTURER
             , B.CATEGORY, B.UNITS_IN_STOCK, B.CONDITION, B.FILENAME, B.QUANTITY
		FROM     LPROD A LEFT OUTER JOIN PRODUCT B ON(A.LPROD_GU = SUBSTR(B.PRODUCT_ID,1,4))
		WHERE  A.LPROD_GU = #{lprodGu}
	</select>
	
	<!-- 
	LprodVO(lprodId=9, lprodGu=P403, lprodNm=문구류2)
	 -->
	<update id="updatePost" parameterType="lprodVO">
		UPDATE LPROD
		SET    LPROD_ID = #{lprodId}, LPROD_NM = #{lprodNm}
		WHERE  LPROD_GU = #{lprodGu}
	</update>
	
	<delete id="deletePost" parameterType="lprodVO">
		DELETE FROM LPROD 
		WHERE LPROD_GU = #{lprodGu}
	
	</delete>

	<!-- LPROD 테이블의 MAX(LPROD_ID)+1 -->
	<select id="getLprodId" resultType="int">
		SELECT NVL(MAX(LPROD_ID),0)+1 FROM LPROD
	</select>
	
	<!-- 
	//ProductVO(productId=P5011, pname=라면, unitPrice=1, ..)
	 -->
	<insert id="insertProduct" parameterType="productVO">
		INSERT INTO PRODUCT(PRODUCT_ID, PNAME, UNIT_PRICE)
		VALUES(#{productId}, #{pname}, #{unitPrice})
	</insert>
</mapper>

 

위 jsp의 코드는 ArticlePage에 pagingArea로써 저장해놨기 떄문에 아래와 같이 사용해도된다.

<div class="row">
    ${articlePage.pagingArea}
</div>

 

검색 기능 추가

//상품분류 목록
List<LprodVO> lprodVOList = this.lprodService.list(map);

 

파라미터로 keyword와 currentPage를 담은 map을 전달한다.

<sql id="where">
    AND(
           LPROD_ID LIKE '%' || #{keyword} || '%' 
        OR LPROD_GU LIKE '%' || #{keyword} || '%' 
        OR LPROD_NM LIKE '%' || #{keyword} || '%'            
        )
</sql>

<select id="getTotal" resultType="int">
    SELECT COUNT(*) 
    FROM   LPROD
    WHERE  1 = 1
    <include refid="where"></include>
</select>

 

where에 keyword값이 들어가고, 그 들어가는 구문을 select절에서 include로 불러오게 한다.

 

검색어로 30을 검색했을 때, 페이지를 넘기면 keyword도 함께 넘어가며 페이징처리가 된다.