위의 이미지와 같이 스크롤이 임의점을 넘어가면 새로운 게시글이 화면에 보여지는 것을 구현하고자 한다.
우선 기존의 로직은 페이지 첫 로드시 모든 게시글 정보를 서버로 부터 받아오고, 그 데이터를 가지고 가공하여 화면에 뿌려주는 방식이었다.
이렇게 로직을 구현하면 페이지를 로드할 때마다 모든 데이터를 보내줘야하니 서버의 부담이 엄청날테고 또, 클라이언트는 지나치게 오래 기다려야 하는 상황을 초래할 수 있다.
이러한 문제점을 해결하고자 페이징 기능을 도입한다.
public interface PostRepository extends JpaRepository<Post, Long> {
Optional<List<Post>> findAllByOrderByCreatedAtDesc(Pageable pageable);
Optional<List<Post>> findByUser(User user);
Optional<List<Post>> findByUserOrderByCreatedAtAsc(User user);
Optional<Post> findByUserAndPostId(User user, Long postId);
}
스프링부트는 아주 간단하게 페이징 기능을 구현할 수 있다.
JpaRepository
에 Pageable
객체를 인자로 넘겨줌으로써 페이징 처리가 된다.
@GetMapping("/public/post/all")
public ResponseEntity<List<Post>> getPosts(@RequestParam String orderBy, @PageableDefault(size = 10, sort = "postId") Pageable pageable){
List<Post> posts;
if (orderBy.equals("createdAt")) {
posts = postService.getPostsOrderByCreatedAtDesc(pageable);
} else {
posts = postService.getPostsOrderByPostLikesAtDesc();
}
return ResponseEntity.ok(posts);
}
API 요청시 Pageable
객체를 파라미터로 추가하기만 하면 된다.
@PageableDefault
설정을 통해 페이징 할 크기, 정렬 기준 등을 기본으로 지정할 수 있으며 해당 내용을 파라미터로 받아온다.
아주 간단하게 백엔드에서 페이징 처리는 끝이났다.
정상적으로 10개의 데이터만 넘어오는 모습이다.
우선 10개 단위로 페이지별로 데이터를 요청하는 로직은 구현을 했으니, 이것을 어떻게 프론트엔드에서 가공 해야할지 생각을 해본다.
- 처음 페이지 로드 시 10개의 게시글을 보여줌.
- 스크롤이 임계점을 넘어가면 다음 페이지를 요청함.
- 새로 받은 데이터를 화면에 뿌려줌.
이렇게 동작한다고 생각하며 리액트 개발을 진행했다.
가장 먼저 사용자의 스크롤을 감지하고 이벤트를 발생시킨다.
window.addEventListener("scroll", () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
const scrollable =
document.documentElement.scrollHeight - window.innerHeight - 100;
const scrolled = window.scrollY;
console.log(scrolled);
if (scrolled > scrollable) {
// 화면의 맨 끝까지 스크롤됨
console.log("맨 끝까지 스크롤됨");
setCount(count + 1);
}
}, 200); // 200ms 딜레이
});
현재 띄워진 창의 스크롤에 이벤트를 더하기 위해서 window
객체에 EventListener
를 추가한다.
현재 창 크기에서 스크롤이 가능한 영역을 scrollable
변수로 선언한 뒤, 스크롤이 scrollable
을 넘어가는 순간 useState
로 관리하는 카운터를 추가한다.
지나친 서버 요청을 방지하기 위한 딜레이를 추가한다.
useEffect(() => {
console.log("API 요청 ");
axios
.get(`/api/public/post/all`, {
params: {
orderBy: orderBy,
page: count,
},
})
.then((res) => {
makePostElement(res.data);
})
.catch((err) => console.log(err));
}, [count, orderBy]);
카운트의 값이 변경되면 서버로 새로운 요청을 보낸다.
파라미터로 현재 카운트의 값을 page
로 보내어 다음 10개의 데이터를 요청한다.
정상적으로 데이터를 수신한 경우 새로운 포스트 엘리먼트를 생성한다.
const makePostElement = (postDatas) => {
console.log(postDatas);
const newPostElement = postDatas.map((post) => {
const key = post.postId;
...
return (
<Link
key={key}
to={`/post/${post.user.userNickName}/${post.postId}`}
className="postBox"
>
...
</Link>
);
});
setPostElement([...postElement, ...newPostElement]);
};
새로운 newPostElement
를 기존의 postElement
와 합쳐준다.
그럼 아주 간단하게 무한 스크롤이 가능한 화면을 구현할 수 있다.