Intersection Observer API로 InfiniteScroll 구현하기

sebinnnnn·2023년 5월 15일
2
post-thumbnail

1️⃣ infiniteScroll이란?

페이징 기능 중 하나로 유저가 페이지의 하단에 도착했을 때, 다음 콘텐츠가 보여지는 즉, 콘텐츠가 계속적으로 로드되는 사용자 경험 방식이다.

무한 스크롤의 경우에 사용자가 페이지 네이션과 같이 클릭할 필요 없이 페이지 하단에 도착하면 자동으로 다음 콘텐츠가 불러와진다는 장점이 있고 또, 더치 스크린을 사용하는 모바일 환경에서 최적화된 기능으로 작용한다는 장점이 있다.

다만, 데이터를 실시간으로 불러오기 때문에 페이징 성능이 느려질 수 있다는 단점이 있고, 수많은 콘텐츠가 스크롤로 끝없이 이어지게 된다면 사용자 입장에서는 페이지 상단으로 이동하는 것이 어려울 수 있다는 단점이 있다.

그럼에도 많은 사이트에서 무한 스크롤 기능을 제공하는 이유는, 데스크톱 말고도 여러 스크린 환경에서 사이트에 접속을 하기 때문에 터치스크린 환경에서도 유용하게 사용할 수 있는 기능을 제공하기 위함이 아닐까 생각한다.

2️⃣ infiniteScroll 구현하기 - Intersection Observer API 사용

무한 스크롤을 구현하는 방법에는 여러 가지가 있다.
그중 오늘은, Intersection Observer API로 무한 스크롤을 구현해 보려고 한다.

🔗 Intersection Observer API에 대한 간단한 설명

IntersectionObserver 👉🏻 MDN 문서 확인!

Intersection Observer API는 Web API 중 하나로 교차 관찰자를 사용하는 방식이다.

교차 관찰자는 관찰 중인 요소가 화면 즉, 뷰포트와 교차하고 있는지를 감지하는 API다.
쉽게 말하면, 관찰 중인 요소가 사용자가 보이는 화면 영역에 들어와있는지를 알려주는 API다.

즉, 개발자가 설정한 관찰 중인 요소(target element)가 뷰포트에 보이지는 지 보이지 않는지를 감지하는 API인 것이다.

사용법은 먼저, new IntersectionObserver(callback, options)로 관찰자 객체를 생성해서 관찰 중인 요소인 관찰 대상을 지정해서 관찰을 시작하면 된다.
이때, IntersectionObserver 생성자 함수는 callback 함수와 options을 매개변수로 받게 되는데 options은 필수가 아니다.
options에는 root, rootMargin, threshold가 있는데 필요할 때 MDN 문서를 보고 설정하는 걸 추천한다.

📍 이번 포스팅에서는 해당 옵션들을 따로 사용하지 않았다. 📍

Intersection Observer API의 기본적인 코드 세팅은 다음과 같다.

const io = new IntersectionObserver(콜백함수, { 옵션 -> 선택 사항 });
io.observe(관찰할 요소);

콜백함수는 entriesio를 매개변수로 갖게 되는데, entries는 관찰 중인 대상을 담은 배열(해당 배열은 entry 객체로 구성되어 있고 각각의 엔트리 객체는 isIntersecting와 같은 속성값을 가지고 있다.)이고 io는 관찰하고 있는 관찰자 객체다.

IntersectionObserverEntry 인터페이스의 읽기 전용 isIntersecting 프로퍼티는 대상 엘리먼트가 교차 관찰자의 루트와 교차하면 참인 Boolean 값이다. 이 값이 참이면 교차 상태로의 전환을 설명하고, 거짓이면 교차에서 교차하지 않는 상태로 전환하는 것을 알 수 있다.
⇒ isIntersecting는 관찰 대상이 화면에 보이는지 아닌지를 불리언 값으로 반환하는 프로퍼티다.
IntersectionObserverEntry 👉🏻 MDN 문서 확인!

엔트리 객체는 관찰 중인 요소에 대한 관찰 정보를 담고 있으며, 이에 대해서는 전부 설명할 수가 없기 때문에 문서를 통해서 필요할 때 찾아보는 것이 좋다.

🔗 Intersection Observer API를 이용한 무한 스크롤 실습 코드

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Javascript infiniteScroll</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <h1>infiniteScroll - Intersection Observer API</h1>

    <div id="box-container">
      <div class="box-group">
        <div class="img">
          <img
            src="https://img.koreatimes.co.kr/upload/newsV2/images/202303/5a855c83c10d460b8a1bb486c03f727b.jpg/dims/resize/740/optimize"
            alt="seventeen"
          />
        </div>
        <div class="comment">💎 seventeen</div>
      </div>

      <div class="box-group">
        ...
      </div>
    </div>
    <div id="observe"></div>
    <!-- Intersection Observer의 target 엘리먼트 -->

    <script src="script.js"></script>
  </body>
</html>
  • 기본적으로 box-group 을 만들어서 해당 div 안에 이미지와 간단한 설명이 들어가도록 구조를 잡았다.
    box-group 은 무한 스크롤에서 스크롤 시 생성되는 콘텐츠 요소이고 페이지를 접속하면 기본적으로 2개의 콘텐츠 요소가 나타나도록 했다.
const $boxContainer = document.getElementById("box-container");
const $observe = document.getElementById("observe");

// box-group 생성해서 container에 붙이는 함수
const makeBox = () => {
  // 2개씩 생성
  Array(2) // 길이 2
    .fill(0) // 0으로 채워넣기
    .forEach(() => {
      const $boxGroup = document.createElement("div");
      $boxGroup.className = "box-group";

      const $img = document.createElement("div");
      $img.className = "img";

      const $imgTag = document.createElement("img");
      $imgTag.setAttribute(
        "src",
        "https://img.koreatimes.co.kr/upload/newsV2/images/202303/5a855c83c10d460b8a1bb486c03f727b.jpg/dims/resize/740/optimize"
      );

      const $comment = document.createElement("div");
      $comment.className = "comment";
      $comment.innerHTML = "💎 seventeen";

      $img.appendChild($imgTag);
      $boxGroup.appendChild($img);
      $boxGroup.appendChild($comment);

      $boxContainer.appendChild($boxGroup);
    });
};
  • makeBox 함수를 정의해서 콘텐츠 요소가 동적으로 생성될 수 있도록 했고 2개씩 생성이 되도록 코드를 작성했다.
let timer;
// 타겟 가져오기
const io = new IntersectionObserver((entries) => {
  clearTimeout(timer);

  if (entries[0].isIntersecting) {
    timer = setTimeout(() => makeBox(), 500);
  }
});

io.observe($observe); // observer에 target element 등록
  • observe(관찰 대상) 을 $observe로 등록하고 $observe가 화면에 나타난다면 0.5초 후에(1000 = 1초) makeBox 함수가 실행되어 2개의 콘텐츠 요소가 뷰포트에 생성되도록 구현했다.

  • if (entries[0].isIntersecting) 코드를 통해서 관찰 대상이 화면에 보이는지를 감지하고 만약에 보인다면 true 값을 반환하기 때문에 0.5초 후에 makeBox 함수가 실행되도록 했다.

  • clearTimeout(timer) 코드를 작성한 이유는 사용자가 아주 빠른 시간에 여러 번 스크롤을 하게 되면 반복적으로 makeBox 함수가 실행되기 때문에, 이를 방지하기 위함이다.
    즉, 여러 번 스크롤을 해도 이전 타이머를 취소하기 때문에 1번만 실행되도록 하기 위해서 사용한 코드다.


블로그를 작성하고 또 직접 코드로 구현을 해보면서 이해하지 못한 부분들도 많고 내가 작성한 코드지만 어떠한 원리로 동작하는지 헷갈리는 부분도 많다. 💧

그럼에도, Intersection Observer를 이용해서 무한 스크롤을 어떻게 구현해야 하는지 전체적인 틀은 잡힌 것 같아 다행이고 익숙해질 때까지 여러 번 반복하는 것이 중요할 것 같다는 생각이 든다.


참고 사이트
https://developer.mozilla.org/ko/docs/Web/API/IntersectionObserver/IntersectionObserver

https://pebblepark.tistory.com/31

https://nohack.tistory.com/124

profile
🏠 블로그 이전 중 → https://gksk.tistory.com/

0개의 댓글