본문 바로가기

Frontend

react-window 적용기

회사에서 진행한 작업 중에 홈 화면 일부를 최적화한 경험이 생각나서 기록해보려고 한다.

문제 상황

통합 홈화면에서 구성원의 근무현황을 확인할 수 있는 위젯이 있다.
여기서 구성원 검색 기능을 추가하게 되었는데 키보드 이벤트가 있을 때마다 모든 리스트를 다시 렌더링하고 있었기 때문에 검색이 원활하지 않았다.
한글자 입력할 때마다 렌더링이 체감 5초는 걸렸다.
로컬 환경은 프로덕션보다 더 느리니까 감안하더라도, 고객사의 데이터는 로컬 데이터보다 훨씬 많을테니 분명히 사용성이 안좋을 것 같았다.
로컬에서 mock 데이터를 1000개정도 만들어서 테스트를 하니까 렌더링이 매우 느렸다. 렌더링 한번에 10초 이상 소요되었다.
따라서 렌더링 속도를 개선할 수 있는 방법이 필요했다.

시도한 방법

처음에는 이를 해소하기 위해 검색 이벤트에 디바운스를 적용하여 렌더링의 빈도를 줄였다.
디바운스는 설정한 시간동안 발생한 이벤트를 하나로 묶어서 렌더링을 한번만 하게 해준다.
그런데 초를 많이 걸면 검색 반응이 더 느려지는 것처럼 느껴졌다.
예를 들어서 디바운스를 위해서 2초의 시간을 걸면 사용자가 2초동안 뭔가 입력한 후에 렌더링이 되니까 뭔가 렉이 있다고 느껴질 것 같았다.
체감되는 성능은 더 안좋아졌다고 느낄 수도 있었다. 왜냐하면 원래는 그래도 입력하면 렌더링이 새로 되기라도 했는데 이제는 입력하고 2초있다가 렌더링이 되는 거니까 좀 더 불편할수가 있겠다.
그런 이유로 많은 시간을 디바운스로 걸 수는 없었고, 아래 코드와 같이 0.2초로 걸었다. 사실상 성능에는 하나마나인 수준!
느린 원인이 많은 이벤트가 몰려서가 아니라 화면을 매번 새로 그린다는 것이라서 그렇다.
한번의 이벤트가 매우 느리면 디바운스도 소용이 없는 것이었다.
그럼에도 아래 코드를 그대로 둔 이유는, 사용자가 너무 느리다고 느낄 정도는 아니면서도 조금이나마 UI 깜빡임을 줄이고자 한 것이었다.

react-window 적용기

그래서 한번의 렌더링 시에도 속도를 줄일수 있는 방법을 모색했다.
개발하려는 UI는 화면에 5명정도의 아이템만 보여주는 작은 화면이었다.
pc에서는 스크롤을 해야 구성원을 더 확인할수 있고, 모바일에서는 더보기 버튼을 눌러야 확인할 수 있는 형식이다.
그래서 가상화 방식을 적용하여 초기에는 구성원 5명만 렌더링하고, 스크롤 시 추가로 렌더링하도록 구현했다.
그동안에는 모든 구성원을 한번에 렌더링하고 있었기 때문에 느렸던 것이다.
원래라면 1000개의 데이터가 있을 때 1000개 아이템을 그대로 렌더링 한 다음에 보여주는거라면
가상화를 적용하면 5명씩 보여줄수 있어서 성능이 대폭 개선되는 것을 기대할수 있었다.
검색해보니 리액트에서 react-window와 react-virtualized라는 가상화 라이브러리를 지원하고 있었다.
react-window는 경량화된 가상화 라이브러리로 성능과 단순성에 초점을 맞췄고, react-virtualized는 다양한 고급 기능을 제공하지만 번들 크기와 복잡도가 높다고 한다.
나는 복잡한 기능은 필요없고 아이템 높이도 고정되어 있었기 때문에 react-window로 충분하여 이것을 선택했다.
예시 코드는 아래와 같다.

import { FixedSizeList as List } from "react-window";

export default function Example() {
return (
<List
height={300} // 리스트 높이
itemCount={1000} // 총 아이템 수
itemSize={35} // 각 row 높이
width={"100%"} // 너비
>
{Row}
</LIST>
);
}

리스트의 전체 높이, 아이템 수, 각 아이템 높이, 너비를 지정하면 라이브러리에서 가상화를 해준다.
일부 코드만 공개하자면 이런식으로 지정했다.
 

결과

초기 렌더링 시간(LCP) 단축과 체감 검색 속도의 큰 개선이 있었다.
체감 속도가 매우 빨라졌고 배포 이후에 데모 환경에서 고객사 데이터로 테스트해보니까 아주 원활하게 검색되는 것을 확인할 수 있었다. 후후

 
 
암튼 간단한 라이브러리로 큰 개선이 있어서 아주 만족스러웠다.