특정 스크롤에 도달하면 스크롤되던 컴포넌트에 fixed를 주는 것
해당 useEffect
문에서는 scroll 이벤트를 탐지해서 scrollEvent
라는 콜백 함수를 실행시킨다.
정상적으로 작동한다면 console.log
가 실행 되어야 하지만, 동작하지 않는다..
const Container = styled.div` display: flex; height: 90vh; overflow-y: auto; `
스크롤 이벤트를 구현하려는 영역이 위의 Container
로 감싸져 있었는데,
스크롤은 화면보다 컨텐츠 요소가 클 경우 발생되기 때문에 높이 자체를 100%나 100vh 로 지정할 경우에도 스크롤 이벤트가 작동되지 않는다.
Container의 height 값을 제거하여 해결할 수 있다
const Container = styled.div`
display: flex;
// height: 90vh; // 제거
overflow-y: auto;
`
또는 컨텐츠의 높이를 충분히 확보하여 스크롤이 생기도록 할 수 있다
const Container = styled.div`
display: flex;
min-height: 90vh; // height 대신 min-height 사용
overflow-y: auto;
`
추가로 스크롤 이벤트를 사용할 때는 이벤트가 빈번하게 발생하므로 최적화를 해주면 좋다.
- 마지막 스크롤 이벤트로부터 일정 시간(예: 100ms) 동안 추가 이벤트가 없을 때만 실행
- 불필요한 함수 호출을 줄여 성능 개선
- API 호출이나 DOM 조작과 같은 무거운 작업이 있을 때 더욱 효과적임
// 디바운싱 적용
useEffect(() => {
let timeoutId: NodeJS.Timeout;
const scrollEvent = () => {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
console.log('scroll event!');
// 스크롤 관련 로직
}, 100);
};
window.addEventListener('scroll', scrollEvent);
return () => window.removeEventListener('scroll', scrollEvent);
}, []);
- 스크롤 이벤트 대신 IntersectionObserver를 사용하면 더 효율적으로 구현가능
- 스크롤 이벤트와 달리 요소가 뷰포트에 들어오거나 나갈 때만 콜백이 실행되어 성능적 이점이 있음
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
// entry.isIntersecting: 요소가 뷰포트와 교차하는지 여부
// entry.intersectionRatio: 요소가 뷰포트와 교차하는 비율
// entry.boundingClientRect: 요소의 경계 정보
if (entry.isIntersecting) {
// 요소가 뷰포트에 들어왔을 때의 로직
} else {
// 요소가 뷰포트에서 벗어났을 때의 로직
}
},
{ threshold: 0.5 } // 50% 이상 보일 때 콜백 실행
// { threshold: [0, 0.5, 1] } // 0%, 50%, 100% 교차 시 콜백 실행
// rootMargin 옵션으로 감지 영역을 확장하거나 축소할 수 있음.
);
if (targetRef.current) {
observer.observe(targetRef.current);
}
return () => observer.disconnect();
}, []);
// 스크롤에 따른 헤더 고정 예시
function Header() {
const headerRef = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (!entry.isIntersecting) {
// 헤더가 뷰포트를 벗어나면 fixed 스타일 적용
headerRef.current.classList.add('fixed');
} else {
// 헤더가 뷰포트에 들어오면 fixed 스타일 제거
headerRef.current.classList.remove('fixed');
}
},
{ threshold: 0 }
);
if (headerRef.current) {
observer.observe(headerRef.current);
}
return () => observer.disconnect();
}, []);
return <header ref={headerRef}>...</header>;
}
스크롤 이벤트를 제대로 감지하기 위해서는 컨테이너의 높이 설정이 중요합니다.
단순히 뷰포트 높이를 기준으로 설정하면 스크롤이 발생하지 않을 수 있으므로, 실제 컨텐츠의 높이를 고려하여 설정해야 합니다.
또한, 성능 최적화를 위해 디바운싱이나 IntersectionObserver와 같은 기술을 활용하면 더 효율적인 구현이 가능합니다.