반응형
250x250
Notice
Recent Posts
Recent Comments
Link
관리 메뉴

짧은코딩

전체 상품 불러오기 무한 스크롤 구현 with react-intersection-observer 본문

wayc 이커머스 프로젝트

전체 상품 불러오기 무한 스크롤 구현 with react-intersection-observer

5_hyun 2023. 12. 26. 13:24

구현 화면 및 사용 기술

fakejsMSW를 이용하여 mock API를 만들어서 구현했다.

"react-intersection-observer"를 이용하여 총 아이템 중 마지막에서 4번째 위치에 있는 아이템을 사용자가 보면 데이터를 더 불러오도록 했다.

(다음엔 serializeQueryArgs, merge, forceRefetch도 같이 이용하여 구현해보려 한다..!)

구현

RTK Query

우선 RTK Query의 endpoints에 위와 같이 코드를 작성했다.

fakejs

백엔드와 위 데이터 형식으로 소통하기로 했기에 fakejs를 통해 위 데이터 구조를 만들었다.

import { faker } from "@faker-js/faker";

export function createRandomItem() {
  return {
    itemId: faker.number.int(),
    itemName: faker.commerce.product(),
    shopName: faker.company.name(),
    basicPrice: faker.commerce.price(),
    imageUrl: faker.image.url(),
    category: faker.commerce.productAdjective(), // userId: faker.string.uuid(),
  };
}

각 아이템에 따른 구조이다.

MSW

import { rest } from "msw";
import { createRandomItem } from "@mock/api/data/items/getItems";
import { faker } from "@faker-js/faker";

export const getItems = [
  rest.get("/items", async (req, res, ctx) => {
    const id = req.url.searchParams.get("page")!;

    const tempItems = faker.helpers.multiple(createRandomItem, { count: 20 });

    const items = {
      items: tempItems,
      finalPage: +id === 10,
    };

    return res(ctx.status(200), ctx.json(items));
  }),
];

MSW에서는 위에서 fakejs로 구현한 아이템을 20개씩 보내주도록 했다.

finalPage는 10번째 페이지 요청이 오면 true를 주도록 했다.

바닥 감지하기(react-intersection-observer)

-전체 상품을 보여주는 컴포넌트(MainItem/index.tsx)

const MainItem = () => {
  const [page, setPage] = useState(1);
  const { data, error, isLoading } = itemsApi.useGetAllItemsQuery(page);
  const [items, setItems] = useState<item[]>([]);
  const [finalPage, setFinalPage] = useState(false);
  const { ref, inView } = useInView();

  useEffect(() => {
    if (inView && !finalPage) {
      setPage((prev) => prev + 1);
    }
  }, [inView, finalPage]);

  useEffect(() => {
    if (data) {
      setItems((prev) => [...prev, ...data.items]);
      setFinalPage(data.finalPage);
    }
  }, [data]);

  return (
    <Wrapper>
      <TitleContainer>
        <h2>전체 상품</h2>
      </TitleContainer>

      {error && <div>새로고침하여 주세요.</div>}

      {isLoading && <div>로딩중...</div>}

      {data && (
        <ItemContainer>
          {items?.map((item: item, index) => {
            return (
              <Link
                to={`/eachitem/${item.itemId}`}
                key={item.itemId}
                ref={items.length - 5 === index ? ref : null}
              >
                <ItemBox>
                  <ItemImg>
                    <img src={item.imageUrl} alt={"상품 사진"} />
                  </ItemImg>
                  <ItemInfo>
                    <ItemName>{item.itemName}</ItemName>
                    <span>{item.category}</span>
                    <ItemPrice>{item.basicPrice}원</ItemPrice>
                  </ItemInfo>
                </ItemBox>
              </Link>
            );
          })}
        </ItemContainer>
      )}
    </Wrapper>
  );
};
  • 변수들
    • page를 이용하여 다음 페이지의 상품들을 가져왔다.
    • {data, error, isLoading}을 이용해 데이터를 가져오고, 에러 처리, 데이터를 가져오는 중을 적용시켰다.
    • items는 가져온 data들을 모아서 보여주는 역할을 했다.
    • finalPage는 마지막 페이지인 경우 값을 저장하는 용도로 사용했다.
    • {ref, inView}을 사용해서 내가 지정한 상품을 보고 있는지 여부를 체크알 수 있다.
  • useEffect
    1. 만약 inView가 true여서 상품을 보고 있고 finalPage가 아니면 page를 +1 해서 데이터를 더 불러왔다.
    2. 데이터를 가져왔으면 items에 가져온 데이터들을 추가하고 finalPage에 마지막 페이지인지를 기록했다. 
  • 화면
    • error가 있으면 새로고침하라는 메시지가 나오도록 했다.
    • isLoading이면 로딩 중이란 메시지가 나오도록 했다.
    • data가 있으면 아이템들을 보여줬다.
      • items에서 마지막 4번째 아이템을 보면 데이터를 더 불러옴

구현 완료

이렇게 구현을 완료했다!

(일단은 10번째 페이지가 마지막 페이지로 설정해서 구현했다.)

728x90
반응형
Comments