Coding Planet

[Next.js]스크롤 컨테이너에 대한 'ScrollToTop' 컴포넌트 구현하기 - 맨 위로 버튼 본문

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;
반응형
Comments