웹사이트를 둘러보다 보면 스크롤할 때 특정 요소가 화면에 나타나면 애니메이션이 실행되거나 데이터가 로드되는 경험을 한 적이 있을 것이다. 또는 무한 스크롤로 자동으로 더 많은 콘텐츠가 로드되는 페이지가 있다. 이런 기능들은 Intersection Observer API를 활용하면 쉽게 구현할 수 있다.
Intersection Observer는 어떤 요소가 화면에 보이는지 감지하는 역할을 하는 JavaScript API다.
사용자가 페이지를 스크롤할 때, 특정 요소가 화면(viewport)에 들어왔는지 나갔는지를 자동으로 감지해준다. 이때 원하는 작업을 실행할 수 있다.
아래와 같은 상황에 쓰인다.
Intersection Observer를 사용하려면 세 가지만 이해하면 된다.
const observer = new IntersectionObserver(callback, options);
observer.observe(element);
const callback = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('요소가 화면에 보입니다!');
}
});
};
기본 예제: 섹션이 보일 때 메시지 출력
import { useEffect, useRef } from 'react';
function ScrollDetectionDemo() {
const sectionRef = useRef(null);
useEffect(() => {
// 1. 콜백 함수 정의
const handleIntersection = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('섹션이 화면에 보입니다!');
} else {
console.log('섹션이 화면을 벗어났습니다');
}
});
};
// 2. Observer 생성
const observer = new IntersectionObserver(handleIntersection);
// 3. 요소 감시 시작
if (sectionRef.current) {
observer.observe(sectionRef.current);
}
// 4. 컴포넌트 언마운트 시 정리
return () => {
if (sectionRef.current) {
observer.unobserve(sectionRef.current);
}
};
}, []);
return (
<div>
<div style={{ height: '100vh', background: 'white' }}>
<h1>위로 스크롤하세요</h1>
</div>
<div ref={sectionRef}>
여기가 감지 대상입니다!
</div>
<div style={{ height: '100vh', background: 'white' }}>
<h1>아래로 스크롤하세요</h1>
</div>
</div>
);
}
export default ScrollDetectionDemo;
여러 섹션을 동시에 감지하기
import { useEffect, useRef } from 'react';
function MultiSectionDetection() {
const section1Ref = useRef(null);
const section2Ref = useRef(null);
const section3Ref = useRef(null);
useEffect(() => {
const handleIntersection = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 보이는 요소에 'visible' 클래스 추가
entry.target.classList.add('visible');
} else {
entry.target.classList.remove('visible');
}
});
};
// 옵션 설정
const options = {
threshold: 0.3 // 요소의 30%가 보일 때 감지
};
const observer = new IntersectionObserver(handleIntersection, options);
// 모든 섹션 감시
[section1Ref, section2Ref, section3Ref].forEach(ref => {
if (ref.current) {
observer.observe(ref.current);
}
});
return () => {
[section1Ref, section2Ref, section3Ref].forEach(ref => {
if (ref.current) {
observer.unobserve(ref.current);
}
});
};
}, []);
const sectionStyle = {
height: '400px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '32px',
fontWeight: 'bold',
margin: '100px 0',
opacity: 0.5,
transition: 'all 0.6s ease-out',
borderRadius: '10px'
};
return (
<div style={{ padding: '20px' }}>
<h1>섹션 감지 데모</h1>
<div ref={section1Ref} style={{ ...sectionStyle, background: '#FF6B6B' }}>
섹션 1
</div>
<style>{`
.visible {
opacity: 1 !important;
transform: translateY(0);
}
`}</style>
<div ref={section2Ref} style={{ ...sectionStyle, background: '#4ECDC4' }}>
섹션 2
</div>
<div ref={section3Ref} style={{ ...sectionStyle, background: '#FFE66D' }}>
섹션 3
</div>
</div>
);
}
export default MultiSectionDetection;
Intersection Observer에는 다양한 옵션이 있다.
초보자가 꼭 알아야 할 욥션 3가지를 알아보자.
const options = {
// 1. threshold: 감지 시점 설정 (0~1 사이)
threshold: 0.5, // 요소의 50%가 보일 때 감지
// 2. root: 감지 범위 설정
root: null, // null이면 브라우저 뷰포트 기준
// 3. rootMargin: 감지 범위 여백 조정
rootMargin: '50px' // 화면에서 50px 전에 감지 시작
};
예시
// 요소가 화면에 조금만 들어오면 감지
const observer = new IntersectionObserver(callback, { threshold: 0.1 });
// 요소가 완전히 보일 때만 감지
const observer = new IntersectionObserver(callback, { threshold: 1 });
// 요소가 화면에 보이기 100px 전부터 감지
const observer = new IntersectionObserver(callback, { rootMargin: '100px' });
const [isVisible, setIsVisible] = useState(false);
const handleIntersection = (entries) => {
entries.forEach(entry => {
// 상태가 바뀔 때만 업데이트
if (entry.isIntersecting && !isVisible) {
setIsVisible(true);
}
});
};
const handleIntersection = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 할 일 처리
observer.unobserve(entry.target);
}
});
};