09. 스프링 부트 (Spring Boot) - thymeleaf 페이징 처리 Pageable [미완성]
OS | Windows 10 Home 64bit 버전 2004 (OS 빌드 19041.630) |
Framework | Spring Boot 2.3.3 RERELEASE |
EditTool | Inellij IDEA 2020.1.3 |
BuildTool | Gradle |

# 참고 사이트
스프링 부트, JPA, Thymeleaf를 이용한 페이징 처리 4 - 페이징 구현 (화면)
이제 Controller에서 데이터를 model에 담아 view로 넘겼기 때문에 마지막으로 이전에 생성한 view에서 paging 로직을 개발하면된다. 게시물 리스트 화면에 뿌리기 먼저 게시물 리스트를 화면에 보여주
ivvve.github.io
# API
메소드 | 설명 | 리턴 타입 |
unpaged() | 페이지 매김 설정이 없음을 나타내는 Pageable 인스턴스를 반환합니다 |
Pageable |
isPaged() | 현재 Pageable에 페이지 매기기 정보가 포함되어 있는지 여부를 반환합니다 |
boolean |
isUnpaged() | 현재 {@link Pageable}에 페이지 매기기 정보가 없는지 여부를 반환합니다. |
boolean |
getPageNumber() | 현재 페이지 번호를 리턴합니다 | int |
getPageSize() | 한 페이지당 보여 줄 개수를 리턴합니다 | int |
getOffset() | 기본 페이지 및 페이지 크기에 따라 취할 오프셋을 반환합니다 |
long |
getSort() | 정렬 매개변수를 반환합니다 | Sort |
getSortOr() | 현재Sort 또는 현재 정렬되지 않은 경우 지정된 정렬을 반환합니다. @param 정렬은 null이 아니어야합니다. |
Sort |
next() | 다음 페이지를 요청하는Pageable 을 반환합니다 | Pageable |
previousOrFirst() | 이전 Pageable 또는 현재 페이지가 이미 첫 번째 인 경우 첫 번째 Pageable 를 반환합니다. |
Pageable |
first() | 첫 페이지를 요청하는 @link Pageable 을 반환합니다. | Pageable |
hasPrevious() | 현재 페이지에서 액세스 할 수있는 이전 Pageable 가 있는지 여부를 반환합니다. 현재 Pageable 이 이미 첫 페이지를 참조하는 경우 false 를 반환합니다. |
boolean |


# PageRequest

# Thymeleaf
[결과]

[JAVA 코드]
@Entity @Getter @ToString @NoArgsConstructor public class Board extends AbstractEntity { @Column(name = "title", nullable = false) public String title; @Lob @Column(name = "content", nullable = false) public String content; @Builder(builderMethodName = "create") private Board(String title, String content) { this.title = title; this.content = content; } }
@Controller @RequiredArgsConstructor @Slf4j public class BoardController { private final BoardService boardService; @GetMapping("/board") public String boardPage(Model model, @PageableDefault(page = 0, size = 10, sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { log.info("[GET] boardPage"); Page<Board> findBoards = boardService.findPage(pageable); model.addAttribute("boards", findBoards); return "board/board"; } }
@Service @Transactional @RequiredArgsConstructor public class BoardService { private final BoardRepository boardRepository; public Page<Board> findPage(Pageable pageable) { return boardRepository.findAll(pageable); } }
[thymeleaf 코드]
<div th:if="${!boards.isEmpty()}"> <nav th:with=" pageNumber = ${boards.pageable.pageNumber}, pageSize = ${boards.pageable.pageSize}, totalPages = ${boards.totalPages}, startPage = ${T(Math).floor(pageNumber / pageSize) * pageSize + 1}, tempEndPage = ${startPage + pageSize - 1}, endPage = (${tempEndPage > totalPages ? totalPages : tempEndPage})" aria-label="Page navigation" > <ul class="pagination "> <li th:classappend="${pageNumber + 1 <= pageSize + 1} ? 'disabled'" class="page-item"> <a class="page-link" th:href="@{/board(page=1)}"> <span>«</span> <span class="sr-only">First</span> </a> </li> <li th:classappend="${boards.first} ? 'disabled'" class="page-item"> <a class="page-link" th:href="${boards.first} ? '#' : @{/board(page=${#numbers.formatDecimal(startPage - pageSize, 0, -1)})}" aria-label="Previous"> <!-- 개인적으로 int 로 변환하는 법을 몰라서 이렇게 길어졌습니다 --> <span aria-hidden="true"><</span> <span class="sr-only">Previous</span> </a> </li> <li th:each="page: ${#numbers.sequence(startPage, endPage)}" th:classappend="${page == pageNumber + 1} ? 'active'" class="page-item"> <a th:text="${page}" class="page-link" th:href="@{/board(page=${page})}"></a> </li> <li th:classappend="${boards.last} ? 'disabled'" class="page-item"> <a class="page-link" th:href="${boards.last} ? '#' : @{/board(page=${#numbers.formatDecimal(startPage + pageSize, 0, -1)})}" aria-label="Next"> <span aria-hidden="true">></span> <span class="sr-only">Next</span> </a> </li> <li th:classappend=" ${T(Math).floor(totalPages/10)*10 <= startPage} ? 'disabled'" class="page-item"> <a class="page-link" th:href="@{/board(page=${totalPages})}"> <span>»</span> <span class="sr-only">First</span> </a> </li> </ul> </nav> </div>
[설명]

+ 개인적으로 다른 템플릿 엔진보다 thymeleaf 템플릿 엔진이 어려운 것 같습니다..
# Vue (준비중)
akageun/hello-bbs
Spring Boot & Vue.js를 사용한 게시판 프로젝트입니다. Contribute to akageun/hello-bbs development by creating an account on GitHub.
github.com
daily-life-of-bsh.tistory.com/208
Vue + Spring Pagination(Paging) 구현
Vue와 Spring을 이용하여 Paging을 다음과 같은 페이징을 구현하는 방법을 알아보겠습니다. Vue 프로젝트의 파일 구조와 router.js 의 코드입니다. import Vue from "vue"; import VueRouter from "vue-router";..
daily-life-of-bsh.tistory.com
bezkoder.com/vue-pagination-axios/
Vue Pagination with Axios and API (Server Side pagination) example - BezKoder
In this tutorial, I will show you how to make Pagination in a Vue.js Application with existing API (Server Side pagination) using Axios and Bootstrap-Vue. Related Posts: – Vue.js 2 CRUD Application with Vue Router & Axios – Vue.js JWT Authentication wi
bezkoder.com
[main.js]
import Vue from 'vue' import App from './App.vue' import store from './store' import router from './router' Vue.config.productionTip = false new Vue({ store, router, render: h => h(App), }).$mount('#app')
[http-common.js]
import axios from 'axios' export default axios.create({ baseURL: "/api", headers: { "Content-type": "application/json" } })
[App.vue]
<template> <div id="app"> <router-view/> </div> </template> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } #nav { padding: 30px; } #nav a { font-weight: bold; color: #2c3e50; } #nav a.router-link-exact-active { color: #42b983; } </style> <script> export default { } </script>
[vue.config.js]
module.exports = { /** 서버 설정 */ devServer: { port: 3000, proxy: { '/api/*': { target: 'http://localhost:8080' } } } }
[router.js]
import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') }, { path: '/board', name: 'board', component: () => import('../views/Board') } ] const router = new VueRouter({ mode: 'history', routes }) export default router
[Board.vue]
<template> <div class="board"> <table class="table table-striped"> <thead class="thead-default"> <tr> <th>번호</th> <th>제목</th> <th>날짜</th> </tr> </thead> <tbody> <tr v-for="board in boards" :key="board.id"> <td scope="row">{{ board.id }}</td> <td>{{ board.title }}</td> <td>{{ board.createdAt }}</td> </tr> </tbody> </table> <pagination :page="page" :totalElements="totalElements" :size="size" :first="first" :last="last" :totalPages="totalPages" :pageRange="pageRange" @change="handlePageChange" /> </div> </template> <script> import boardService from "../service/boardService"; import Pagination from "./Pagination"; export default { name: "Board", components: { Pagination }, data() { return { boards: [/* id title createdAt*/], searchTitle: '', page: 1, totalPages: 1, totalElements: 0, size: 10, first: false, last: false, pageRange: [] } }, created() { this.retrieveBoards() }, methods: { getRequestParams(searchTitle, page, pageSize) { let params = {} if (searchTitle) { params["title"] = searchTitle } if (page) { params["page"] = page } if (pageSize) { params["size"] = pageSize } return params }, /** 기본 데이터 */ retrieveBoards() { const params = this.getRequestParams( this.searchTitle, this.page, this.pageSize ); console.log('현재 페이지 번호는 ', this.page) boardService.getAll(params) .then((response) => { const { content, totalElements, number, size, totalPages, first, last } = response.data; this.boards = content; this.totalElements = totalElements; this.page = number + 1 this.size = size this.first = first this.last = last this.totalPages = totalPages console.log(response.data); /** pagination*/ const startPage = Math.floor((this.page -1) / this.size ) * this.size + 1 const tempEndPage = startPage + this.size - 1 const endPage = tempEndPage > this.totalPages ? this.totalPages : tempEndPage let tempArray = [] for (let i = startPage; i <= endPage; i++) { tempArray.push(i) } this.pageRange = [].concat(tempArray) }) .catch((e) => { console.log(e); }); }, /** 페이지 변경 */ handlePageChange(pageNumber) { this.page = pageNumber this.retrieveBoards() }, handlePageSizeChange(event) { this.pageSize = event.target.value; this.page = 1; this.retrieveBoards(); } } } </script>
[Pagination.vue]
<template> <nav aria-label="Page navigation"> <ul class="pagination"> <li class="page-item" :class="firstCss()"> <a class="page-link" href="javascript:void(0)" aria-label="Previous" @click="prevPage()"> <span aria-hidden="true">«</span> <span class="sr-only">Previous</span> </a> </li> <li class="page-item" :class="pageActive(pageNumber)" v-for="pageNumber in pageRange" :key="pageNumber"> <a class="page-link" href="javascript:void(0)" @click="changePage(pageNumber)">{{ pageNumber }}</a> </li> <li class="page-item" :class="lastCss()"> <a class="page-link" href="javascript:void(0)" aria-label="Next" @click="nextPage()"> <span aria-hidden="true">»</span> <span class="sr-only">Next</span> </a> </li> </ul> </nav> </template> <script> export default { name: 'pagination', props: ['page', 'totalElements', 'size', 'first', 'last', 'totalPages', 'pageRange'], methods: { /** 페이지 변환 */ changePage(pageNumber) { this.$emit('change', pageNumber) }, /** 페이지 active CSS */ pageActive(pageNumber) { return pageNumber === this.page ? 'active' : '' }, firstCss() { return this.page < this.size + 1 ? 'disabled' : '' }, lastCss() { return Math.floor(this.totalPages / 10) * 10 <= this.pageRange[0] ? 'disabled' : '' }, prevPage() { this.$emit('change', this.page - this.size) }, nextPage() { this.$emit('change', this.page + this.size) } } } </script>
[boardService.js]
import http from '../http-common' class BoardService { getAll(params) { return http.get('/boards', { params }) } } export default new BoardService()
'10. Spring > BOOT' 카테고리의 다른 글
스프링 부트 MVC - Handler Interceptor (0) | 2022.04.14 |
---|---|
11. 스프링 부트 (Spring Boot 2) & 뷰 (Vue 2)- 환경 구축하기 (0) | 2021.03.26 |
10. 스프링부트 (Spring Boot 2.4.3) - javax Transactional과 spring Transactional (0) | 2021.03.14 |
06. 스프링 부트 (Spring Boot) - 자바 메일 센더 (Java Mail Sender) (0) | 2020.08.29 |
02. 스프링부트 (Spring Boot) Profile, JPA Naming, Exception 전략 (0) | 2020.07.11 |
댓글
이 글 공유하기
다른 글
-
11. 스프링 부트 (Spring Boot 2) & 뷰 (Vue 2)- 환경 구축하기
11. 스프링 부트 (Spring Boot 2) & 뷰 (Vue 2)- 환경 구축하기
2021.03.26 -
10. 스프링부트 (Spring Boot 2.4.3) - javax Transactional과 spring Transactional
10. 스프링부트 (Spring Boot 2.4.3) - javax Transactional과 spring Transactional
2021.03.14 -
06. 스프링 부트 (Spring Boot) - 자바 메일 센더 (Java Mail Sender)
06. 스프링 부트 (Spring Boot) - 자바 메일 센더 (Java Mail Sender)
2020.08.29 -
02. 스프링부트 (Spring Boot) Profile, JPA Naming, Exception 전략
02. 스프링부트 (Spring Boot) Profile, JPA Naming, Exception 전략
2020.07.11
댓글을 사용할 수 없습니다.