이전 포스팅에서 페이지 구조에 생략된 게 있었는데
바로 showHeight 를 prop으로 넘겨주고있는 부분이다.
<>
<LandingImage />
<HeaderComponent showHeight={550}>
<main>
{children}
</main>
<ToTopBtn showHeight={550}>
</>
특정 스크롤 높이 조건을 만족하면 컴포넌트가 렌더링되도록 했다.
export default function ToTopButton({ showHeight }) {
const [show, setShow] = useState(false);
useEffect(() => {
const handleScroll = () => {
if (window.scrollY > showHeight) {
setShow(true);
} else {
setShow(false);
}
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [showHeight]);
🤔그런데 스크롤 이벤트를 사용하는건 지양해야한다고 하는데 왜그럴까?
구글링을 해보니 이와 관련된 포스팅이 거의 없었다.
감사하게도 직접 성능 테스트를 진행한 결과를 공유한 포스팅 을 보니 퍼포먼스 면에서 intersection observer가 균일한 결과(프레임율)를 내는 것을 알수 있다.
Intersection Observer 는 비동기적으로 동작한다
따라서 레이아웃, 페인팅, 렌더링 작업이 끝난 뒤에 콜백을 호출하기때문에 일정한 프레임율을 유지할 수 있다.
Scroll Event 와 Intersection Observer 비교
요즘 회사에서 진행하는 프로젝트가 스크롤 이벤트로 구현되어야 하는 기능들이 굉장히 많다.
medium.com
반대로
스크롤 이벤트는 사용자가 페이지를 스크롤하는동안 즉각 실행되며,계속 호출되기 때문에 매우 자주 발생하게 된다.
이벤트가 렌더링 작업과 동기적으로 실행되기 때문에 프레임이 불안정해지는 것이다.
호출 빈도를 제어하기 위해서는 throttle 이나 debounce와 같은 기술이 추가로 요구된다.
또한 scrollY 값은 절대적인 픽셀 값을 기준이므로, 뷰포트나 요소의 위치 변경과 무관하기때문에
반응형 레이아웃이나 동적 콘텐츠에서 유지 보수가 어렵다는 단점이 있다.
🤔프레임율이란?
프레임율 (FPS, Frames Per Second)
: 1초 당 화면에 보여주는 이미지 (프레임) 개수
더 높은 FPS 일수록 화면이 부드럽게 움직이는것처럼 느낀다
따라서 사용자 경험과도 이어지기 때문에 중요한 요소이다!
일정한 FPS는 코드가 최적화있음을 나타내는 성능 지표로도 쓰인다고 한다.
프레임율이 낮아지는 이유는 아래와 같다
- 무거운 작업 (복잡한 계산, DOM 업데이트)
- 과도한 이벤트 호출
- 하드웨어 성능 부족
그래서 이 부분도 Intersection Observer를 사용한 코드로 바꿔주었다 !
이전 코드와 크게 다르지 않다
페이지 컴포넌트가 옵저버 관련 코드로 너무 길어져서 커스텀 훅으로 분리해주었다
const useHeaderObserver = () => {
const [show, setShow] = useState(false);
// 헤더 show 여부를 결정하는 옵저버
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const { id } = entry.target;
if (entry.isIntersecting) {
if (id === "showExtra") {
setShow(true);
} else if (id === "doNotShowExtra") {
setShow(false);
}
}
});
},
{
rootMargin: "-50% 0px",
}
);
// showHeader
const show = document.getElementById("showExtra");
if (show) {
observer.observe(show);
}
// doNotShowHeader
const doNotShowHeader = document.getElementById("doNotShowExtra");
if (doNotShowHeader) {
observer.observe(doNotShowHeader);
}
return () => {
observer.disconnect();
};
}, []);
return show;
};
export default useHeaderObserver;

'개발 공부 일지 > JavaScript' 카테고리의 다른 글
함수에서 this 바인딩 (2) | 2025.02.01 |
---|---|
입출력 - readline이 뭘까? (0) | 2025.01.22 |
Intersection Observer API로 스크롤 기반 네비게이션 구현하기 (0) | 2025.01.16 |
File vs FileList (FormData에 문자열 배열을 추가하면 FileList가 된다?) (0) | 2025.01.08 |
Blob (블롭) 알아보기 (0) | 2025.01.08 |