본문 바로가기
✨ Back-end/Spring-Boot

[Spring] 쇼핑몰 - 59 페이징처리

by 환풍 2023. 4. 28.
728x90

페이징 처리를 해보자


모든 오라클 db에서 사용가능한 정렬 -1

 

ROWNUM을 넣은 후 정렬이 되기 때문에, 조회된 행 번호를 보면 뒤죽박죽으로 정렬되어있다.

따라서 오른쪽과 같이 SELECT절에 한번 감싸주어 ROWNUM을 정렬하게 되면

이렇게 ROWNUM이 제대로 정렬되어 뜨는 것을 확인할 수 있다.

 

ROWNUM 은 조회된 데이터에 행번호를 붙이기 때문에,  ROWNUM > 11 에서 데이터를 전혀 불러올 수 없다.

ROWNUM이 1보다 큰 데이터들을 조건으로 주면 데이터가 나올 수 없다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
SELECT *
FROM
(
        SELECT ROWNUM
            , BUY_CODE
            , MEM_ID
            , BUY_PRICE
            , STATUS_NAME
            , BUY_DATE
            , MEM_TELL
    FROM(
         SELECT OS.BUY_CODE
                , SM.MEM_ID
                , TO_CHAR(BUY_DATE,'YYYY-MM-DD') AS BUY_DATE
                ,SI.STATUS_NAME
                , BUY_PRICE
                , MEM_TELL
            FROM ORDER_STATUS OS
                , STATUS_INFO SI
                , SHOP_BUY SB
                , SHOP_MEMBER SM
            WHERE  OS.BUY_CODE = SB.BUY_CODE
             AND OS.STATUS_CODE = SI.STATUS_CODE
            AND OS.MEM_ID = SM.MEM_ID
            AND SI.STATUS_CODE IN
                 (  1   ,     2   ) 
            AND TO_CHAR(BUY_DATE, 'YYYY-MM-DD'<= '2023-04-28'
            AND UPPER(SB.BUY_CODE) LIKE '%' || UPPER('buy'|| '%'
            ORDER BY BUY_DATE DESC
      )
);
cs

 

이렇게 SELECT문으로 한번 더 감싸주었다.

그리고 ROWNUM에 별칭 ROW_NUM을주어, SELECT절에서 ROWNUM말고, ROW_NUM을 정렬하고 WHERE 조건을 주면 잘 타는 것을 확인할 수 있다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
SELECT  ROW_NUM
            , BUY_CODE
            , MEM_ID
            , BUY_PRICE
            , STATUS_NAME
            , BUY_DATE
            , MEM_TELL
FROM
(
        SELECT ROWNUM AS ROW_NUM
            , BUY_CODE
            , MEM_ID
            , BUY_PRICE
            , STATUS_NAME
            , BUY_DATE
            , MEM_TELL
    FROM(
         SELECT OS.BUY_CODE
                , SM.MEM_ID
                , TO_CHAR(BUY_DATE,'YYYY-MM-DD') AS BUY_DATE
                ,SI.STATUS_NAME
                , BUY_PRICE
                , MEM_TELL
            FROM ORDER_STATUS OS
                , STATUS_INFO SI
                , SHOP_BUY SB
                , SHOP_MEMBER SM
            WHERE  OS.BUY_CODE = SB.BUY_CODE
             AND OS.STATUS_CODE = SI.STATUS_CODE
            AND OS.MEM_ID = SM.MEM_ID
            AND SI.STATUS_CODE IN
                 (  1   ,     2   ) 
            AND TO_CHAR(BUY_DATE, 'YYYY-MM-DD'<= '2023-04-28'
            AND UPPER(SB.BUY_CODE) LIKE '%' || UPPER('buy'|| '%'
            ORDER BY BUY_DATE DESC
      )
)
WHERE ROW_NUM>11;
cs

 

 

즉, 쉽게 말해 SELECT로 두번 감싸서 ROWNUM을 ROW_NUM으로 변환하여 조회한 후 WHERE 조건을 주는 것이다.


오라클 12이상에서 사용하는 정렬 -2

admin-mapper-mxl

OFFSET을 사용하면 훨씬 쉽게 페이징을 할 수 있다. 나는 이것을 쓸 것이다. 

admin-mapper에 있는 쿼리에 추가해주었다.

단, OFFSET 0 ROWS FETCH FIRST 10 ROWS ONLY을 쓰면,

BUY_DATE 중에서 또 임의로 섞어서 데이터들이 출력된다.

왜냐하면 AS 때문에 BUY_DATE에서 시분초까지 포함하지 않았기때문이다.

 

 

VO와 RESULT에 굳이 추가해주지 않아도 된다.

왜냐하면 정렬때문에 설정해준 것이지, JAVA에서 가져와 HTML에서 출력할 필요가 없기 때문이다.

AdminController 컨트롤러

컨트롤러에 BUY_DATE_ORDER를 강제로 넣어준다. ORDER BY에 있는 #{orderBy} 값을 대체할 데이터이다.

bootstrap에서 페이징처리할 html을 가져왔다.

order_manage.html

html에 붙여넣었고, 이전 페이지 기능인 << 을 disabled로 임시로 주었다.

 

 

쿼리에서 OFFSET 0 ROWS FETCH FIRST 10 ROWS ONLY 을 주었기 때문에, 0개부터 10개의 데이터만 출력하고,

bootstrap에서 복사해온 페이징 하는 html을 추가해넣었다.

PageVO

board에서 사용했던 PageVO를 가져왔다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package com.study.shop.admin.vo;
 
public class PageVO {
    private int nowPage; // 현재 선택된 페이지 번호
    private int totalDataCnt; // 전체 데이터 수
    private int beginPage; // 화면에 보이는 첫 번째 페이지 번호
    private int endPage; // 화면에 보이는 마지막 페이지 번호
    private int displayCnt; // 한 페이지에 보여지는 게시글 수
    private int displayPageCnt; // 한번에 보여지는 페이지 수  
    private boolean prev; // '이전' 버튼의 유무 
    private boolean next; // '다음' 버튼의 유무
    private int startNum; // 시작 ROW_NUM 
    private int endNum;  // 마지막 ROW_NUM
    
    // 생성자
    public PageVO() {
        nowPage = 1;
        displayCnt = 5;
        displayPageCnt = 3;
    }
    
    // 이 메서드가 실행되면 page 처리를 위한 모든 변수 값을 세팅
    public void setPageInfo() {
        // 마지막에 보이는 페이지 번호
        endPage = displayPageCnt * (int)Math.ceil(nowPage / (double)displayPageCnt); // math.ceil 은 올림을 준다.
        //math.ceil은 리턴 결과가 실수(double)이다. 그래서 displayPagCnt를 곱하면 에ㅐ러가뜬다. 하나 바꿔주ㅏ.
        
        //처음에 보이는 페이지 번호
        beginPage = endPage - displayPageCnt +1;
        
        //전체 페이지 수  ex) 155/10 = 15가 나오니까, 하나를 실수형으로 바꿔준다.
        int totalPageCnt = (int)Math.ceil(totalDataCnt / (double)displayCnt); 
        
        //next버튼 유무
        if(endPage < totalPageCnt)
        {
            next = true;
        } else {
            next = false;
            endPage = totalPageCnt;
        }
        
        //prev 버튼 유무
        prev= beginPage == 1 ? false : true;
        
        //검색 시작과 마지막 ROW_NUM
        startNum = (nowPage -1* displayCnt +1;
        endNum = nowPage * displayCnt;
    }
 
    public int getNowPage() {
        return nowPage;
    }
 
    public void setNowPage(int nowPage) {
        this.nowPage = nowPage;
    }
 
    public int getTotalDataCnt() {
        return totalDataCnt;
    }
 
    public void setTotalDataCnt(int totalDataCnt) {
        this.totalDataCnt = totalDataCnt;
    }
 
    public int getBeginPage() {
        return beginPage;
    }
 
    public void setBeginPage(int beginPage) {
        this.beginPage = beginPage;
    }
 
    public int getEndPage() {
        return endPage;
    }
 
    public void setEndPage(int endPage) {
        this.endPage = endPage;
    }
 
    public int getDisplayCnt() {
        return displayCnt;
    }
 
    public void setDisplayCnt(int displayCnt) {
        this.displayCnt = displayCnt;
    }
 
    public int getDisplayPageCnt() {
        return displayPageCnt;
    }
 
    public void setDisplayPageCnt(int displayPageCnt) {
        this.displayPageCnt = displayPageCnt;
    }
 
    public boolean isPrev() {
        return prev;
    }
 
    public void setPrev(boolean prev) {
        this.prev = prev;
    }
 
    public boolean isNext() {
        return next;
    }
 
    public void setNext(boolean next) {
        this.next = next;
    }
 
    public int getStartNum() {
        return startNum;
    }
 
    public void setStartNum(int startNum) {
        this.startNum = startNum;
    }
 
    public int getEndNum() {
        return endNum;
    }
 
    public void setEndNum(int endNum) {
        this.endNum = endNum;
    }
    
     
    
    
}
 
cs

PageVO

이렇게 startNum과 endNum을 지워주고, offsetCnt를 만들어 주었다.

이후 getter와 setter를 추가했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package com.study.shop.admin.vo;
 
public class PageVO {
    private int nowPage; // 현재 선택된 페이지 번호
    private int totalDataCnt; // 전체 데이터 수
    private int beginPage; // 화면에 보이는 첫 번째 페이지 번호
    private int endPage; // 화면에 보이는 마지막 페이지 번호
    private int displayCnt; // 한 페이지에 보여지는 게시글 수
    private int displayPageCnt; // 한번에 보여지는 페이지 수  
    private boolean prev; // '이전' 버튼의 유무 
    private boolean next; // '다음' 버튼의 유무
    private int offsetCnt; // 건너뛸 개수
    
    
    // 생성자
    public PageVO() {
        nowPage = 1;
        displayCnt = 5;
        displayPageCnt = 3;
    }
    
    // 이 메서드가 실행되면 page 처리를 위한 모든 변수 값을 세팅
    public void setPageInfo() {
        // 마지막에 보이는 페이지 번호
        endPage = displayPageCnt * (int)Math.ceil(nowPage / (double)displayPageCnt); // math.ceil 은 올림을 준다.
        //math.ceil은 리턴 결과가 실수(double)이다. 그래서 displayPagCnt를 곱하면 에ㅐ러가뜬다. 하나 바꿔주자.
        
        //처음에 보이는 페이지 번호
        beginPage = endPage - displayPageCnt +1;
        
        //전체 페이지 수  ex) 155/10 = 15가 나오니까, 하나를 실수형으로 바꿔준다.
        int totalPageCnt = (int)Math.ceil(totalDataCnt / (double)displayCnt); 
        
        //next버튼 유무
        if(endPage < totalPageCnt)
        {
            next = true;
        } else {
            next = false;
            endPage = totalPageCnt;
        }
        
        //prev 버튼 유무
        prev= beginPage == 1 ? false : true;
        
        //검색 시작과 마지막 ROW_NUM
        offsetCnt = (nowPage -1* displayCnt;
    }
 
    public int getNowPage() {
        return nowPage;
    }
 
    public void setNowPage(int nowPage) {
        this.nowPage = nowPage;
    }
 
    public int getTotalDataCnt() {
        return totalDataCnt;
    }
 
    public void setTotalDataCnt(int totalDataCnt) {
        this.totalDataCnt = totalDataCnt;
    }
 
    public int getBeginPage() {
        return beginPage;
    }
 
    public void setBeginPage(int beginPage) {
        this.beginPage = beginPage;
    }
 
    public int getEndPage() {
        return endPage;
    }
 
    public void setEndPage(int endPage) {
        this.endPage = endPage;
    }
 
    public int getDisplayCnt() {
        return displayCnt;
    }
 
    public void setDisplayCnt(int displayCnt) {
        this.displayCnt = displayCnt;
    }
 
    public int getDisplayPageCnt() {
        return displayPageCnt;
    }
 
    public void setDisplayPageCnt(int displayPageCnt) {
        this.displayPageCnt = displayPageCnt;
    }
 
    public boolean isPrev() {
        return prev;
    }
 
    public void setPrev(boolean prev) {
        this.prev = prev;
    }
 
    public boolean isNext() {
        return next;
    }
 
    public void setNext(boolean next) {
        this.next = next;
    }
    
    public void setOffsetCnt(int offsetCnt) {
        this.offsetCnt = offsetCnt;
    }
    
    public int getOffsetCnt() {
        return offsetCnt;
    }
 
    
}
 
cs

 

admin-mapper.xml

PageVO에서 만들어준 offsetCnt를 넣어줬고, displayCnt값도 가져와 넣을 수 있게 했다.

OrderListSearchVO

이제 OrderListSearchVO는 PageVO에 있는 모든 변수를 상속받아 사용할 수 있게 된다.

admin-mapper.xml

STATUS에 있는 쿼리를 복사하고 붙여준 후 수만 뽑아준다.

Service

쿼리를 만들었으니 Service에서 만들어주었다.

AdminController 컨트롤러

setPageInfo 메소드를 실행하면 PageVO에 있는 모든 변수들이 값을 찾아가게 되고,

쿼리에 각 데이터들을 넣어주면서 조회가 될 것이다.

html에서 실행 결과를 확인해보면, 총 55개의 데이터중 OFFSET을 통해 5개를 뽑아준 것을 콘솔에서 확인할 수 있다.

order_manage.html

반복문을 통해 1 2 3모두 지운 후 th 문법으로 1에서 5까지 출력해보았다.

prev는 자료형이 boolean이다.

th:classappend="${orderListSearchVO.prev ? ' ' : 'disabled' }"  

참 또는 거짓을 판단할 수 있는 조건이 들어온다.

 PageVO에 있는 데이터 값에 따라 1 2 3이 출력된 것을 확인할 수 있다.

 

 

now 페이지가 2가 들어가야 2번의 페이지를 세팅할거다. 따라서 페이지 번호 데이터를 가져가야한다.

nowPage 데이터를 컨트롤러에 던지면, OrderListSearchVO가 데이터를 받아서 PageVO안에 있는 nowPage를 세팅한다.

html에서 2번 3번을 누르면 이렇게 콘솔에 각각 OFFSET에 맞게 페이징 처리가 잘 되어있는걸 확인할 수 있다.

 

previous와 next 데이터들도 nowPage에 데이터를 담아 기능을 구현해주었다.

 

내가 지금 현재 들어와 있는 데이터를 활성화 시키기 위해 classappend에 active를 주었다.

 

하지만 치명적인 결함이 있다. 검색이랑 함께 하면 안된다.

 

href="javascript:void(0);은 클릭해도 페이지이동 안한다.

th:onclick시 js로 이동하며, 데이터를 form에 감싸 컨트롤러에 보낸다.

form태그 밑에 hidden으로 id를 nowPage로 설정하고, value 값으로 nowPage데이터를 js로 가져가자.

order_manage.js

js에서 함수를 만들어 pageNum 데이터를 받아와 nowPage에 value값을 넣어주고, 검색버튼 클릭시 실행되는 함수도 return시켜준다.

PageVO

 

 

반응형

댓글