코코무의 코딩캔버스
[Spring] MyBatis의 페이징 처리 본문
※ 교재 [코드로 배우는 스프링 웹 프로젝트 - 구멍가게 코딩단]을 바탕으로 작성되었습니다.
MyBatis는 SQL을 그대로 사용할 수 있기 때문에 인라인뷰(In-line View)를 이용하는
SQL을 작성하고 필요한 파라미터를 지정하는 방식으로 페이징 처리를 하게 됩니다.
인라인뷰는 SELECT FROM(SELECT FROM)으로 이해하면 됩니다.
페이징 처리를 할 때는 SQL을 실행할 때 몇 가지 파라미터가 필요합니다.
1. 페이지 번호(pageNum)
2. 한 페이지 당 띄울 데이터 개수(amount)
이것들을 별도의 파라미터로 전달할 수도 있겠지만, 아싸리 하나의 객체로 묶어 전달하면 확장성면에서 좋습니다.
domain 패키지에 Criteria(검색 기준) 클래스를 생성합니다.
@Getter //게터 생성용
@Setter //세터 생성용
@ToString //객체 문자로 변환용(출력시)
public class Criteria { //페이징 처리의 필요한 검색 처리 조건용 코드(검색 기준)
private int pageNum; //페이지 번호
private int amount; //목록 개수
public Criteria() {
this(1, 10); //페이지 번호는 1, 목록 개수는 10
}
public Criteria(int pageNum, int amount) {
this.pageNum = pageNum;
this.amount = amount;
}
}
이 클래스의 용도는 pageNum과 amount 값을 함께 전달하는 용도입니다.
생성자로 기본값을 1페이지에 10개로 지정해야 합니다.
MyBatis 처리와 테스트
BoardMapper는 인터페이스와 어노테이션을 사용하기 때문에
페이징 처리와 같이 경우에 따라 SQL 구문 처리가 필요한 상황에서는 복잡하게 작성됩니다.
mapper 인터페이스에는 Criteria 타입을 파라미터로 사용하여 getListWithPaging() 메서드를 씁니다.
public interface BoardMapper {
public List<BoardVO> getListWithPaging(Criteria cri);
}
mapper.xml에는 getListWithPaging에 해당하는 태그를 추가합니다.
<select id="getListWithPaging" mappingType="mapping_list">
<![CDATA[
select bno, title, content, writer, regdate, updatedate
from (
select /*+ INDEX_DESC(tbl_board pk_board) */
rownum rn, bno, title, content, writer, regdate, updatedate
from tbl_board
where rownum <= 20
)
where rn > 10
]]>
</select>
CDATA는 xml에서 사용할 수 없는 부등호를 사용하려고 쓰는 건데,
xml을 사용할 경우 '<, >'는 태그로 인식되어 생기는 문제를 막아줍니다.
테스트 하기 전에 반드시 sql 파일로 가서 쿼리문을 테스트하는 좋은 습관을 들여야 합니다.
sql 파일에서 이상이 없다면 이제 페이징 테스트를 해줍니다.
이 테스트는 xml 설정이 제대로 동작하는지 확인하기 위함입니다.
@Test
public void testPaging() {
Criteria cri = new Criteria();
List<BoardVO> list = mapper.getListWithPaging(cri);
list.forEach(board -> log.info(board));
}
Criteria 클래스에서 생성된 객체인 pageNum과 amount는 각각 1과 10의 값을 가지기 때문에 별도의 파라미터 없이 생성됩니다.
문제 없이 테스트가 진행되었다면 이제 Criteria 객체 내부의 값을 이용해서 SQL이 동작하도록 수정합니다.
mapper.xml로 가서
20을 #{pageNum} * #{amount}로, 10을 (#{pageNum}-1) * #{amount}로 바꿔줍니다.
그리고 다시 테스트 클래스로 넘어가 수정해줍니다.
@Test
public void testPaging() {
Criteria cri = new Criteria();
//10개씩 3페이지
cri.setPageNum(3);
cri.setAmount(10);
List<BoardVO> list = mapper.getListWithPaging(cri);
list.forEach(board -> log.info(board));
}
확인을 위해 Criteria 객체를 생성할 때 파라미터를 추가해보거나 setter를 이용하여 내용을 수정합니다.
Controller와 Service 수정
위에서 했던 걸 요약해보자면,
Criteria 라는 검색 기준 코드를 적은 클래스를 만들고 mapper 인터페이스에 Criteria를 파라미터로 사용하는 메서드를 생성했습니다. 그리고 mapper.xml에서 앞서 만든 메서드와 같은 이름의 아이디로 태그를 추가했습니다.
페이징 처리는 브라우저에서 들어오는 정보들을 기준으로 동작하기 때문에 Controller와 Service 역시 전달되는 파라미터들을 받는 형태로 수정해야 합니다.
1. Service 수정
Service는 Criteria를 파라미터로 처리하도록 Service 인터페이스와 ServiceImpl 클래스를 수정합니다.
1) Service 인터페이스 수정
public List<BoardVO> getList(Criteria cri);
인터페이스이므로 실질적인 동작은 하지 않습니다(구현 클래스에서 함). 때문에 세미콜론으로 마무리 해줍니다.
Criteria 파라미터를 getList 메서드에 담아 List 안의 BoardVO 객체로 넣습니다.
기존의 public List<BoardVO> getList( );는 주석 처리합니다.
2) ServiceImpl 클래스 수정
@Override
public List<BoardVO> getList(Criteria cri){
log.info("get List with criteria: " + cri);
return mapper.getListWithPaging(cri);
}
Service의 getList 메서드에 Criteria를 받아 log로 값을 찍어보기도 하고,
mapper의 getListWithPaging 메서드에 담아 반환합니다.
기존의 public List<BoardVO> getList( ) 메서드는 주석 처리합니다.
3) ServiceTests 클래스 수정 후 테스트
@Test
public void testGetList() { //전체리스트용
// service.getList().forEach(board -> log.info(board));
service.getList(new Criteria(2,10)).forEach(board -> log.info(board));
//서비스 계층에 있는 getList메서드를 실행하면 mapper에 있는 getList가 실행되며 sql문이 동작한다.
//리턴은 list 안의 BoardVO 객체를 가지고 돌아온다.
}
방금 위에서 Service를 수정했기 때문에 service 계층에는 getList 메서드가 존재합니다.
그 메서드를 실행하면 mapper에 있는 getList가 실행되면서 sql문이 동작하게 되는데요,
여기서 우리는 getList 메서드에 새로운 Criteria 객체를 넣었습니다. 그렇다면 그 객체의 값인 2와 10이 mapper의 getList에 있는 sql문(pageNum, amount)에 들어가겠죠?
테스트를 실행시키면 아주 잘 들어간 것을 확인할 수 있겠습니다.
2. Controller 수정
@GetMapping("/list")
public void list(Criteria cri, Model model) {
log.info("list: "+ cri);
model.addAttribute("list", service.getList(cri));
}
list 메서드에 파라미터로 Criteria와 Model 타입의 변수들을 받았습니다.
model 영역에 서비스 계층의 getList(Criteria 변수를 담음)를 대입한 'list'와 cri에 101을 담은 새 PageDTO 객체를 대입한 'pageMaker'를 넣어줍니다.
이제 브라우저에 url을 입력해주면 원하는 대로 일이 진행됩니다.
테스트를 해봅니다.
@Test
public void testList() throws Exception {
log.info(
mockMvc.perform(MockMvcRequestBuilders.get("/board/list") //가짜로 만든 웹브라우저에 get 방식으로 경로 넣음
.param("pageNum", "2")
.param("amount", "7"))
.andReturn() //결과는
.getModelAndView() //model에 있는 데이터를 view 처리하고
.getModelMap()); //결과를 map 컬렉션처럼 표로 받겠다는 의미
}
이번 포스팅에서는 MyBatis를 이용한 스프링의 페이징 처리 방법에 대해 다뤄봤습니다.
다음 포스팅은 페이징 화면 처리를 다뤄보겠습니다.
총총🐰
'Spring' 카테고리의 다른 글
[Spring] AOP와 트랜잭션(@Transactional) (0) | 2024.03.08 |
---|---|
[Spring] AOP에 대하여 (0) | 2024.03.07 |
[Spring] 댓글 처리(REST와 Ajax...스압주의) (0) | 2024.03.07 |
[Spring] 의존성 주입 테스트 (0) | 2024.02.26 |
[Spring] 스프링의 개념과 특징 총정리 (0) | 2024.02.22 |