개발/[Spring] 블로그 만들기

[코드로 배우는 스프링 웹 프로젝트] 13강. MyBatis와 스프링에서 페이징 처리

ee2ee2 2022. 2. 3. 23:09
728x90
반응형

해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다.


MyBatis는 SQL을 그대로 사용할 수 있기 때문에, 앞 게시글에서 공부한 인라인뷰를 이용하는 SQL을 통해 페이징 처리를 진행해 볼 것이다.

여기서, 중효한 점은! 페이징 처리를 위한 SQL을 실행할 때 몇 가지 파라미터가 필요하다.

1) 페이지 번호(pageNum), 2) 한 페이지당 몇 개의 데이터(amount)를 보여줄 것인지가 결정되어야 한다.  이를 하나의 객체로 묶어서 전달하는 방식으로 진행해본다.

 

org.zerock.domaim 패키지 내 Criteria Class 생성하기

Criteria.java

package org.zerock.domain;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Criteria {
	
	private int pageNum;
	private int amount;	//한 페이지에 보일 게시글의 개수
	
	public Criteria() {
		this(1, 20);	//기본 값을 1페이지, 20개로 지정
	}
	
	public Criteria(int pageNum, int amount) {
		this.pageNum = pageNum;
		this.amount = amount;
	}
}

13.1 MyBatis 처리와 테스트

org.zerock.mapper 패키지의 BoardMapper에 Criteria 타입을 파라미터로 사용하는 getListWithPaging() 메소드를 작성한다.

BoardMapper.java (Interface)

package org.zerock.mapper;

import java.util.List;

import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

public interface BoardMapper {
	
	//게시글 전체 조회
	//@Select("SELECT * FROM tbl_board WHERE bno > 0")
	public List<BoardVO> getList();
	
	//게시글의 페이지 번호와 읽어올 개수에 따라 게시글을 조회하는 함수
	public List<BoardVO> getListWithPaging(Criteria cri);
	
    
    ...생략

기존에 만들어둔 src/main/resources의 BoardMapper.xml에 getListWithPaging에 해당하는 태그를 추가한다.

 

BoardMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper 
PUBLIC "-//mtbatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.zerock.mapper.BoardMapper">

<!-- 게시글 전체 조회 -->
<select id="getList" resultType="org.zerock.domain.BoardVO">
	<![CDATA[
		select * from tbl_board where bno > 0 order by cdate desc
	]]>
</select>

<!-- 게시글의 페이지 번호와 읽어올 개수에 따라 게시글을 조회하는 함수 -->
<!-- 아래는 게시글을 40개 읽어와서 rownum이 20이 넘는 게시글 20개만 추출한 결과를 보인다. -->
<select id ="getListWithPaging" resultType="org.zerock.domain.BoardVO">
	<![CDATA[
		select *
		from
			(select /*+ INDEX_DESC(tbl_board pk_board) */
			 		rownum rn, bno, title, content, writer, cdate, udate
			 from tbl_board
			 where rownum <= 40
			)
		where rn > 20
	]]>
</select>

<!-- CDATA 섹션은 XML에서 사용할 수 없는 부등호를 사용하기 위함. XML의 경우 '<,>'는 태그로 인식하는데, 이를 막기 위함이다. -->

..생략

 

실행결과

STS 로그에 출력된 결과
실제 DB에서 검색한 결과와 일치함을 확인


13.1.1 페이징 테스트와 수정

MyBatis의 '#{ }'를 적용하여 원하는 양만큼 페이징 처리를 할 수 있도록 BoardMapper.xml을 수정해보자.

BoardMapper.xml

: pageNum, amount 변수 값을 받아와 게시글을 조회할 수 있다.

<!-- 게시글의 페이지 번호와 읽어올 개수에 따라 게시글을 조회하는 함수 -->
<select id ="getListWithPaging" resultType="org.zerock.domain.BoardVO">
	<![CDATA[
		select *
		from
			(select /*+ INDEX_DESC(tbl_board pk_board) */
			 		rownum rn, bno, title, content, writer, cdate, udate
			 from tbl_board
			 where rownum <= #{pageNum} * #{amount}
			)
		where rn > (#{pageNum} - 1) * #{amount}
	]]>
</select>

XML 설정이 제대로 동작하는지 테스트를 위해 testGetListWithPaging() 메소드를 작성한다.

src/test/java 내 BoardMapperTests.java

package org.zerock.mapper;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {
	
	@Setter(onMethod_ = @Autowired)
	private BoardMapper mapper;
	
	/*
	@Test
	public void testGetList() {
		mapper.getList().forEach(board -> log.info(board));
	}
	*/
	
	@Test
	public void testGetListWithPaging() {
		Criteria cri = new Criteria();
		cri.setPageNum(3);
		cri.setAmount(10);
		
		List<BoardVO> list = mapper.getListWithPaging(cri);
		
		list.forEach(board -> log.info(board));
	}
	
    
    
    ...생략

-> 10개씩 출력되는 페이지의 3번째 페이지 데이터를 보기 위함.

테스트 실행 결과

실제 DB에서 검색한 결과와 일치함을 확인

 

반응형

13.2 BoardController와 BoardService 수정

페이징 처리는 브라우저에서 들어오는 정보들을 기준으로 동작하기 때문에, Controller와 Service단을 파라미터를 받는 형태로 수정해줘야한다.

 

13.2.1 BoardService 수정

BoardService.java

package org.zerock.service;

import java.util.List;

import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

public interface BoardService {
	
	public void register(BoardVO board);
	
	public BoardVO get(Long bno);
	
	public boolean modify(BoardVO board);
	
	public boolean remove(Long bno);
	
	//public List<BoardVO> getList();
	
	public List<BoardVO> getList(Criteria cri);
}

BoardServiceImpl.java

package org.zerock.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.mapper.BoardMapper;

import lombok.AllArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j;


@Log4j
@Service
@AllArgsConstructor
public class BoardServiceImpl implements BoardService{

	@Setter(onMethod_ = @Autowired)
	private BoardMapper mapper;
	
	...생략

	/*
	@Override
	public List<BoardVO> getList() {
		log.info("getList...");
		
		return mapper.getList();
	}
	*/

	@Override
	public List<BoardVO> getList(Criteria cri) {
		log.info("get List With Criteria : " + cri);
		
		return mapper.getListWithPaging(cri);
	}

}

 

src/test/java 내 BoardServiceTests.java 수정

package org.zerock.service;

import static org.junit.Assert.assertNotNull;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
//Java Config
//@ContextConfiguration(classes = {org.zerock.config.RootConfig.class})
@Log4j
public class BoardServiceTests {
	
	@Setter(onMethod_ = @Autowired)
	private BoardService service;
	
	@Test //BoardService 객체가 제대로 주입이 가능한지 확인하는 메소드
	public void testExist() {
		
		log.info("Service 객체 주입 확인 : " + service);
		assertNotNull(service);
	}
	
	    ...생략
	
	/*
	@Test //service를 이용한 전체 게시글 조회 테스트
	public void testgetList() {
		service.getList().forEach(board -> log.info(board));
	}
	*/
	
	@Test //service를 이용한 페이지 및 읽을 게시글 개수에 따른 조회 테스트
	public void testgetList() {
		service.getList(new Criteria(3, 10)).forEach(board -> log.info(board));
	}
    
    
    
    ...생략

13.2.2 BoardController 수정

기존 BoardController의 list() 메소드를 아무런 파라미터 없이 처리되었기 때문에 PageNum과 amount를 처리하기 위해서 메소드 파라미터 부분 수정이 필요하다.

BoardController.java

package org.zerock.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.service.BoardService;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;

@Controller
@Log4j
@RequestMapping("/board/*")
@AllArgsConstructor //생성자를 만들고 자동으로 주입하도록 함
public class BoardController {

	//@AllArgsConstructor를 안쓰려면, 아래와 같이 작성하여 처리
	//@Setter(onMethod_ = {@Autowired}) 
	private BoardService service;
	
	/*
	//전체 리스트 조회 메소드
	@GetMapping("/list")	
	public void list(Model model) {
		log.info("list..");
		
		model.addAttribute("list", service.getList());
	}
	*/
	
	//페이지 및 읽을 게시글 개수에 따른 조회 메소드
		@GetMapping("/list")	
		public void list(Criteria cri, Model model) {
			log.info("list : " + cri);
			
			model.addAttribute("list", service.getList(cri));
		}

	...생략
}

 

src/test/java 내 BoardControllerTests.java 수정

package org.zerock.controller;

import static org.springframework.test.web.client.match.MockRestRequestMatchers.queryParam;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
//Test for Controller
@WebAppConfiguration
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml","file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})

//Java 설정일 때,
// @ContextConfiguration(classes= {org.zerock.config.RootConfig.class, org.zerock.config.ServletConfig.class})
@Log4j
public class BoardControllerTests {
	
	@Setter(onMethod_= {@Autowired})
	private WebApplicationContext ctx;
	
	//가짜 MVC라고 생각하면 됨. 가짜로 URL과 파라미터 등을 브라우저에서 사용하는 것 처럼 만들어서 Contoroller를 실행해 볼 수 있음.
	private MockMvc mockMvc;
	
	@Before
	public void setup() {
		this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
	}
	
	/*
	@Test
	public void testsList() throws Exception{
		log.info(
				mockMvc.perform(MockMvcRequestBuilders.get("/board/list"))
				.andReturn()
				.getModelAndView()
				.getModelMap());
	}
	*/
	
	@Test
	public void testsList() throws Exception{
		log.info(
				mockMvc.perform(MockMvcRequestBuilders.get("/board/list")
				.param("pageNum", "3")
				.param("amount", "10"))
				.andReturn()
				.getModelAndView()
				.getModelMap());
	}
	
	
    
    ...생략

 

다음 장은 페이징 화면 처리에 대해서 작성할 것이다!