Intersection Observer 를 활용한 Infinite Scroll Pagination 구현 (feat. Notion API)


블로그를 작업하면서 포스팅을 작성하니 벌써 10개 이상의 포스팅이 만들어졌다. 그래서 pagination 기능을 구현하기 위해 Notion API 에서 제공하는 pagination 기능을 활용하여 스크롤을 내리면 새로운 페이지를 가져오는 Infinite scroll 방식으로 기능을 구현하였다.
그래서 포스팅을 통해 기능에 대한 설명과 기능 구현을 하면서 생각했던 것들을 작성해보려고 한다.

Notion API 의 Pagination

Notion API 에서 리스트에 관련된 항목들에 대해 Pagination 기능을 제공해주고 있다.
 
요청 값에는 해당 항목들을 적용해줘야 한다.
  • start_cursor: 전달을 받으려고 하는 리스트의 첫번째 아이템 id
  • page_size: 전달을 받으려는 아이템 항목 수 (= limit)
 
응답 값에는 여러 값들이 있지만, 그중에서 이 3가지 항목이 제일 중요한 항목이다.
  • results: 보여줄 아이템 리스트
  • next_cursor: 다음 페이지의 첫 번째 아이템 id
  • has_more: 다음 페이지 존재 여부
 

간단한 흐름

pagination 의 흐름을 간단하게 설명하면 이렇게 된다. (응답:next_cursor → 요청:start_cursor)
 
  1. 초기 1페이지를 요청 시 page_size 만 적용하여 요청한 다음, 응답 값으로 3가지 항목을 전달받음
  1. 2페이지를 요청하는 경우 1페이지에서 받은 응답 값 next_cursor 를 요청 값인 start_cursor 항목에 적용 후 요청
  1. 3페이지를 요청하는 경우 2페이지에서 받은 응답 값 next_cursor 를 요청 값인 start_cursor 항목에 적용 후 요청
  1. 반복

Infinite Scroll 기능 적용

💡
해당 코드들은 현재 프로젝트에 적용된 코드 그대로가 아닌 설명을 위해 작성된 코드입니다. Next.js 의 API routes 를 통해 GET:api/pagination 에 요청 시 리스트 데이터를 응답받는 것을 전제로 합니다.
 
useState 를 통해 페이지 요청에 대한 카운트, 리스트 데이터, 응답받은 pagination 관련 정보들에 대한 상태값을 관리하고, useRef 를 통해 Intersection Observer API 를 사용할 때 교차 여부의 기준이 되는 타겟 항목을 관리한다.
 
useEffect 를 통해 하나는 SSG 를 통해 props 로 전달받은 초기 페이지 데이터를 상태 값에 초기화시켜주는 기능을 구현하고, 나머지 하나는 페이지 요청 카운트인 page 상태 값을 deps 에 적용하여 Intersection Observer API 를 통해 page 상태 값이 변경되는 경우 Notion API 를 통해 다음 페이지의 리스트 데이터와 pagination 에 대한 데이터를 fetching 하는 기능을 구현하였다.
 
useState 의 리스트 데이터를 렌더링하는 영역 하단에 Intersection Observer API 의 타겟 요소가 되는 div 태그를 추가해준 뒤 useRef 를 통해 생성한 baseRef 를 적용해준다.
그리고 리스트 데이터가 존재하지 않고 다음 페이지가 존재하지 않을 경우, css 의 display 속성을 none 으로 적용해 초기에 데이터가 없어 바로 타겟 요소가 교차하는 경우나 더 불러올 페이지가 없음에도 계속해서 데이터를 요청을 하는 상황을 방지하였다.
 
그리고 Intersection Observer API 를 통해 타겟 요소가 브라우저 화면에 보이게 되면 setPage 를 통해 페이지 요청 카운트 값인 page 값을 +1 해줌으로써 위에서 작성한 useEffect 의 데이터 fetching 영역이 동작하도록 적용하였다.
 
위의 단계를 하나의 파일에 적용하면 이런 코드가 완성된다.
지금까지의 코드는 설명하기 위해 작성되었기 때문에 실제 프로젝트에 반영하는 경우 custom hook 을 통해 코드를 작성해야 더 깔끔하고 재사용이 가능한 코드가 된다.

구현한 기능을 더 향상해보자!

위의 코드를 통해 Infinite Scroll 로 페이지를 가져오는 기능이 완성되었는데 다음 페이지의 데이터를 가져올 때 페이지에서 아무런 반응이 없어 사용자 경험이 많이 부족해지는 현상이 발생한다.
 
이런 경우 Skeleton UI 를 통해서 해결이 가능하다.
페이지 아이템의 UI 를 기반으로 한 Skeleton 컴포넌트를 생성한 뒤 Intersection Observer API 의 타겟이 되는 div 태그 안에 추가하면 적용이 완료된다.
 
우선 NotionPageItem/Skeleton 컴포넌트를 생성해준다.
  • text-indent 속성을 통해 넓이, 높이 값을 지정하는 것이 아닌, UI 의 기반이 되는 NotionPageItem 컴포넌트의 font-size 속성을 따라가기 위해 적용하였다.
  • animation 속성을 통해 배경 색상이 반복해서 변경되도록 적용하여 로딩이 진행중이라는 상황을 더욱 강조하였다.
 
사용하려는 페이지에 생성한 NotionPageItem/Skeleton 컴포넌트를 적용해준다. 이 코드 또한 설명하기 위해 작성된 것이기 때문에 추가한 Skeleton 컴포넌트에 대해서는 NotionPageList/Skeleton 컴포넌트를 생성한 뒤에 적용해주는게 더욱 깔끔하다.
 

 
이렇게 Notion API 의 Pagination 을 Infinite Scroll 방식으로 구현해봤는데 생각보다 더 깔끔하게 동작해서 되게 뿌듯했다.
 

 
참고