ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Intersection Observer - Lazy-loading, 무한스크롤을 구현하기 좋은 API!
    Frontend 2020. 11. 23. 11:15

    나는 Intersection Observer API를 좋아한다.. 

    그냥 정이 간다.

     

    Intersection Observer는 특정 엘리먼트가 사용자의 화면에 노출되어있는지 감시할 수 있는 API이다.

    머하는지 훔쳐봄

    이 짤 생각나고 귀엽다. 

     


    • web api 중 하나
    • 2016년 4월 구글 개발자 페이지 통해 소개됨
    • 특정 Element가 화면에 노출되어있는지 감시할 수 있는 API

    Intersection Observer를 사용해서 구현할 수 있는 멋진 것들

    1. 이미지 Lazy-loading (지연 로딩)

      웹사이트에 이미지가 많을 때 화면 상에 존재하는 이미지를 한꺼번에 불러오면 불필요한 네트워킹 비용이 증가한다.

      사용자가 스크롤을 내려 모든 이미지를 보기 전에 사이트를 끌 수도 있는데, 굳이 처음부터 다 불러올 필요가 없다.

      처음에 모든 이미지를 불러오면 초기 리소스 로딩도 길어진다. (사용자 이탈!)

      그럴 때 Intersection Observer를 이용하면 된다.

      사용자가 스크롤을 내려서, 화면에 이미지 영역이 보이면 그 때 이미지를 선별적으로 로딩하면 된다.

     

     

    예시)

    jsfiddle.net/Mottie/nd60mos9/

     

      Intersection Observer 말고 이전에는 element.getBoundingClientRect 같은 방법을 사용하기도 했다.

      얘는 몇가지 단점이 있는데

    • 브라우저가 해당 엘리먼트의 크기와 위치값을 계산해야한다.
    • reflow가 일어나서 메인 스레드 과부화가 될 수 있다. 
    • iframe 내부에선 가시성을 판단할 수 없다.

      => 하지만 intersection observer를 사용하면 reflow도 일으키지 않고 iframe 내부에서도 가시성을 판단할 수 있다.

     

    2. 무한 스크롤 구현 (Infinite scrolling)

      무한 스크롤을 어떻게 구현할 지 생각해보면

      "스크롤이 바닥을 치면 다음 리스트를 불러와야겠다.🧐"

      가 제일 먼저 떠오른다.

     

      Intersection Observer를 사용하면 좀 더 기름기 빼고 구현할 수 있다.

      기름기 : 기존에 사용되던 scroll 이벤트는 단시간에 수백번, 수천번 호출될 수 있다.

                      또한 동기적으로 실행되기 때문에 메인 스레드에 영향을 준다.

     

      리스트 하단에 target Element을 추가해놓고

      해당 target이 화면에 보이면 특정 액션(callback)을 수행하는 방식으로 무한 스크롤을 구현하면 된다. 💡

     

    예시)

    googlechrome.github.io/samples/intersectionobserver/

    3. 광고 수익 측정

      광고수익 측정은 Intersection Observer로 실제로 구현해본적은 없다. 

      우리 팀은 Vue를 사용해서.. mounted 훅에 GA 트래킹 등을 걸어놓으니까..

      근데! 스크롤을 내려야 노출되는 특정 배너광고 노출 측정을 할 때 유용할 것 같다.💡💡

    4. 애니메이션 구현

      뭔가 스크롤 내리면 뾰로롱! 하는 것들.. 💫

     

     

     

    기본 사용법을 뜯어보자

    // 콜백함수와 옵션을 받는다.
    const io = new IntersectionObserver(callback, {
      root: null, // 또는 scrollable 한 element
      rootMargin: '0px', // 기본값 0px
      threshold: 0.5 // 0.0 ~ 1.0 사이의 숫자. 배열도 가능
    })

     

    옵션값을 하나하나 살펴보자

    root

    target (관측 대상) 을 감싸는 element (container)

    default인 null 로 지정하면 viewport가 된다.

    rootMargin

    root 요소를 감싸는 margin

    문자열로 작성해야하며 px 또는 % 단위로 작성할 수 있다.

    threshold

    root와 target element의 교차 영역 비율

    몇 % 교차했을때 callback을 실행할것인가 결정하는 값 (0.0 ~ 1.0)

     

    콜백함수를 살펴보자

    target element 의 visibility 가 threshold 만큼 도달했을 때 호출될 함수

    Element 배열인 entries와 자기 자신인 observer 가 매개 변수로 전달됨

     

     

    관련 메소드들을 살펴보자

    IntersectionObserver.observe(targetElement)

    • 타겟 엘리먼트에 대한 IntersectionObserver를 등록할 때(관찰을 시작할 때) 사용

    IntersectionObserver.unobserve(targetElement)

    • 타겟 엘리먼트에 대한 관찰을 멈추고 싶을 때 사용

    IntersectionObserver.disconnect()

    • 다수의 엘리먼트를 관찰하고 있을 때, 이에 대한 모든 관찰을 멈추고 싶을 때 사용

    IntersectionObserver.takerecords()

    • IntersectionObserverEntry 객체의 배열을 리턴

     

     

    코드를 보면서 살펴보자

    (Intersection Observer로 무한 스크롤을 구현한 Vue 코드)

    mounted() {
      this.initIntersectionObserver();
    }

    : 마운트 될 때, interserction observer를 초기화할 함수를 호출함

     

    initIntersectionObserver() {
      // element는 하나니까 배열에서 하나만 가져옴
      const io = new IntersectionObserver(async ([entry], observer) => {
        // 가시성이 생기면
        if (entry.isIntersecting) {
          // 중복 실행을 막기 위해 관찰을 멈춘다
          observer.unobserve(entry.target);
          // Article을 더 가져오는 메소드
          await this.handleGetMoreArticles();
          // 실행 뒤에는 다시 관찰을 시작한다
          observer.observe(entry.target);
        }
      }, {
        // 30% 정도 보이면 실행 시작
        threshold: 0.3
      })
      
      io.observe(this.$refs.scrollObserver);
    }
    <div ref="scrollObserver" v-if="isMore">
      <div class="spinner"></div>
    </div>

    : 리스트 맨 아래 로딩 엘리먼트를 만들고 scrollObserver라는 ref를 건다. 그 ref에 Intersection Observer를 걸었다. 

     

     

     

     

    브라우저 호환성

     

     

     

    IE를 지원하지 않는다 😐

    만약 IE에서도 사용하고 싶다면 아래의 polyfill을 사용하면 된다.

    w3c/IntersectionObserver

    댓글

Designed by Tistory.