2 분 소요

TIL

원티드 프리온보딩 개인 과제

구현

무한 스크롤

IntersectionObserver

IntersectionObserver는 타겟 엘레멘트와 타겟의 부모 혹은 상위 엘레멘트의 뷰포트가 교차되는 부분을 비동기적으로 관찰하는 API 화면(뷰포트) 상에 내가 지정한 타겟 엘레멘트가 보이고 있는지를 확인할 수 있는 API이다.

무한 스크롤을 구현할 때 사용한다.

callback 함수와 option 객체를 인수로 받는다.

  • 장점:
    Scroll event 를 사용하여 구현하는 것과는 다르게 호출 빈도를 줄이기 위해 debounce나 throttle을 이용하지 않아도 된다.

    reflow 하지 않아 성능이 더 좋다.

  • 메서드
    IntersectionObserver.observe(target): 관찰 시작 IntersectionObserver.unobserve(target): 관찰 종료 IntersectionObserver.disconnect(target): 관찰 멈추기

  • 프로퍼티
    boundingClientRect: reflow현상 없이 Element.getBoundingClientRect()와 동일한 정보를 반환 isIntersecting: target이 root 영역에 교차되고 있는지의 정보를 boolean으로 반환

구현 사항

일단, 스크롤이 뷰포트 밑에 닿았을 때 즈음 데이터를 가지고 와야 했다.
그냥 가지고 오는 것이 아닌 일단 처음에 10개를 보여준 후 다음 load를 했을 때 그 다음 10개를 붙이고,
또 그 다음 10개를 붙이는 식으로 구현해야 했다.

useEffect(() => {
  const onIntersect = async ([entry], observer) => {
    // 겹침 유무 확인 => 겹칠 경우에만 실행
    if (entry.isIntersecting) {
      // 겹쳤다면 잠시 관찰 종료
      observer.unobserve(entry.target);
      // **next page (그 다음 데이터 불러오기 부분)**
      setPage((prev) => prev + 1);
      // 다시 관찰 시작
      observer.observe(entry.target);
    }
  };

  // observer 생성
  let observer;
  if (target) {
    observer = new IntersectionObserver(onIntersect, {
      threshold: 0.4,
    });
    observer.observe(target);
  }

  // unmount 시 관찰 멈추기
  return () => observer && observer.disconnect();
}, [target]);
// return
{
  data?.items.length > 0 && page !== maxPage && <TargetDiv ref={setTarget} />;
}

페이지의 가장 밑에 target div를 만든다.
이 또한 data가 fetching이 된 경우와 maxPage가 아닌 경우에만 div가 생성되도록 한다.

useEffect(() => {
  // 불러온 데이터가 없을 경우는 교차한다고 해서 데이터를 더 불러올 이유가 없음
  // page가 마지막에 다다랐어도 그만 불러와야 함
  if (!data?.items && page !== maxPage) return;

  // 페이지가 변경될 때마다 (위에서 관찰하며 교차될때마다 1 증가시켜주니까)
  // 그 페이지에 해당하는 데이터를 불러옴
  // fetch more
  dispatch(loadMore(page));
}, [dispatch, page, data?.items, maxPage]);
// reducers
  loadMore: (state, action) => {
    // 현재 페이지를 받음
    const page = action.payload
    // 현재 fetch된 data (처음 검색했을 당시 전체 데이터/store에 있음)
    const { data } = current(state)
    // maxPage가 얼마인지 계산
    state.maxPage = Math.ceil(data?.items.length / 10)
    // 10개씩 잘라서 pageItems에 저장 => 사용할 컴포넌트에서는 pageItems를 map 돌리면 됨
    state.pageItems = [...state.pageItems, ...data?.items.slice((page - 1) * 10, page * 10)]
  },

TODO

레포 추가 제한 (4개) o
레포 추가 시 같은 레포 block o
레포 추가 및 삭제 시 alert 혹은 toast msg

레포 추가 후 다시 메인으로 갔을 때 load more 시 다시 처음부터 불러오는 현상 개선

레포 보관함에 있는 레포 선택 시 모든 issue 불러오기
issue pagenation 적용
issue 클릭 시 url 연결

회고 (TIL)

2022.03.20 Daily 회고

✏오늘 한 일

  • 기업 과제 개인 진행
    • 무한 스크롤 구현

⁉느낀 점

처음으로 무한 스크롤을 구현해보았는데, 완전하게 이해가 되지 않은 것이 좀 아쉽다.
다른 프로젝트에서 다시 시도해보면서 이해도를 높일 것이다.
항상 당연하게 사용할 때, 궁금한 것도 생겨나고, 이해가 더 잘 되니까 계속 해보자.

🎃현재 나의 상태

다시 머리가 돌아오는 것 같다.
슬럼프에 빠진 것 같긴 하지만, 다시 일어설 수 있을 정도이다.


댓글남기기