모르는게 많은 개발자

[SpringBoot] thymeleaf + Mybatis + PageHelper 페이징 간단예제 본문

스프링

[SpringBoot] thymeleaf + Mybatis + PageHelper 페이징 간단예제

Awdsd 2020. 10. 31. 01:05
반응형

토이 프로젝트를 진행하며 페이징 작업을 해야하는데 PageHelper + thymeleaf + Mybatis로 진행한 정보가 부족한것 같아 직접 포스팅하고자 글을 쓴다. 이 글에는 검색기능까지 포함된 리스트 페이징을 다뤄본다.


1. PageHelper 의존성 추가, application.yml 설정

일반적으로 페이징을 구현한다하면 여러가지 다소 귀찮은 작업들이 들어간다.(DB 데이터 개수, 현재 페이지, 이전 페이지, 다음 페이지 등등)

PageHelper는 이러한 귀찮은 작업들을 다 구현해놓은 Github에 올라와있는 오픈소스다 

github.com/pagehelper/Mybatis-PageHelper

 

pagehelper/Mybatis-PageHelper

Mybatis通用分页插件. Contribute to pagehelper/Mybatis-PageHelper development by creating an account on GitHub.

github.com

일단 프로젝트에 gradle에 pagehelper를 추가해주자

기본적으로 mybatis와 thymeleaf도 사용하기에 두개의 의존성도 같이 추가해준다.

다음은 아래와 같이 application.yml을 설정해준다.

mapper-locations은 resources/mapper 경로에 mapper.xml을 설정했을 때 기준이다.


2. PageHelper

PageHelper는 아래와 같이 paging적용에 필요한 값들을 알아서 만들어 객체화 해주고, 페이지에 따른 리스트 쿼리를 자동으로 생성해 각 페이지에 맞는 데이터를 list에 넣어준다. 


3. 구현

DB Table

DB table은 user라는 이름의 table로 다음처럼 데이터가 들어있다.

Entity

name, email을 넣을 Entity 클래스

//PagingEntity.java
@Data
public class PagingEntity {
    String email;
    String name;
}
SearchDto

검색값 넣을 dto

//SearchDto.java
@Data
public class SearchDto {
    private String keyword;
    private String search;
}
Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cjw.shorturl.paging.PagingRepository">
    <select id="findUser" resultType="com.cjw.shorturl.paging.PagingEntity">
        SELECT email, name
        FROM user
        WHERE 1=1
        <if test="keyword == 'name'">
            AND name LIKE CONCAT('%',#{search},'%')
        </if>
        <if test="keyword == 'email'">
            AND email LIKE CONCAT('%',#{search},'%')
        </if>
    </select>
</mapper>

mybatis mapper작성한다. name과 email만 가져오고, 검색 기능때문에 동적 쿼리를 넣어 name, email검색을 할 수 있다.

Repository

Page<>는 Paging 관련 데이터와 리스트 데이터를 넣는 객체다.

//PagingRepository.java
@Repository
@Mapper
public interface PagingRepository {
    Page<PagingEntity> findUser(SearchDto search);
}
Service

PageHelper.startPage(현재 페이지, 페이지에 출력될 데이터 개수)를 설정하면 mybatis에서 페이지에 맞는 데이터와 설정한 개수로 가져온다.

//PagingService.java
@Service
@RequiredArgsConstructor
public class PagingService {
    private final PagingRepository repository;

    public Page<PagingEntity> getUserList(int pageNo, SearchDto search) throws Exception {
        PageHelper.startPage(pageNo, 10);
        return repository.findUser(search);
    }
}
Controller

이제 Service에서 가져온 데이터를 PageInfo를 통해 프론트에 출력될 데이터(페이지 번호 리스트 등)을 계산해준다.
new PageInfo<>(Page객체, 페이징 번호 개수);

//PagingController.java
@Controller
@RequiredArgsConstructor
@Slf4j
public class PagingController {
    private final PagingService service;

    @GetMapping("/page")
    public String page(@ModelAttribute SearchDto search,
                       @RequestParam(required = false, defaultValue = "1") int pageNum, Model model) throws Exception {
        PageInfo<PagingEntity> p = new PageInfo<>(service.getUserList(pageNum, search), 10);
        model.addAttribute("users", p);
        model.addAttribute("search", search);
        return "/paging";
    }
}
thymeleaf

다음 페이지를 갈 때 기존 검색어를 유지하기 위해 href에 검색데이터를 controller로 계속 보내준다.

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org">
<body>
<!-- 검색 -->
<form action="" method="get">
    <div>
        <select name="keyword" id="keyword">
            <option value="name">이름</option>
            <option value="email">이메일</option>
        </select>
        <input type="text" id="search" name="search">
        <button type="submit">검색</button>
    </div>
</form>

<!-- 리스트 테이블 -->
<table>
    <thead>
    <tr>
        <th scope="col">이름</th>
        <th scope="col">이메일</th>
    </tr>
    </thead>
    <tbody>
    <tr th:each="user : ${users.getList()}">
        <th th:text="${user.getName()}"></th>
        <td th:text="${user.getEmail()}"></td>
    </tr>
    </tbody>
</table>

<!-- 페이징 -->
<nav>
    <ul>
        <!-- getPrePage : 이전 페이지 -->
        <li th:classappend="${users.getPrePage() == 0} ? 'disabled'">
            <!-- href: /page?pageNum=이전페이지&keyword=검색분류&search=검색어 -->
            <a th:href="@{/page(pageNum=${users.getPrePage()}, keyword=${search.getKeyword()}, search=${search.getSearch()})}">Previous</a>
        </li>
        <!-- getNavigateFirstPage() : 페이징의 처음 숫자 getNavigateLastPage : 페이징의 마지막 숫자를 이용해 페이징 숫자 출력 -->
        <!-- href : /page?pageNum=선택한 페이지&keyword=검색분류&search=검색어 -->
        <li th:each="page: ${#numbers.sequence(users.getNavigateFirstPage(), users.getNavigateLastPage())}" th:classappend="${page == users.getPageNum()} ? 'active'">
            <a th:text="${page}" th:href="@{/page(pageNum=${page}, keyword=${search.getKeyword()}, search=${search.getSearch()})}"></a>
        </li>
        <!-- getPrePage : 다음 페이지 -->
        <li th:classappend="${users.getNextPage() == 0} ? 'disabled'">
            <!-- href: /page?pageNum=다음페이지&keyword=검색분류&search=검색어 -->
            <a th:href="@{/page(pageNum=${users.getNextPage()}, keyword=${search.getKeyword()}, search=${search.getSearch()})}">Next</a>
        </li>
    </ul>
</nav>
</body>
</html>

4. 결과

화면 결과를 살펴보면 아래처럼 검색창, 리스트, 페이징으로 나타난다. 예제 알아보기 쉽게 디자인은 고려하지 않았다.

 

 

반응형
Comments