본문 바로가기
학습/SPRING

13. 스프링 - Paging

by Elfen Lied 2022. 12. 30.
반응형

블로그에선 프로젝트 파일 트리 위쪽에서 아래쪽 순서로 기록함(프로그램 작성순서 아님)

 


 

- sql

- 게시판 테이블

DROP TABLE BOARD_PAGING;

CREATE TABLE BOARD_PAGING (
    BNO NUMBER(10),
    TITLE VARCHAR2(500) NOT NULL,
    CONTENT VARCHAR2(4000) NOT NULL,
    WRITER VARCHAR2(100) NOT NULL,
    WRITEDATE DATE DEFAULT SYSDATE,
    CONSTRAINT PK_BOARD PRIMARY KEY(BNO)
);

DROP SEQUENCE BNO_PAGING_SEQ;
CREATE SEQUENCE BNO_PAGING_SEQ;

COMMIT;

SELECT * FROM BOARD_PAGING;

 

- 게시판 데이터 삽입 sql

DECLARE
    I INT;
BEGIN
    FOR I IN 1 .. 412
    LOOP
        INSERT INTO BOARD_PAGING(BNO, TITLE, CONTENT, WRITER)
        VALUES(BNO_PAGING_SEQ.NEXTVAL, '제목임.'||I, '내용임.'||I, '관리자');
    END LOOP;

END;

 


 

- pom.xml

<dependencies>
	
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.22.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.22.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.2.22.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jdbc</artifactId>
        <version>10.1.4</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>com.oracle.database.jdbc</groupId>
        <artifactId>ojdbc6</artifactId>
        <version>11.2.0.4</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>

</dependencies>

 


 

- src/main/java

- com.jafa.config 패키지 RootConfig, ServletConfig, WebConfig 클래스 생성

 

- RootConfig

package com.jafa.config;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RootConfig {
	
	@Bean
	public DataSource dataSource() {
		DataSource dataSource = new DataSource();
		dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
		dataSource.setUrl("jdbc:oracle:thin:@localhost:1521:XE");
		dataSource.setUsername("springdb");
		dataSource.setPassword("1234");
		return dataSource;
	}
}

 

 

- ServletConfig

package com.jafa.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
@ComponentScan("com.jafa")
public class ServletConfig implements WebMvcConfigurer {
	
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp("/WEB-INF/views/", ".jsp");
	}
	
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**")
				.addResourceLocations("/resources/");
	}
}

 

 

- WebConfig

package com.jafa.config;

import javax.servlet.Filter;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] {RootConfig.class};
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[] {ServletConfig.class};
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] {"/"};
	}

	@Override
	protected Filter[] getServletFilters() {
		CharacterEncodingFilter filter = new CharacterEncodingFilter();
		filter.setEncoding("utf-8");
		filter.setForceEncoding(true);
		return new Filter[] {filter};
	}
}

 


 

- com.jafa.domain 패키지 BoardVO, Criteria, Pagination 클래스 생성

 

- BoardVO

package com.jafa.domain;


import java.sql.Date;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BoardVO {
	private Long bno;
	private String title;
	private String content;
	private String writer;
	private Date writeDate;
}

 

 

- Criteria

package com.jafa.domain;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Criteria {
	
	private int pageNum; // 현재 페이지
	private int amount = 10; // 한페이지에 나타낼 게시물수(10)
	
	public Criteria() {
		this(1, 10); // 1페이지 10개씩 페이징
	}
	
}

 

 

- Pagination

package com.jafa.domain;

import lombok.Getter;

@Getter
public class Pagination {
	Criteria criteria;
	private int startPage; // 시작 페이지 버튼 번호
	private int endPage; // 마지막 페이지 버튼 번호
	private int tempEndPage; // 실제 마지막 페이지 버튼 번호
	private int totalCount; // 총 게시물 수
	private int displayPageNum = 10; // 화면에 보여질 페이지 버튼 수
	private boolean prev; // 이전페이지 활성화여부
	private boolean next; // 다음페이지 활성화여부
	
	public Pagination(Criteria criteria, int totalCount) {
		this.criteria = criteria;
		this.totalCount = totalCount;
		int pageNum = criteria.getPageNum();
		
		// 올림(pageNum / displayPageNum) * displayPageNum
		endPage = (int) Math.ceil(pageNum / (double)displayPageNum) * displayPageNum;
		startPage = endPage - displayPageNum + 1;
		tempEndPage = (int) Math.ceil(totalCount / (double)criteria.getAmount());
		System.out.println("끝 페이지 : " + endPage);
		System.out.println("마지막 페이지 : " + tempEndPage);
		prev = startPage != 1;
		next = endPage < tempEndPage;
		
		if(endPage > tempEndPage) endPage = tempEndPage;
	}
/*
	displayPageNum = 10
	1~10 : [1][2]...[9][10]
		endPage = 10
		startPage = 10 - 10 + 1
		
	11~20 : [11][12]...[19][20]
		endPage = 20
		startPage = 20 - 10 + 1

	21~30 : [21][22]...[29][30]
		endPage = 30
		startPage = 30(endPage) - 10(displayPageNum) + 1
	
	올림(페이지번호/버튼수) * 10
	
*/
	
	
}

/*
	displayPageNum = 10 // 화면에 보여질 페이지 버튼 수
	totalCount = 412 // 총 게시물 수
	
	1~10 페이지 :
	[1][2][3][4][5]....[9][10][다음페이지]
  		startPage = [1]
  		endPage = [10]
  		prev = false
  		next = true
  		
	11~20 페이지 :
	[이전페이지][11][12][13][14][15]....[19][20][다음페이지]
  		startPage = [11]
  		endPage = [20]
  		prev = true
  		next = true
  		
	41~50 페이지 :
	[이전페이지][41][42][43][44][415]....[49][50][다음페이지]
  		startPage = [41]
  		endPage = [50]
  		prev = true
  		next = true
  		
  	게시물 수 : 412
  	페이징 처리 수 : 10
  	실제 보여질 페이지
	[이전페이지][41][42]
  		startPage = [41]
  		endPage = 올림(총게시물수/페이징처리수)
  		prev = true
  		next = false
  	
*/

 


 

- com.jafa.controller 패키지 BoardController, HomeController 클래스 생성

 

- BoardController

package com.jafa.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.jafa.dao.BoardDao;
import com.jafa.domain.BoardVO;
import com.jafa.domain.Criteria;
import com.jafa.domain.Pagination;

@Controller
@RequestMapping("/board")
public class BoardController {
	
	@Autowired
	BoardDao dao;
	
	@GetMapping("/list")
	public String list(Criteria criteria, Model model) {
		List<BoardVO> list = dao.list(criteria);
		Pagination pagination = new Pagination(criteria, dao.totalCount());
		model.addAttribute("list", list);
		model.addAttribute("p", pagination);	
		return "/board/list";
	}
	
	@GetMapping("/detail")
	public String detail(@RequestParam("bnumber") Long bno, Model model) {
		
		try {
			BoardVO detail = dao.detail(bno);
			System.out.println(detail);
			model.addAttribute("board", detail);
		} catch (EmptyResultDataAccessException e) {
			System.out.println("장난no");
			return "/error/notFoundArticle";
		}
		return "/board/detail";
	}
}

 

 

- HomeController 

package com.jafa.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
	
	@GetMapping("/")
	public String home() {
		return "index";
	}
}

 


 

- com.jafa.dao 패키지 BoardDao 클래스 생성

 

- BoardDao

package com.jafa.dao;

import java.util.List;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import com.jafa.domain.BoardVO;
import com.jafa.domain.Criteria;

@Repository
public class BoardDao {
	JdbcTemplate jdbcTemplate;
	
	@Autowired
	public BoardDao(DataSource dataSource) {
		jdbcTemplate = new JdbcTemplate(dataSource);
	}
	
	RowMapper<BoardVO> rowMapper = 
		(rs, rowNum) ->
		BoardVO.builder()
			.bno(rs.getLong("bno"))
			.title(rs.getString("title"))
			.content(rs.getString("content"))
			.writer(rs.getString("writer"))
			.writeDate(rs.getDate("writeDate")).build();

	
	public List<BoardVO> list(Criteria criteria) {
		String sql = "SELECT ROWNUM, BNO, TITLE, CONTENT, WRITER, WRITEDATE";
		sql += " FROM (SELECT /*+INDEX_DESC(BOARD_PAGING PK_BOARD)*/ "; 
		sql += " ROWNUM AS RN, BNO, TITLE, CONTENT, WRITER, WRITEDATE "; 
		sql += " FROM BOARD_PAGING WHERE ROWNUM <= ?) WHERE RN > ? "; 
		int maxRow = criteria.getPageNum() * criteria.getAmount();
		int minRow = (criteria.getPageNum() - 1) * criteria.getAmount();
		
		return jdbcTemplate.query(sql, rowMapper, maxRow, minRow);
	}
	
	public int totalCount() {
		return jdbcTemplate.queryForObject("select count(*) from board_paging", Integer.class);
	}
	
	public BoardVO detail(Long bno) {
		String sql = "select * from board_paging where bno=?";
		return jdbcTemplate.queryForObject(sql, rowMapper, bno);
		
	}
}

 


 

- src/main/webapp/WEB-INF/views/ index.jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file="layout/header.jsp" %>

<div class="container">		
    <h1>메인페이지</h1>	
</div>

<%@ include file="layout/footer.jsp" %>

 


 

- src/main/webapp/WEB-INF/views/ layout, board, error 폴더 생성


- layout/header.jsp, footer.jsp

 

- header.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>

<link rel="stylesheet" href="${contextPath}/resources/css/style.css">
<script src="${contextPath}/resources/js/main.js"></script>
</head>
<body>

 

- footer.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<footer>footer</footer>
</body>
</html>

 


 

- board/list.jsp, detail.jsp

 

- list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ include file="../layout/header.jsp" %>

<div class="jumbotron bg-warning my-3 text-center">
	<h1>페이징 처리 연습</h1>
</div>
<div class="container">
	<table class="table">
		<tr>
			<th>번호</th>
			<th>제목</th>
			<th>작성자</th>
			<th>작성일</th>
		</tr>
		<c:forEach items="${list}" var="b">
			<tr>
				<td>${b.bno}</td>
				<td>
					<a href="${contextPath}/board/detail?bnumber=${b.bno}">${b.title}</a>
				</td>
				<td>${b.writer}</td>
				<td>${b.writeDate}</td>
			</tr>
		</c:forEach>
	</table>
	<ul class="pagination d-flex justify-content-center">
		<c:if test="${p.prev}">
			<li class="page-item"><a class="page-link" href="?pageNum=${p.startPage-p.displayPageNum}">이전페이지</a></li>
		</c:if>
		
		<c:forEach begin="${p.startPage}" end="${p.endPage}" var="pageBtn">
			<li class="page-item ${pageBtn==param.pageNum ? 'active' : ''}">
				<a class="page-link" href="?pageNum=${pageBtn}">${pageBtn}</a>
			</li>
		</c:forEach>
		
		<c:if test="${p.next}">
			<li class="page-item">
				<a class="page-link" href="?pageNum=${p.endPage+1}">다음페이지</a>
			</li>
		</c:if>
	</ul>
</div>

</body>
</html>

 

 

- detail.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%@ include file="../layout/header.jsp" %>

<div class="container">
	<table class="table">
		<tr>
			<th>제목</th>
			<td colspan="3">${board.title}</td>
		</tr>				
		<tr>
			<th>내용</th>
			<td colspan="3">${board.content}</td>
		</tr>				
		<tr>
			<th>작성자</th>
			<td>${board.writer}</td>
			<th>작성일</th>
			<td>${board.writeDate}</td>
		</tr>
		<tr>
			<td colspan="4">
				<button class="btn btn-primary">수정</button>
				<button class="btn btn-danger">삭제</button>
				<button class="btn btn-dark">목록</button>
			</td>
		</tr>				
	</table>
</div>
</body>
</html>

 


 

- error/notFoundArticle.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>잘못된 요청입니다.</h1>

</body>
</html>

 


프로젝트 시작하고 http://localhost:8090/board_paging/board/list 주소창에 요청

첫페이지에는 이전페이지 버튼이 없고

마지막 페이지는 게시물의 수에따라 페이지 수가 표시되고 다음페이지 버튼 없음

 

 

 

 

detail(상세) 페이지

 

주소창 뒤에 쿼리스트링을 없는 요청을 보내보자

bnumber=에 4130000000번째 글은 없다.

요청해보면

notFoundArticle.jsp 쪽으로 가서 에러 페이지를 띄워준다.

반응형

댓글