웹 개발/Spring Framework
[Spring] 페이징처리를 위해 써먹는 Paging 객체 만들기 및 활용
희랍인 조르바
2019. 5. 18. 18:12
회사 내에서 페이징 객체를 만들어야겠다고 생각한 계기는 특정 고객사에서 특정 메뉴를 주로 사용하는데 이 메뉴가 페이징 처리가 안되어있어 너무 느리다는 불만사항을 자주 들었다. 그 이외 UI 라이브러리 문제도 있었어서(고객사는 이 문제에 대한 불만이 더 많았다.) 새로 회사에서 사용하는 라이브러리로 바꿀 겸 이 메뉴에 대한 페이징 처리를 하기로 마음 먹었다.
기존에는 페이징 처리를 메서드마다 페이지 인덱스와 페이지 사이즈를 계산하고 Map에 담아 return 방식으로 페이징 처리를 하고 있었는데, 이 부분 때문에..
페이징 객체를 만들자!!!
먼저 내가 구성한 건, 페이징 처리를 위한 변수 정하기. 내가 생각한 리스트는 아래와 같다.
- pageIndex : 현재 이 페이지가 몇번째 페이지냐
- pageSize : 페이지 사이즈가 얼마나 되냐? 10개를 보여줄거냐 50개를 보여줄거냐
- startRow : 그럼 DB는 몇번째 데이터부터 찾으면 되냐
- endRow : 그럼 DB는 몇번째 데이터까지 찾으면 되냐
- resultList : DB에서 가져온 리스트 결과를 담아줄 변수
- totalCount : 페이징 인덱스를 매기기 위해 실제 데이터는 몇개가 되냐
코드로 표현해보면?
public class Paging {
private int pageIndex;
private int startRow;
private int endRow;
private int pageSize;
private List<?> resultList;
private int totalCount;
public int getPageIndex() {
return pageIndex;
}
public int getStartRow() {
return startRow;
}
public int getEndRow() {
return endRow;
}
public int getPageSize() {
return pageSize;
}
public List<?> getResultList() {
return resultList;
}
public int getTotalCount() {
return totalCount;
}
}
- ResultList는 어떤 형의 데이터든지 받을 수 있도록 와일드카드(List<?>) 방식을 사용했다.
- 의미없는 setter는 안 만들고 값을 꺼내쓰기 위한 getter만 구현
- 내 생각에 MySQL, Maria 사용자는 변수 endRow는 필요없을 것 같다. 쿼리에서 limit를 사용해 데이터를 가져올 때 페이지 사이즈로 마지막 row를 찾으니까 말이다. 난 oracle도 사용하기 때문에 그에 대비하여 만들었다.
이렇게 기본적인 골격을 짰고, 이제 페이지 인덱스와 페이지 사이즈를 활용하여 startRow와 endRow를 계산할 메서드를 만들어야 한다.
public void handlePaging(int pageIndex, int pageSize) {
this.pageIndex = pageIndex < 1 ? 1 : pageIndex;
this.pageSize = pageSize;
this.startRow = ((pageIndex-1) * pageSize);
this.endRow = startRow + pageSize;
}
- 페이지 인덱스에 0 이하로 들어올 일은 없겠지만, 혹시나 하는 마음에 1 미만의 숫자는 강제로 1부터 시작하도록 만들었다.
- startRow는 페이지 인덱스 1이 들어오면(1페이지), -1을 통해 0, 2 페이지면서 페이지 사이즈가 10이라면 index가 10부터 시작할 수 있도록 했다.
- endRow는 단순하게 startRow에서 페이지 사이즈만큼만 더해주었다.
처리된 데이터들을 Paging객체에 담아줄 수 있는 메서드는.
public void handlePagingList(List<?> resultList, int totalCount) {
this.resultList = resultList;
this.totalCount = totalCount;
}
- DB의 리스트 결과값은 resultList에 담기고, 총 데이터 갯수는 totalCount에 담긴다.
완성된 페이징 객체를 보면 아래처럼 구성된다.
public class Paging {
private int pageIndex;
private int startRow;
private int endRow;
private int pageSize;
private List<?> resultList;
private int totalCount;
// startRow, endRow를 계산한다.
public void handlePaging(int pageIndex, int pageSize) {
this.pageIndex = pageIndex < 1 ? 1 : pageIndex;
this.pageSize = pageSize;
this.startRow = ((pageIndex-1) * pageSize);
this.endRow = startRow + pageSize;
}
// 페이징 처리된 결과값을 담는 메서드
public void handlePagingList(List<?> resultList, int totalCount) {
this.resultList = resultList;
this.totalCount = totalCount;
}
public int getPageIndex() {
return pageIndex;
}
public int getStartRow() {
return startRow;
}
public int getEndRow() {
return endRow;
}
public int getPageSize() {
return pageSize;
}
public List<?> getResultList() {
return resultList;
}
public int getTotalCount() {
return totalCount;
}
}
활용편
Service layer
@Service
public class ZorbaServiceImpl {
private final ZorbaDao zorbaDao;
public ZorbaServiceImpl(ZorbaDao zorbaDao) {
this.zorbaDao = zorbaDao;
}
public Paging findZorbaUsers(UserDto userDto) {
Paging paging = new Paging();
paging.handlePaging(userDto.getPageIndex(), userDto.getPageSize());
// startRow, endRow, pageSize가 계산된 페이징 객체값을 파라미터 변수에 set.
userDto.setPaging(paging);
return zorbaDao.findZorbaUsers(userDto);
}
}
- handlePaging 메서드를 통해 startRow, endRow, pageSize를 계산해서 페이징 객체에 set해둔다.
- 처리가 된 페이징 객체(Paging)을 DAO 메서드에 보내기 위한 파라미터인 UserDto에 담아준다.
- 내부적으로 startRow, endRow, pageSize를 set한다.
Repository layer
@Repository
public class ZorbaDao {
public Paging getSearchAttReqMainMgrList(UserDto userDto) {
Paging paging = new Paging();
// findList라는 list 쿼리를 조회하는 메서드, findOne이라는 하나의 데이터만 가져오는 메서드가 있다고 가정
return paging.handlePagingList(findList("findZorbaUsers",userDto)
,findOne("findZorbaUsers_totalcount", userDto));
}
- 쿼리의 리스트 결과 값을 곧바로 페이징 객체의 resultList에 담고, totalCount 결과 값을 페이징 객체의 totalCount에 담는다.
정리
전체 데이터를 가져와서 리스트에 뿌려주는 것보다 페이징 처리된 메뉴가 빠르다는 걸 몸소 느꼈고, 앞으로 리스트를 보여줘야할 메뉴는 사용자를 위해 페이징 처리가 필수일 것 같다.