Oneul Code - 12. Beyond Scroll Events: Mastering Intersection Observer for Lazy Loading and Infinite Scroll

2025-10-02

Intersection Observer로 웹 성능 최적화하기

이번 글에서는 웹 애플리케이션의 성능을 향상시키는 데 유용한 Intersection Observer API에 대해 알아보겠습니다. 이 API는 요소의 가시성을 비동기적으로 관찰할 수 있어, 스크롤 이벤트 처리나 이미지 지연 로딩(lazy loading), 무한 스크롤 구현 등에서 성능 최적화에 큰 도움이 됩니다.


1. Intersection Observer란?

Intersection Observer API는 특정 DOM 요소가 뷰포트(viewport) 또는 지정된 부모 요소와 교차(intersect)하는지 감지할 수 있는 비동기 API입니다.
이전에는 스크롤 이벤트에 직접 scroll 이벤트를 걸고 계산해야 했지만, Intersection Observer는 브라우저가 최적화하여 비동기적으로 처리하므로 성능 부담이 훨씬 적습니다.

const targetElement = document.querySelector('#myElement');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('요소가 뷰포트에 들어왔습니다!');
    }
  });
}, {
  root: null, // 뷰포트를 기준
  rootMargin: '0px', // 마진 없음
  threshold: 0.1 // 요소 10% 이상 보이면 콜백 실행
});

observer.observe(targetElement);
  • entries: 관찰 대상 요소들의 상태 배열

  • entry.isIntersecting: 요소가 관찰 기준과 교차하면 true

  • root: 관찰 기준 요소. null이면 뷰포트

  • rootMargin: 관찰 기준에 추가 마진

  • threshold: 교차 비율(0~1) 또는 배열. 예: [0, 0.5, 1]


2. 옵션 상세 설명

root

  • 교차 감지의 기준이 되는 요소

  • null이면 브라우저 뷰포트

  • 예: 스크롤 영역이 제한된 div 안에서 관찰

rootMargin

  • 기준 요소에 마진을 추가

  • 예: ‘0px 0px -50% 0px’ → 아래쪽 50% 마진 추가

threshold

  • 요소가 얼마나 보여야 콜백을 실행할지 비율 설정

  • 단일 값 또는 배열 가능

  • 예: [0, 0.25, 0.5, 0.75, 1] → 각 단계에서 콜백 호출


3. 실용 예제

3-1. 이미지 지연 로딩

const images = document.querySelectorAll('img[data-src]');

const loadImage = (image) => {
  image.src = image.dataset.src;
  image.onload = () => image.classList.add('loaded');
};

const observer = new IntersectionObserver((entries, obs) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      loadImage(entry.target);
      obs.unobserve(entry.target);
    }
  });
}, { threshold: 0.1 });

images.forEach(img => observer.observe(img));
  • 초기에는 src를 빈 값 또는 저용량 이미지로 설정

  • data-src에 실제 이미지 URL

  • 요소가 뷰포트에 10% 이상 보이면 로드

  • 로드 후 unobserve로 관찰 종료 → 성능 최적화

3-2. 무한 스크롤 구현

const sentinel = document.querySelector('#sentinel');

const loadMoreContent = () => {
  const newItem = document.createElement('div');
  newItem.textContent = '새로운 콘텐츠';
  document.body.appendChild(newItem);
};

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      loadMoreContent();
    }
  });
}, { threshold: 1.0 });

observer.observe(sentinel);
  • #sentinel이 뷰포트에 완전히 보일 때 다음 콘텐츠 로드

  • 기존 scroll 이벤트 방식보다 훨씬 효율적


4. React에서의 활용

React에서는 Intersection Observer를 커스텀 훅으로 만들어 재사용 가능

import { useState, useEffect } from 'react';

const useInView = (targetRef, options) => {
  const [inView, setInView] = useState(false);

  useEffect(() => {
    if (!targetRef.current) return;

    const observer = new IntersectionObserver(([entry]) => {
      setInView(entry.isIntersecting);
    }, options);

    observer.observe(targetRef.current);

    return () => observer.disconnect();
  }, [targetRef, options]);

  return inView;
};
  • targetRefuseRef()로 생성한 참조

  • 특정 요소의 가시성 상태를 쉽게 추적 가능


5. 심화: trackVisibility와 scrollMargin

  • trackVisibility: true → 요소의 가시성 비율 추적 가능 (FPS 측정, 애니메이션 트리거 등)

  • scrollMargin → 내부 스크롤 컨테이너의 마진 설정

const observer = new IntersectionObserver(callback, {
  root: document.querySelector('.scroll-container'),
  rootMargin: '10px',
  threshold: 0.5,
  trackVisibility: true,
  delay: 100 // ms 단위
});
  • 고급 UI/UX 트리거에 활용 가능

6. 성능 최적화 팁

  • 콜백 내 무거운 연산 최소화

  • unobserve()로 더 이상 필요 없는 요소 관찰 종료

  • 적절한 threshold 설정으로 불필요한 호출 방지

  • 대규모 리스트는 lazy loading + virtual scrolling과 결합 추천


7. Oneul Code를 정리하며…

오늘 배운 핵심은 Intersection Observer를 활용한 효율적 DOM 관찰과 성능 최적화입니다.

  1. Intersection Observer는 스크롤 이벤트를 직접 처리하는 것보다 성능 부담이 적음

  2. root, rootMargin, threshold 옵션을 적절히 활용

  3. 이미지 lazy loading, 무한 스크롤, 애니메이션 트리거 등에 활용 가능

  4. React에서 훅으로 재사용하면 UI 최적화와 코드 가독성 모두 확보

  5. 고급 옵션(trackVisibility, scrollMargin)으로 세밀한 관찰 가능

Oneul Code는 오늘 배운 내용을 기록하며, 여러분도 브라우저 콘솔과 React 프로젝트에서 직접 사용해보시길 권장합니다. Intersection Observer를 제대로 이해하고 활용하면, 복잡한 프론트엔드 환경에서도 성능과 사용자 경험을 동시에 잡는 개발자가 될 수 있습니다.