이 문서는 React와 styled-components를 활용해 "한 화면씩 넘기는 원 페이지 스크롤" 기능을 구현하는 방법을 설명합니다. 사용자는 마우스 휠을 통해 슬라이드를 하나씩 전환하며, 각 슬라이드는 전체 화면(viewport)을 채우는 구조로 구성됩니다.
원 페이지 스크롤의 핵심은 사용자의 휠 스크롤 이벤트를 감지하는 것입니다.
브라우저는 기본적으로 사용자가 마우스 휠을 움직일 때마다 wheel 이벤트를 발생시킵니다.
이 이벤트는 deltaY라는 값을 포함하고 있어, 사용자가 스크롤을 위로 움직였는지(음수) 아래로 움직였는지(양수)를 확인할 수 있습니다.
스크롤 이벤트를 감지하려면 다음을 설정합니다:
passive: false 옵션을 주어 기본 동작(브라우저의 스크롤)을 막을 수 있음휠 스크롤이 감지되면 다음 단계는 현재 사용자가 보고 있는 슬라이드 인덱스를 갱신하는 작업입니다.
이를 위해 내부적으로 유지되는 currentPage 상태값을 사용하여:
currentPage를 1 증가currentPage를 1 감소이 로직은 사용자 스크롤에 따라 어떤 슬라이드를 보여줄지를 판단하는 핵심 기준이 됩니다.
슬라이드 인덱스가 변경되면, 해당 인덱스에 맞는 화면 위치로 스크롤을 프로그래밍적으로 이동시킵니다.
이를 위해 scrollTo 또는 scrollTop 값을 직접 조작하여:
2번째 슬라이드 = 2 * 화면 높이 (100vh)로 계산behavior: 'smooth' 옵션 사용이 과정을 통해 마우스 휠 한 번에 정확히 한 슬라이드씩 이동하는 느낌을 줄 수 있습니다.
스크롤 이벤트는 매우 자주 발생할 수 있으므로, 다음과 같은 처리가 추가로 필요합니다:
touchstart, touchend)도 별도 감지 필요원 페이지 스크롤은 마우스 휠을 감지하고, 현재 페이지를 기준으로 다음 화면 위치를 계산해 스크롤하는 구조입니다. 이 과정은 스크롤 이벤트 감지 → 현재 위치 판단 → 위치 이동이라는 순서를 반복하면서 동작합니다.
구현은 간단해 보이지만, UX 관점에서 부드럽고 안정적인 전환을 위해 이벤트 제어와 스크롤 상태 관리가 중요합니다.
기본 구조는 다음과 같습니다:
Base: 전체 스크롤을 제어하는 컨테이너 (스크롤 대상)SliderContainer: 모든 슬라이드를 포함하는 요소 (높이 = 슬라이드 개수 x 100vh)Slide: 개별 페이지 (높이 100vh)<Base ref={containerRef}>
<SliderContainer>
<Slide />
<Slide />
<Slide />
</SliderContainer>
</Base>
window.innerHeight를 기준으로 슬라이드 위치 계산:
const scrollToPage = (index: number) => {
if (!containerRef.current) return;
const targetScrollTop = index * window.innerHeight;
containerRef.current.scrollTo({
top: targetScrollTop,
behavior: "smooth",
});
setCurrentPage(index);
};
사용자의 휠 스크롤을 감지해 현재 페이지 상태에 따라 scrollToPage 호출:
useEffect(() => {
const handleWheel = (e: WheelEvent) => {
e.preventDefault();
if (e.deltaY > 0 && currentPage < slides.length - 1) {
scrollToPage(currentPage + 1);
} else if (e.deltaY < 0 && currentPage > 0) {
scrollToPage(currentPage - 1);
}
};
const container = containerRef.current;
if (container) {
container.addEventListener("wheel", handleWheel, { passive: false });
}
return () => {
if (container) {
container.removeEventListener("wheel", handleWheel);
}
};
}, [currentPage, slides.length]);
const Base = styled.div`
height: 100vh;
overflow-y: auto;
scroll-behavior: smooth;
`;
const SliderContainer = styled.div`
height: calc(100vh * 슬라이드 개수);
`;
const Slide = styled.div`
height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
`;
슬라이드는 반드시 height: 100vh로 설정되어야 하며, 컨테이너는 슬라이드 개수만큼 높이를 갖도록 해야 스크롤이 제대로 작동합니다.
touchstart / touchend 이벤트 별도 추가throttle 또는 debounce를 통해 이벤트 최적화 필요React 환경에서 원 페이지 스크롤을 구현하기 위해서는 각 섹션을 정확히 100vh로 분할하고, 사용자 스크롤에 맞춰 정확한 위치로 이동시키는 제어가 핵심입니다. 이를 통해 보다 몰입감 있는 인터랙티브 웹페이지를 제작할 수 있습니다.