코드로 배우는 스프링 웹 프로젝트 보면서 따라함
페이징 처리
- 영속(persistence) 계층, 비즈니스 계층
페이징 처리: 사용자에게 필요한 최소한의 데이터를 전송하기 위해서 전체 데이터 중의 일부분만을 보여주는 방식입니다.
페이징 처리 핵심은 1) 사용자에게 필요한 만큼의 데이터를 전송해야 하고, 2) 서버에서 최대한 빠르게 결과를 만들어 내야 한다는 점입니다.
페이징 처리를 공부할 때는 다음과 같은 단계를 통해서 접근하면 편리합니다.
페이징 처리를 공부할 때는 다음과 같은 단계를 통해서 접근하면 편리합니다.
- URI의 문자열을 조절해서 원하는 페이지의 데이터가 출력되도록 하는 단계
- 목록 페이지 하단에 페이지 번호를 보여주고, 클릭하면 페이지가 이동하는 단계
- 목록 페이지에서 조회나 수정 작업을 한 후에 다시 원래의 목록 페이지로 이동할 수 있게 처리하는 단계
1단계의 처리는 페이지 번호 등을 파라미터로 전달하고, 이를 이용해서 SQL에 적절한 데이터를 뽑아서 보여주는 단계입니다. 이 단계에서 중요한 것은 오직 페이지 번호에 해당하는 데이터를 출력하는 것입니다.
2단계는 목록 페이지의 하단에 페이지 번호를 보여주고, 이를 클릭하면 원하는 페이지로 이동하는 단계입니다. 이때는 1) 원하는 페이지의 데이터를 구하는 작업과 2) 화면 하단에 페이지를 출력해 주는 작업이 필요합니다. 이 단계는 ‘이전(prev)’이나 ‘다음(next)’에 대한 처리, 시작 페이지 번호와 끝 페이지 번호에 대한 계산도 같이 처리됩니다.
<< |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
>> |
3단계는 페이징 처리에서 가장 어려운 목록 페이지에서 클릭을 통해 조회나 수정 작업등을 하고 다시 원래의 목록 페이지를 볼 수 있게 하는 기능입니다. 예를 들어 3페이지에서 어떤 게시물을 조회하고, 다시 ‘목록 가기’ 버튼을 클릭해서 기존의 3페이지를 다시 볼 수 있게 하는 기능입니다. 이 기능이 구현되지 않으면 사용자는 매번 페이지를 이동하고, 조회 한 후에 다시 1페이지에서부터 데이터를 찾아야 하기 때문에 반드시 필요한 기능입니다.
8.1 페이징 처리 방식
페이징 처리의 핵심은 모든 작업에 필요한 리스트를 보는 데 필요한 정보가 같이 유지돼야만 한다는 점입니다. 웹 페이지는 이런 작업을 복잡하게 계산하여 처리합니다.
복잡하게 조회 페이지로 이동하는 이유는 조회 페이지에서 다시 목록으로 이동할 때 필요하기 때문입니다.
이러한 처리를 하기 위해서 jsp 내에서는 꽤나 복잡한 처리 작업이 필요합니다. 이 작업을 하는 방식은 크게 다음과 같이 구분됩니다.
- <a> 태그의 href 속성을 이용해서 직접 이동할 URI를 지정하는 방식
- <form> 태그를 이용해서 링크를 클릭하면 여러 정보를 전달하는 방식
<a> 태그를 이용하는 경우, 최대의 장점은 검색엔진에 노출이 쉬워진다는 점입니다. 대부분의 검색엔진은 웹 페이지의 HTML을 파싱(parsing)해서 해당 게시물 관련 정보를 처리합니다. 모든 게시물에 위와 같이 링크가 처리되면 한 번에 모든 연결정보를 파악할 수 있다는 장점이 생깁니다.
PART2 에서는 이러한 처리를 보다 간결하게 할 수 있도록 링크에는 최소한의 정보를 이용하고, 빠르게 개발할 수 있는 <form> 태그를 이용해서 처리할 것입니다. <form> 태그를 이용하는 방식은 필요한 정보를 클릭하는 경우 <form> 태그 내에 필요한 정보를 담아서 처리하는 방식입니다.
<a> 태그로 모든 링크에 대한 정보를 처리하려면 많은 양의 반복적인 링크 정보가 생성되지만, <form> 태그를 이용하는 경우, 필요한 정보는 <form> 태그 내에서 작성되어서 사용되기 때문에 구현에 있어서는 보다 편리합니다.
8.1.1 페이징 처리의 원칙
페이징 처리에는 반드시 다음과 같은 원칙이 지켜져야만 합니다.
- 페이징 처리는 반드시 GET 방식만을 이용해서 처리합니다.
- 페이지는 다른 사람에게 URI로 전달하는 경우가 많기 때문에, 반드시 GET 방식으로만 처리됩니다.
- 페이징 처리가 되면 조회 화면에서 반드시 ‘목록 가기’가 필요합니다.
- 목록 페이지에서 3페이지를 보다가 특정 게시물을 보았다면, 다시 ‘목록 가기’ 버튼을 눌러서 다시 목록에서 3페이지로 이동하는 기능이 구현되어야 합니다.
- 페이징 처리에는 반드시 필요한 페이지 번호만을 제공합니다.
- 예컨대, 한 페이지에 10개씩 데이터를 출력하는 경우라면, 전체 데이터가 32건이 있을 때에는 4페이지까지만 화면에 출력돼야 합니다. 만일 더 많은 데이터가 있을 때에는 적당한 수의 페이지 번호를 출력하고, 뒤로 가는 화살표 등을 이용해서 보여줘야 합니다.
8.2 페이징 처리 개발에 필요한 지식
페이징 처리를 위해서도 몇 가지 기술적인 내용을 알아야만 합니다.
- 페이징 처리를 위한 SQL
- 데이터 개수 파악을 위한 SQL
- 자바스크립트 혹은 <a> 태그를 통한 이벤트 처리
페이징 처리의 sql은 현재 사용하는 데이터베이스에 따라 처리하는 sql이 달라질 수 있습니다. Sql을 제외하면 가장 복잡한 부분은 계산을 통해서 원하는 페이지의 번호나 링크를 처리하는 작업입니다.
앞서 말했듯이 페이징 처리 개발의 순서는 크게 다음과 같습니다.
- 단순히 페이지 데이터가 화면에 출력하는 작업
- 화면 하단에 페이지 번호가 표시되고, 이를 클릭할 경우 제대로 이동하는 작업
- 조회 페이지에서 목록 가기를 선택하면 보던 페이지 정보를 유지한 채로 이동하는 작업
Mysql의 limit를 이용한 페이지 출력 sql
Sql을 처리할 때 가장 중요한 점은 빠른 데이터의 출력을 위해서 가능하면 결과의 양을 적게 유지하는 것이 좋다는 점입니다. 즉 페이지당 10개의 데이터를 출력한다면, 특정 페이지 번호에 맞는 데이터를 가져올 때 필요한 10개의 데이터만 가져와야만 합니다.
우선 게시물 관리의 특직상 게시물은 작성된 시간의 역순으로 출력됩니다. 앞 장에서 만든 테이블의 경우 게시물 번호인 bno 칼럼이 자동으로 다음 번호를 생성하는 auto-increment로 되어 있으므로, 시간의 역순 대신에 bno의 역순으로 되는 sql 문을 그대로 활용할 수 있습니다.
Mysql의 경우 전체 데이터 중에서 일부분의 데이터만 출력하고 싶은 경우에는 limit 구문을 이용해서 처리합니다.
Ex) 10개씩 데이터를 출력하는 경우
1 page limit 0,10
2 page limit 10,10
만일 2페이지에 해당하는 데이터를 보고 싶다면
Mybatis의 boardDAO 처리
SQL 문으로 페이징의 결과를 미리 보았다면 이제 BoardDAO를 수정해서 실제로 페이지가 나오는지를 확인해 봐야 합니다.
BoardDAO, XML Mapper, BoardDAOImpl의 처리
BoardDAO 인터페이스에 페이징 처리와 관련된 기능을 추가합니다.
resultType
Orz.zerock.persistence.BoardDAOImpl의 일부
DAO의 경우 가장 쉽게 테스트를 진행 할 수 있으므로, 테스트 코드를 작성해서 테스트를 진행합니다.
페이징 처리의 SQL 테스트
화면에 10개씩 데이터를 보여준다고 가정할 때, 사용자가 1페이지를 원한다면 limit 0,10의 구문이 완성되어야 하고, 2페이지를 원한다면 limit 10,10,3페이지를 원한다면 limit 20,10과 같은 형태가 되어야 합니다.
위의 테스트 코드를 통해서 페이지 번호에 맞게 데이터들이 올바르게 출력되는지를 확인해야 합니다.
Sql과 코드에서 신경 쓰이는 부분은 다음과 같이 두 부분입니다.
- 만일 한 페이지에서 보여지는 데이터가 10개가 아니라면 limit 구문의 마지막에 사용되는 10이라는 숫자 역시 변경될 필요가 없습니다.
- 매번 원하는 페이지를 처리할 때마다 계산을 해야 하는 불편함
이런 고민을 해결하기 위해서는 파라미터를 두개 받는 방법이 있습니다.
#pageSize%%30$$pageNum%%3
위와 같이 사용자가 목록 페이지에서 볼 수 있는 개수를 조절하게 되면, 서버에는 ‘페이지 번호 + 목록 페이지에 출력하는 데이터의 개수를 같이 전달합니다.
파라미터로 처리하는 경우 매번 개발자가 2개의 적절한 데이터를 넘겨야 하는 불편함이 있습니다. 일반적인 경우라면 게시물의 성격에 따라서 10개 혹은 20개가 지정되는 것이 일반적이므로 좀 더 편하게 두 개의 파라미터를 하나로 묶어서 활용하는 형태로 사용합니다.
DAO 처리를 도와줄 Criteria 클래스 만들기
Mybatis의 sql mapper에는 공통적인 규칙이 하나 존재하는 데 그것은 #{page}와 같은 파라미터를 사용할 때 내부적으로 page 속성의 getter에 해당하는 getPage() 메소드를 호출한다는 점입니다.
예를 들어 아래와 같은 sql이 존재한다고 가정해 봅니다.
Select * from tbl_board where bno > 0 order by bno desc limit #{pageStart}, #{perPageNum}
위의 sql은 pagestart. perpagenum이라는 인라인 파라미터가 존재합니다. 만일 sql을 실행한다면, 파라미터로 전되는 객체는 getPageStart(), getPerPageNum()이라는 메소드를 가지면 됩니다.
파라미터가 여러 개로 늘어나면 관리하기 어려워지기 때문에 아예 클래스로 만들어서 객체로 처리하는 것이 더 바람직합니다. 이를 위해서 다음과 같은 클래스를 작성해 봅니다.
Orz.zerock.domain.Criteria
//파라미터가 여러 개로 늘어나면 관리하기 어려워지기 때문에 아예 클래스로 만들어서 객체로 처리하는 것이 바람직하다. package org.zerock.domain;
public class Criteria {
private int page; private int perPageNum;
public Criteria() { this.page = 1; this.perPageNum = 10; }
public void setPage(int page) {
if (page <= 0) { this.page = 1; return; } this.page = page;
}
public void setPerPageNum(int perPageNum) { if (perPageNum <= 0 || perPageNum > 100) { this.perPageNum = 10; return; } }
public int getPage() { return page; }
// method for MyBatis SQL Mapper - public int getPageStart() { return (this.page - 1) * perPageNum; }
// method for mybatis sql mapper public int getPerPageNum() { return this.perPageNum; }
@Override public String toString() { return "Criteria [page=" + page + "," + "perPageNum=" + perPageNum + "]"; }
}
|
작성된 criteria 클래스는 사전적인 의미로는 ‘검색 기준, 분류 기준’에 해당합니다. Criteria는 객체의 내부에 page와 perPageNum 속성을 보관하도록 하고, 라인 33, 39를 이용해 XML Mapper에서 사용하는 getter를 제공합니다.
특이한 점은 기본값으로 페이지 번호는 1페이지로 지정하고, 리스트당 데이터의 수는 10으로 지정해서 강제로 부여합니다. 이후에 set 메소드를 이용하면서 사용자가 고의로 잘못 입력할 수 있는 값에 대해 필요한 데이터를 조정할 수 있습니다.
getPageStart()는 limit 구문에서 시작 위치를 지정할 때 사용합니다. 예를 들어 10개씩 출력하는 경우 3페이지의 데이터는 limit 20,10과 같은 형태가 되어야 합니다. 따라서 계싼 공식은 다음과 같습니다.
시작 데이터 번호 = (페이지 번호 -1)*페이지 당 보여지는 개수
getPerPageNum()은 limit 뒤의 숫자를 의미해서 한 페이지당 보여지는 개수를 의미합니다.
8.4.1 BoardDAO 인터페이스의 수정
BoardDAO에 리스트를 출력하는 부분을 아래와 같이 listCriteria()를 추가합니다.
BoardDAOImpl의 수정
Xml mapper를 이용하는 boarddaoimpl은 아래와 같이 메소드를 구현합니다.
XML Mapper의 수정
DAO 인터페이스에 새로운 메소드가 추가됐으므로, XML에도 SQL 문을 추가합니다.
BoardDAOTest에서의 테스트 작업
새롭게 추가된 listCriteria() 메소드를 테스트학 위해서 BoardDAOTest 클래스에 다음과 같은 테스트 코드를 추가합니다.
테스트 코드에서는 가장 먼저 Criteria 타입의 객체를 생성합니다. 페이지 번호는 2페이지가 되고, 라인 88을 지나면 한 페이지당 데이터의 수가 20개로 지정됩니다. 테스트 결과가 정확한지를 확인하는 작업 역시 중요합니다. 위 코드의 경우 20개씩 출력하는, 2페이지이므로 sql의 실행은 limit 20,20이 됩니다.
Mysql의 workbench를 이용해서 20개씩, 2페이지의 결과(limit 20,20)를 확인합니다.
BoardService 수정하기
BoardDAO의 테스트 작업이 완료되면, 이제 서비스 계층을 수정합니다.
구현 클래스인 BoardServiceImpl은 아래와 같이 listCriteria()를 추가해서 구현합니다.
Orz.zerock.service.BoardServiceImpl의 일부
스프링 MVC는 파라미터를 수집하는 기능이 강력하기 때문에 여러 종류의 필요한 데이터를 하나의 클래스로 구성해도 작업의 양이 많이 늘어나지 않습니다. 작성한 Criteria는 필요한 경우 확장해서 사용할 수 있습니다.
굳이 Criteria를 작성하고 싶지 않다면, 파라미터를 두 개 이상 받도록 설계하면 됩니다. 하지만, 검색과 같은 기능이 붙게 되면 점점 전달되는 파라미터의 양이 늘어나게 되므로, 관리가 복잡해질 수 있습니다.
코드로 배우는 스프링 웹 프로젝트 보면서 따라함
'JAVA > spring' 카테고리의 다른 글
paging processing (페이징 처리)- 3 (0) | 2019.08.09 |
---|---|
paging processing(페이징 처리) - 2 (0) | 2019.08.09 |
select Implementation (0) | 2019.08.08 |
spring redirection (0) | 2019.08.07 |
spring redirect (4) | 2019.07.04 |
댓글