프로그래밍 공부방

[Next.js] 당근마켓 프로젝트 성능 최적화 과정1 - 상품 출력하는 작업 본문

프론트엔드/Next.js

[Next.js] 당근마켓 프로젝트 성능 최적화 과정1 - 상품 출력하는 작업

김갱갱 2023. 6. 27. 11:58

🐥 당근마켓 프로젝트 성능 최적화 과정1

제품이 500개일 경우에 상품을 출력하는 최적화하는 과정입니다!


무한 스크롤을 구현해서 상품 20개씩 가져오도록 설정하기

 

전체 상품을 한꺼번에 가져오지 않고 무한 스크롤을 이용해서 필요한 만큼만 가져오도록 설정해보겠습니다.

1. 무한 스크롤을 구현해서 상품 20개씩 가져오도록 설정하기

첫 번째 문제

fetch에 시간이 오래걸리는 문제가 있습니다.

두 번째 문제

style을 구성하는 데에 오래걸리는 문제가 있습니다.


개선 후

useSWRInfinite과 IntersectionObserver를 이용해서 무한 스크롤을 구현했습니다.

useSWRInfinite을 이용해서 구현할 경우 지금까지 스크롤한 모든 내용을 다 저장해서 출력시켜줄 수 있기 때문에 스크롤의 위치를 기억할 수 있습니다. 이걸 따로 구현안해도 된다는 점이 좋았던 것 같습니다.

const Home: NextPage = () => {
  const loadRef = useRef<HTMLDivElement>(null);
  const [visible, setVisible] = useState(true);
  const [search, setSearch] = useState("");
  const [load, setLoad] = useState(false);
  const { data, size, setSize } = useSWRInfinite<ProductResponse>(
    (pageIndex: number, previousPageData: ProductResponse) => {
      if (previousPageData && previousPageData.products.length < 20) {
        setVisible(false);
        return null;
      }
      if (!load) return;
      return search
        ? previousPageData
          ? `/api/products?cursor=${previousPageData.cursor}&search=${search}`
          : `/api/products?search=${search}`
        : previousPageData
        ? `/api/products?cursor=${previousPageData.cursor}`
        : `/api/products`;
    }
  );

  const callback = (entries: any) => {
    const [entry] = entries;
    if (entry.isIntersecting && data) {
      setSize(size + 1);
    }
  };
  const options = useMemo(() => {
    return {
      root: null,
      rootMargin: "0px",
      threshold: 0.3, // 대상의 30%가 표시될 때 콜백 호출
    };
  }, []);
  useEffect(() => {
    const observer = new IntersectionObserver(callback, options);
    const target = loadRef.current;
    if (target) observer.observe(target);
    return () => {
      if (target) observer.unobserve(target);
    };
  }, [loadRef, options, data]);
  const searchFunc = (search: string) => {
    localStorage.setItem("productSearch", search);
    if (search.trim().length <= 0) return;
    setSearch(search);
    setSize(1);
    setVisible(true);
  };
  useEffect(() => {
    const productSearch = localStorage.getItem("productSearch");
    if (productSearch && search === "") {
      setSearch(productSearch);
      setVisible(true);
    }
    setLoad(true);
  }, [data]);

  const reload = () => {
    localStorage.removeItem("productSearch");
  };
  useEffect(() => {
    (() => {
      window.addEventListener("beforeunload", reload);
    })();
    return () => {
      window.removeEventListener("beforeunload", reload);
    };
  }, []);
};

일단 fetch와 관련된 첫 번째 Task는개선 전 1918ms에서 45ms로 줄어들었습니다.

 

style와 관련된 두 번째 Task는개선 전 400ms에서 16ms로 줄어들었습니다.


✨결과물