[Spring] 19.SpringProject-페이징 처리(1)
페이징 처리 - 영속 계층, 비즈니스 계층
지금까지의 저희 게시물 목록 페이지(listAll.jsp)를 보면 전체 게시물이 한 페이지에 모두 표현되었습니다!
보기에 정말 불편하죠! 또 매번 my sql 서버에서 전체 데이터를 조회해오는 것도 너무 부담입니다!
그래서 한 페이지에 일정한 게시물만 표현되는 페이징 기능을 저희 프로젝트에 추가해보겠습니다!
일단 위의 URI 를 보면 page와 perPageNum가 있죠!
page는 현재 페이지를 나타내고, perPageNum은 페이지 당 보여지는 게시물의 개수를 의미합니다~!
이제부터 천천히 구현해볼까요?
Mapper 추가
만약 현재 페이지 page=3 이고, 페이지 당 보여지는 게시물의 개수 perPageNum=10 라고 한다면,
my sql 서버에 있는 board 테이블에서 21번째 데이터부터 10개를 조회해와야합니다!
my sql에서는 limit 구문을 이용하면 됩니다!
select
...
where xxx
order by xxx
limit 시작데이터, 데이터의 개수
시작데이터는 0부터 시작합니다~!
따라서 select 결과 중 21번째 데이터부터 10개 데이터를 가져오려면 다음과 같이 작성합니다.
-- select 결과 중 21번째 데이터부터 10개 데이터를 가져옴
select *
from board
order by bno desc, regdate desc
limit 20,10
이를 이용해서 BoardMapper에 다음 SQL문을 추가해줍니다!
<!-- boardMapper.xml -->
...
<!-- 일정 페이지 씩 조회 -->
<select id="listPage" resultType="BoardVO">
select *
from board
where bno > 0
order by bno desc, regdate desc
limit #{pageStart}, #{perPageNum}
</select>
위에서 pageStart는 (page-1)*perPageNum 입니다!
Criteria 생성
위의 Mapper에서 추가할 때 봤듯이 전체 목록을 페이지 별로 나눠서 보여주려면, page와 perPageNum 값이 필요합니다.
즉, BoardController에서 페이징 기능을 수행하는 메서드는 page와 perPageNum를 매개변수로 받아야하는데요~!
//BoardController.java
package com.gguri.swp.controller;
...
@Controller
@RequestMapping("/board/*")
public class BoardController {
private static final Logger logger = LoggerFactory.getLogger(BoardController.class);
@Inject
private BoardService service;
...
//페이징 기능으로 목록을 보여주는 메서드
@RequestMapping(value = "/listPage", method = RequestMethod.GET)
public void listPage(@RequestParam("page") int page,
@RequestParam("perPageNum") int perPageNum,
Model model) throws Exception{
...
}
}
@RequestParam 으로 매개변수를 받다보니 너무 복잡해집니다! 또, 매개변수가 더 많아진다면 관리하기도 힘들어지구요!
따라서, 파라미터들을 맡아서 관리하는 클래스 Criteria를 만들겠습니다!
//Criteria.java
package com.gguri.swp.domain;
public class Criteria {
private int page;
private int perPageNum;
public Criteria() {
this.page = 1;
this.perPageNum = 10;
}
//pageStart를 반환
public int getPageStart() {
return (this.page - 1)*perPageNum;
}
public int getPage() {
return page;
}
public void setPage(int page) {
if(page <= 0) {
this.page = 1;
}else {
this.page = page;
}
}
public int getPerPageNum() {
return perPageNum;
}
public void setPerPageNum(int perPageNum) {
if(perPageNum <=0 || perPageNum > 100) {
this.perPageNum = 10;
}else {
this.perPageNum = perPageNum;
}
}
@Override
public String toString() {
return "Criteria [page=" + page + ", perPageNum=" + perPageNum + "]";
}
}
여기서 주목할 점은 Criteria 메소드 중에 getPageStart() 가 있죠?? 아까 mapper에 추가한 sql문에 있는 limit의 시작 데이터인 pageStart를 계산해서 반환해주는 메소드입니다!
Criteria 클래스를 매개변수로 받으면 아까의 listPage 메서드는 좀 더 간단해집니다!
//BoardController.java
package com.gguri.swp.controller;
...
@Controller
@RequestMapping("/board/*")
public class BoardController {
private static final Logger logger = LoggerFactory.getLogger(BoardController.class);
@Inject
private BoardService service;
...
//페이징 기능으로 목록을 보여주는 메서드
@RequestMapping(value = "/listPage", method = RequestMethod.GET)
public void listPage(Criteria cri, Model model) throws Exception{
...
}
}
BoardService 수정
이제 본격적으로 아까 boardMapper에 추가한 쿼리를 실행할 수 있도록 BoardService와 BoardDAO를 수정해보겠습니다.
먼저, 인터페이스 BoardService에 listPage 메소드를 추가해줍니다!
//BoardService.java
package com.gguri.swp.service;
...
public interface BoardService {
...
//listPage 메소드 추가
List<BoardVO> listPage(Criteria cri) throws Exception;
}
그리고 BoardService 구현체 BoardServiceImpl에서 listPage를 구현해줍니다!
//BoardServiceImpl.java
package com.gguri.swp.service;
...
@Service
public class BoardServiceImpl implements BoardService{
@Inject
private BoardDAO boardDAO;
//listPage 구현
@Override
public List<BoardVO> listPage(Criteria cri) throws Exception {
return boardDAO.listPage(cri);
}
}
아직 BoardDAO 에는 listPage라는 메소드가 없죠?? 추가하러 가봅시다!
BoardDAO 수정
인터페이스 BoardDAO 에 listPage 메소드를 추가해줍니다!
//BoardDAO.java
package com.gguri.swp.persistence;
...
public interface BoardDAO {
...
//listPage 메소드 추가
List<BoardVO> listPage(Criteria cri) throws Exception;
}
그 다음에 BoardDAO 구현체 BoardDAOImpl에서도 listPage를 구현해줍니다!
//BoardDAOImpl.java
package com.gguri.swp.persistence;
...
@Repository
public class BoardDAOImpl implements BoardDAO{
@Inject
private SqlSession session;
...
private static String LISTPAGE = NS + ".listPage";
...
//listPage 구현
@Override
public List<BoardVO> listPage(Criteria cri) throws Exception {
return session.selectList(LISTPAGE,cri);
}
}
위의 listPage에서 session은 아까 boardMapper에 추가한 sql문을 실질적으로 실행시킵니다.
<!-- boardMapper.xml -->
<select id="listPage" resultType="BoardVO">
select *
from board
where bno > 0
order by bno desc, regdate desc
limit #{pageStart}, #{perPageNum}
</select>
이 때 Criteria를 매개변수로 넘겨주면 pageStart 부분은 Criteria의 getPageStart()를 호출되서 채워집니다!
TEST
페이징 처리를 위해 비즈니스 계층과 영속 계층에 추가해야할 것은 끝났구요~!
서비스객체에서 DAO 객체가 제대로 생성되고 DAO가 아까 추가한 쿼리를 제대로 실행하는지 테스트를 해보겠습니다!
page=1 이고 perPageNum=10 일 때 과연 mysql 서버에서 제대로 데이터를 가져올까요?
//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 listPageTest() throws Exception{
Criteria cri = new Criteria();
cri.setPage(1);
cri.setPerPageNum(10);
List<BoardVO> boards = service.listPage(cri);
for (BoardVO board : boards) {
logger.info(board.getBno()+ ":" + board.getTitle());
}
}
}