1저번장에서는 전체 데이터 수와 해당 페이지의 데이터 수 를 출력했습니다.

이제 추가할 기능은 사용자가 3페이지에서 글을 작성하고 다시 목록으로 돌아가면

보통의 다른 사이트 같은 경우 1페이지로 돌아가지만 3페이지로 페이지를 유지할 수 있는 기능을 해보려고 합니다.

 /board/listPage?page=6&perPageNum=10 , /board/read?page=6&perPageNum=10&bno=118

이런식으로 구현을 할건데요.

이렇게 하기 위해 UriComponenets와 UriComponentsBuilder 클래스를 이용할 것입니다.

 

해당 테스트 코드는  다음과 같습니다.

 

package kr.co.web;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

public class URITestData {
	private static final Logger logger = LoggerFactory.getLogger(URITestData.class);
	
	@Test
	public void uriTest() {
		int page = 4;
		int perPageNum = 10;
		
		UriComponents uriComponets = UriComponentsBuilder.newInstance()
				.path("/board/listPage")
				.queryParam("page", page)
				.queryParam("perPageNum", perPageNum)
				.build();
		
		String uri = "/board/listPage?page=" + page + "&perPageNum=" + perPageNum;
		
		logger.info(uri);
		logger.info(uriComponets.toString());
	}
}

 

이렇게 코드를 작성하고 테스트를 진행 해보면

 

            

다음과 같이 uri가 생성되었습니다.

또 encode()메서드도 제공하는데 URI에 허용되지 않는 값들이 인코딩 됩니다.

 

package kr.co.web;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

public class URITestData {
	private static final Logger logger = LoggerFactory.getLogger(URITestData.class);
	
	@Test
	public void uriTest() {
		int page = 4;
		int perPageNum = 10;
		
		UriComponents uriComponets = UriComponentsBuilder.newInstance()
				.path("/board/listPage")
				.queryParam("page", page)
				.queryParam("perPageNum", perPageNum)
				.queryParam("search", "진연석")
				.build()
				.encode();
		
		String uri = "/board/listPage?page=" + page + "&perPageNum=" + perPageNum;
		
		logger.info(uri);
		logger.info(uriComponets.toString());
	}
}

 

결과는

 

 

다음과 같이 진연석이라는 문자가 인코딩되었습니다.

 

이제 본격적으로 해당 프로젝트에 URIcomponents를 추가할건데요

PageMaker와 Criteria에 makeQuery라는 메서드를 추가해줍니다.

 

public String makeQuery(int page) {
	UriComponents uriComponents = UriComponentsBuilder.newInstance()
			.queryParam("page", page)
			.queryParam("perPageNum", this.cri.getPerPageNum())
			.build()
			.encode();
	return uriComponents.toString();
}

 

'JAVA > blog' 카테고리의 다른 글

페이징 처리(5)  (0) 2020.07.19
페이징 처리(4)  (0) 2020.07.16
페이징 처리(2)  (0) 2020.07.11
페이징 처리(1)  (0) 2020.07.11
게시글 삭제, 수정 기능 추가  (0) 2020.07.09

저번장에서 비즈니스 계층과 영속 계층 수정했습니다.

하지만 controller는 구현하지 않았기 때문에 controller를 구현하도록 하겠습니다.

 

저희가 해야하는 작업은 페이징 작업할 때 화면 하단에 다음과 같은 페이지 번호를 출력해야 합니다.

 

 

- 시작 페이지 번호(StartPage) : 화면상에서 10개의 페이지 번호를 출력한다고 한다면 현재 페이지가 1에서 10사이에 있는 번호라면 시작 페이지는 1이 출력이 되야하고, 현재페이지가 13이면 시작 페이지는 11이 되야 합니다.

- 맨끝 페이지 번호(endPage) : 시작 페이지 번호부터 몇개의 번호를 보여줘야 하는지를 결정해야 합니다. 이때 전체 데이터의 개수가 필요합니다. 예를 들어 전체 데이터가 89개이고 현재 페이지가 3페이지면 시작 페이지 번호는 1이고, 끝페이지번호를 89개이니까 9페이지가 되야겠죠

- 이전 페이지(perv) : 현재 페이지의 시작 페이지가 11페이지면 이전 버튼을 클릭하여 이전 페이지를 조회해야합니다.

- 다음 페이지(next) : 현재 페이지의 끝페이지(10페이지) 이후에 더 많은 데이터가 존재하면 다음 페이지로 이동할 수 있도록 다음페이지로 가는 버튼을 만들어야 합니다.

그리고 현재 페이지이면 현재 페이지라고 사용자가 알기 쉽도록 파란색으로 활성화 시키겠습니다.

이제 페이지 번호를 출력하는 기능을 처리하는 PageMaker 클래스를 생성하겠습니다.

 

PageMaker.java 생성

 

PageMaker 설계를 위해 필요한 데이터를 살펴 보겠습니다.

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

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

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

 

package kr.co.web.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(페이지 당 보여질 게시물의 수)
	private int finalPage; // 맨끝
	private int firstPage; // 맨처음
	
	public PageMaker(Criteria cri) {
		this.cri = cri;
	}
	
	
	public int getDisplayPageCnt() {
		return displayPageCnt;
	}
	
	public void setDisplayPageCnt(int displayPageCnt) {
		this.displayPageCnt = displayPageCnt;
	}
	
	public int getTotalDataCount() {
		return totalDataCount;
	}

	public void setTotalDataCount(int totalDataCount) {
		this.totalDataCount = totalDataCount;
		calcData();
	}
	
	//startPagem endPage, prev, next, firstPagem finalPage 계산
	public void calcData() {
		int page = this.cri.getPage();
		int perPageNum = this.cri.getPerPageNum();
		
		// 현재 페이지가 2면 2/10 = 0.2 Math.ceil객체를 이용하여 무조건 올림 하면 1 * displayPageCnt = 10
		this.endPage = (int)(Math.ceil(page / (double)displayPageCnt) * displayPageCnt);
		
		// endPage - 화면에 보여줄 페이지수를 한뒤 + 1을 하면 된다. 
		this.startPage = (this.endPage - displayPageCnt) + 1;
		
		// 전체 게시물 수를 통한 endPage 
		// 데이터가 89 개면 9페이지까지 출력
		int tempEndPage = (int)(Math.ceil(totalDataCount / (double)perPageNum));
		
		if(this.endPage > tempEndPage) {
			this.endPage = tempEndPage;
		}
		
		this.prev = startPage == 1? false : true;
		this.next = (endPage * perPageNum) <= totalDataCount ? true : false;
		
		// 맨끝
		// +1하는 이유는 저렇게 계산하면 맨끝에서 2번째 페이지가 출력되기때문.
		this.finalPage = (int)(Math.ceil(totalDataCount / (double)perPageNum)+1);
						
		// 맨처음
		this.firstPage = 1;
	}
	
	public int getStartPage() {
		return startPage;
	}
	
	public void setStartPage(int startPage) {
		this.startPage = startPage;
	}
	
	public int getEndPage() {
		return endPage;
	}
	
	public void setEndPage(int endPage) {
		this.endPage = endPage;
	}
	
	public boolean isPrev() {
		return prev;
	}
	
	public void setPrev(boolean prev) {
		this.prev = prev;
	}
	
	public boolean isNext() {
		return next;
	}
	
	public void setNext(boolean next) {
		this.next = next;
	}
	
	public Criteria getCri() {
		return cri;
	}
	
	public void setCri(Criteria cri) {
		this.cri = cri;
	}
	
	public int getFinalPage() {
		return finalPage;
	}
	
	public void setFinalPage(int finalPage) {
		this.finalPage = finalPage;
	}
	
	public int getFirstPage() {
		return firstPage;
	}
	
	public void setFirstPage(int firstPage) {
		this.firstPage = firstPage;
	}
	
}

 

 

PageMaker는 끝페이지를 구하기 위해서 DB로부터 전체 데이터의 개수를  받아와야 합니다.

전체 데이터의 개수를 가져오기 위해 mapper와 service, dao를 추가하겠습니다.

 

 

<!-- 전체 데이터 구하기 -->
<select id="totalCount" resultType="int">
	select count(board_number)
	from board
</select>

 

<!-- BoardService.java -->

public int totalCount(Criteria cri) throws Exception;

 

<-- BoardServiceImpl.java -->

@Override
public int totalCount(Criteria cri) throws Exception {
	return boardDAO.totalCount(cri);
}

 

<!-- BoardDAO.java -->

public int totalCount(Criteria cri) throws Exception;

 

<!-- BoardDAOImpl.java -->

@Override
public int totalCount(Criteria cri) throws Exception {
	return session.selectOne(TOTALCOUNT, cri);
}

 

전체 게시물의 개수를 구하는데 Criteria를 전달하는 것을 볼 수 있는데요

이는 페이징을 구현한 뒤에 검색기능을 추가할 것인데 

예를 들어 '안녕' 이라는 검색을 하면 안녕을 포함하는 게시글이 총 몇개인지도 알아야 하기 때문에

Criteria 값도 나중을 위해 미리 전달하는 것입니다.

 

이제 게시물의 총 개수를 구하는 기능이 잘 구현되었는지 TEST를 해야겠죠?

 

아까 만들었던 pageTest.java에 메서드만 추가하겠습니다.

 

<!-- pageTest.java -->

package kr.co.web;

import java.util.List;

import javax.inject.Inject;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;

import kr.co.web.domain.BoardVO;
import kr.co.web.domain.Criteria;
import kr.co.web.service.BoardService;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

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

 

이제 JUnit TEST를 해보겠습니다.

 

 

정상적으로 게시물의 총 갯수와 10개의 게시물이 무사히 출력됐습니다!!

'JAVA > blog' 카테고리의 다른 글

페이징 처리(4)  (0) 2020.07.16
페이지 처리(3)  (0) 2020.07.16
페이징 처리(1)  (0) 2020.07.11
게시글 삭제, 수정 기능 추가  (0) 2020.07.09
해당 게시물 조회  (0) 2020.07.09

저희는 지금까지 게시글 작성, 상세보기, 수정, 삭제, 게시글 목록을 구현했는데요

현재 게시글 목록은 페이징 기능이 없어서 mysql 서버에서 데이터를 조회해오는 것도 부담이고, 사용자 입장에서도

보기가 그렇게 썩 좋지는 않기 때문에 페이징기능을 추가해보도록 하겠습니다!

 

먼저 BoardMapper에 다음과 같은 쿼리문을 만들어줍니다.

 

<!-- board 페이징 조회 -->
<select id="listPage" resultType="BoardVO">
	select *
	from board
	order by board_number sesc, create_dat dsc
	limit #{dataStart}, #{perPagNum}
</select>

 

이해를 돕기 위해서 예시를 들어보겠습니다.

limit 절을 이용해서 

 

select *
from board
limit 10, 10

 

limit 10, 10의 의미는 11번째 데이터부터 10개를 출력한다! 라는 의미입니다.

dataStart는 (page-1) * perPageNum 입니다.

page가 1이라고 하면 1번 게시글부터 10번 게시글까지 나와야 하겠죠?

page가 2이라고 하면 11번 게시글부터 20번 게시글까지 출력 됩니다.

 

이제 페이징 처리를 도와줄 Critera.java를 작성하겠습니다.

 

Criteria.java 생성

 

Criteria.java는 파라미터 값으로 가져와서 사용할것이기 때문에 kr.co.web.domain패키지의 VO로 사용할 것입니다.

 

package kr.co.web.domain;

public class Criteria {
	
	private int page;
	private int perPageNum;
	
	public Criteria() {
		// TODO Auto-generated constructor stub
		this.page = 1;
		this.perPageNum = 10;
	}
	
	public int getDataStart() {
		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) {
		this.perPageNum = perPageNum;
	}
	
	@Override
	public String toString() {
		return "Criteria [page=" + page + ", perPageNum=" + perPageNum + "]";
	}
	
	
}

 

이 코드에서 꼭 알아야 하는 코드가 있습니다.

Critera.java에서 멤버변수가 없는데 DataStart getter가 있는데요?

이 getDataStart는 mapper에서 작성한 SQL문의  limit부분의 시작데이터인 dataStart를 계산하여 SQL문으로 반환하는 메서드 입니다.

이제 Criteria클래스를 매개변수로 받아야 합니다.

 

 

listPage 메서드 추가

 

<!-- BoardController.java -->

@RequestMapping(value = "/listPage", method = RequestMethod.GET)
public void listPage(Criteria cri, Model model) throws Exception {
	logger.info("listPageGET!!!");
}

 

컨트롤러를 만들었으니 이제 mapper에 추가한 SQL처리를 하기 위한 Service와 Dao를 추가하겠습니다.

 

BoardService, BoardServiceImpl 수정

 

<!-- BoardService.java -->

public List<BoardVO> listPage(Criteria cri) throws Exception;

 

<!-- BoardServiceImpl.java -->

@Override
public List<BoardVO> listPage(Criteria cri) throws Exception {
	return boardDAO.listPage(cri);
}

 

이제 service단을 작성했으니 Dao단도 추가해줘야 합니다.

 

<!-- BoardDAO.java -->

public List<BoardVO> listPage(Criteria cri) throws Exception;

 

<!-- BoardDAOImpl.java -->

private static String LISTPAGE = NS + ".listPage";

@Override
public List<BoardVO> listPage(Criteria cri) throws Exception {
	return session.selectList(LISTPAGE, cri);
}

 

위의 listPage에서 session은 아까 mapper에 추가한 sql문을 실행시켜주는데요

CriteriaVO에 있는 멤버변수로 SQL문에 있는 pageStart 부분은 Criteria의 getDataStart()를 호출해서 채워집니다.

 

이제 Service 객체에서 Dao 객체가 제대로 생성되고 Dao 객체가 아까 mapeer에 있는 쿼리를 제대로 실행하는지 테스트를 해보겠습니다.

page가 1이고, perPageNum가 10일 때 mysql 서버에서 데이터를 잘 가지고 오는지 확인하겠습니다.

 

 

에러를 확인하면 mapper쪽에 문제가 있는 것 같습니다.

저희는 그동안 root-context.xml에 있는 다음과 같은 코드를 사용했지만 CRUD는 잘 출력됐는데 말이죠

아마 테스트코드는 조금 다른것 같습니다.

다시 에러를 보면 BoardMapper.listPage쪽에 문제가 있는것 같습니다. 

저희는 root-context.xml에 다음과 같이 알맞게 설정했습니다만

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <property name="configLocation" value="classpath:/mybatis-config.xml"></property>
    <property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property>
</bean>

 

에러가 발생하니 mapper 경로를 명확하게 해보겠습니다.

 

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <property name="configLocation" value="classpath:/mybatis-config.xml"></property>
    <property name="mapperLocations">
    	<list>
    		<value>classpath:mappers/boardMapper.xml</value>
    	</list>
     </property>
</bean>

 

다시 UnitTest를 진행해보면 

 

 

다음과 같이 잘 출력이 되는것을 확인했습니다!!

이제 다음과 같은 에러가 발생하면 mapper 값을 명확히 하는게 좋을 것 같습니다.

'JAVA > blog' 카테고리의 다른 글

페이지 처리(3)  (0) 2020.07.16
페이징 처리(2)  (0) 2020.07.11
게시글 삭제, 수정 기능 추가  (0) 2020.07.09
해당 게시물 조회  (0) 2020.07.09
게시글 목록 구현  (0) 2020.07.09

+ Recent posts