훅(Hook)

useIntersectionObserver 뷰포트 진입 감지 훅

IntersectionObserver를 React 훅으로 추상화. 카드가 화면에 진입할 때 페이드인되고, 누적/현재 가시 카드 수도 실시간 추적합니다.

ReactTypeScriptIntersectionObserver APITailwind CSS
라이브 데모
새 탭에서 열기
데모 불러오는 중…

제작 과정

왜 만들었나

스크롤 페이드인이나 lazy load는 예전엔 scroll 이벤트 + getBoundingClientRect로 했지만, 이건 메인 스레드에 직접 부하를 주고 layout thrashing 위험이 있다. IntersectionObserver는 브라우저가 별도 스레드에서 처리하므로 60fps를 안정적으로 유지한다. 거의 모든 모던 프론트엔드 프로젝트가 이 패턴을 한 번 이상 쓴다.

구현 포인트

- freezeOnceVisible 옵션으로 페이드인 후 disconnect 한 번 보이면 setState가 더 이상 발생하지 않아 메모리/리렌더 절약

- threshold가 배열일 때 매 렌더마다 새 참조라 useEffect deps에 그대로 넣으면 무한 루프 join(",")으로 stable key 생성

- 같은 요소에 두 번 옵저버 가능 데모에선 페이드인용(freeze ON)과 가시성 카운트용(freeze OFF) 두 개를 같이 사용

- ref는 useRef로 stable 보장. deps에 넣지 않고 effect 내부에서 ref.current로만 접근

알아둘 점

- rootMargin: "0px 0px -80px 0px"처럼 음수 bottom margin을 주면 요소가 화면 하단에 살짝 보이기 전에 트리거되어 애니메이션이 부드러워진다

- iframe 안에서는 root가 iframe의 viewport 쇼케이스 임베드와 자연스럽게 호환

- entry.intersectionRatio도 사용 가능 (얼마나 보이는지 0~1) progressive 효과에 활용

실무 활용 예시

- 이미지 lazy loading (특정 거리 안 들어왔을 때만 src 로드)

- 무한 스크롤 (sentinel 요소가 보이면 다음 페이지 fetch)

- 분석 (배너/CTA가 사용자에게 실제로 보였는지 impressions 추적)

- 스크롤 reveal 애니메이션 (현재 데모처럼)

소스 코드

· 데모 페이지에서 자동 추출
import type { Metadata } from "next";
import { UseIntersectionObserverDemo } from "./-components/UseIntersectionObserverDemo";

export const metadata: Metadata = {
  title: "useIntersectionObserver — 뷰포트 진입 감지 훅 (데모)",
  description:
    "스크롤하며 카드들이 진입할 때 페이드인되는 useIntersectionObserver 훅 라이브 시연. 진입 카운트와 가시 카드 수도 함께 보여줍니다.",
  robots: { index: false, follow: false },
};

export default function Page() {
  return (
    <main data-demo-embed-fixed="" className="min-h-[100dvh] bg-white">
      <UseIntersectionObserverDemo />
    </main>
  );
}
62조회수

댓글