쌓고 쌓다
[스프링 부트] 게시글 목록 페이징 - 8 본문
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를 생성할 수 있다. 생성자를 사용하는 방법과 동일함.
PageRequest의 생성자로 두 가지가 존재한다.
- PageRequest(int page, int size)
- PageRequest(int page, int size, Sort sort)
최종 결과
+ 댓글순, 날짜순 등 정렬 기준을 변경할 수 있도록 해보기
'프로그래밍 > spring' 카테고리의 다른 글
IoC, DI, 컨테이너 (0) | 2023.07.07 |
---|---|
관심사의 분리 with DI? (0) | 2023.07.06 |
[스프링 부트] 게시글 수정 - 7 (0) | 2023.07.01 |
[스프링 부트] 게시글 작성 유효성 검사 및 폼 데이터 유지 - 6 (0) | 2023.06.30 |
[스프링 부트] 게시글 삭제 - 5 (0) | 2023.06.28 |