본문으로 바로가기
TaeyoungKim.dev

IntersectionObserver threshold 0.5: 스크롤할 때 콜백이 다시 실행되는 이유

웹작성 약 3분 읽기TaeyoungKim
LinkedInX

스크롤해서 카드를 절반쯤 보여 주면 등장 효과가 한 번 실행될 줄 알았다. 그런데 다시 올리고 내리니 카드가 또 움직인다. threshold: 0.5를 '50% 이상일 때 딱 한 번'으로 읽었다면 충분히 헷갈릴 만하다. 이 값은 콜백을 알려 줄 경계다. 한 번만 실행할지는 우리가 정해야 한다.

IntersectionObserver의 threshold 0.5는 언제 콜백을 호출할까?

관찰 대상의 면적 중 root와 겹친 비율이 0.5 경계를 위로 또는 아래로 지날 때 콜백이 호출된다. observe()로 처음 관찰을 시작할 때도 현재 상태를 알리는 콜백이 온다. 0.5는 콜백을 통과시키는 영구 조건이 아니다. 이 동작은 MDN의 Intersection Observer 설명에서도 확인할 수 있다.

아래 그림은 같은 카드가 70% 보이다가 스크롤 뒤 30%만 남는 순간을 나타낸다. 두 상태 모두 50% 경계의 양쪽에 있으므로, 내려가는 방향에서도 콜백이 다시 온다.

70%에서 30%로 바뀌면 실제로 무엇이 찍힐까?

높이 100px인 스크롤 영역에 높이 100px 카드를 넣어 보자. 아래 내용을 HTML 파일로 저장해 브라우저에서 열면 된다.

html
<div id="scroll" style="height:100px; overflow:auto; border:1px solid">
  <div style="height:150px"></div>
  <div id="card" style="height:100px">카드</div>
  <div style="height:300px"></div>
</div>

<script>
  const root = document.querySelector('#scroll');
  const card = document.querySelector('#card');

  const observer = new IntersectionObserver((entries) => {
    for (const entry of entries) {
      console.log(entry.intersectionRatio.toFixed(2));
    }
  }, { root, threshold: 0.5 });

  observer.observe(card);
</script>

개발자 도구 콘솔에서 root.scrollTop = 180을 실행하고 콜백을 확인한 뒤, root.scrollTop = 220을 실행해 보자. 이 크기의 예제에서는 카드가 각각 약 70%, 30% 보인다.

순간노출 비율콜백이 오는 이유
관찰 시작0.00첫 상태 알림
scrollTop = 1800.7050% 경계를 위로 통과
scrollTop = 2200.3050% 경계를 아래로 통과

intersectionRatio는 카드 면적의 교차 비율이다. 이 예제에서는 너비가 유지돼 높이의 70%·30%와 맞지만, 모든 화면에서 숫자가 꼭 이 값으로 찍힌다는 뜻은 아니다. 중요한 건 아래로 다시 지날 때도 콜백이 온다는 점이다.

50% 이상에서 한 번만 등장시키려면?

앞의 observer를 다음 코드로 바꿔 보자. 콜백 호출 자체를 막을 수는 없지만, 실행할 조건을 직접 검사하고 한 번 처리한 카드는 관찰에서 뺄 수 있다.

js
const observer = new IntersectionObserver((entries, observer) => {
  for (const entry of entries) {
    if (entry.intersectionRatio < 0.5) continue;

    entry.target.classList.add('visible');
    observer.unobserve(entry.target);
  }
}, { root, threshold: 0.5 });

observer.observe(card);

관찰을 계속했다면 30% 상태는 비율 검사에서 걸린다. 하지만 70% 보이는 첫 순간에 visible 클래스를 붙이고 unobserve()하므로, 이 수정 코드에서는 다시 스크롤해도 같은 카드의 콜백 자체가 오지 않는다. 별도의 visible 스타일이나 애니메이션은 화면 디자인에 맞게 정의하면 된다.

isIntersecting만으로 '50% 이상'을 판정하지는 말자. 이 속성은 이름처럼 교차 여부를 표현하며, 원하는 노출 비율 조건은 intersectionRatio로 명시하는 편이 정확하다. 스크롤 영역 안의 요소를 관찰한다면 위 예제처럼 root를 그 영역으로 지정해야 한다. 생략하면 브라우저 전체 화면을 기준으로 계산해 숫자가 달라진다.

핵심 요약

threshold: 0.5는 50% 경계를 넘는 양방향 변화와 최초 관찰 상태를 알려 준다. 한 번만 등장시킬 카드라면 콜백 안에서 intersectionRatio >= 0.5를 확인하고 처리 직후 unobserve()하자. 값은 경보의 기준이고, 실제 행동의 조건은 코드가 결정한다.

작성자

TaeyoungKim

기초 개념을 구현과 검증, 실제 운영 판단까지 연결해 기록합니다.

#IntersectionObserver#threshold#intersectionRatio#스크롤 애니메이션#CSS 애니메이션#JavaScript

함께 읽으면 좋은 글