티스토리 뷰
SpringBoot Mybatis Oracle을 이용한 Pagination 객체, Paging 처리 화면처리 하기 총 정리
뵙뵙 2020. 11. 23. 08:31안녕하세요 InnerPeace 뵙뵙 입니다.
이번주는 엄청나게 Flex 한 한주였던 것 같습니다. 카메라도 사고, 핸드폰도 아이폰12 mini 로 바꾸고.. 패딩도 사면서, 몇 년 동안 옷 한벌 제대로 사지 못했던 내게 보상하는 한 주여서 기분이 들뜨기도 합니다 ! ( ice breaking )
하지만 포스팅을 할 때마다 상당히 오랜만이라는 느낌이 들 정도로 자주 정리를 하지 못해 스스로 반성하면서 들 뜬 마음을 가라앉히고 포스팅을 해보도록 하겠습니다.
또한 프로젝트를 진행하면서 꾸준히 정리하고, 임시저장글로 남겨뒀지만, 정확한 포스팅을 위해 제가 알던 내용을 다시 맞춰보는 작업을 하고, 추가적으로 설명을 돕기 위해 제 스스로도 공부하고 있습니다. ( 최대한 빨리 올려보도록 하겠습니다. 요즘은 주말밖에 시간이 없네요ㅠㅠ )
사실 포스팅 하는 내용들은 상당히 기초적이지만, 기초적인 부분들을 어설프게 넘어가면 앞으로 복잡해 지는 과정들에서 응용하여 사용하기 힘들 것이기 때문에 기초 위주로 포스팅을 하고 있습니다.
참고 하셔서 더 좋은 코드를 구성하는데 도움이 되시길 바랍니다.
* 제 포스팅은 마음대로 가셔다 쓰셔도 무방합니다.
Paging (페이징) 이란 ?
위키백과에 따르면, 컴퓨터가 메인 메모리에서 사용하기 위해 2차 기억 장치로부터 데이터를 저장하고, 검색하는 메모리 관리 기법, 블록으로 편성하여 운용하는 기법이라고 설명하고 있습니다.
이를 웹 화면을 예로 들어 쉽게 설명하자면, 많은 데이터를 한 화면에서 출력하면 페이지의 로딩속도가 느려지고, 특정 위치의 내용을 검색하기에 상당히 불리한 점이 있을 것입니다.
유튜브를 예로 들면 인기 있는 댓글의 영상을 수없이 많이 내려야 한다는 점을 생각한다면 이해가 빠를 것 같습니다.
( 당장 떠오르는 페이지가 없어서 페이징을 처리하는 이유에 대해 유튜브 댓글을 비교 설명한 점 양해 바랍니다. )
이미 수없이 많은 웹페이지에서 페이징이 사용되고, 사용해 오셨기 때문에 쉽게 이해하시리라 생각합니다.
데이터들을 블록화하여, 쪽수를 매기는 방법을 Paing 처리 그리고 Pagination이라고 합니다.
실제로 교육받던 당시 처음 Paging 처리를 하였을 때는 쉽지 않은 작업이라고 생각했습니다.
전혀 모르던 상태에서 교육을 받았던 이유와 더불어 전혀 생각하지 못한 부분을 미리 생각하고 구성해야 했기 때문이였던 이유였던 것 같습니다. ( 요즘 학과에서 배우는지 모르겠습니다. 저는 학과에서 들어본 적이 없는걸 보면.. 공부를 안했던 거 같습니다 ..ㅠㅠㅠ )
하지만 Paging 처리는 사실 어렵지는 않은 작업이지만, 꽤 번거로운 작업임은 맞는 것 같습니다.
생각해야 할 부분들이 몇 가지가 있으며, 쿼리를 생각해야 할 점이 번거로운 작업이 되겠지요.
작업 순서를 생각해서 구성하게 된다면 전혀 어렵지 않은 작업임은 분명하기에 진행에 앞서 작업 구성을 미리 설명하도록 하겠습니다. ( 실제로 당장 코드를 구현하여 고쳐나가는 것보다 작업 흐름을 미리 생각하는 것이 오히려 코드를 짜는 속도와 더불어 좋은 코드를 구성하는데 도움이 됩니다. )
페이징 처리를 위한 작업 구성
1. 쿼리 구성
2. 화면 구성
3. 페이징 처리에 필요한 변수 생성 및 알고리즘 처리
쿼리 구성
첫번째, 쿼리가 올바른 구성인지 확인을 하는 것 입니다.
올바른 쿼리를 구성하지 않은 채 작업을 진행한다면, 문제점을 찾기가 어려울 것입니다.
본인의 데이터베이스에 맞게 가장 먼저 where 절이 잘 동작하는가를 확인해 보셔야 합니다.
특히 Paging을 함에 있어서 가장 중요한 것은 검색하고 싶은 부분을 찾을 수 있어야 한다는 점입니다.
예를 들어 100개의 데이터 중 20~30번째만을 찾아낼 수 있어야 한다는 것입니다.
이는 MySQL 에서는 LIMIT 함수를 사용할 수 있기 때문에 그닥 어려운 부분이 아닐 것입니다.
하지만 오라클은 LIMIT 함수를 제공하지 않기 때문에 ROWNUM을 이용하여 구성하셔야 할 겁니다.
오라클은 ROWNUM 함수를 어떻게 사용하는가에 따라서 검색 속도에도 영향을 미치며, 쿼리가 동작하더라도 올바른 검색이 되지 않는 경우가 많기 때문에 꼭 Developer 등을 이용하여 쿼리를 테스트해보시고 진행 하시는 것을 추천드립니다.
이번 포스팅에서 본인은 오라클을 사용하여 진행하였으며, 가장 간단한 ROW_NUMBER() OVER를 이용하여 Paging 처리를 하였습니다. ( MySQL은 더욱 간단하기 때문에 쉽게 진행하실 것이라고 예상합니다. )
BETWEEN을 사용하는 방법은 데이터가 많아질수록 검색속도에 좋은 구성은 아닙니다.
하지만, 과도하게 데이터를 많이 받는 경우가 아니라면 ( 몇천만건의 데이터가 아니라면 ) 크게 고려하지 않을 정도이며, 어떤 프로젝트냐에 따라서 다를 수 있기 때문에 이점은 고려하지 않겠습니다. 사실 엄청나게 인기 있는 사이트나 대용량 데이터를 다루는 경우가 아니라면 크게 고려하지 않으셔도 될 듯합니다.
저는 예제로 지난 프로젝트에 사용한 TestSpringBoot 파일을 이용해 Pagination을 구현해 보도록 하겠습니다.
필요하신 분들은 아래글의 제일 하단 파일을 다운받아 따라하시기를 바랍니다.
hanhyx.tistory.com/43?category=886868
다운 받으신 후 아래 쿼리를 테스트 해보시길 바랍니다. ( developer 등 사용하셔서 )
해당 파일의 데이터가 부족하기 때문에 500개 정도의 테스트 데이터를 넣을 필요가 있을 것 같습니다.
혹시나 테이블 없으신 분들은 아래 create table 해주세요.
1
2
3
4
5
6
7
8
9
10
11
|
create table testTable(
userCode NUMBER(20),
userName VARCHAR2(50)
);
insert into testTable VALUES ('1234','홍길동');
insert into testTable VALUES ('5678','홍길순');
insert into testTable VALUES ('3435','둘리');
select * from testTable;
commit;
|
cs |
[더보기]를 클릭하셔서 데이터를 추가하시던지, LOOP 문을 구성하셔서 본인 스타일대로 데이터를 500개 정도 추가해주세요.
(저는 귀찮아서 엑셀로 insert문을 만들었습니다..ㅋㅋㅋㅋㅋㅋㅋㅋ 저같이 무식하게 하지 마세요. )
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
|
insert into testTable VALUES ('1','둘리1');
insert into testTable VALUES ('2','둘리2');
insert into testTable VALUES ('3','둘리3');
insert into testTable VALUES ('4','둘리4');
insert into testTable VALUES ('5','둘리5');
insert into testTable VALUES ('6','둘리6');
insert into testTable VALUES ('7','둘리7');
insert into testTable VALUES ('8','둘리8');
insert into testTable VALUES ('9','둘리9');
insert into testTable VALUES ('10','둘리10');
insert into testTable VALUES ('11','둘리11');
insert into testTable VALUES ('12','둘리12');
insert into testTable VALUES ('13','둘리13');
insert into testTable VALUES ('14','둘리14');
insert into testTable VALUES ('15','둘리15');
insert into testTable VALUES ('16','둘리16');
insert into testTable VALUES ('17','둘리17');
insert into testTable VALUES ('18','둘리18');
insert into testTable VALUES ('19','둘리19');
insert into testTable VALUES ('20','둘리20');
insert into testTable VALUES ('21','둘리21');
insert into testTable VALUES ('22','둘리22');
insert into testTable VALUES ('23','둘리23');
insert into testTable VALUES ('24','둘리24');
insert into testTable VALUES ('25','둘리25');
insert into testTable VALUES ('26','둘리26');
insert into testTable VALUES ('27','둘리27');
insert into testTable VALUES ('28','둘리28');
insert into testTable VALUES ('29','둘리29');
insert into testTable VALUES ('30','둘리30');
insert into testTable VALUES ('31','둘리31');
insert into testTable VALUES ('32','둘리32');
insert into testTable VALUES ('33','둘리33');
insert into testTable VALUES ('34','둘리34');
insert into testTable VALUES ('35','둘리35');
insert into testTable VALUES ('36','둘리36');
insert into testTable VALUES ('37','둘리37');
insert into testTable VALUES ('38','둘리38');
insert into testTable VALUES ('39','둘리39');
insert into testTable VALUES ('40','둘리40');
insert into testTable VALUES ('41','둘리41');
insert into testTable VALUES ('42','둘리42');
insert into testTable VALUES ('43','둘리43');
insert into testTable VALUES ('44','둘리44');
insert into testTable VALUES ('45','둘리45');
insert into testTable VALUES ('46','둘리46');
insert into testTable VALUES ('47','둘리47');
insert into testTable VALUES ('48','둘리48');
insert into testTable VALUES ('49','둘리49');
insert into testTable VALUES ('50','둘리50');
insert into testTable VALUES ('51','둘리51');
insert into testTable VALUES ('52','둘리52');
insert into testTable VALUES ('53','둘리53');
insert into testTable VALUES ('54','둘리54');
insert into testTable VALUES ('55','둘리55');
insert into testTable VALUES ('56','둘리56');
insert into testTable VALUES ('57','둘리57');
insert into testTable VALUES ('58','둘리58');
insert into testTable VALUES ('59','둘리59');
insert into testTable VALUES ('60','둘리60');
insert into testTable VALUES ('61','둘리61');
insert into testTable VALUES ('62','둘리62');
insert into testTable VALUES ('63','둘리63');
insert into testTable VALUES ('64','둘리64');
insert into testTable VALUES ('65','둘리65');
insert into testTable VALUES ('66','둘리66');
insert into testTable VALUES ('67','둘리67');
insert into testTable VALUES ('68','둘리68');
insert into testTable VALUES ('69','둘리69');
insert into testTable VALUES ('70','둘리70');
insert into testTable VALUES ('71','둘리71');
insert into testTable VALUES ('72','둘리72');
insert into testTable VALUES ('73','둘리73');
insert into testTable VALUES ('74','둘리74');
insert into testTable VALUES ('75','둘리75');
insert into testTable VALUES ('76','둘리76');
insert into testTable VALUES ('77','둘리77');
insert into testTable VALUES ('78','둘리78');
insert into testTable VALUES ('79','둘리79');
insert into testTable VALUES ('80','둘리80');
insert into testTable VALUES ('81','둘리81');
insert into testTable VALUES ('82','둘리82');
insert into testTable VALUES ('83','둘리83');
insert into testTable VALUES ('84','둘리84');
insert into testTable VALUES ('85','둘리85');
insert into testTable VALUES ('86','둘리86');
insert into testTable VALUES ('87','둘리87');
insert into testTable VALUES ('88','둘리88');
insert into testTable VALUES ('89','둘리89');
insert into testTable VALUES ('90','둘리90');
insert into testTable VALUES ('91','둘리91');
insert into testTable VALUES ('92','둘리92');
insert into testTable VALUES ('93','둘리93');
insert into testTable VALUES ('94','둘리94');
insert into testTable VALUES ('95','둘리95');
insert into testTable VALUES ('96','둘리96');
insert into testTable VALUES ('97','둘리97');
insert into testTable VALUES ('98','둘리98');
insert into testTable VALUES ('99','둘리99');
insert into testTable VALUES ('100','둘리100');
insert into testTable VALUES ('101','둘리101');
insert into testTable VALUES ('102','둘리102');
insert into testTable VALUES ('103','둘리103');
insert into testTable VALUES ('104','둘리104');
insert into testTable VALUES ('105','둘리105');
insert into testTable VALUES ('106','둘리106');
insert into testTable VALUES ('107','둘리107');
insert into testTable VALUES ('108','둘리108');
insert into testTable VALUES ('109','둘리109');
insert into testTable VALUES ('110','둘리110');
insert into testTable VALUES ('111','둘리111');
insert into testTable VALUES ('112','둘리112');
insert into testTable VALUES ('113','둘리113');
insert into testTable VALUES ('114','둘리114');
insert into testTable VALUES ('115','둘리115');
insert into testTable VALUES ('116','둘리116');
insert into testTable VALUES ('117','둘리117');
insert into testTable VALUES ('118','둘리118');
insert into testTable VALUES ('119','둘리119');
insert into testTable VALUES ('120','둘리120');
insert into testTable VALUES ('121','둘리121');
insert into testTable VALUES ('122','둘리122');
insert into testTable VALUES ('123','둘리123');
insert into testTable VALUES ('124','둘리124');
insert into testTable VALUES ('125','둘리125');
insert into testTable VALUES ('126','둘리126');
insert into testTable VALUES ('127','둘리127');
insert into testTable VALUES ('128','둘리128');
insert into testTable VALUES ('129','둘리129');
insert into testTable VALUES ('130','둘리130');
insert into testTable VALUES ('131','둘리131');
insert into testTable VALUES ('132','둘리132');
insert into testTable VALUES ('133','둘리133');
insert into testTable VALUES ('134','둘리134');
insert into testTable VALUES ('135','둘리135');
insert into testTable VALUES ('136','둘리136');
insert into testTable VALUES ('137','둘리137');
insert into testTable VALUES ('138','둘리138');
insert into testTable VALUES ('139','둘리139');
insert into testTable VALUES ('140','둘리140');
insert into testTable VALUES ('141','둘리141');
insert into testTable VALUES ('142','둘리142');
insert into testTable VALUES ('143','둘리143');
insert into testTable VALUES ('144','둘리144');
insert into testTable VALUES ('145','둘리145');
insert into testTable VALUES ('146','둘리146');
insert into testTable VALUES ('147','둘리147');
insert into testTable VALUES ('148','둘리148');
insert into testTable VALUES ('149','둘리149');
insert into testTable VALUES ('150','둘리150');
insert into testTable VALUES ('151','둘리151');
insert into testTable VALUES ('152','둘리152');
insert into testTable VALUES ('153','둘리153');
insert into testTable VALUES ('154','둘리154');
insert into testTable VALUES ('155','둘리155');
insert into testTable VALUES ('156','둘리156');
insert into testTable VALUES ('157','둘리157');
insert into testTable VALUES ('158','둘리158');
insert into testTable VALUES ('159','둘리159');
insert into testTable VALUES ('160','둘리160');
insert into testTable VALUES ('161','둘리161');
insert into testTable VALUES ('162','둘리162');
insert into testTable VALUES ('163','둘리163');
insert into testTable VALUES ('164','둘리164');
insert into testTable VALUES ('165','둘리165');
insert into testTable VALUES ('166','둘리166');
insert into testTable VALUES ('167','둘리167');
insert into testTable VALUES ('168','둘리168');
insert into testTable VALUES ('169','둘리169');
insert into testTable VALUES ('170','둘리170');
insert into testTable VALUES ('171','둘리171');
insert into testTable VALUES ('172','둘리172');
insert into testTable VALUES ('173','둘리173');
insert into testTable VALUES ('174','둘리174');
insert into testTable VALUES ('175','둘리175');
insert into testTable VALUES ('176','둘리176');
insert into testTable VALUES ('177','둘리177');
insert into testTable VALUES ('178','둘리178');
insert into testTable VALUES ('179','둘리179');
insert into testTable VALUES ('180','둘리180');
insert into testTable VALUES ('181','둘리181');
insert into testTable VALUES ('182','둘리182');
insert into testTable VALUES ('183','둘리183');
insert into testTable VALUES ('184','둘리184');
insert into testTable VALUES ('185','둘리185');
insert into testTable VALUES ('186','둘리186');
insert into testTable VALUES ('187','둘리187');
insert into testTable VALUES ('188','둘리188');
insert into testTable VALUES ('189','둘리189');
insert into testTable VALUES ('190','둘리190');
insert into testTable VALUES ('191','둘리191');
insert into testTable VALUES ('192','둘리192');
insert into testTable VALUES ('193','둘리193');
insert into testTable VALUES ('194','둘리194');
insert into testTable VALUES ('195','둘리195');
insert into testTable VALUES ('196','둘리196');
insert into testTable VALUES ('197','둘리197');
insert into testTable VALUES ('198','둘리198');
insert into testTable VALUES ('199','둘리199');
insert into testTable VALUES ('200','둘리200');
insert into testTable VALUES ('201','둘리201');
insert into testTable VALUES ('202','둘리202');
insert into testTable VALUES ('203','둘리203');
insert into testTable VALUES ('204','둘리204');
insert into testTable VALUES ('205','둘리205');
insert into testTable VALUES ('206','둘리206');
insert into testTable VALUES ('207','둘리207');
insert into testTable VALUES ('208','둘리208');
insert into testTable VALUES ('209','둘리209');
insert into testTable VALUES ('210','둘리210');
insert into testTable VALUES ('211','둘리211');
insert into testTable VALUES ('212','둘리212');
insert into testTable VALUES ('213','둘리213');
insert into testTable VALUES ('214','둘리214');
insert into testTable VALUES ('215','둘리215');
insert into testTable VALUES ('216','둘리216');
insert into testTable VALUES ('217','둘리217');
insert into testTable VALUES ('218','둘리218');
insert into testTable VALUES ('219','둘리219');
insert into testTable VALUES ('220','둘리220');
insert into testTable VALUES ('221','둘리221');
insert into testTable VALUES ('222','둘리222');
insert into testTable VALUES ('223','둘리223');
insert into testTable VALUES ('224','둘리224');
insert into testTable VALUES ('225','둘리225');
insert into testTable VALUES ('226','둘리226');
insert into testTable VALUES ('227','둘리227');
insert into testTable VALUES ('228','둘리228');
insert into testTable VALUES ('229','둘리229');
insert into testTable VALUES ('230','둘리230');
insert into testTable VALUES ('231','둘리231');
insert into testTable VALUES ('232','둘리232');
insert into testTable VALUES ('233','둘리233');
insert into testTable VALUES ('234','둘리234');
insert into testTable VALUES ('235','둘리235');
insert into testTable VALUES ('236','둘리236');
insert into testTable VALUES ('237','둘리237');
insert into testTable VALUES ('238','둘리238');
insert into testTable VALUES ('239','둘리239');
insert into testTable VALUES ('240','둘리240');
insert into testTable VALUES ('241','둘리241');
insert into testTable VALUES ('242','둘리242');
insert into testTable VALUES ('243','둘리243');
insert into testTable VALUES ('244','둘리244');
insert into testTable VALUES ('245','둘리245');
insert into testTable VALUES ('246','둘리246');
insert into testTable VALUES ('247','둘리247');
insert into testTable VALUES ('248','둘리248');
insert into testTable VALUES ('249','둘리249');
insert into testTable VALUES ('250','둘리250');
insert into testTable VALUES ('251','둘리251');
insert into testTable VALUES ('252','둘리252');
insert into testTable VALUES ('253','둘리253');
insert into testTable VALUES ('254','둘리254');
insert into testTable VALUES ('255','둘리255');
insert into testTable VALUES ('256','둘리256');
insert into testTable VALUES ('257','둘리257');
insert into testTable VALUES ('258','둘리258');
insert into testTable VALUES ('259','둘리259');
insert into testTable VALUES ('260','둘리260');
insert into testTable VALUES ('261','둘리261');
insert into testTable VALUES ('262','둘리262');
insert into testTable VALUES ('263','둘리263');
insert into testTable VALUES ('264','둘리264');
insert into testTable VALUES ('265','둘리265');
insert into testTable VALUES ('266','둘리266');
insert into testTable VALUES ('267','둘리267');
insert into testTable VALUES ('268','둘리268');
insert into testTable VALUES ('269','둘리269');
insert into testTable VALUES ('270','둘리270');
insert into testTable VALUES ('271','둘리271');
insert into testTable VALUES ('272','둘리272');
insert into testTable VALUES ('273','둘리273');
insert into testTable VALUES ('274','둘리274');
insert into testTable VALUES ('275','둘리275');
insert into testTable VALUES ('276','둘리276');
insert into testTable VALUES ('277','둘리277');
insert into testTable VALUES ('278','둘리278');
insert into testTable VALUES ('279','둘리279');
insert into testTable VALUES ('280','둘리280');
insert into testTable VALUES ('281','둘리281');
insert into testTable VALUES ('282','둘리282');
insert into testTable VALUES ('283','둘리283');
insert into testTable VALUES ('284','둘리284');
insert into testTable VALUES ('285','둘리285');
insert into testTable VALUES ('286','둘리286');
insert into testTable VALUES ('287','둘리287');
insert into testTable VALUES ('288','둘리288');
insert into testTable VALUES ('289','둘리289');
insert into testTable VALUES ('290','둘리290');
insert into testTable VALUES ('291','둘리291');
insert into testTable VALUES ('292','둘리292');
insert into testTable VALUES ('293','둘리293');
insert into testTable VALUES ('294','둘리294');
insert into testTable VALUES ('295','둘리295');
insert into testTable VALUES ('296','둘리296');
insert into testTable VALUES ('297','둘리297');
insert into testTable VALUES ('298','둘리298');
insert into testTable VALUES ('299','둘리299');
insert into testTable VALUES ('300','둘리300');
insert into testTable VALUES ('301','둘리301');
insert into testTable VALUES ('302','둘리302');
insert into testTable VALUES ('303','둘리303');
insert into testTable VALUES ('304','둘리304');
insert into testTable VALUES ('305','둘리305');
insert into testTable VALUES ('306','둘리306');
insert into testTable VALUES ('307','둘리307');
insert into testTable VALUES ('308','둘리308');
insert into testTable VALUES ('309','둘리309');
insert into testTable VALUES ('310','둘리310');
insert into testTable VALUES ('311','둘리311');
insert into testTable VALUES ('312','둘리312');
insert into testTable VALUES ('313','둘리313');
insert into testTable VALUES ('314','둘리314');
insert into testTable VALUES ('315','둘리315');
insert into testTable VALUES ('316','둘리316');
insert into testTable VALUES ('317','둘리317');
insert into testTable VALUES ('318','둘리318');
insert into testTable VALUES ('319','둘리319');
insert into testTable VALUES ('320','둘리320');
insert into testTable VALUES ('321','둘리321');
insert into testTable VALUES ('322','둘리322');
insert into testTable VALUES ('323','둘리323');
insert into testTable VALUES ('324','둘리324');
insert into testTable VALUES ('325','둘리325');
insert into testTable VALUES ('326','둘리326');
insert into testTable VALUES ('327','둘리327');
insert into testTable VALUES ('328','둘리328');
insert into testTable VALUES ('329','둘리329');
insert into testTable VALUES ('330','둘리330');
insert into testTable VALUES ('331','둘리331');
insert into testTable VALUES ('332','둘리332');
insert into testTable VALUES ('333','둘리333');
insert into testTable VALUES ('334','둘리334');
insert into testTable VALUES ('335','둘리335');
insert into testTable VALUES ('336','둘리336');
insert into testTable VALUES ('337','둘리337');
insert into testTable VALUES ('338','둘리338');
insert into testTable VALUES ('339','둘리339');
insert into testTable VALUES ('340','둘리340');
insert into testTable VALUES ('341','둘리341');
insert into testTable VALUES ('342','둘리342');
insert into testTable VALUES ('343','둘리343');
insert into testTable VALUES ('344','둘리344');
insert into testTable VALUES ('345','둘리345');
insert into testTable VALUES ('346','둘리346');
insert into testTable VALUES ('347','둘리347');
insert into testTable VALUES ('348','둘리348');
insert into testTable VALUES ('349','둘리349');
insert into testTable VALUES ('350','둘리350');
insert into testTable VALUES ('351','둘리351');
insert into testTable VALUES ('352','둘리352');
insert into testTable VALUES ('353','둘리353');
insert into testTable VALUES ('354','둘리354');
insert into testTable VALUES ('355','둘리355');
insert into testTable VALUES ('356','둘리356');
insert into testTable VALUES ('357','둘리357');
insert into testTable VALUES ('358','둘리358');
insert into testTable VALUES ('359','둘리359');
insert into testTable VALUES ('360','둘리360');
insert into testTable VALUES ('361','둘리361');
insert into testTable VALUES ('362','둘리362');
insert into testTable VALUES ('363','둘리363');
insert into testTable VALUES ('364','둘리364');
insert into testTable VALUES ('365','둘리365');
insert into testTable VALUES ('366','둘리366');
insert into testTable VALUES ('367','둘리367');
insert into testTable VALUES ('368','둘리368');
insert into testTable VALUES ('369','둘리369');
insert into testTable VALUES ('370','둘리370');
insert into testTable VALUES ('371','둘리371');
insert into testTable VALUES ('372','둘리372');
insert into testTable VALUES ('373','둘리373');
insert into testTable VALUES ('374','둘리374');
insert into testTable VALUES ('375','둘리375');
insert into testTable VALUES ('376','둘리376');
insert into testTable VALUES ('377','둘리377');
insert into testTable VALUES ('378','둘리378');
insert into testTable VALUES ('379','둘리379');
insert into testTable VALUES ('380','둘리380');
insert into testTable VALUES ('381','둘리381');
insert into testTable VALUES ('382','둘리382');
insert into testTable VALUES ('383','둘리383');
insert into testTable VALUES ('384','둘리384');
insert into testTable VALUES ('385','둘리385');
insert into testTable VALUES ('386','둘리386');
insert into testTable VALUES ('387','둘리387');
insert into testTable VALUES ('388','둘리388');
insert into testTable VALUES ('389','둘리389');
insert into testTable VALUES ('390','둘리390');
insert into testTable VALUES ('391','둘리391');
insert into testTable VALUES ('392','둘리392');
insert into testTable VALUES ('393','둘리393');
insert into testTable VALUES ('394','둘리394');
insert into testTable VALUES ('395','둘리395');
insert into testTable VALUES ('396','둘리396');
insert into testTable VALUES ('397','둘리397');
insert into testTable VALUES ('398','둘리398');
insert into testTable VALUES ('399','둘리399');
insert into testTable VALUES ('400','둘리400');
insert into testTable VALUES ('401','둘리401');
insert into testTable VALUES ('402','둘리402');
insert into testTable VALUES ('403','둘리403');
insert into testTable VALUES ('404','둘리404');
insert into testTable VALUES ('405','둘리405');
insert into testTable VALUES ('406','둘리406');
insert into testTable VALUES ('407','둘리407');
insert into testTable VALUES ('408','둘리408');
insert into testTable VALUES ('409','둘리409');
insert into testTable VALUES ('410','둘리410');
insert into testTable VALUES ('411','둘리411');
insert into testTable VALUES ('412','둘리412');
insert into testTable VALUES ('413','둘리413');
insert into testTable VALUES ('414','둘리414');
insert into testTable VALUES ('415','둘리415');
insert into testTable VALUES ('416','둘리416');
insert into testTable VALUES ('417','둘리417');
insert into testTable VALUES ('418','둘리418');
insert into testTable VALUES ('419','둘리419');
insert into testTable VALUES ('420','둘리420');
insert into testTable VALUES ('421','둘리421');
insert into testTable VALUES ('422','둘리422');
insert into testTable VALUES ('423','둘리423');
insert into testTable VALUES ('424','둘리424');
insert into testTable VALUES ('425','둘리425');
insert into testTable VALUES ('426','둘리426');
insert into testTable VALUES ('427','둘리427');
insert into testTable VALUES ('428','둘리428');
insert into testTable VALUES ('429','둘리429');
insert into testTable VALUES ('430','둘리430');
insert into testTable VALUES ('431','둘리431');
insert into testTable VALUES ('432','둘리432');
insert into testTable VALUES ('433','둘리433');
insert into testTable VALUES ('434','둘리434');
insert into testTable VALUES ('435','둘리435');
insert into testTable VALUES ('436','둘리436');
insert into testTable VALUES ('437','둘리437');
insert into testTable VALUES ('438','둘리438');
insert into testTable VALUES ('439','둘리439');
insert into testTable VALUES ('440','둘리440');
insert into testTable VALUES ('441','둘리441');
insert into testTable VALUES ('442','둘리442');
insert into testTable VALUES ('443','둘리443');
insert into testTable VALUES ('444','둘리444');
insert into testTable VALUES ('445','둘리445');
insert into testTable VALUES ('446','둘리446');
insert into testTable VALUES ('447','둘리447');
insert into testTable VALUES ('448','둘리448');
insert into testTable VALUES ('449','둘리449');
insert into testTable VALUES ('450','둘리450');
insert into testTable VALUES ('451','둘리451');
insert into testTable VALUES ('452','둘리452');
insert into testTable VALUES ('453','둘리453');
insert into testTable VALUES ('454','둘리454');
insert into testTable VALUES ('455','둘리455');
insert into testTable VALUES ('456','둘리456');
insert into testTable VALUES ('457','둘리457');
insert into testTable VALUES ('458','둘리458');
insert into testTable VALUES ('459','둘리459');
insert into testTable VALUES ('460','둘리460');
insert into testTable VALUES ('461','둘리461');
insert into testTable VALUES ('462','둘리462');
insert into testTable VALUES ('463','둘리463');
insert into testTable VALUES ('464','둘리464');
insert into testTable VALUES ('465','둘리465');
insert into testTable VALUES ('466','둘리466');
insert into testTable VALUES ('467','둘리467');
insert into testTable VALUES ('468','둘리468');
insert into testTable VALUES ('469','둘리469');
insert into testTable VALUES ('470','둘리470');
insert into testTable VALUES ('471','둘리471');
insert into testTable VALUES ('472','둘리472');
insert into testTable VALUES ('473','둘리473');
insert into testTable VALUES ('474','둘리474');
insert into testTable VALUES ('475','둘리475');
insert into testTable VALUES ('476','둘리476');
insert into testTable VALUES ('477','둘리477');
insert into testTable VALUES ('478','둘리478');
insert into testTable VALUES ('479','둘리479');
insert into testTable VALUES ('480','둘리480');
insert into testTable VALUES ('481','둘리481');
insert into testTable VALUES ('482','둘리482');
insert into testTable VALUES ('483','둘리483');
insert into testTable VALUES ('484','둘리484');
insert into testTable VALUES ('485','둘리485');
insert into testTable VALUES ('486','둘리486');
insert into testTable VALUES ('487','둘리487');
insert into testTable VALUES ('488','둘리488');
insert into testTable VALUES ('489','둘리489');
insert into testTable VALUES ('490','둘리490');
insert into testTable VALUES ('491','둘리491');
insert into testTable VALUES ('492','둘리492');
insert into testTable VALUES ('493','둘리493');
insert into testTable VALUES ('494','둘리494');
insert into testTable VALUES ('495','둘리495');
insert into testTable VALUES ('496','둘리496');
insert into testTable VALUES ('497','둘리497');
insert into testTable VALUES ('498','둘리498');
insert into testTable VALUES ('499','둘리499');
insert into testTable VALUES ('500','둘리500');
|
cs |
사실, 테이블 구성을 페이징 처리에 사용할 계획이 아니였기 때문에, 유저 이름에 숫자를 붙이는것이 맞지 않는 표현이지만, 지금은 예제이기 때문에 그냥 넘어가도록 하겠습니다. userCode가 게시글 순서라고 생각해주시면 편할 것 같습니다.
다음으로 생각해야할 부분은, 한 화면에서 보여질 테이터 갯수에 따른 부분 검색 쿼리를 구성하셔야 합니다.
앞서 말씀드렸다시피 MySQL은 LIMIT 함수를 사용하시면 됩니다.
오라클은 다음과 같이 ROW_NUMBER() OVER를 이용하여, userCode를 역순으로 정렬하여 사용하였습니다.
( 최신 글들이 가장 위로 온다는 구성 )
1
2
3
4
5
6
7
8
9
10
|
SELECT *
FROM (
SELECT ROW_NUMBER() OVER(ORDER by userCode DESC) AS
row_num
,userCode
,userName
FROM testTable
)
WHERE row_num BETWEEN 0 AND 10;
|
cs |
해당 쿼리를 실행해보면, 0~10개의 데이터만 보여주게 됩니다. 앞으로 Pagination (쪽수매기기)을 이 쿼리를 이용하여 10~20번째 20~30번째 등을 검색해 쪽수를 매길 것입니다.
또한 오라클의 rownum 함수를 이용할 때 SELECT 문 안에서 한번 더 SELECT 문을 이용하여야 한다는 점입니다. 이 점을 유의하시면서 작업을 진행하셔야 하며, rownum 사용에 대한 자세한 방법과 검색 속도에 더 좋은 쿼리 구성을 찾아보시길 바랍니다.
화면 구성
두번째는 화면구성입니다.
왜 화면구성을 먼저 생각하는지 의문이 드실 수도 있겠지만, 화면이 잘 구성되어 있어야지만 이에 맞춰 필요한 기능들을 생각해 볼 수 있으며, 가장 직관적으로 확인 할 수 있는 방법이기 때문입니다.
저는 아래 이미지와 같이 [이전버튼], [다음버튼], [제일 처음페이지으로], [제일 마지막페이지로], [페이지 당 보여줄 데이터 갯수를 10개씩,20개씩,30개씩] selectBox를 통해 화면에 보여질 수 있도록 구성해 보겠습니다.
또한 해당 페이지의 숫자를 클릭하면 페이지 이동 및 해당 페이지 수를 10개씩 보여지도록 구성할 계획입니다.
이에 따른 몇가지 추가 CSS와 화면 코드를 추가하도록 하겠습니다.
css 파일은 아직 분리하는 포스팅을 올리지 않았기에, 한 페이지에서 처리하도록 하겠습니다.
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
|
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>List View</title>
</head>
<style>
table, td, th {
border : 1px solid black;
}
th {
background : #F3F5F5;
}
table {
margin-top : 5%;
margin-left : auto;
margin-right: auto;
text-align: center;
width: 80%;
}
a:link { color: red; text-decoration: none;}
a:visited { color: black; text-decoration: none;}
/* paginate */
.paginate {
padding: 0;
line-height: normal;
text-align: center;
position: relative;
margin: 20px 0 20px 0;
z-index: 1;
}
.paginate .paging {
text-align: center;
}
.paginate .paging a, .paginate .paging strong {
margin: 0;
padding: 0;
width: 20px;
height: 24px;
line-height: 24px;
text-align: center;
color: #848484;
display: inline-block;
vertical-align: middle;
text-align: center;
font-size: 12px;
}
.paginate .paging a:hover, .paginate .paging strong {
color: #DAA520;
font-weight: 600;
font-weight: normal;
}
.paginate .paging .direction {
z-index: 3;
vertical-align: middle;
background-color: none;
margin: 0 2px;
border: 1px solid #777;
border-radius: 2px;
width: 28px;
}
.paginate .paging .direction:hover {
border: 1px solid #C40639;
}
.paginate .paging .direction.prev {
margin-right: 4px;
}
.paginate .paging .direction.next {
margin-left: 4px;
cursor: pointer;
}
.paginate .paging img {
vertical-align: middle;
}
.paginate .right {
position: absolute;
top: 0;
right: 0;
}
.bottom-left, .bottom-right {
position: relative;
z-index: 5;
}
.paginate ~ .bottom {
margin-top: -50px;
}
.bottom select {
background: transparent;
color: #aaa;
cursor: pointer;
}
/* paginate */
.paginate {
padding: 0;
line-height: normal;
text-align: center;
position: relative;
margin: 20px 0 20px 0;
}
.paginate .paging {
text-align: center;
}
.paginate .paging a, .paginate .paging strong {
margin: 0;
padding: 0;
width: 20px;
height: 28px;
line-height: 28px;
text-align: center;
color: #999;
display: inline-block;
vertical-align: middle;
text-align: center;
font-size: 14px;
}
.paginate .paging a:hover, .paginate .paging strong {
color: #C40639;
font-weight: 600;
font-weight: normal;
}
.paginate .paging .direction {
z-index: 3;
vertical-align: middle;
background-color: none;
margin: 0 2px;
}
.paginate .paging .direction:hover {
background-color: transparent;
}
.paginate .paging .direction.prev {
margin-right: 4px;
}
.paginate .paging .direction.next {
margin-left: 4px;
}
.paginate .paging img {
vertical-align: middle;
}
.paginate .right {
position: absolute;
top: 0;
right: 0;
}
</style>
<body>
<div>
<table>
<thead>
<tr>
<th>userCode</th>
<th>userName</th>
</tr>
</thead>
<tbody>
<c:choose>
<c:when test="${fn:length(Alllist) > 0}">
<c:forEach items="${Alllist}" var="Alllist">
<tr>
<td>${Alllist.USERCODE}</td>
<td>${Alllist.USERNAME}</td>
</tr>
</c:forEach>
</c:when>
<c:otherwise>
<tr>
<td colspan="4">조회된 결과가 없습니다.</td>
</tr>
</c:otherwise>
</c:choose>
</tbody>
</table>
</div>
<!--paginate -->
<div class="paginate">
<div class="paging">
<a class="direction prev">
<<
</a>
<a class="direction prev">
<
</a>
<a>1</a>
<a class="direction next">
>
</a>
<a class="direction next">
>>
</a>
</div>
</div>
<!-- /paginate -->
<div class="bottom">
<div class="bottom-left">
<select id="cntSelectBox" name="cntSelectBox" class="form-control" style="width: 100px;">
<option value="10">10개씩</option>
<option value="20">20개씩</option>
<option value="30">30개씩</option>
</select>
</div>
</div>
</body>
</html>
|
cs |
css의 경우는 특별하게 설명하지 않도록 하겠습니다.
제 프로젝트의 일부를 발췌하여 사용했기 때문에 조금 쓸때없는 코드도 많습니다. 당장 예제로 사용하기에 무리 없는 정도로만 구성하였습니다.
Pagination
이제 Paging 처리를 위한 사전 준비는 끝났습니다.
이제 해당 쿼리와 화면을 이용하여, 서버에서의 작업을 통해 출력해 보도록 하겠습니다.
Pagination 같은 경우 한 웹사이트에서 대체적으로 공통 사용이 많기 때문에 객체로 만들어 사용하도록 구성하겠습니다.
또한 아래 해당 포스팅을 참고하여 수정 사용함을 말씀드립니다.
com.example.spring.myapp.paging 패키지 생성 및 Pagination 클래스 생성
다음으로, 페이징 처리를 위한 변수를 생성하고, 처리해보도록 하겠습니다.
필요한 변수는 아래와 같습니다.
currentPage | 현재 페이지 |
cntPerPage | 페이지당 출력할 데이터 갯수 ( ex. 10개씩,20개씩,30개씩) |
pageSize | 화면 하단 페이지 갯수 (1~10페이지) |
totalRecordCount | 전체 데이터의 갯수 |
totalPageCount | 전체 페이지의 갯수 ( ex. 데이터가 500개이고 10개씩 보여준다면 총 페이지는 50페이지 ) |
firstPage | 페이지 리스트의 첫 페이지 번호 ( 1~10 개면 첫페이지는 1번 ) |
lastPage | 페이지 리스트의 마지막 페이지 번호 |
firstRecordIndex | Oracle 조건절에 사용되는 첫 ROWNUM |
lastRecordIndex | Oracle 조건절에 사용되는 마지막 ROWNUM |
hasPreviousPage | 이전 페이지 존재 여부 |
hasNextPage | 다음 페이지 존재 여부 |
위의 표를 참고하시면서, 아래 코드를 보도록 하겠습니다.
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
package com.kr.lgcare.qrmanage.web.paging;
public class Pagination {
// 현재페이지
private int currentPage;
// 페이지당 출력할 페이지 갯수
private int cntPerPage;
// 화면 하단 페이지 사이즈 1~10, 10~20 20~30 ...
private int pageSize;
// 전체 데이터 개수
private int totalRecordCount;
// 전체 페이지 개수
private int totalPageCount;
// 페이지 리스트의 첫 페이지 번호
private int firstPage;
// 페이지 리스트의 마지막 페이지 번호
private int lastPage;
// SQL의 조건절에 사용되는 첫 RNUM
private int firstRecordIndex;
// SQL의 조건절에 사용되는 마지막 RNUM
private int lastRecordIndex;
// 이전 페이지 존재 여부
private boolean hasPreviousPage;
// 다음 페이지 존재 여부
private boolean hasNextPage;
public Pagination(int currentPage, int cntPerPage, int pageSize) {
//강제입력방지
if (currentPage < 1) {
currentPage = 1;
}
//10,20,30개 단위 이외 처리 방지
if (cntPerPage != 10 && cntPerPage != 20 && cntPerPage != 30) {
cntPerPage = 10;
}
// 하단 페이지 갯수 10개로 제한
if (pageSize != 10) {
pageSize = 10;
}
this.currentPage = currentPage;
this.cntPerPage = cntPerPage;
this.pageSize = pageSize;
}
public void setTotalRecordCount(int totalRecordCount) {
this.totalRecordCount = totalRecordCount;
if (totalRecordCount > 0) {
calculation();
}
}
private void calculation() {
// 전체 페이지 수 (현재 페이지 번호가 전체 페이지 수보다 크면 현재 페이지 번호에 전체 페이지 수를 저장)
totalPageCount = ((totalRecordCount - 1) / this.getCntPerPage()) + 1;
if (this.getCurrentPage() > totalPageCount) {
this.setCurrentPage(totalPageCount);
}
// 페이지 리스트의 첫 페이지 번호
firstPage = ((this.getCurrentPage() - 1) / this.getPageSize()) * this.getPageSize() + 1;
// 페이지 리스트의 마지막 페이지 번호 (마지막 페이지가 전체 페이지 수보다 크면 마지막 페이지에 전체 페이지 수를 저장)
lastPage = firstPage + this.getPageSize() - 1;
if (lastPage > totalPageCount) {
lastPage = totalPageCount;
}
// SQL의 조건절에 사용되는 첫 RNUM
firstRecordIndex = (this.getCurrentPage() - 1) * this.getCntPerPage();
// SQL의 조건절에 사용되는 마지막 RNUM
lastRecordIndex = this.getCurrentPage() * this.getCntPerPage();
// 이전 페이지 존재 여부
hasPreviousPage = firstPage == 1 ? false : true;
if(hasPreviousPage == false) {
if(currentPage != firstPage) {
hasPreviousPage = true;
}else {
hasPreviousPage = false;
}
}
// 다음 페이지 존재 여부
hasNextPage = (lastPage * this.getCntPerPage()) >= totalRecordCount ? false : true;
if(hasNextPage == false) {
//마지막 페이지에서 현재페이지가 마지막 페이지가 아닌경우 next처리
if(currentPage != lastPage) {
hasNextPage = true;
}else {
hasNextPage = false;
}
}
}
/*
GETTER SETTER
//*/
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getCntPerPage() {
return cntPerPage;
}
public void setCntPerPage(int cntPerPage) {
this.cntPerPage = cntPerPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalPageCount() {
return totalPageCount;
}
public void setTotalPageCount(int totalPageCount) {
this.totalPageCount = totalPageCount;
}
public int getFirstPage() {
return firstPage;
}
public void setFirstPage(int firstPage) {
this.firstPage = firstPage;
}
public int getLastPage() {
return lastPage;
}
public void setLastPage(int lastPage) {
this.lastPage = lastPage;
}
public int getFirstRecordIndex() {
return firstRecordIndex;
}
public void setFirstRecordIndex(int firstRecordIndex) {
this.firstRecordIndex = firstRecordIndex;
}
public int getLastRecordIndex() {
return lastRecordIndex;
}
public void setLastRecordIndex(int lastRecordIndex) {
this.lastRecordIndex = lastRecordIndex;
}
public boolean isHasPreviousPage() {
return hasPreviousPage;
}
public void setHasPreviousPage(boolean hasPreviousPage) {
this.hasPreviousPage = hasPreviousPage;
}
public boolean isHasNextPage() {
return hasNextPage;
}
public void setHasNextPage(boolean hasNextPage) {
this.hasNextPage = hasNextPage;
}
public int getTotalRecordCount() {
return totalRecordCount;
}
}
|
cs |
28~43번째 코드
저는 3개의 파라미터를 이용하여 페이지를 컨트롤 하는 구성을 짰습니다.
우선 사용자가 임의적으로 파라미터에 값을 입력하는 것을 방지하기 위해 현재 페이지가 0일 때 첫번째 페이지를 보여주도록 하였고, 하단에 보여지는 페이지의 갯수를 10개로 고정시켰습니다.
또한, selectBox 의 10개씩, 20개씩, 30개씩 제어하기 위해 조건을 주었으며 이외의 값에 대해서 한번에 보여주는 데이터의 갯수를 10개로 보여질 수 있게 제한하였습니다.
47~53번째 코드
페이지 처리에 있어서 또 한가지 필요한 조건이 있다면, 전체 데이터의 갯수를 알아내야 하는 것입니다.
SQL문의 Count 함수를 사용하여 전체 데이터를 파악 할 수 있습니다.
또한, 전체 데이터의 갯수가 0개 이상 일 경우에만 페이징 처리를 하도록 구성하였습니다.
55~91번째 코드
페이지 처리를 계산하기 위한 부분이며, 페이징 처리에 있어서 가장 중요한 부분입니다.
totalPageCount | 전체 페이지의 갯수 입니다. ( (전체 데이터 개수 -1 ) / 페이지에서 보여질 데이터 갯수 ) +1 조금 더 추가 설명을 하겠습니다. 전체 503개의 데이터 중 한 페이지 당 10개씩 보여준다면 필요한 전체 페이지의 갯수는 51개가 될 것입니다. 또한 현재 페이지 번호가 전체 페이지 갯수보다 클 수 없기 때문에 아래 if문을 통해 제어를 하도록 합니다. |
firstPage | 페이지 별 가장 첫번째 페이지 번호 입니다. ( (현재 페이지 번호 - 1) / 화면 하단의 페이지 갯수 ) * 화면 하단의 페이지 개수 +1 쉽게 예를 들자면 화면 하단 페이지 번호를 10개씩 보여준다고 하면 1~10까지를 볼 수 있습니다. 이 때 첫번째 페이지 번호는 1입니다. 하지만 다음페이지로 넘어가게 된다면 11~20번째 페이지가 출력될 것이고, 이때 첫번째 페이지 번호는 11로 보여주기 위한 처리입니다. |
lastPage | 페이지의 가장 마지막 번호 입니다. (첫 페이지 번호 + 화면 하단의 페이지 갯수 ) -1 하단 페이지를 1~10까지 보여지고 있고, 현재 페이지 번호가 3번째일때 마지막 보여질 페이지 번호는 10일 것입니다. 해당 처리를 위한 부분입니다. 또한 전체 페이지의 갯수보다 넘어가는 페이지는 아무 데이터도 보여질 것이 없는 페이지 이므로 마지막 페이지보다 초과하는 페이지는 없애기 위해 if문 처리를 하였습니다. |
firstRecordIndex | 오라클에서 부분 데이터를 조회하기 위해 필요한 첫번째 조건입니다. (LIMIT도 동일합니다.) ex) Mybatis 를 통해 WHERE row_num BETWEEN #{firstRecordIndex} AND #{lastRecordIndex} 해당 부분을 제어하기 위한 부분입니다. |
lastRecordIndex | 오라클에서 부분 데이터를 조회하기 위해 필요한 마지막 조건입니다. (LIMIT도 동일합니다.) ex) Mybatis 를 통해 WHERE row_num BETWEEN #{firstRecordIndex} AND #{lastRecordIndex} 해당 부분을 제어하기 위한 부분입니다. |
hasPreviousPage | 이전 페이지의 존재여부를 통해 페이지 이동을 위한 부분입니다. 저는 우선 첫페이지가 1이라면, 이전 버튼을 클릭 시 더 이상 이동을 하지 않게 하기 위해 다음과 같이 처리하였으며, 이 때 상세 조건으로 1~10페이지에서의 이동 또한 1페이지를 제외한 나머지는 이전버튼을 동작시키기 위한 if문을 작성하였습니다. |
hasNextPage | 다음 페이지의 존재여부를 통해 페이지 이동을 위한 부분입니다. (마지막 페이지 번호 * 페이지당 출력할 데이터의 갯수) 해당 코드 또한 마찬가지로, 전체 데이터 갯수보다 크다면 false를 반환하여, 마지막 페이지를 초과할 시 다음 버튼을 작동시키지 않게 처리 하였습니다. 이 때 해당 마지막 페이지가 50~55페이지라고 가정 할 경우 if 문을 통해 55페이지가 아니라면 다음 버튼이 동작하도록 if문을 작성하였습니다. 해당 코드가 이해가지 않으신다면, if문을 잠시 지우고 테스트 해보시길 바랍니다. |
페이징 처리는 웹에서 공통으로 사용될 일이 많기 때문에, 객체화 하여 사용할 수 있도록 구성을 모두 끝냈습니다.
이제 사용하는 일만 남았는데요.
SpringBoot - Mybatis 설정을 잡고, 페이징 처리를 통해 버튼이 동작하도록 하는 화면 처리를 하도록 하겠습니다.
우선 가장 기본 Mybatis의 셋팅은 잡혀있다는 가정하에 진행하겠습니다.
보통 Mybatis 에 새로운 쿼리를 추가할 때마다 작업 설정을 잘못 잡아 오류를 발생하는 경우가 많습니다.
아무런 흐름 없이 코드를 추가한다면 어디서 오류가 발생했는지 매우 간단한 작업임에도 찾지 못할 수 있기 때문에 저는 다음과 같은 흐름으로 보통 코드를 작성합니다.
1. Mybatis 쿼리를 추가하는 SQL Mapper .xml 파일에 쿼리문 작성
2. Mapper 인터페이스에 Mybatis의 id값과 같은 메소드명의 인터페이스 추가
3. Service 인터페이스에 2번과 같은 코드 추가
4. Service 인터페이스를 implements 하는 클래스에 해당 메소드 추가
5. Controller 처리
뭔가 작업이 많아보이는 듯 하지만 사실상 복사 붙여넣기 수준의 간단한 작업입니다.
하지만 간단한 작업임에도 흐름을 파악하지 못한다면, 오류를 유발 할 수 있으므로, 본인만의 작업 흐름을 만들어 두시는 것을 추천드립니다.
TestTableMapper.xml
기존 쿼리문을 약간 수정하겠습니다. 또한 총 데이터의 갯수를 세는 select문을 추가적으로 생성하도록 합니다.
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
|
<?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="com.example.spring.myapp.model.mapper.TestTableMapper">
<select id="SelectAllList" resultType="hashmap">
SELECT *
FROM (
SELECT ROW_NUMBER() OVER(ORDER by userCode DESC) AS
row_num
,userCode
,userName
FROM testTable
)
<if test="firstRecordIndex !=null and lastRecordIndex != null">
WHERE row_num BETWEEN #{firstRecordIndex} AND
#{lastRecordIndex}
</if>
</select>
<select id="testTableCount" resultType="int">
SELECT count(*) FROM
testTable
</select>
</mapper>
|
cs |
TestTableMapper.java
다음으로 Mapper interface를 수정하도록 하겠습니다.
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
|
/**
*
*/
package com.example.spring.myapp.model.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import com.example.spring.myapp.paging.Pagination;
/**
* @author CafeAlle
*
*/
@Mapper
public interface TestTableMapper {
//select * from Test_Table
public List<Map<String, Object>> SelectAllList() throws Exception;
//Paging
public List<Map<String, Object>> SelectAllList(Pagination pagination) throws Exception;
//count
public int testTableCount() throws Exception;
}
|
cs |
* 여기서 가장 중요한 부분은 메소드 이름과 쿼리문의 아이디를 통일시켜야 한다는 것입니다.
24번째 코드
전체 데이터에 대한 페이징 객체를 넣어 페이징 처리를 한 후, Map형식으로 받아서 List에 저장하도록 처리하였습니다.
객체화를 시켜둠으로 인해서, 앞으로 페이징 처리에 관련된 부분을 객체 삽입만으로 처리가 가능하도록 구성하였습니다.
27번째코드
전체 데이터 갯수를 확인하기 위한 부분으로, 갯수를 확인하기 때문에 int로 받게됩니다.
TestTableService.java
Service 부분 또한 Mapper에서 생성한 메소드를 그대로 복사해서 붙여넣도록 하여 오타 생성을 방지하도록 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.example.spring.myapp.service;
import java.util.List;
import java.util.Map;
import com.example.spring.myapp.paging.Pagination;
public interface TestTableService {
//select * from Test_Table
public List<Map<String, Object>> SelectAllList() throws Exception;
//Paging
public List<Map<String, Object>> SelectAllList(Pagination pagination) throws Exception;
//count
public int testTableCount() throws Exception;
}
|
cs |
추가설명은 따로 하지 않겠습니다. 위와 동일.
TestTableServiceImpl.java
위 코드까지 작성하셨다면, TestTableServiceImpl.java 클래스에 아래와 같이 빨간줄을 보실 수 있으실텐데요.
해당 빨간줄 위에 마우스를 가져다두어서 Add unimplemented methods를 클릭합니다.
생성하신 후에 아래 코드와 같이 return 값만 수정해줍니다.
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
|
package com.example.spring.myapp.service;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.spring.myapp.model.mapper.TestTableMapper;
import com.example.spring.myapp.paging.Pagination;
@Service
public class TestTableServiceImpl implements TestTableService{
@Autowired
TestTableMapper testtableMapper;
@Override
public List<Map<String, Object>> SelectAllList() throws Exception {
// TODO Auto-generated method stub
return testtableMapper.SelectAllList();
}
@Override
public List<Map<String, Object>> SelectAllList(Pagination pagination) throws Exception {
// TODO Auto-generated method stub
return testtableMapper.SelectAllList(pagination);
}
@Override
public int testTableCount() throws Exception {
// TODO Auto-generated method stub
return testtableMapper.testTableCount();
}
}
|
cs |
TestTableController.java
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
|
package com.example.spring.myapp.controller;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.example.spring.myapp.paging.Pagination;
import com.example.spring.myapp.service.TestTableService;
@RestController
public class TestTableController {
@Resource
private TestTableService testtableService;
@RequestMapping(value = "list")
public ModelAndView AllListView(
@RequestParam(value = "currentPage", required = false, defaultValue = "1") int currentPage,
@RequestParam(value = "cntPerPage", required = false, defaultValue = "10") int cntPerPage,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") int pageSize,
Map<String, Object> map, HttpServletRequest request) throws Exception {
ModelAndView mav = new ModelAndView();
int listCnt = testtableService.testTableCount();
Pagination pagination = new Pagination(currentPage, cntPerPage, pageSize);
pagination.setTotalRecordCount(listCnt);
mav.addObject("pagination",pagination);
mav.addObject("Alllist",testtableService.SelectAllList(pagination));
mav.setViewName("list");
return mav;
}
}
|
cs |
기존 코드에서 수정해서 사용하는 것이기 때문에, 메소드 명칭이나 약간 지저분하긴 하지만 필요 부분을 잘 수정해서 사용하시길 바랍니다.
설명을 돕자면, 우리가 페이지 처리에서 http://localhost:8888/list?currentPage=1&cntPerPage=10&pageSize=10 다음과 같은 파라미터를 통해 페이지를 처리할 것입니다.
( 페이지 화면 처리 전에 다음과 같은 파라미터를 전송해서 페이징을 테스트 해보세요.)
24~26번째코드
해당 코드에서 처음 list 페이지에 들어가면, 기본 파라미터가 없을 것이기 때문에 defaultValue를 통해 기본값을 정해줍니다. 또한 다음과 같은 파라미터를 Request해줍니다.
30번째 코드
해당 데이터의 총 갯수를 받아옵니다.
31~32번째 코드
Pagination 객체를 생성하고 currentPage, cntPerPage, pageSize 의 값을 생성자에 넘겨줍니다.
또한 총 레코드 수에 따른 페이지 처리 method에 데이터의 총 갯수를 전달합니다.
34~35번째 코드
이제 처리된 부분을 화면에 전달해 줍니다.
다음과 같이 전달하여 화면에서 JSTL문을 통해 ${pagination}, ${Alllist} 등으로 받을 수 있습니다.
(여기서 Pagination의 Getter,Setter가 필요합니다.)
View 처리
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
|
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>List View</title>
</head>
<script src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
<style>
table, td, th {
border: 1px solid black;
}
th {
background: #F3F5F5;
}
table {
margin-top: 5%;
margin-left: auto;
margin-right: auto;
text-align: center;
width: 80%;
}
a:link {
color: red;
text-decoration: none;
cursor: pointer;
}
a:visited {
color: black;
text-decoration: none;
}
/* paginate */
.paginate {
padding: 0;
line-height: normal;
text-align: center;
position: relative;
margin: 20px 0 20px 0;
z-index: 1;
}
.paginate .paging {
text-align: center;
}
.paginate .paging a, .paginate .paging strong {
margin: 0;
padding: 0;
width: 20px;
height: 24px;
line-height: 24px;
text-align: center;
color: #848484;
display: inline-block;
vertical-align: middle;
text-align: center;
font-size: 12px;
}
.paginate .paging a:hover, .paginate .paging strong {
color: #DAA520;
font-weight: 600;
font-weight: normal;
}
.paginate .paging .direction {
z-index: 3;
vertical-align: middle;
background-color: none;
margin: 0 2px;
border: 1px solid #777;
border-radius: 2px;
width: 28px;
}
.paginate .paging .direction:hover {
border: 1px solid #C40639;
}
.paginate .paging .direction.prev {
margin-right: 4px;
}
.paginate .paging .direction.next {
margin-left: 4px;
cursor: pointer;
}
.paginate .paging img {
vertical-align: middle;
}
.paginate .right {
position: absolute;
top: 0;
right: 0;
}
.bottom-left, .bottom-right {
position: relative;
z-index: 5;
}
.paginate ~ .bottom {
margin-top: -50px;
}
.bottom select {
background: transparent;
color: #aaa;
cursor: pointer;
}
/* paginate */
.paginate {
padding: 0;
line-height: normal;
text-align: center;
position: relative;
margin: 20px 0 20px 0;
}
.paginate .paging {
text-align: center;
}
.paginate .paging a, .paginate .paging strong {
margin: 0;
padding: 0;
width: 20px;
height: 28px;
line-height: 28px;
text-align: center;
color: #999;
display: inline-block;
vertical-align: middle;
text-align: center;
font-size: 14px;
}
.paginate .paging a:hover, .paginate .paging strong {
color: #C40639;
font-weight: 600;
font-weight: normal;
}
.paginate .paging .direction {
z-index: 3;
vertical-align: middle;
background-color: none;
margin: 0 2px;
}
.paginate .paging .direction:hover {
background-color: transparent;
}
.paginate .paging .direction.prev {
margin-right: 4px;
}
.paginate .paging .direction.next {
margin-left: 4px;
}
.paginate .paging img {
vertical-align: middle;
}
.paginate .right {
position: absolute;
top: 0;
right: 0;
}
</style>
<body>
<div>
<table>
<thead>
<tr>
<th>userCode</th>
<th>userName</th>
</tr>
</thead>
<tbody>
<c:choose>
<c:when test="${fn:length(Alllist) > 0}">
<c:forEach items="${Alllist}" var="Alllist">
<tr>
<td>${Alllist.USERCODE}</td>
<td>${Alllist.USERNAME}</td>
</tr>
</c:forEach>
</c:when>
<c:otherwise>
<tr>
<td colspan="4">조회된 결과가 없습니다.</td>
</tr>
</c:otherwise>
</c:choose>
</tbody>
</table>
</div>
<!--paginate -->
<div class="paginate">
<div class="paging">
<a class="direction prev" href="javascript:void(0);"
onclick="movePage(1,${pagination.cntPerPage},${pagination.pageSize});">
<< </a> <a class="direction prev" href="javascript:void(0);"
onclick="movePage(${pagination.currentPage}<c:if test="${pagination.hasPreviousPage == true}">-1</c:if>,${pagination.cntPerPage},${pagination.pageSize});">
< </a>
<c:forEach begin="${pagination.firstPage}"
end="${pagination.lastPage}" var="idx">
<a
style="color:<c:out value="${pagination.currentPage == idx ? '#cc0000; font-weight:700; margin-bottom: 2px;' : ''}"/> "
href="javascript:void(0);"
onclick="movePage(${idx},${pagination.cntPerPage},${pagination.pageSize});"><c:out
value="${idx}" /></a>
</c:forEach>
<a class="direction next" href="javascript:void(0);"
onclick="movePage(${pagination.currentPage}<c:if test="${pagination.hasNextPage == true}">+1</c:if>,${pagination.cntPerPage},${pagination.pageSize});">
> </a> <a class="direction next" href="javascript:void(0);"
onclick="movePage(${pagination.totalRecordCount},${pagination.cntPerPage},${pagination.pageSize});">
>> </a>
</div>
</div>
<!-- /paginate -->
<div class="bottom">
<div class="bottom-left">
<select id="cntSelectBox" name="cntSelectBox"
onchange="changeSelectBox(${pagination.currentPage},${pagination.cntPerPage},${pagination.pageSize});"
class="form-control" style="width: 100px;">
<option value="10"
<c:if test="${pagination.cntPerPage == '10'}">selected</c:if>>10개씩</option>
<option value="20"
<c:if test="${pagination.cntPerPage == '20'}">selected</c:if>>20개씩</option>
<option value="30"
<c:if test="${pagination.cntPerPage == '30'}">selected</c:if>>30개씩</option>
</select>
</div>
</div>
</body>
<script>
//10,20,30개씩 selectBox 클릭 이벤트
function changeSelectBox(currentPage, cntPerPage, pageSize){
var selectValue = $("#cntSelectBox").children("option:selected").val();
movePage(currentPage, selectValue, pageSize);
}
//페이지 이동
function movePage(currentPage, cntPerPage, pageSize){
var url = "${pageContext.request.contextPath}/list";
url = url + "?currentPage="+currentPage;
url = url + "&cntPerPage="+cntPerPage;
url = url + "&pageSize="+pageSize;
location.href=url;
}
</script>
</html>
|
cs |
일일히 설명하기는 어려울 것 같네요.
몇가지 주의 사항만 적고 넘어가겠습니다.
243~251 selectBox 처리
이부분에서 선택 후 현재 값을 selected 가 되어있어야 하기 때문에, c:if 문을 통해 cntPerPage의 값으로 선택할 수 있게 처리하는 부분입니다.
또한 onclick이 아닌 onchange를 쓰셔야 한다는점이 중요합니다.
나머지는 디버깅을 통해 어떤 값이 들어오는지 확인해 보시고, 이에 맞는 계산 처리를 하시면 됩니다.
특별히 어려운 점이 있으시거나 이해가 안가신다면 댓글을 달아주신다면 답변해드리도록 하겠습니다.
* 추가 주의 사항
CSS가 조금 지저분해서 페이지 숫자 클릭 시 살짝 아래 커서포인트가 잡히면 클릭하셔야 이동합니다..ㅎㅎ
죄송합니다.
이상으로 Pagination ( 쪽수매기기 )에 대해 알아보았습니다.
조금 급하게 작성한 감이 있지만, 페이지 처리에서 필요한 대부분 기능이 있기 때문에 이 틀을 가지고 변형하셔서 사용하시기에 무리가 없다고 봅니다.
마지막으로 해당 파일을 업로드 해두겠습니다.
참고하시길 바랍니다.
'Web > SpringBoot-mybatis' 카테고리의 다른 글
SpringBoot Maven Oracle JDBC 연동 시 repository의 지정된 경로를 못 찾을 때 Local에서 Maven 빌드 하는 방법 (0) | 2020.10.28 |
---|---|
Springboot JSP view 설정하기 (0) | 2020.10.28 |
STS SpringBoot-mybatis-maven Oracle 신규 프로젝트 설정 및 DB 연동 Mapper.xml 방식 예제 (11) | 2020.10.14 |