본문 바로가기
챌린지/패스트캠퍼스 공부 루틴 챌린지

패스트캠퍼스 챌린지 38일차 (Infinite Scoll - 스크롤 방식)

by 무벅 2022. 3. 2.
반응형

22.3.2 (수) +38days

무한 스크롤(Infinite Scroll)이란?

API 를 통해서 많은 양의 데이터를 가져와 화면에 렌더링해야 할 때 성능을 최적화 하기 위한 방법중 페이지네이션을 구현해 봤다.

모바일에서 페이지네이션 방식을 사용하려면 원하는 페이지로 이동하기 위해 숫자 버튼을 일일이 손으로 눌러야 하기 때문에 매우 불편하다.

이 떄, 사용할 수 있는게 무한 스크롤이다.

사용자가 스크롤링 하다가 미리 로드된 컨텐츠를 다 확인하면 다음 목록을 또 로드해서 별도의 인터랙션 없이 목록을 계속 불러오는 방식이다.


스크롤이 최하단에 왔는지 판단하기

Element.scrollHeight

: 엘리먼트의 총 높이를 나타내며 바깥으로 넘쳐서 보이지 않는 콘텐츠도 포함

Element.clientHeight

: 엘리먼트의 내부 높이 (padding 포함, scroll bar 높이, margin, border 미포함)

Element.offsetHeight

: 엘리먼트의 내부 높이 (padding 포함, scroll bar 높이, margin, border 포힘)

Element.scrollTop

: 스크롤 바의 Top 부분이 화면에 내려온 위치

즉, scrollHeight - clientHeight - scrollTop이 미리 정해높은 offset 미만 일 때 스크롤이 최하단에 왔다고 판단해서 다음 페이지를 가져오고 기존 항목들에 덧붙여(append) 주면 된다.


스크롤 방식의 한계

스크롤을 움직일 때마다 이벤트가 발생하기 때문에 성능 문제가 야기될 수 있다.

이를 해결하기 위해 보통 스크롤 이벤트에 쓰로틀링(throttle) 을 적용하여 이벤트를 제한한다.


Debounce vs Throttling

주로 DOM 이벤트를 기반으로 실행하는 자바스크립트를 성능상의 이유로 이벤트를 제한할 때 debounce와 throttling을 적용한다. 이 둘이 차이는 뭘까.

debounce

: 이벤트를 그룹핑해서 특정 시간이 지난 후 하나의 이벤트만 발생하도록 하는 기술. 연달아서 호출되는 함수들 중 마지막 함수만 호출되도록 하는 것

throttling

: 이벤트를 일정한 주기마다 발생하는 기술. 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 기술

 

App.tsx

import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import { throttle } from 'throttle-debounce';
import './index.css';

interface Airline {
  slogan: string;
  head_quaters: string;
  website: string;
  established: string;
}

interface Passenger {
  _id: string;
  name: string;
  trips: number;
  airline: Airline;
  __v: number;
}

function App() {
  const listRef = useRef<HTMLUListElement>(null);
  const currentPageRef = useRef<number>(0);

  const [passengers, setPassengers] = useState<Array<Passenger>>([]);
  const [isLast, setIsLast] = useState<boolean>(false);
  const [isScrollBottom, setIsScrollBottom] = useState<boolean>(false);

  const getPassengers = async (init?: boolean) => {
    const params = { page: currentPageRef.current, size: 100 };

    try {
      const response = await axios.get(
        'https://api.instantwebtools.net/v1/passenger',
        { params }
      );

      const isLast = response.data.totalPages === currentPageRef.current;

      init
        ? setPassengers(response.data.data)
        : setPassengers(passengers.concat(response.data.data));
      // setPassengers(
      //   init ? response.data.data : passengers.concat(response.data.data)
      // );

      setIsLast(isLast);
    } catch (e) {
      console.error(e);
    }
  };

  const handleScroll = throttle(1000, () => {
    if (listRef.current) {
      const { scrollHeight, offsetHeight, scrollTop } = listRef.current;

      const offfset = 50;

      console.log('trigger');

      setIsScrollBottom(scrollHeight - offsetHeight - scrollTop < offfset);
    }
  });

  useEffect(() => {
    if (isScrollBottom) {
      currentPageRef.current += 1;

      !isLast && getPassengers();
    }
  }, [isScrollBottom, isLast]);

  useEffect(() => {
    getPassengers(true);
  }, []);

  // console.log(passengers.length);
  return (
    <div className="App">
      <ul ref={listRef} className="list" onScroll={handleScroll}>
        {passengers.map((passenger) => (
          <li className="item" key={passenger._id}>
            {passenger.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

 

 

 

 

 

 

 

https://bit.ly/37BpXiC

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다

 

 

 

 

 

 

반응형

댓글