front
[Next.js]스크롤 컨테이너에 대한 'ScrollToTop' 컴포넌트 구현하기 - 맨 위로 버튼
jhj.sharon
2024. 8. 12. 09:03
반응형
일반적으로 ScrollToTop 컴포넌트는 window 객체의 스크롤을 감지하여 구현한다. 하지만 특정 div 내에서만 스크롤이 발생하는 경우 window.pageYOffset은 항상 0을 반환하여 스크롤 위치를 정확히 파악할 수 없다. 무한 스크롤 게시판의 경우 맨 위로 가기 버튼이 필수적이므로 이 기능을 스크롤 컨테이너에서만 적용되도록 구현했다. 다만 예시코드에서는 무한 스크롤 부분은 코드의 가독성을 위해 제거하고 ScrollToTop 기능만 나와있다.
| 접근 방법
1. 스크롤 가능한 컨테이너에 ref를 할당한다.
2. 이 ref를 'ScrollToTop' 컴포넌트에 전달한다.
3. 'ScrollToTop' 컴포넌트 내에서 해당 ref의 scrollTop 속성을 사용하여 스크롤 위치를 감지한다.
| useRef
- useRef는 참조를 저장하기 위해 사용된다. 주로 DOM 요소에 접근하거나, 렌더링 사이에서 값을 유지하기 위해 사용하는데 이 경우 스크롤값을 참조하고 저장한다.
- 값이 변경되어도 컴포넌트가 다시 렌더링되지 않는다
| useCallback
- 메모이제이션된 콜백 함수를 반환한다. 컴포넌트가 재렌더링될 때, 동일한 콜백 함수를 다시 생성하지 않도록 함으로써 성능을 최적화한다.
| 소스
Board
import React, { useState, useRef } from 'react';
import ScrollToTop from "@/app/components/util/ScrollToTop";
export default function Board() {
const scrollContainerRef = useRef(null);
return (
<>
<div
ref={scrollContainerRef}
className="w-full bg-white p-4 pb-20 box-border overflow-y-auto h-[calc(100vh-105px)] mt-[80px] focus:outline-none"
>
<ScrollToTop scrollContainerRef={scrollContainerRef} />
{/* 컨텐츠 */}
</div>
</>
);
}
ScrollToTop Component
import React, { useEffect, useState, useCallback } from 'react';
const ScrollToTop = ({ scrollContainerRef }) => {
const [isVisible, setIsVisible] = useState(false);
const handleScroll = useCallback(() => {
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
console.log('Scroll position:', scrollContainer.scrollTop);
if (scrollContainer.scrollTop > 100) {
setIsVisible(true);
} else {
setIsVisible(false);
}
}
}, [scrollContainerRef]);
const scrollToTop = () => {
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
scrollContainer.scrollTo({
top: 0,
behavior: 'smooth',
});
}
};
useEffect(() => {
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
scrollContainer.addEventListener('scroll', handleScroll);
return () => {
scrollContainer.removeEventListener('scroll', handleScroll);
};
}
}, [handleScroll, scrollContainerRef]);
return (
<>
{isVisible && (
<div
onClick={scrollToTop}
className="fixed bottom-5 right-5 bg-blue-600 text-white p-3 rounded-full cursor-pointer shadow-lg"
style={{ zIndex: 9999 }}
>
위로
</div>
)}
</>
);
};
export default ScrollToTop;
반응형