오라클 사용할 때는 해당페이지의 첫 글번호와 마지막 글번호를 구해 조건절 안에서 between a and b로 접근
MySQL에서는 limit과 offset속성을 이용
10개씩 자료를 보여주되, 총 5개 단위로 페이지 번호가 출력된다.
오라클-jsp프로젝트에서 페이징 처리했던 걸 기반으로 쿼리쪽만 바꿔 스프링 프로젝트에서 페이징을 시도해보았다. 따라서 비효율적인 방법일 확률 큼
View
1. 페이징 요소 기본
① startNum : 현재 화면에서 시작 페이지번호
페이지번호는 한 화면에 5페이지 단위로 출력할 계획이므로, startNum은 1, 6, 11, ...가 된다.
<c:set var ="page" value="${(empty param.p)?1:param.p}"/> 삼항연산자 이용 : 현재 쿼리스트링으로 넘어온 page번호가 있다면 해당 값을 page에 담고, 없다면 1을 기본값으로 설정
<c:set var="startNum" value="${page-(page-1)%5}"/> 등차수열 이용 : 자세한 내용 아래 참고
예시를 통한 이해 16 17 18 19 20 startNum을 구하는 식을 이해하기 위해서 다음과 같은 전제가 존재한다. - 현재 페이지는 18 - 첫 페이지는 16 - 첫 페이지를 먼저 구한 후, 첫 페이지에서 +1, 2, 3, 4하여 해당 페이지에서 보여줄 페이지번호를 차례로 구할 수 있다.
page - (page-1) % 5 1. (page-1) % 5 : 현재 페이지와 첫 페이지 간의 거리를 구한다. - 한 화면당 우리는 5개의 페이지번호를 출력할 것이므로 5로 나눈 나머지를 이용한다. ex. 1과 5사이의 간격은 4 6과 9사이의 간격은 3 11과 12사이의 간격은 1 16과 20사이의 간격은 2, .... 현재 페이지를 기준으로 -1한 수를 5로 나누면 첫페이지와의 간격을 구할 수 있다.
2. 현재페이지에서 해당 거리를 차감하면 첫 페이지 번호를 알 수 있다. startNum = page - (page-1) % 5 18(현재페이지) - 17%5 = 첫페이지 (=16)
<c:set var ="page" value="${(empty param.p)?1:param.p}"/> <!-- p는 쿼리스트링으로 넘겨준 p 아래참고 -->
<c:set var="startNum" value="${page-(page-1)%5}"/>
<!-- el태그안에서 나누기 연산, 실수값으로 변환 -->
<c:set var="lastNum" value="${fn:substringBefore(Math.ceil(count/10),'.')}"/>
② lastNum : 현재 화면에서 마지막 페이지 번호
마지막 페이지 번호는 쿼리 수행 결과 반환된 레코드의 수, 즉 전체 항목의 수에 따라 달려있다.
예를 들어 총 24개의 글만 존재한다면, 마지막 페이지 번호는 3이고 3페이지에는 4개의 항목만이 존재한다.
따라서 lastNum을 단순 startNum + 5로 구할 수 없다.
<c:set var="lastNum" value="${fn:substringBefore(Math.ceil(count/10),'.')}"/> count는 컨트롤러에서 설정해준 모델 변수로, select 쿼리를 실행한 결과 레코드의 수를 의미한다. select count(*) - 라는 쿼리를 따로 실행해서 가져온다.
한 페이지당 10개씩 출력하기 때문에, 레코드 수를 10으로 나눈 값을 다시 Math.ceil 메소드를 통해 올린다. cf. fn 함수에서 나눗셈 값은 자바에서의 나눗셈과 달리 실수값을 반환한다. fn:substringBefore 메소드는 두번째 파라미터로 지정해준 구분자까지 문자열을 끊고 구분자 이전의 내용만을 가져온다.
ex. 37개의 레코드가 존재할 때 - 마지막 페이지 번호는 4페이지이며, 마지막 페이지에는 7개의 항목만이 존재한다 Math.ceil(37/10) = 4.0 -- substringBefore(4.0, '.') = 4
③ p : 현재페이지 name은 "p"로 설정
p는 다른 페이지 번호를 클릭 시 넘겨줄 해당 페이지의 번호를 담는 파라미터이다. 쿼리스트링(a링크)으로 넘겨주는 값으로, 꼭 p라는 이름을 사용할 필요는 없다(사용자 지정 가능)
특정 페이지 번호를 클릭하면 controller의 getmapping 메소드가 해당 페이지번호를 파라미터로 받아 이를 처리하고, 서비스함수 호출 - DAO 호출 - 쿼리 실행 - 새롭게 생성된 offset을 바탕으로 얻은 결과 집합 반환 - Controller에서 해당 객체 Model함수에 넣어 전달 - View 출력의 과정을 거쳐 새로운 페이지가 사용자에게 보여진다.
② forEach 반복문과 if 조건문 처리를 통한 나열
앞서 파라미터로 전달받은 값을 매개로<c:set> 태그를 통해 startNum 변수가 정의된다면, 이제 startNum을 활용하여 반복문을 돌리며 5개의 페이지 번호를 나열할 차례다.
더해줄 값 i =0 으로 시작하여 i=4가 될 때까지(즉 5번의 반복횟수) 반복한다. 이때, 조건처리를 통해 현재 페이지에서 보여줄 페이지번호들이 5개보다 적을 때 페이지번호가 lastNum와 같아지는 시점까지만 페이지번호가 표시되도록 한다. (ex. 77개의 레코드가 반환된다면 두번째 블럭에서는 6~8페이지만이 출력되어야 한다.)
1. @RequestParam(name="p", dafaultValue="1") int page
쿼리스트링으로 전달한 페이지 번호를 받아온다
default는 1페이지, 이후 페이지 번호를 클릭할 때마다 주소 뒤에 ?p=2와 같이 쿼리스트링이 전달됨
@RequestParam 어노테이션을 굳이 사용하는 이유는, 1) 기본값을 설정하고, 2) 문자열로 넘어온 값을 숫자로 형변환하기 위해서이다. 처음 메뉴 클릭시 페이지번호는 1, 쿼리스트링을 사용할 경우 문자열로 넘어오기 때문에 바로 int로 형변환
2. 서비스함수 호출 시 page를 파라미터로 넘겨준다.
ServiceImpl
1. size, offset 변수를 선언 후 초기화
① int size : 한 페이지에서 보여줄 항목의 수를 변수에 담는다 ② int offset : 건너뛸 자료의 개수를 변수에 담는다.
등차수열을 이용하여 offset을 구한다. an = a + (n-1)d 페이징에서 초항 a=0 | 첫 페이지에서 건너 뛰는 자료 없이 offset=0이기 때문에
offset = 0 + (page-1) * size 건너뛸 자료의 수는 현재 페이지-1 * 페이지 당 항목 수
2. dao 함수를 호출할 때 두 변수를 파라미터로 넘긴다.
DAO
파라미터 받아준 뒤 List<객체> 혹은 List<HashMap<String, Object(String)>>을 리턴
Mapper
1. SQL 사용시 limit, offset을 통해 제어
order by 절 뒤에 오며, 두 가지 방식을 통해 표현 가능하다. a) limit 10 offset 10 : 10개씩 보여주되, 10개의 자료를 건너뛰고 보여준다. 즉 2페이지 (11~20) b) limit 0, 10 : offset을 명시적으로 넣지 않으면 첫번째 오는 숫자가 offset이다. 10개씩 보여주되, 0개의 자료를 건너뛰고 보여준다. 1페이지(1~10)
2. MyBatis : 쿼리 끝에 limit #{offset}, #{limit}을 붙여준다.
offset과 limit값은 DAO에서 파라미터로 넘겨받는다.
offset은 건너뛸 자료의 개수 예를 들어 10개씩 보여주는 페이지에서, 3페이지는 20개를 건너뛰고 21번부터 출력할 것 여기서 limit은 10개, offset은 20개이다. offset은 10(p-1)