관리 메뉴

bright jazz music

guestbook : 06. 목록처리(2) 데이터 페이지 처리 본문

Framework/Spring

guestbook : 06. 목록처리(2) 데이터 페이지 처리

bright jazz music 2022. 7. 6. 08:43

● 데이터가 페이지 처리를 할 수 있도록 속성추가

  • 화면에 전달되는 데이터는 PageResultDTO이다.
  • 이를이용해서 화면에서 페이지 처리를 진행한다.

 

화면에서는 PageResultDTO를 이용하여 아래와 같이 구성한다.

  • 페이지 번호를 10개씩 출력
  • 1~10까지는 [이전으로] 버튼 보이지 않도록 처리
  • 10페이지 이후에는 이전으로 가는 링크 생성
  • 마지막 페이지의 링크 계산

 

1. 페이징 처리의 구성 요소

  • 화면에서 시작 페이지(start)
  • 화면에서 끝 페이지 번호(end)
  • 이전/다음 이동 링크 여부(prev, next)
  • 현재 페이지 번호(page)

 

- 페이징 처리를 위한 가장 중요한 정보는 사용자가 보고 있는 page 정보이다.

- 사용자가 5 페이지를 보고 있다면 페이지 번호는 1부터 시작 (pageSize를 10으로 가정)

- 사용자가 19페이지를 보고 있다면 페이지 번호는 11부터 시작 (pageSize를 10으로 가정)

 

페이지를 계산할 때는 끝 번호를 시작 번호보다 먼저 계산하는 것이 수월하다.

끝 번호는 아래와 같이 계산할 수 있다.(pageSize를 10으로 가정)

 

tempEnd = (int)(Math.ceil( 페이지번호 / 10.0)) * 10;

//Math.ceil()은 소수점을 올림으로 처리한다.

// 1페이지의 경우: Math.ceil(0.1) * 10 = 1
// 10페이지의 경우: Math.ceil(1) * 10 = 1
// 11페이지의 경우: Math.ceil(1.1) * 10 = 20

 

 

만일 화면에 10개씩 보여준다면 시작번호는 무조건 임시로 만든 끝 번호에서 9라는 값을 뺀 값이 된다.

 

start = tempEnd - 9;

 

 

끝 번호는 실제 마지막 페이지와 다시 비교할 필요가 있다. 예를 들어 Page<Guestbook>의 마지막 페이지가 33이라면 위의 계산에선 40이 되기 때문에 이를 반영해야 한다. 이를 위해서는 Page<Guestbook>의 getTotalPages()를 이용할 수 있다.

 

totalPage = result.getTotalPages(); //result는 Page<Guestbook>

end = totalPage > tempEnd ? tempEnd : totalPage;

 

 

- 이전(prev)은 시작번호(start)가 1보다 큰 경우라면 존재한다.

- 다음(next)는 realEnd가 끝 번호(End)보다 큰 경우에만 존재하게 된다.

prev = start > 1;

next = totalPage > tempEnd;

 

 

2. 페이징 처리 구성 요소를 ResultDTO에 반영

위 내용을 PageResultDTO 클래스에 반영

//PageResultDTO.java

package com.example.guestbook.dto;

import lombok.Data;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Data
public class PageResultDTO<DTO, EN> {
    // 다양한 곳에서 사용할 수 있도록 제네릭 타입을 이용해서 DTO와 EN 타입을 지정. (EN = Entity)

    //DTO리스트
    private List<DTO> dtoList;

    //총 페이지 번호
    private int totalPage;

    //현재 페이지 번호
    private int page;

    //목록 사이즈
    private int size;

    //시작 페이지 번호, 끝 페이지 번호
    private int start, end;

    //이전, 다음
    private boolean prev, next;

    //페이지 번호 목록
    private List<Integer> pageList;




    //PageResultDTO는 Page<Entity> 타입을 이용해서 생성할 수 있도록 생성자로 작성.
    public PageResultDTO(Page<EN> result, Function<EN, DTO> fn){
        //Function: 엔티티 객체들을 DTO로 변환해 주는 기능

        dtoList = result.stream().map(fn).collect(Collectors.toList());

        totalPage = result.getTotalPages();

        makePageList(result.getPageable());
    }

    private void makePageList(Pageable pageable){
        this.page = pageable.getPageNumber() + 1; //0부터 시작하므로 1추가
        this.size = pageable.getPageSize();

        //temp end page
        int tempEnd = (int)(Math.ceil(page/10.0)) * 10;

        start = tempEnd - 9;

        prev = start > 1;

        end = totalPage > tempEnd ? tempEnd : totalPage;

        next = totalPage > tempEnd;

        pageList = IntStream.rangeClosed(start, end).boxed()
                .collect(Collectors.toList());

    }



    // 이와 같은 제네릭 방식을 적용하면 추후 추가적인 클래스를 작성하지 않고도 목록 데이터를 처리할 수 있다.

    /*
    * PageResultDTO는 List<DTO> 타입으로 DTO 객체들을 보관한다.
    * 그렇기 때문에 Page<Entity>의 내용물 중에서 엔티티 객체를 DTO로 변환하는 기능이 필요하다.
    * 가장 일반적인 형태는 추상 클래스를 이용해서 이를 처리하는 방식이다.
    * 그러한 경우 매번 새로운 클래스가 필요하다는 단점이 있다.
    *
    * 이번 프로젝트의 경우 엔티티 객체의 DTO 변환은 서비스 인터페이스에 정의한
    * entityToDto() 메서드와 별도로 Function객체로 만들어서 처리한다.
    * */

}

 

3. 수정한 내용 확인하기 위해 테스트 코드 수정

PageResultDTO 클래스를 수정했으므로 GuestbookServiceTests.java의 내용도 수정하여 재 테스트

 

//GuestbookServiceTests.java

package com.example.guestbook.service;

import com.example.guestbook.dto.GuestbookDTO;
import com.example.guestbook.dto.PageRequestDTO;
import com.example.guestbook.dto.PageResultDTO;
import com.example.guestbook.entity.Guestbook;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class GuestbookServiceTests {
    @Autowired
    private GuestbookService service;

    @Test
    public void testRegister(){
        //테스트 객체 생성
        GuestbookDTO guestbookDTO = GuestbookDTO.builder()
                .title("Sample title...")
                .content("Sample Content...")
                .writer("user0")
                .build();

        //service.register() 테스트: 테스트 객체 사용
        System.out.println(service.register(guestbookDTO));
    }

    @Test
    public void testList(){ //목록처리 테스트
        PageRequestDTO pageRequestDTO = PageRequestDTO.builder()
                .page(1)
                .size(10)
                .build();

        PageResultDTO<GuestbookDTO, Guestbook> resultDTO = service.getList(pageRequestDTO);

        //테스트에 값 확인 추가
        System.out.println("PREV " + resultDTO.isPrev());
        System.out.println("NEXT " + resultDTO.isNext());
        System.out.println("TOTAL: " + resultDTO.getTotalPage());




        for(GuestbookDTO guestbookDTO : resultDTO.getDtoList()) {
            System.out.println(guestbookDTO);
        }
		
        //추가
        System.out.println("====================================");
        resultDTO.getPageList().forEach(i -> System.out.println(i));
    }
}

 

 

테스트 결과

 

//console


2022-07-08 11:35:47.361  WARN 6164 --- [    Test worker] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2022-07-08 11:35:48.544  INFO 6164 --- [    Test worker] c.e.g.service.GuestbookServiceTests      : Started GuestbookServiceTests in 5.068 seconds (JVM running for 6.892)
Hibernate: 
    select
        guestbook0_.gno as gno1_0_,
        guestbook0_.moddate as moddate2_0_,
        guestbook0_.reg_date as reg_date3_0_,
        guestbook0_.content as content4_0_,
        guestbook0_.title as title5_0_,
        guestbook0_.writer as writer6_0_ 
    from
        guestbook guestbook0_ 
    order by
        guestbook0_.gno desc limit ?
Hibernate: 
    select
        count(guestbook0_.gno) as col_0_0_ 
    from
        guestbook guestbook0_
PREV false
NEXT true
TOTAL: 31
GuestbookDTO(gno=302, title=Sample title..., content=Sample Content..., writer=user0, regDate=null, modDate=null)
GuestbookDTO(gno=301, title=Sample title..., content=Sample Content..., writer=user0, regDate=null, modDate=null)
GuestbookDTO(gno=300, title=Change Title!!!!!!!!!!!!!!!, content=change content!!!!!!!!!!!!!!!, writer=user0, regDate=null, modDate=null)
GuestbookDTO(gno=299, title=Title....299, content=Content...299, writer=user9, regDate=null, modDate=null)
GuestbookDTO(gno=298, title=Title....298, content=Content...298, writer=user8, regDate=null, modDate=null)
GuestbookDTO(gno=297, title=Title....297, content=Content...297, writer=user7, regDate=null, modDate=null)
GuestbookDTO(gno=296, title=Title....296, content=Content...296, writer=user6, regDate=null, modDate=null)
GuestbookDTO(gno=295, title=Title....295, content=Content...295, writer=user5, regDate=null, modDate=null)
GuestbookDTO(gno=294, title=Title....294, content=Content...294, writer=user4, regDate=null, modDate=null)
GuestbookDTO(gno=293, title=Title....293, content=Content...293, writer=user3, regDate=null, modDate=null)
====================================
1
2
3
4
5
6
7
8
9
10
2022-07-08 11:35:49.230  INFO 6164 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2022-07-08 11:35:49.238  INFO 6164 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-07-08 11:35:49.252  INFO 6164 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

See https://docs.gradle.org/7.4.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 20s
6 actionable tasks: 5 executed, 1 up-to-date
오전 11:35:49: Task execution finished ':test --tests "com.example.guestbook.service.GuestbookServiceTests.testList"'.

 

 

 

 

 

 

 

 

 

 

 

 

Comments