무한스크롤(Intersection Obsever) 리트라이 1일차

박효정·2023년 8월 11일

TIL

목록 보기
1/13

무한스크롤 리트라이 1일차

(코드 작성하면서 바로바로 기록하기 위해 설명하는투가 아니라 간략하게 작성)

const target = useRef<HTMLDivElement>(null);
...
<div ref={target}></div>

코드를 짰을 때, console.log(target.current)로 확인하니 현재 ref가 target으로 설정되어 있는 요소를 가져옴.


const target = useRef<HTMLDivElement | null>(null);
  // console.log(target.current); // <div></div>

  useEffect(() => {
    if(target.current){
      return observer.observe(target.current);
    }
  }, []);

  const options = {
    threshold: 1.0,
  };

  const callback = () => {
    if(target.current){
      // target.current.innerText = '관측 중';
      console.log(page);
      setPage((prev) => prev + 1);
    }
  };

  const observer = new IntersectionObserver(callback, options);

위의 코드를 브라우저에서 뷰포트에 들어왔는지 아닌지 추적을 못하는 상황

원인: 콜백함수에 전달되는 첫 번째 인자 entries 정보가 담겨있지 않음.

const callback = (entries: IntersectionObserverEntry[]) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        console.log('Element is intersecting:', entry.target);
  
        // 페이지 업데이트 등 원하는 동작 수행
        setPage((prev) => prev + 1);
        
        // 교차 영역 관찰 중단
        // observer.unobserve(entry.target);
      }
    });
  };

entries인자 추가


브라우저 새로고침 시에는 추적이 안되고 브라우저 켜놓은 상태에서 vscode 저장을 해야 추적이 되는 요상한 상황 발생.


콜백 함수 내에서 state 갱신함수를 쓰고 동시에 console.log() 로 확인하려고 하니까 비동기처리 때문에 바로 확인하지 못함. useEffect 를 사용하여 isEightUnder 가 변화할 때 마다 isEightUnder 값을 확인하니까 제때 잘 변경된 것을 확인함.


무한스크롤에 필요한 모든 함수를 callback에 넣어서 가져오려고 하니까 비동기 때문에 제대로 작동을 안함.
그래서 callback에는 뷰포트에 타겟이 추적되면 페이지 증가시키는 상태 갱신 함수만 넣어둠.
그리고 useEffect로 페이지 변화가 감지될 때마다 새로운 페이지의 get 요청을 불러오는 코드를 작성함.
제대로 가져오는 것처럼 보이나 콘솔 창에는 이미 불러온 데이터가 또 불러오는 지 부딪히는 경고창 뜸.

const target = useRef<HTMLDivElement | null>(null);
  const pageRef = useRef(page);

  useEffect(() => {
    if(target.current){
      return observer.observe(target.current);
    }
  }, []);

  const options = {
    threshold: 1.0,
  };

  // 이전 데이터가 8개 미만인지 상태
  const [isEightUnder, setIsEightUnder] = useState(false);

  useEffect(() => {
    if(isEightUnder){
      return;
    };

    // console.log(page); // page 증가되는 것 확인

    const showPageCommu = async () => {
      try {
        const response = await axios.get(`https://api.portfolly.site/boards/pages?division=${division}&page=${page}&size=${size}`);
        const currentData = response.data.data;
        console.log(currentData);
        console.log(currentData.length < 8);
        setIsEightUnder(currentData.length < 8); // 값 변경
        setData([...data, ...currentData]);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };
    
    showPageCommu();
  }, [page]);

  // 콜백 함수에는 뷰포트에 타겟이 닿으면 페이지만 증가시키는 함수 넣기
  // 콜백 함수 위에 UseEffect로 page 변경이 감지되면 실행시키는 함수
  // 그 함수안에는 바뀐 페이지로 get 요청 보내오고
  // 받은 데이터 개수가 8개 미만이면 IsEightUnder 상태 바꾸기
  // 다음 페이지 요청이 와도 isEightUnder 가 true 면 바로 리턴 때리기


  const callback = (entries: IntersectionObserverEntry[]) => {
    entries.forEach(async entry => {
      // console.log(entry.isIntersecting); // boolean 값으로 나옴 추적중이면 true 아니면 false
      if (entry.isIntersecting) {
        console.log('Element is intersecting:', entry.target);

        
        
        setPage(prev => prev + 1);
  
        // const currentPage = pageRef.current; // 현재 페이지 값을 캡처
  
      }
    });
  };

초기에 1페이지를 렌더링하는 것과 intersection observer로 가져오는 페이지의 데이터가 부딪히는 것으로 확인.
원인 : 초기렌더링의 1페이지가 observer 콜백 함수를 호출할 때도 갱신되지 않고 남아있는 것으로 보임.

...
if(page > 1){
  showPageCommu();
};
...

으로 변경해보았으나 페이지 변경은 확인되는데 실제 페이지에 해당하는 데이터 값을 불러오지 않음.

const target = useRef<HTMLDivElement | null>(null);
  const [isEightUnder, setIsEightUnder] = useState(false); // 이전 데이터가 8개 미만인지 상태
  // const [isLoading, setIsLoading] = useState(false); // 뷰포트에 타겟이 왔는지 아닌지

  // console.log(isLoading);

  useEffect(() => {
    const showPageCommu = async () => {
      try {
        const response = await axios.get(`https://api.portfolly.site/boards/pages?division=${division}&page=${page}&size=${size}`);
        const currentData = response.data.data;
        console.log(currentData);
        setData([...data, ...currentData]);
        if (currentData.length < 8) {
          setIsEightUnder(true);
          // observer.unobserve(target.current); // Intersection Observer 중지
          return;
        }
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };
    if(page > 1){
      showPageCommu();
    }
  }, [page]);

  // 페이지 바뀌면 실행시켜~ 페이지는 콜백함수로 바꿀거야~

  useEffect(() => {
    if(target.current){
      observer.observe(target.current);
    }
    return () => {
      if (target.current) {
        observer.unobserve(target.current);
      }
    };
  }, []);
  
  const options = {
    threshold: 1.0,
  };
  
  // 지금 뷰포트에 들어왔는지 아닌지 분간하는 state 만들어보자
  const callback = (entries: IntersectionObserverEntry[]) => {
    entries.forEach(async entry => {
      if (entry.isIntersecting) {
        // setIsLoading(entry.isIntersecting);
        if(isEightUnder){
          console.log('stop');
          return;
        }
        console.log('Element is intersecting:', entry.target);
        setPage((prev) => prev + 1);
      }
    });
  };

  const observer = new IntersectionObserver(callback, options);

코드 순서 바꾸고 몇 가지 수정해주니까 페이지 별로 데이터 잘 불러오는 것 확인함.
그래도 여전히 초기렌더링 단계에서 observer 인식을 못하는 문제 있음.


처음엔 아무것도 안되니까 막막했는데 블로그 찾아보고 구글링하고 챗지피티한테 물어보면서 하니까 무한스크롤이다라고 할 수 있는 정도의 구현은 70% 완성한 것 같습니다. 페이지별로 get 요청해 데이터를 불러오는 것에도 크게 문제는 없습니다. 아 같은 id인 게시글이 하나 있어서 콘솔창에 계속해서 경고 메시지가 뜨던데 이 부분은 담당 백엔드 개발자와 이야기를 해봐야겠습니다. 누구 쪽 문제인지 알아보고 고쳐서 콘솔창에 경고창이 뜨지 않도록!!!! 오늘은 결국 초기렌더링에서 observer가 인식되지 않는 문제를 해결하진 못했지만 이 문제를 해결하고나면 무한스크롤의 90%는 완성할 수 있다고 생각합니다. 나머지 10%는 디테일로 채워야겠습니다. 지금 동작하는 무한스크롤을 보면 넘 휘휙 지나가는 것 같아서 로딩 이미지도 넣어볼 계획입니다. 사용자의 입장에서 볼 때 좀 더 페이지스러운 ㅠㅠ 불편하지 않은 ㅠㅠ 어색하지 않은 ㅠㅠ 무한스크롤이 될 때까지.. 휴
profile
코린이 일기장

1개의 댓글

comment-user-thumbnail
2023년 8월 11일

정보에 감사드립니다.

답글 달기