[React] Material-UI로 구현한 가격필터 슬라이더 성능 개선하기

eunjin·2021년 1월 4일
1

React Study

목록 보기
4/6

에어비앤비 숙소 리스트 페이지의 가격 필터를 구현했는데, 슬라이더가 유난히 느리게 반응하는 것이 신경쓰였다. 슬라이더를 material-ui 라이브러리를 활용해서 내 앱이 무거워진건 줄 알았는데 정말 비효율적인 코드를 짰다는 것을 나중에야 알게 됐다.

1. 이전 코드

// PriceFilterDropDown 컴포넌트
const PriceFilterDropDown = ({ handleExit, filterByPrice }) => {  
  const popUpRef = useRef();
  const backgroundRef = useRef();
  const [value, setValue] = useState([]);
  const [curVal, setCur] = useState([]);
  const [min, setMin] = useState();
  const [max, setMax] = useState();
  const [avg, setAvg] = useState();

  useEffect(() => {
    fetch(`${API}/homes`)
      .then((res) => res.json())
      .then((data) => {
    const prices = data.homes.map((home) => +home.price["1박비용"]);
    const min = prices.reduce((pre, cur) => {
      return pre > cur ? cur : pre;
    });
    const max = prices.reduce((pre, cur) => {
      return pre > cur ? pre : cur;
    });
    setAvg(data.avg_price);
    setValue([min, max]);
    setMin(min);
    setMax(max);
    if (curVal.length > 0) {
      setValue([curVal[0], curVal[1]]);
    }
   });
  }, [curVal]);

  const getPriceFilterResult = () => {
    fetch(`${API}/homes?price_min=${curVal[0]}&price_max=${curVal[1]}`)
      .then((res) => res.json())
      .then((data) => {
        filterByPrice(data.homes);
      })
      .catch((err) => console.log(err));
    handleExit();
  };

처음 적용해보는 라이브러리인 material-ui의 슬라이더를 가져다가 커스텀하고, 구글링으로 내 프로젝트에 적합한 코드를 찾아 적용하는 데 급급하다 보니 성능을 생각할 여력이 없었다. 그런데 슬라이더가 내가 마우스를 이용해서 움직이는데 쓸데없이 너무 느리게 반응해서 따라온다는 것을 알았다. 기존에 몇 개 안 되는 내 목데이터를 이용했을 때는 그나마 괜찮았는데, 백엔드에서 보내주는 데이터를 받자마자 문제가 두드러지기 시작했다.

이 문제는 조금 신경써 보니 바로 해결되었다. 쓸데없이 팝업 슬라이더 컴포넌트에서 fetch를 받지 않고, 부모에서 fetch받은 데이터를 가지고 최댓값과 최솟값, 변경된 값을 구하니 바로 슬라이더가 정상적으로 작동했다. 어차피 부모에서는 전체 숙소에 대한 데이터를 받아오는데, 그걸 가지고 컴포넌트에 props로 넘겨주면 되는 걸 굳이 컴포넌트 안에서까지 이중으로 fetch를 받은 게 문제였다. 한 번 fetch 받는 것도 시간이 걸리는데, 슬라이더를 움직일 때마다 fetch를 받도록 코드를 짰으니 느려질 만 했다. 그렇게 해서 고친 코드는 다음과 같다.

2. 리팩토링 후 코드

// RoomList 부모컴포넌트
    {isPriceFilterPop && (
      <PriceFilterDropDown
        handleExit={handlePriceFilterPop}
        filterByPrice={filterByPrice}
        data={data}
      />
    )}
            
// PriceFilterDropDown 자식컴포넌트
const PriceFilterDropDown = ({ handleExit, filterByPrice, data }) => {
  const popUpRef = useRef();
  const backgroundRef = useRef();
  const [value, setValue] = useState([]);
  const [curVal, setCur] = useState([]);
  const [min, setMin] = useState();
  const [max, setMax] = useState();
  const [avg, setAvg] = useState();

  useEffect(() => {
    const prices = data.homes.map((home) => +home.price["1박비용"]);
    const min = prices.reduce((pre, cur) => {
      return pre > cur ? cur : pre;
    });
    const max = prices.reduce((pre, cur) => {
      return pre > cur ? pre : cur;
    });
    setAvg(data.avg_price);
    setValue([min, max]);
    setMin(min);
    setMax(max);
    if (curVal.length > 0) {
      setValue([curVal[0], curVal[1]]);
    }
  }, [curVal]);

  const getPriceFilterResult = () => {
    fetch(`${API}/homes?price_min=${curVal[0]}&price_max=${curVal[1]}`)
      .then((res) => res.json())
      .then((data) => {
        filterByPrice(data.homes);
      })
      .catch((err) => console.log(err));
    handleExit();
  };

부모 컴포넌트에서 PriceFilterDropDown 컴포넌트에 이미 fetch받은 data를 props로 전달해주어서, 그걸 가지고 필터링을 했더니 비교도 안 되게 빨라졌다. 슬라이더를 움직일 때마다 fetch받는 게 아니라 그냥 값만 변화시켜 주고 fetch는 저장 버튼을 누르면 실행되는 getPriceFilterResult 함수에서 쿼리스트링으로 실행되도록 하였다. 코드리뷰를 받은 건 아니고 자체적인 코드 리팩토링을 한 거라 이 방법이 최선인지는 모르겠지만, 일단 아까까지 너무 답답했는데 잘됐다...!!!

profile
빵굽는 프론트엔드 개발자

1개의 댓글

comment-user-thumbnail
2021년 1월 4일

안녕하세요, tech 기업에서 일하는/ 일하기를 희망하는 여성들을 모아서 모임을 만드는데 관심있으시면 참여해주세요!
자세한 사항은 및 링크 참조바랍니다 :)
https://velog.io/@emilyscone/SheKorea-1%EA%B8%B0-%EB%A9%A4%EB%B2%84%EB%A5%BC-%EB%AA%A8%EC%A7%91%ED%95%A9%EB%8B%88%EB%8B%A4

답글 달기