본문 바로가기
React

React-virtualized 로 보이는 부분만 렌더링 하여 최적화 시키기

by 윤-찬미 2021. 2. 12.

React + React-virtualized 를 조합한 무한 스크롤링 구현에 대해 다루고 있습니다.

목차
  • 무한스크롤링이란 무엇인가?
  • 무한스크롤링을 개선하는 두가지 방법
  • 리액트에서 무한스크롤링 최적화를 도와주는 React-virtualized 사용해보기

무한스크롤링이란 무엇인가 ?

여러분들도 한번쯤은 무한 스크롤링 처리가 되어있는 웹사이트를 이용해보신 경험이 있을 것 입니다.

보통 컨텐츠를 로드할때는 수천 수만개의 컨텐츠를 한번에 로드 하는 것이 아닌,

사용자가 스크롤을 내릴때마다 10개 씩 20개씩 불러옵니다.

무한 스크롤 자체의 구현은 그리 어렵지 않았습니다.

내 스크롤이 창 아래에 닿으면 새로운 게시물을 불러오면 됩니다.

하지만, 로드한 컨텐츠가 많아지면 많아질 수록 리플로우의 계산횟수가 많아지기에 렌더링 속도는 더욱 느려질 겁니다.

무한스크롤링 개선기

네이버 D2에서 진행한 세션을 참고해 알아보았습니다.

 

무한 스크롤링 시 일어나는 렌더링 속도 저하를 위해 생각해낸 방법은 보이는 부분만 렌더링 하자! 입니다.

이를 위해 우리는 두가지 방법을 이용할 수 있습니다.

 

display: none

 

 

 

dom이 그려지는 과정을 이해하고 있다면,

display: none은 렌더링 비용을 낮출 수 있다는 것을 알고 있을 것입니다.

 

하지만 퍼포먼스 성능의 향상은 크게 이루지 못했습니다.

아래는 Flicking 라이브러리의 각 렌더링 overview 입니다.

 

기존의 렌더링 비용 overview

 

 

display: none overview

 

 

 

기존 보다는 개선이 되었지만 여전히 Item 수가 많아질 수록 렌더링 비용도 동시에 증가합니다.

 

DOM 개수 줄이기

"DOM 개수 줄이기" 를 생각해 보았습니다. 보이지 않는 DOM은 삭제를 시켜 버리는 방법입니다.

 

 

렌더링 해야할 DOM의 개수가 많아질 수록 렌더링 비용이 증가하는 display: none과 달리 DOM을 삭제하는 경우 그러한 현상이 보이지 않는걸 확인할 수 있습니다.

리액트에서 무한스크롤링 최적화를 도와주는 React-virtualized 사용해보기

위의 DOM삭제를 직접 구현할 수는 있지만, 이를 도와주는 라이브러리가 있습니다.

바로 React-virtualized 입니다.

우선 여러분이 사용하는 프로젝트에 React-virtualized를 install 합니다.

npm install react-virtualized --save

근데 간혹 의존성 에러가 난다는 사람들이 많더군요.

 

그럴땐 아래 명령어로 설치를 진행하시면 됩니다.

npm install react-virtualized --legacy-peer-deps

"the workaround is using --legacy-peer-deps, but it would be great if react-virtualized would officially support React 17." - 리액트 17을 지원하지 않아 발생하는 문제 라고 하네요.

 

그리고 샘플 데이터를 만들어 줄건데 각 데이터 별로 랜덤한 아이디를 주기 위해

uuid를 설치해보도록 하겠습니다.

npm i uuid --save

우선 무한스크롤을 구현하기 앞서 임시의 가짜데이터를 만들어주겠습니다.

import React, { useState } from 'react';
import { v4 as uuidv4 } from 'uuid';

const Sample1 = () => {
  const mockData = [ 
    {
      id: uuidv4(),
      content: "content입니다."
    },
    {
      id: uuidv4(),
      content: "content입니다."
    },
    {
      id: uuidv4(),
      content: "content입니다."
    },
    {
      id: uuidv4(),
      content: "content입니다."
    },
  ];

  const [data, setData] = useState(mockData);

  return (
    <div></div>
  )
}

export default Sample1;

이번 react-virturelized예제 에서 사용할 컴포넌트들은 다음과 같습니다.

  • AutoSizer

    부모 element의 너비와 높이를 자식 컴포넌트에 전달해주는 HOC입니다.

    이걸 활용하면 부모의 너비와 높이만큼 자식을 꽉 채울 수 있습니다.

  • List

    이 구성 요소는 요소의 창 목록 (행)을 렌더링합니다.

react-virturelized에서 무한 스크롤을 구현할땐 InfiniteLoader를 사용하시는게 좋긴 합니다만,

간단하게 이 두가지 요소를 활용해 무한스크롤링을 구현해보도록 하겠습니다.

실제로 이렇게만 써도 큰문제는 없습니다.

기본 구조는 다음과 같습니다.

 

<AutoSizer AutoSizer> 
  {({width}) => (
    <List
      rowCount={data.length} // 항목의 개수
      height={500} // 실제 렌더링 되는 높이범위
      rowHeight={50} // 항목의높이
      width={width} // 항목의 너비
      rowRenderer={rowRanderer} // 항목렌더링할때쓰는 함수
      onScroll={scrollListener} // scroll 함수
      overscanRowCount={5} // 다음에 로드해올 항목 미리 컨텐츠 높이 잡기
    />
  )}
</AutoSizer>

 

이제 rowRenderer, onScroll를 만들어보겠습니다.

const rowRanderer = ({ index, style }) => {
  const post = data[index];
  return (
    <div style={style}>
      {post.content}
    </div>
  );
};
const scrollListener = (params) => {
	if (params.scrollTop + params.clientHeight >= params.scrollHeight - 300) {
	  if (data.length <= 100) {
	    setData([
	    ...data,
	    {
	      id: uuidv4(),
	      content: "추가된 content입니다."
	    },
	    {
	      id: uuidv4(),
	      content: "추가된 content입니다."
	    },
	    {
	      id: uuidv4(),
	      content: "추가된 content입니다."
	    },
	    {
	      id: uuidv4(),
	      content: "추가된 content입니다."
	    }])
	  }
	}
};

onScroll에 들어가는 함수는 인자값으로 scrollTop, clientHeight, scrollHeight 이 들어옵니다.

params.scrollTop + params.clientHeight >= params.scrollHeight - 300

params.scrollTop + params.clientHeight >= params.scrollHeight - 300

스크롤의 위치가 바닥에 닿기 전 살짝 위에서부터 데이터를 추가하도록 만들었습니다.

rowRenderer 은 실제로 컨텐츠를 그릴 렌더링 함수 입니다.

여기서 많이들 실수 하시는데

로드할 컨텐츠의 인덱스와 스타일을 꼭 부여해주어야합니다.

const post = data[index]; // index는 react-virturized에서 새로 그릴 아이템의 index를 구해 인자로 넘겨준다.
 <div style={style}>
    {post.content}
 </div>

이제 확인해보시면 스크롤이 바닥에 닿을때마다 컨텐츠가 추가되지만, 실제로 화면에 보이는 돔만 그려지는 걸 알 수 있습니다.

 

 

 

전체코드

import React, { useState } from 'react';
import { AutoSizer, List } from 'react-virtualized';
import { v4 as uuidv4 } from 'uuid';

const Sample1 = () => {
  const mockData = [ 
    {
      id: uuidv4(),
      content: "content입니다."
    },
    {
      id: uuidv4(),
      content: "content입니다."
    },
    {
      id: uuidv4(),
      content: "content입니다."
    },
    {
      id: uuidv4(),
      content: "content입니다."
    },
  ];

  const [data, setData] = useState(mockData);
  const scrollListener = (params) => {
    if (params.scrollTop + params.clientHeight >= params.scrollHeight - 300) {
      if (data.length <= 100) {
        setData([
        ...data,
        {
          id: uuidv4(),
          content: "추가된 content입니다."
        },
        {
          id: uuidv4(),
          content: "추가된 content입니다."
        },
        {
          id: uuidv4(),
          content: "추가된 content입니다."
        },
        {
          id: uuidv4(),
          content: "추가된 content입니다."
        }])
      }
    }
  };

  const rowRanderer = ({ index, style }) => {
    const post = data[index];
    return (
      <div style={style}>
        {post.content}
      </div>
    );
  };

  return (
    <AutoSizer AutoSizer> 
      {({width}) => (
        <List
          rowCount={data.length} // 항목의 개수
          height={400} // 실제 렌더링 되는 높이범위
          rowHeight={200} // 항목의높이
          width={width} // 항목의 너비
          rowRenderer={rowRanderer} // 항목렌더링할때쓰는 함수
          onScroll={scrollListener} // scroll 함수
          overscanRowCount={2} // 다음에 로드해올 항목 미리 컨텐츠 높이 잡기
        />
      )}
    </AutoSizer>
  )
}

export default Sample1;

 

참고자료

https://github.com/bvaughn/react-virtualized

https://stackoverflow.com/questions/38762914/infiniteloader-and-react-redux

 

저는 벨로그와 티스토리 모두 운영하고 있어 벨로그에서 저와 같은 글을 발견한다면 아마도 저입니다.

velog.io/@cksal5911

cksal5911 (YoonSam) - velog

Ts babel&wepack초기 환경 셋팅 스크린샷 2019-11-25 오전 11.29.40.png 시작하며 본 포스팅은 제가 타입스크립트를 처음 공부하면서 바벨이랑 웹팩 그리고 tsconfig.json 설정파일을 작성

velog.io