사이드 프로젝트
네브바 스크롤에 따라 사라지고 나타나게 하기 (w. React & Tailwind)
개발쉐발
2025. 2. 1. 16:21
728x90
반응형
👉 네브바에 해당 기능을 넣은 이유
일단 네브바가 기본적으로 계속 따라오는 것도 좋지만 아래로 내릴때 화면의 일정 부분을 차지하는게 '포트폴리오' 사이트라는 점에서 시야를 방해하는 요소로 작용할 수도 있다고 생각했다.
🛠 적용 방법 및 코드
일단 나는 리액트 + Nextjs + tailwind를 사용하여 프로젝트를 진행 중이고 useState와 useRef, useEffect를 활용해서 구현했다.
- 스크롤 위치를 감지 (window.scrollY)
- 아래로 스크롤하면 Navbar를 숨기고, 위로 스크롤하면 다시 보이게 설정
- useEffect를 이용해서 scroll 이벤트 핸들러 등록
'use client';
import { useState, useEffect, useRef } from 'react';
export default function Navbar() {
const [isVisible, setIsVisible] = useState(true);
const lastScrollY = useRef(0); // 리렌더링 방지
useEffect(() => {
const handleScroll = () => {
if (window.scrollY > lastScrollY.current) {
setIsVisible(false); // 아래로 스크롤하면 숨김
} else {
setIsVisible(true); // 위로 스크롤하면 표시
}
lastScrollY.current = window.scrollY; // 현재 스크롤 위치 업데이트
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []); // 빈 배열로 설정 → 한 번만 실행되도록
return (
<header
className={`fixed top-0 left-0 w-full bg-[#0F111A] shadow-md z-50 transition-transform duration-300 ${
isVisible ? "translate-y-0" : "-translate-y-full"
}`}
>
<nav>
...
</nav>
}
🛠 코드 실행 흐름
1. 초기 상태 설정
const [isVisible, setIsVisible] = useState(true);
const lastScrollY = useRef(0);
- isVisible: 요소의 가시성을 관리하는 상태값 (초기값: true → 요소가 보이는 상태).
- lastScrollY: 이전 스크롤 위치를 저장하는 ref 값 (초기값: 0).
- useRef(0)을 사용하면 컴포넌트가 리렌더링되어도 값이 유지됨.
- useState 대신 useRef를 사용하면 값이 변경될 때마다 리렌더링이 발생하지 않음.
2. useEffect 실행 (컴포넌트 마운트 시)
useEffect(() => {
const handleScroll = () => {
if (window.scrollY > lastScrollY.current) {
setIsVisible(false); // 아래로 스크롤하면 숨김
} else {
setIsVisible(true); // 위로 스크롤하면 표시
}
lastScrollY.current = window.scrollY; // 현재 스크롤 위치 업데이트
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
- 컴포넌트가 마운트(화면에 나타남)되면 한 번 실행됨 ([] 의존성 배열 덕분).
- handleScroll 함수를 정의한 후, window 객체에 scroll 이벤트 리스너를 등록.
- 사용자가 스크롤할 때마다 handleScroll이 실행됨.
- handleScroll이 실행될 때:
- 현재 window.scrollY(현재 스크롤 위치)를 lastScrollY.current(이전 위치)와 비교.
- 아래로 스크롤 (scrollY > lastScrollY.current) → isVisible을 false로 설정해 숨김.
- 위로 스크롤 (scrollY < lastScrollY.current) → isVisible을 true로 설정해 다시 표시.
- lastScrollY.current 값을 현재 window.scrollY 값으로 업데이트.
3. return () => window.removeEventListener("scroll", handleScroll); 동작
- useEffect 내부에서 return으로 반환한 함수는 클린업(clean-up) 함수.
- 컴포넌트가 언마운트(화면에서 사라짐)되거나, useEffect가 재실행될 때 자동으로 실행됨.
- 여기서는 scroll 이벤트 리스너를 제거해서 메모리 누수를 방지하는 역할을 함.
- 만약 이걸 하지 않으면, 컴포넌트가 사라져도 handleScroll이 계속 실행될 수도 있음.
🔍 return () => window.removeEventListener("scroll", handleScroll); 자동 실행 원리
- React에서는 useEffect가 실행될 때, 이전 useEffect의 클린업 함수를 자동으로 실행한 후 새로운 이펙트를 적용.
- 언마운트 시에도 실행됨 → 그래서 컴포넌트가 사라질 때 removeEventListener가 호출됨.
- 이 과정 덕분에 이벤트 리스너가 중복으로 등록되지 않도록 막아줌.
👉 쉽게 말하면
- 처음 마운트될 때 → scroll 이벤트 리스너 등록.
- 언마운트되면 → scroll 이벤트 리스너 제거.
- 의존성이 업데이트되면 → 기존 scroll 이벤트 리스너 제거 후 다시 등록.
🎯 useRef란?
✅ useRef의 특징
- 리렌더링 없이 값을 유지할 수 있음
- useState를 쓰면 값이 변경될 때마다 컴포넌트가 리렌더링되지만,
useRef는 값이 변경되어도 리렌더링되지 않음.
- useState를 쓰면 값이 변경될 때마다 컴포넌트가 리렌더링되지만,
- .current 속성을 통해 값을 직접 조작 가능
- useRef는 { current: 값 } 형태의 객체를 반환함.
- 따라서 lastScrollY.current = window.scrollY; 이렇게 값을 직접 변경할 수 있음.
- 초기값을 설정할 수 있음
- useRef(0)을 사용하면 current 값이 0으로 설정됨.
✅ useRef를 사용하는 이유
- lastScrollY는 이전 스크롤 위치만 저장하면 되므로, 상태 업데이트를 강제로 트리거할 필요 없음.
- 만약 useState를 사용하면, 스크롤할 때마다 setLastScrollY(window.scrollY);가 실행되어
불필요한 리렌더링이 발생함. - useRef를 사용하면 리렌더링 없이도 값이 유지되므로 성능이 최적화됨.
📌 window.scrollY 값의 변화
window.scrollY는 현재 문서의 최상단에서부터 현재 스크롤 위치까지의 거리(px)를 의미.
- 아래로 스크롤하면 window.scrollY 값이 커짐.
→ 즉, 스크롤을 내릴수록 window.scrollY 값이 증가. - 위로 스크롤하면 window.scrollY 값이 작아짐.
→ 즉, 스크롤을 올릴수록 window.scrollY 값이 감소.
🔍 비교(window.scrollY > lastScrollY.current vs window.scrollY < lastScrollY.current)
const handleScroll = () => {
if (window.scrollY > lastScrollY.current) {
setIsVisible(false); // 아래로 스크롤하면 숨김
} else {
setIsVisible(true); // 위로 스크롤하면 표시
}
lastScrollY.current = window.scrollY; // 현재 스크롤 위치 업데이트
};
📌 아래로 스크롤할 때 (window.scrollY > lastScrollY.current)
- 현재 window.scrollY 값이 이전보다 커짐.
- 즉, 스크롤이 아래로 이동했다는 뜻.
- setIsVisible(false)를 실행 → 요소를 숨김.
✔ 이유: 스크롤을 내릴 때 요소를 숨기면 화면을 더 넓게 활용할 수 있어서, 네비게이션 바 같은 UI 요소를 감추는 데 자주 사용됨.
📌 위로 스크롤할 때 (window.scrollY < lastScrollY.current)
- 현재 window.scrollY 값이 이전보다 작아짐.
- 즉, 스크롤이 위로 이동했다는 뜻.
- setIsVisible(true)를 실행 → 요소를 다시 표시.
✔ 이유: 사용자가 위로 스크롤하는 경우, 보통 네비게이션 바가 다시 보여야 편리하기 때문.
📌 예제: window.scrollY 값 변화 보기
이전 lastScrollY.current 값 | 현재 window.scrollY 값 | 비교 결과 | isVisible 값 | |
처음 페이지 로드 | 0 | 0 | = | true (보임) |
⬇ 아래로 스크롤 (100px) | 0 | 100 | > | false (숨김) |
⬇ 더 아래로 스크롤 (300px) | 100 | 300 | > | false (숨김) |
⬆ 위로 스크롤 (150px) | 300 | 150 | < | true (표시) |
⬆ 더 위로 스크롤 (50px) | 150 | 50 | < | true (표시) |
이렇게 동작하는 이유가 window.scrollY 값이 증가하면 아래로 스크롤한 것이고, 감소하면 위로 스크롤한 것이기 때문.
728x90
반응형