
웹 개발하다 보면 특정 섹션으로 부드럽게 스크롤링하는 기능이 필요할 때가 있음. 특히 React로 SPA(Single Page Application) 만들다 보면 이런 기능 하나로 사용자 경험(UX)이 확 좋아질 수 있음. 최근 프로젝트에서 React Router랑 react-scroll 써서 부드러운 스크롤링 네비게이션 구현해봤는데, 삽질 좀 하다가 해결해서 공유해봄.
React로 웹 앱 만들던 중에 이런 요구사항이 나옴:
이거 구현하다 보니까 다음 같은 문제들이 터짐:
일단 react-router-dom이랑 react-scroll 설치함. 각각 라우팅이랑 스크롤 애니메이션 기능 제공해줌.
npm install react-router-dom react-scroll
react-scroll의 scroller 사용해서 특정 섹션으로 부드럽게 이동하도록 구현함.
import { scroller } from 'react-scroll';
const handleScrollTo = (sectionId) => {
scroller.scrollTo(sectionId, {
duration: 800,
delay: 0,
smooth: 'easeInOutQuart',
offset: -80, // 고정 헤더 높이만큼 오프셋
});
};
SPA 특성상 다른 페이지에서 네비게이션 클릭하면 메인 페이지로 돌아가 특정 섹션으로 스크롤해야 했음. React Router의 navigate를 써서 상태 전달함.
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
const handleNavigation = (sectionId) => {
navigate('/', { state: { scrollTo: sectionId } });
};
메인 페이지에서는 useEffect로 상태 기반 스크롤 처리했음.
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { scroller } from 'react-scroll';
const MainPage = () => {
const location = useLocation();
useEffect(() => {
if (location.state?.scrollTo) {
scroller.scrollTo(location.state.scrollTo, {
duration: 800,
delay: 0,
smooth: 'easeInOutQuart',
offset: -80,
});
}
}, [location]);
return (
<div>
{/* 섹션들 */}
<section id="section1">...</section>
<section id="section2">...</section>
<section id="section3">...</section>
</div>
);
};
IntersectionObserver 써서 현재 보이는 섹션 감지하고, 해당 섹션에 맞는 탭을 활성화함.
import { useEffect, useState } from 'react';
const useActiveSection = (sections) => {
const [activeSection, setActiveSection] = useState(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveSection(entry.target.id);
}
});
}, { threshold: 0.6 });
sections.forEach((id) => {
const element = document.getElementById(id);
if (element) observer.observe(element);
});
return () => {
sections.forEach((id) => {
const element = document.getElementById(id);
if (element) observer.unobserve(element);
});
};
}, [sections]);
return activeSection;
};
useActiveSection 훅으로 활성화된 섹션 감지해서 탭 상태 업데이트함.
const MainPage = () => {
const activeSection = useActiveSection(['section1', 'section2', 'section3']);
return (
<div>
<header>
<Tabs activeTab={activeSection} />
</header>
<section id="section1">섹션 1</section>
<section id="section2">섹션 2</section>
<section id="section3">섹션 3</section>
</div>
);
};
const Tabs = ({ activeTab }) => (
<nav>
<button onClick={() => handleScrollTo('section1')} className={activeTab === 'section1' ? 'active' : ''}>
섹션 1
</button>
<button onClick={() => handleScrollTo('section2')} className={activeTab === 'section2' ? 'active' : ''}>
섹션 2
</button>
<button onClick={() => handleScrollTo('section3')} className={activeTab === 'section3' ? 'active' : ''}>
섹션 3
</button>
</nav>
);
이 작업 하면서 몇 가지 깨달은 점:
useEffect랑 라이브러리 조합으로 문제 해결 가능.react-scroll이랑 React Router 조합으로 UX 크게 개선 가능.이렇게 부드러운 스크롤링 네비게이션 구현하는 과정을 정리해봤음. 혹시 질문이나 피드백 있으면 댓글로 남겨주셈! 😊