[React/Web API] IntersectionObserver

dosilv·2021년 5월 30일
11
post-thumbnail

구현하고자 하는 기능은 ✨전 게시글✨ 참조

✨ IntersectionObserver API

🍋 언제 쓸까?

특정 요소의 가시성(화면에 나타나는지 사라졌는지!)에 따라 어떤 동작을 수행해야 할 때. 무한스크롤이나 지연 로딩, 그리고 어떤 요소가 뷰포트에 들어왔을 때 애니메이션 발생시키기... 등! 스크롤 동작과 관련된 여러 작업에 사용할 수 있다. document의 top부터 bottom까지 모든 스크롤 움직임을 감지하는 게 아니라 특정한 요소의 가시성을 관찰하는 것이기 때문에 그냥 scroll 이벤트 + throttle을 사용하는 것보다 더 효율적이라고 함!


🍋 어떻게 쓸까?

1. IntersectionObserver 생성하기

const Observer = new IntersectionObserver(callback, [option])

2. 콜백함수 설정하기

콜백함수는 인자로 entries를 받는다. entries는 entry를 담은 배열인데, entry는 해당 Observer가 관찰하고 있는 요소들을 말한다. (이따 4번에서 Observer가 관찰할 특정 요소 혹은 요소들을 지정해 줘야 함!)

🔺 콘솔로그로 entries를 찍었을 때

(entry들을 담은 배열임을 알 수 있다!)

🔺 entries의 요소인 entry를 찍었을 때

보이는 것처럼 entry는 저렇게나 많고 뭔지 모를.. 프로퍼티를 가진다...😬 그중 중요한 것만 정리해 보자면!

  • isIntersecting
    후에 설정할 옵션을 고려해서, 타겟이 현재 root에서 관찰되는지 여부! boolean으로 표시
  • boundingClientRect
    target의 xy좌표 같은 위치 정보나 width, heigth 같은 치수 정보를 담은 객체
  • target
    관찰 중인 타겟 엘리먼트

그래서 isIntersecting의 true/false 값을 이용해 타겟이 관찰될 때 실행할 코드를 콜백함수에 넣어줄 수 있다!

const callback = (entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      타겟이 관찰될 때 실행할 코드
    }
  }
}

3. 옵션 설정하기

옵션 객체는 root, rootMargin, threshold를 가질 수 있다.

  • root
    타겟이 될 요소의 가시성을 확인할 요소! 당연히 타겟의 조상요소 중 하나여야 하고, 디폴트 값은 브라우저 뷰포트.
  • rootMargin
    css margin처럼 px이나 %로, 그리고top right bottom left 순으로 설정할 수 있다. (차이점은 0이더라도 꼭 단위를 붙여줘야 함!) 양수 값을 주면 root 영역이 확대되고, 음수 값을 주면 root 영역이 감소한다. 예를 들어 {rootMargin: '0 0 -100px 0'}으로 설정할 경우 root의 bottom 영역이 줄어들어서, 뷰포트의 끝에서 100px 더 올라와야 관찰된 걸로 인지됨!
  • threshold
    대상이 얼만큼 관찰되었을 때 콜백함수를 실행할지 설정하는 옵션. 0에서 1 사이의 값을 가질 수 있고, 0일 경우 1px이라도 관찰되었을 때, 1이면 요소 전체가 root 영역에 들어왔을 때를 의미한다. 디폴트는 0.
const option = {rootMargin: '-10%', threshold: 0.5}

이런 식으로 필요한 옵션만 설정할 수도 있고, 옵션이니까 아예 생략도 가능가능~~~

4. 관찰할 타겟 요소 지정해 주기

콜백함수와 옵션까지 넣어 열심히 만든 옵저버에게... 이제 특정 요소를 관찰하라고 명령을 해 줘야 한다! observe 메서드에 인자로 타겟이 될 엘리먼트를 넣어주면 되는데, React에선 DOM node를 직접 선택할 수 없기 때문에 이전 글에서 공부한 useRef를 이용해야 한다.

const target = useRef();
...
observer.observe(target.current);
...
<div ref={target}>...</div>

여기까지 잘 했다면! 타겟이 관찰될 때마다 콜백함수를 실행하는 IntersectionObserver 완성~~~ (아마도...ㅎㅎ)


🍋 적용한 코드

관찰할 요소에 fetch 받아와야 하는 데이터가 포함되어 있어서 fetch가 끝나면 실행하도록 하기 위해 useEffect에 넣어줬고, 관찰할 요소가 여러 개라서 타겟 설정 과정에서도 forEach를 사용했다. 결론적으로 쪼끔 더 복잡해진 코드...

const tabRef = useRef([]);
const [currentTab, setCurrentTab] = useState();

useEffect(() => {
  const changeTab = entries => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        setCurrentTab(entry.target);
      }
    });
  };

  const observerOption = { rootMargin: '-10% 0px', threshold: 0.1 };


  const tabObserver = new IntersectionObserver(
    changeTab,
    observerOption
  );

  tabRef.current.forEach(tab => tabObserver.observe(tab));

  return () => tabObserver.disconnect();
  }, [course]);
*[21.06.15 추가] changeTab 함수와 observerOption도 한 번만 선언되면 될 것 같아서... 브라우저 최적화를 위해 useEffect 안에 같이 넣어 줌!


🔻 아직 데이터는 미완성이지만..... 결과물

🔻 모바일 버전도 잘 작동함~~~!

별거 아니게 생겨서 얕봤는데 은근 오래걸렸다....😖
그래도 이번 기회에 useRef랑 IntersectionObserver에 대해 나름 깊이 공부한 것 같아서 뿌듯✨✨



intersection-observer API MDN - Mozilla
profile
DevelOpErUN 성장일기🌈

4개의 댓글

comment-user-thumbnail
2021년 5월 30일

너무 멋져요~~!!! 도은님은 프론트의 끝판왕🕶

1개의 답글
comment-user-thumbnail
2021년 5월 30일

도은님 저 lazy loading 해야되는데 도은님 글 보면서 할래요 😎😎😎

1개의 답글