Intersection Observer는 아마도 모두가 좋아하는 API 중 하나이며 모든 주요 브라우저에서 사용할 수 있습니다. 개발자는 이미지 및 동영상 지연 로드, 요소가 position: sticky에 도달할 때 알림, 분석 이벤트 실행 등 다양한 사용 사례에 이 API를 사용해 왔습니다.
가장 기본적인 형태의 Intersection Observer v1 API는 다음과 같습니다.
const onIntersection = (entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
console.log(entry);
}
}
};
const observer = new IntersectionObserver(onIntersection);
observer.observe(document.querySelector('#some-target'));
Intersection Observer v1의 가시성 문제
Intersection Observer v1 API를 사용하면 요소가 창의 표시 영역으로 스크롤되는 시점을 알 수 있습니다. 하지만 해당 요소가 폐색이라고 하는 다른 페이지 콘텐츠로 가려져 있는지 또는 transform, opacity, filter과 같은 CSS에 의해 수정되어 요소가 보이지 않게 되는지 확인할 수는 없습니다.
최상위 문서의 요소의 경우 JavaScript로 DOM을 분석하여 이 정보를 확인할 수 있습니다(예: DocumentOrShadowRoot.elementFromPoint() 사용).
반면 문제가 되는 요소가 서드 파티 iframe에 있는 경우에는 동일한 정보를 얻을 수 없습니다.
가시성이 중요한 이유
안타깝게도 인터넷에는 악의적인 행위자가 있습니다. 예를 들어 부정직한 게시자는 웹사이트에서 클릭당 지불 광고를 사용할 수 있습니다. 광고 네트워크에서 이 계획을 발견할 때까지는 더 많은 수익을 얻기 위해 사용자를 속여 이러한 광고를 클릭하도록 유도할 수 있습니다. 일반적으로 이러한 광고는 iframe에 게재됩니다.
사용자를 속이기 위해 게시자는 CSS를 사용하여 광고 iframe을 완전히 투명하게 만들 수 있습니다(iframe { opacity: 0; }). 그런 다음 사용자가 클릭하고 싶어 하는 귀여운 고양이 동영상과 같은 매력적인 콘텐츠 위에 투명 iframe을 배치할 수 있습니다.
이를 클릭재킹이라고 합니다.
데모의 상단 섹션에서 클릭재킹 공격이 작동하는 것을 확인할 수 있습니다. 고양이 동영상을 '시청'하고 트릭 모드를 활성화해 보세요. iframe이 투명한 상태에서 의도치 않게 클릭한 경우에도 iframe의 광고는 클릭을 적법한 것으로 등록합니다.

Intersection Observer v2의 개선사항
Intersection Observer v2는 사람이 정의하는 대로 요소의 '표시 여부'를 추적할 수 있습니다. IntersectionObserver 생성자에서 옵션을 설정하면 결과 IntersectionObserverEntry 인스턴스에 isVisible라는 새 불리언 필드가 포함됩니다. isVisible가 true인 경우 브라우저는 요소가 다른 콘텐츠에 의해 완전히 가려지지 않고 표시를 숨기거나 변경하는 시각 효과가 없는지 확인합니다. isVisible이 false인 경우 브라우저는 이를 보장할 수 없습니다.
사양은 거짓 음성을 허용합니다. 요소가 실제로 표시되고 변경되지 않은 경우에도 isVisible이 false일 수 있습니다. 성능을 위해 브라우저는 경계 상자, 직사각형과 같은 더 간단한 계산을 사용하며 border-radius과 같은 복잡한 세부정보에 대해 모든 픽셀을 확인하지 않습니다.
하지만 어떠한 상황에서도 오탐은 허용되지 않습니다. 즉, 요소가 완전히 표시되고 수정되지 않은 경우 isVisible이 true이 아닙니다.
이 변경사항 적용
이제 IntersectionObserver 생성자는 다음 두 가지 구성 속성을 추가로 사용합니다.
delay은 지정된 타겟에 대해 관찰자의 알림 간 최소 지연 시간(밀리초)을 나타내는 숫자입니다.trackVisibility는 관찰자가 타겟의 가시성 변경사항을 추적할지 여부를 나타내는 불리언입니다.
trackVisibility가 true인 경우 delay는 100 이상으로 설정해야 합니다(즉, 100ms마다 알림이 하나 이하임).
가시성은 계산하는 데 비용이 많이 들기 때문에 성능 저하와 배터리 소모를 방지하기 위한 예방 조치입니다. 책임감 있는 개발자는 허용되는 가장 큰 지연 값을 사용해야 합니다.
사양은 가시성을 계산합니다. 버전 1과 마찬가지로 관찰자의 trackVisibility 속성이 false이면 타겟이 표시된 것으로 간주됩니다.
버전 2에서는 다음과 같은 경우 타겟이 표시되지 않는 것으로 간주됩니다.
2D 변환이나 비례 2D 업스케일링이 아닌 유효한 변환 행렬이 있습니다.
타겟 또는 타겟이 포함된 블록 체인의 요소의 유효 불투명도가 1.0보다 작습니다.
타겟 또는 타겟이 포함된 블록 체인의 요소에 필터가 적용되어 있습니다.
타겟이 다른 페이지 콘텐츠에 의해 완전히 가려지지 않는다고 구현에서 보장할 수 없는 경우
즉, 현재 구현은 가시성을 보장하는 데 상당히 보수적입니다. 예를 들어 거의 눈에 띄지 않는 그레이스케일 필터(filter: grayscale(0.01%))를 적용하거나 가장 작은 투명도(opacity: 0.99)를 설정하면 요소가 보이지 않게 됩니다.
다음은 새로운 API 기능을 보여주는 코드 샘플입니다. 데모의 두 번째 섹션에서 클릭 추적 로직이 작동하는 것을 확인할 수 있습니다. 강아지 동영상을 '시청'해 보세요. 트릭 모드를 활성화하여 악의적인 행위자로 변신하고 Intersection Observer v2가 적법하지 않은 광고 클릭이 추적되지 않도록 하는 방법을 확인하세요. Intersection Observer v2가 보호해 줍니다.

<!DOCTYPE html>
<!-- This is the ad running in the iframe -->
<button id="callToActionButton">Buy now!</button>
// This is code running in the iframe.
// The iframe must be visible for at least 800ms prior to an input event
// for the input event to be considered valid.
const minimumVisibleDuration = 800;
// Keep track of when the button transitioned to a visible state.
let visibleSince = 0;
const button = document.querySelector('#callToActionButton');
button.addEventListener('click', (event) => {
if ((visibleSince > 0) &&
(performance.now() - visibleSince >= minimumVisibleDuration)) {
trackAdClick();
} else {
rejectAdClick();
}
});
const observer = new IntersectionObserver((changes) => {
for (const change of changes) {
// ⚠️ Feature detection
if (typeof change.isVisible === 'undefined') {
// The browser doesn't support v2, fallback to v1 behavior.
change.isVisible = true;
}
if (change.isIntersecting && change.isVisible) {
visibleSince = change.time;
} else {
visibleSince = 0;
}
}
}, {
threshold: [1.0],
// 🆕 Track the actual visibility of the element
trackVisibility: true,
// 🆕 Set a minimum delay between notifications
delay: 100
}));
// Require that the entire iframe be visible.
observer.observe(document.querySelector('#ad'));
추가 리소스
- Intersection Observer 작업 초안
- Chrome 플랫폼 상태의 Intersection Observer v2
감사의 말씀
검토해 주신 시메온 빈센트, 요아브 바이스, 마티아스 바이넨스님과 Chrome에서 기능을 검토하고 구현해 주신 스테판 자거님께 감사드립니다.