[Spring] 20.SpringProject-페이징 처리(2)




페이징 처리 - PageMaker



저번 장에서 페이징 기능을 처리하기 위해 비즈니스 계층과 영속 계층을 수정해줬습니다!

저희가 앞으로 해야할 일은 다음과 같습니다!


  • BoardController에 페이징 기능을 하는 listPage 메소드 추가 (컨트롤러)



  • listPage.jsp 생성 (프리젠테이션 계층)




listPage.jsp를 구현할 때 화면 하단에 페이지 번호를 출력하는 작업이 되게 까다롭습니다!

왜냐하면 현재 페이지(page)와 페이지 당 보여지는 개수(perPageNum)에 따라 페이지 번호를 다르게 출력해야하기 때문입니다!

고려해야하는 부분은 다음과 같습니다!


image




  • 시작 페이지 번호(StartPage) : 예를 들어 화면 상에 10개의 페이지 번호를 출력한다고 했을 때, 현재 페이지가 1에서 10사이의 있는 번호라면 시작 페이지는 1, 현재 페이지가 13이면 시작페이지 11



  • 끝 페이지 번호(endPage) : 시작 페이지 번호부터 몇개의 번호를 보여줘야 하는지를 결정해야함. 이 때 영향을 미치는 것이 전체 데이터의 개수이다. 예를 들어 전체 데이터가 65개이고, 현재 페이지가 5페이지라면 시작 페이지 번호는 1이고, 끝 페이지 번호는 7(65개이므로 7페이지까지는 표시) 돼야 함



  • 이전 페이지 링크(prev) : 현재 페이지의 시작페이지가 1이 아니라면 화면상의 링크를 통해 이전페이지를 조회할 수 있어야만 함



  • 다음 페이지 링크(next) : 현재 페이지의 끝 페이지 이후에 더 많은 데이터가 존재하는 경우 이동이 가능하도록 링크를 제공해야 함




또, 현재 페이지는 위에서 보듯이 파란색으로 활성화도 시켜야합니다!

이처럼 페이지 번호를 출력하기 위해 고려해야할 사항도 많고, 계산해야하는 부분도 많습니다!

따라서 저는 페이지 번호를 출력하는 기능을 처리하는 클래스를 따로 설계하겠습니다!


PageMaker 클래스 생성



페이지 번호를 출력하는 기능을 처리하는 클래스 PageMaker 작성을 위해서 필요한 데이터를 점검해보면 다음과 같습니다!


  • 외부에서 입력되는 데이터 : page, perPageNum (Criteria)



  • DB에서 계산되는 데이터 : totalDataCount(데이터 전체 개수)



  • 계산을 통해 만들어지는 데이터 : startPage(시작페이지), endPage(끝 페이지), prev(이전 버튼 활성화 여부), next(다음 버튼 활성화 여부)




한 번 만들어보죠!


//PageMaker.java


package com.gguri.swp.domain;

public class PageMaker {
	private int displayPageCnt = 10; // 화면에 보여질 페이지 번호 수
	private int totalDataCount; // 실제 게시물 수
	private int startPage; // 현재 페이지 기준 시작 페이지 번호 
	private int endPage; // 현재 페이지 기준 끝 페이지 번호
	private boolean prev; // 이전 버튼 활성화 여부
	private boolean next; // 다음 버튼 활성화 여부
	private Criteria cri; //page(현재 페이지), perPageNum(페이지 당 보여질 게시물의 수)
	
	//생성자
	public PageMaker(Criteria cri) {
		this.cri = cri;
	}
	
	//전체 게시물의 수를 입력 받음 
	public void setTotalCount(int totalDataCount) {
		this.totalDataCount = totalDataCount;
		calcData(); 
	}
	
	//startPage, endPage, prev, next 를 계산
	public void calcData() {
		int page = this.cri.getPage();
		int perPageNum = this.cri.getPerPageNum();
		
		//예: 현재 페이지가 13이면 13/10 = 1.3 올림-> 2 끝페이지는 2*10=20
		this.endPage = (int)(Math.ceil(page/(double)displayPageCnt)*displayPageCnt);
		
        //예: 현재 페이지가 13이면 20-10 +1 = 11 
		this.startPage = (this.endPage-displayPageCnt) + 1;
		
        //실제로 사용되는 페이지의 수
        //예: 전체 게시물 수가 88개이면 88/10=8.8 올림-> 9
		int tempEndPage = (int)(Math.ceil(totalDataCount / (double) perPageNum));
		
		//만약 계산된 끝 페이지 번호보다 실제 사용되는 페이지 수가 더 작으면 실제 사용될 페이지 번호만 보여줌
		if(this.endPage > tempEndPage) {
			this.endPage = tempEndPage;
		}
		
		this.prev = (startPage != 1); // startPage 1이 아니면 false
		this.next = (endPage * perPageNum < totalDataCount); //아직 더 보여질 페이지가 있으면 true 
	}
	
	
	//멤버변수 getter, setter
	...
    
}




복잡하긴 하지만 반복해서 보신다면 충분히 이해하실 수 있습니다!


getTotalCount



PageMaker는 DB로 부터 전체 데이터의 개수를 입력받아야 합니다!

따라서 전체 데이터를 가져오는 쿼리를 작성해서 Mapper에 추가하고 BoardService와 BoardDAO를 수정해 주겠습니다!


<!-- boardMapper.xml -->

<select id="gettotalcount" resultType="int">
	select count(bno)
	from board
	where bno > 0;
</select>




//BoardService.java


package com.gguri.swp.service;
...

public interface BoardService {
	...
	int getTotalCount(Criteria cri) throws Exception;
}




//BoardServiceImpl.java


package com.gguri.swp.service;
...

@Service
public class BoardServiceImpl implements BoardService{
	@Inject
	private BoardDAO boardDAO;
	
    ...
    
	@Override
	public int getTotalCount(Criteria cri) throws Exception {
		return boardDAO.getTotalCount(cri);
	}
}




//BoardDAO.java


package com.gguri.swp.persistence;
...

public interface BoardDAO {
	
	 ...
	 int getTotalCount(Criteria cri) throws Exception; 
}




//BoardDAOImpl.java


package com.gguri.swp.persistence;
...

@Repository
public class BoardDAOImpl implements BoardDAO{
	@Inject
	private SqlSession session;
	
	...
	private static String GETTOTALCOUNT = NS + ".gettotalcount";
	
	...
	@Override
	public int getTotalCount(Criteria cri) throws Exception {
		return session.selectOne(GETTOTALCOUNT,cri);
	}
}




전체 게시물 수를 구하는데 왜 Criteria 를 전달해야하는지 의문점을 가질 수 있는데요~

페이징 기능을 구현하고 나면 후에 검색 기능을 추가할 것인데~ 검색기능은 동적 SQL을 사용해서 구현합니다!

그 때 Criteria의 속성이 필요하기 때문에 미리 전달하는 것입니다!


제대로 게시물 총 개수를 구하는지 Test 까지 해보죠!


//ServiceTest.java


package com.gguri.swp;
...

//Runner 클래스(테스트 메소드를 실행하는 클래스) 를 SpringJUnit4ClassRunner로 함
@RunWith(SpringJUnit4ClassRunner.class)
//location 속성 경로에 있는 xml 파일을 이용해서 스프링이 로딩됨
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/**/root-context.xml")
public class ServiceTest {
	@Inject
	private BoardService service;
	private static Logger logger = LoggerFactory.getLogger(ServiceTest.class);
    
	...
    
	@Test
	public void getTotalCountTest() throws Exception {
		Criteria cri = new Criteria();
		Integer totalCount = service.getTotalCount(cri);
		logger.info("totalCount: "+totalCount.toString());
	}
}




image




전체 게시물의 수가 제대로 나왔음을 볼 수 있습니다!