먼저 Repository의 기존에 List인터페이스 형을반환하던 findAllDesc 기능이
Page인터페이스를 구현한 객체를 리턴하게 하고, Pageable을 파라미터로 제공합니다.
Page인터페이스는 객체의 리스트의 서브리스트에 대한 인터페이스입니다. Page객체를 통하여 전체 리스트에서의 위치를 파악할 수 있습니다.
Page는 Slice 인터페이스를 확장했는데요, Slice인터페이스는 이전, 이후 슬라이스의 존재에 대해 알 수 있는 기능을 구현해야하기에, Pageable이 슬라이스의 전후에 대해 요청을 할 수 있습니다.
Pageable은 페이징 정보를 담고있는 인터페이스입니다.
public interface PostsRepository extends JpaRepository<Posts,Long> {
@Query("SELECT p FROM Posts p ORDER BY p.id DESC")
Page<Posts> findAllDesc(Pageable pageable);
}
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
private final UserRepository userRepository;
private final UserDetailService userDetailService;
.
.
.
.
/* 페이징 1 */
@Transactional(readOnly = true) //트랜젝션 범위는 유지하나, 조회 기능만 남겨서 조회 속도를 개선함
public Page<Posts> findAllDesc(Pageable pageable) {
return postsRepository.findAllDesc(pageable);
}
}
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
private final HttpSession httpSession;
/* 페이징 1 */
@GetMapping("/")
public String index(Model model, @PageableDefault(size = 3)Pageable pageable) {
Page<Posts> list = postsService.findAllDesc(pageable);
model.addAttribute("posts", list);
model.addAttribute("prev", pageable.previousOrFirst().getPageNumber());
model.addAttribute("next", pageable.next().getPageNumber());
model.addAttribute("hasNext", list.hasNext());
model.addAttribute("hasPrev", list.hasPrevious());
SessionUser user = (SessionUser) httpSession.getAttribute("user");
if (user != null) {
model.addAttribute("userName", user.getName());
}
return "index"; // 머스태치 스타터가 앞의 경로 src/main/resources/templates로, 뒤의 확장자 .mustache 로 자동 지정
}
.
.
.
}
앞서서 Pageable이 페이징에 대한 정보를 담고 있는 인터페이스라고 했죠, 이 정보를 @PageableDefault를 통해 전달하고 있습니다.
- 컨트롤러에 Pageable을 주입할 때 해당 애노테이션을 활용합니다.
prev : 이전 페이지의 페이지번호
next : 다음 페이지의 페이지번호
이들을 모델에 넘겨서 화면에서 이를 통해 페이지를 이동합니다.
hasNext와 hasPrev는 더이상 보여줄 페이지가 없음에도 페이지가 계속 넘어가는 것을 막기 위한 boolean입니다.
{{>layout/header}}
<h1>스프링 부트 테스트</h1>
<div class="col-md-12">
<!-- 로그인 기능 영역 -->
<div class="row">
<div class="col-md-6">
<a href="/posts/save" role="button" class="btn btn-primary">글 등록</a>
{{#userName}} <!-- userName이 있다면 이를 노출 -->
Logged in as : <span id="user"> {{userName}}</span>
<a href = "/logout" class="btn btn-info active" role = "button">Logout</a>
{{/userName}}
{{^userName}} <!--userName이 존재하지 않는 경우 로그인버튼 -->
<a href ="/oauth2/authorization/google"
class = "btn btn-success active" role="button">Google Login</a>
{{/userName}}
</div>
</div>
<br>
<!-- 목록 출력 영역 -->
<table class="table table-horizontal table-bordered">
<thead class="thead-strong">
<tr>
<th>게시글번호</th>
<th>제목</th>
<th>작성자</th>
<th>최종수정일</th>
</tr>
</thead>
<tbody id="tbody">
{{#posts}} <!-- 리스트를 순회, 변수 는 여기서 뽑아낸 객체의 필드, 컨트롤러에서 model 객체를 통해 넘겨줌-->
<tr>
<td>{{id}}</td>
<td><a href="/posts/{{id}}">{{title}}</a></td>
<td>{{userName}}</td>
<td>{{modifiedDate}}</td>
</tr>
{{/posts}}
</tbody>
</table>
<ul class="pagination justify-content-center">
{{#hasPrev}}
<a class="page-link" href="?page={{prev}}">Previous</a>
{{/hasPrev}}
{{#hasNext}}
<a class="page-link" href="?page={{ next }}">Next</a>
{{/hasNext}}
</ul>
</div>
{{>layout/footer}}
모델로 넘겨받은 prev와 next를 통해 페이지를 이동하는 버튼을 만들어줍니다.
만약 이전, 다음 페이지가 없다면 버튼을 보여주지 않음으로써 페이지가 무제한으로 넘어가는 것을 방지합니다,