쌓고 쌓다

[스프링 부트] 게시글 목록 페이징 - 8 본문

프로그래밍/spring

[스프링 부트] 게시글 목록 페이징 - 8

승민아 2023. 7. 3. 19:12

1. SpringDataJpa로 변경 및 edit 변경 사항

SpringDataJpaPosterRepository (interface)

public interface SpringDataJpaPosterRepository extends JpaRepository<Poster, Long>, PosterRepository {

}

 

PosterService

@Service
@Transactional
public class PosterService {

    private SpringDataJpaPosterRepository posterRepository; // 변경

    @Autowired
    public PosterService(SpringDataJpaPosterRepository posterRepository) { // 변경
        this.posterRepository = posterRepository;
    }
    public void editPoster(Long id, Poster newPoster) { // 변경
        Poster oldPoster = posterRepository.findById(id).get();
        oldPoster.setTitle(newPoster.getTitle());
        oldPoster.setWriter(newPoster.getWriter());
        oldPoster.setContent(newPoster.getContent());
        oldPoster.setRegdate(LocalDateTime.now());
    }
}

 

PosterController

@Controller
public class PosterController {

    @PostMapping("/poster/edit")
    public String edit(@RequestParam(value="id") Long id, @Valid Poster poster, Errors errors) {

        if(errors.hasErrors()) {
            return "posters/editPosterForm";
        }

        posterService.editPoster(id, poster); // 추가
        return "redirect:/posters";
    }
}

=> 서비스단에서 수정을 원하는 게시글 id와 변경할 내용의 poster를 통해 게시글 수정이 이뤄지도록 변경함.

 

 

 

2. 페이징 버튼

PosterService

@Service
@Transactional
public class PosterService {
    ...
    
    public Page<Poster> pageList(Pageable pageable) {
        return posterRepository.findAll(pageable);
    }

}

Repository의 findAll 메소드 파라미터에

Pageable 또는 Pageable의 구현체인 PageRequest를 넣어 사용한다.

넣어준 Pageable에 맞춰 부분 데이터를 받아올 수 있다.

 

PosterController

@Controller
public class PosterController {
 	...

    @GetMapping("/posters")
    public String list(Model model, @PageableDefault(sort="id", value=5, direction = Sort.Direction.DESC) Pageable pageable) {

        Page<Poster> pageList = posterService.pageList(pageable); // 페이징된 데이터 가져옴

        model.addAttribute("posters", pageList);
        model.addAttribute("postersSize", pageList.isEmpty()); // 데이터가 비었는지 체크용

        /* 이전 및 다음 페이지 넘버 가져옴 */
        model.addAttribute("previous", pageable.previousOrFirst().getPageNumber());
        model.addAttribute("next", pageable.next().getPageNumber());

        /* 이전 및 다음 페이지가 존재하는지 체크용 */
        model.addAttribute("hasNext", pageList.hasNext());
        model.addAttribute("hasPrevious", pageList.hasPrevious());

        return "posters/posterList";
    }
}

현재 매개변수에 Pageable를 추가했고 기본 값을 설정했다.

sort : 정렬 기준 값

direction : 정렬 방법

value : 한 페이지에서 보여줄 데이터의 크기

 

posterList.html

<button type="button" th:if=${hasPrevious} th:onclick="|location.href='@{/posters(page=${previous})}'|">이전</button>
<button type="button" th:if="${hasNext}" th:onclick="|location.href='@{/posters(page=${next})}'|">다음</button>

이전 및 다음 페이지를 넘기기 위한 버튼을 추가했다.

th:if로 boolean 값을 체크하여 페이지 이동이 가능하면 버튼 태그가 생기게 했다.

버튼 클릭시 이동 페이지는 previous와 next로 판별한다.

 

이전 및 다음 버튼

3. 페이지 이동 링크

PosterController

@Controller
public class PosterController {

    @GetMapping("/posters")
    public String list(Model model, @PageableDefault(sort="id", value=5, direction = Sort.Direction.DESC) Pageable pageable) {
    
        model.addAttribute("postersSize", pageList.isEmpty());

        int endPage = (int)(Math.ceil((pageable.getPageNumber()+1)/10.0)*10); // 10, 20, 30, ...
        int startPage = endPage-9; // 10개씩 보여주기 -1 = 9
        if(endPage>pageList.getTotalPages())
            endPage=pageList.getTotalPages();

        model.addAttribute("endPage", endPage);
        model.addAttribute("startPage", startPage);

        return "posters/posterList";
    }
 
}

위의 PosterController와 중복된 부분의 코드는 생략했으니 참고!

 

posterSize?

사용자가 아래와 같이 URL로 페이지를 조절했을 때를 생각해 봤는데

페이징 된 링크 계산이 꼬여 링크 태그가 생성된다.

방지하고자 가져온 데이터가 존재하는지 유무를 위해 작성했다.

 

endPage?

Pageable은 페이지 시작 번호가 1이 아닌 0부터 시작한다. 이것을 생각하여 현재 페이지에서 보일 끝 페이지 이동 링크를 계산해야 한다.

우리는 10개 페이지씩 링크를 생성하기로 했다.

현재 페이지가 1~10이라면 보이는 마지막 페이징 링크는 10

2~20이라면 20이 보여야 한다.

 

+ JAVA에서 반환값

정수 나누기 정수는 정수, 정수 나누기 double은 double 형이 반환된다.

1/10.0 -> 0.1 -> ceil 함수 -> 1

1/10 -> 0 -> ceil 함수 -> 0

 

startPage?

우리가 보여줄 페이지 링크는 10개씩이다. 그러므로 endPage에서 9를 빼면 보일 첫 링크가 계산된다.

 

*endPage와 startPage는 1부터 시작한다는 기준이다. Pageable은 0부터 시작하므로 뷰에서 뿌리든 뭘 하든 차이를 생각해야 한다.

 

if문?

마지막 페이지는 4인데

우리는 10개씩 페이지 링크를 보이기로 했다.

1~4 페이지 링크만 생성하면 되는데 끝 페이지가 10이라고 계산해 놔서 1~10 페이지 링크를 보이면 안 된다.

계산한 끝 페이지 endPage가 실제 끝 페이지 보다 클 경우 계산한 끝 페이지를 실제 끝 페이지로 갱신하는 과정을 추가했다.

 

 

posterList.html

<th:block th:unless="${posters.isEmpty()}" th:each="num : ${#numbers.sequence(startPage,endPage)}">
    <a th:text="${num}" th:href="@{/posters(page=${num-1})}"></a>
</th:block>

model.attribute로 넘겨받은 데이터... 메소드 실행이 가능하다!~!~!~!

th:block: 반복문 돌릴 때처럼 타임리프 실행을 위한 태그가 필요할 때 block을 사용한다.

th:unless: 가져온 데이터가 비어있을 경우 페이징 링크를 계산 안 하게 했다.

 

${#numbers.sequnce)? 번호 생성할 수 있다.

*우리가 가져온 startPage, endPage는 1부터 시작하는 페이징 번호이다. Pageable로 접근은 0부터 이뤄지므로 num-1이 필요하다.

 

 

+ 기타 메소드

System.out.println(pageList.getTotalPages()); // 총 페이지
System.out.println(pageList.getTotalElements()); // 총 게시글
System.out.println(pageList.getSize()); // 보여줄 게시글 크기

System.out.println(pageable.getPageSize()); // 보여줄 게시글 크기와 동일 (@PageableDefault의 value)
System.out.println(pageable.getOffset()); // 시작 게시글 번호
System.out.println(pageable.getPageNumber()); // 현재 페이지 넘버

 

4. 페이징 정렬 조건 - PageRequest

PosterController

@Controller
public class PosterController {
    
    ...

    @GetMapping("/posters")
    public String list(Model model, @RequestParam(value="page") int page, @RequestParam int size) {
        PageRequest pageable = PageRequest.of(page,size, Sort.by("id").descending());
        Page<Poster> pageList = posterService.pageList(pageable);
        ...
    }
}

URL을 통해 들어온 파라미터로 PageRequest를 생성하여 페이징 기능을 구현할 수 있다.

 

PageRequest.of

PageRequest.of 메소드로 PageRequest를 생성할 수 있다. 생성자를 사용하는 방법과 동일함.

 

PageRequest의 생성자로 두 가지가 존재한다.

  1. PageRequest(int page, int size)
  2. PageRequest(int page, int size, Sort sort)

 

최종 결과

 

 

+ 댓글순, 날짜순 등 정렬 기준을 변경할 수 있도록 해보기

Comments