포트폴리오 페이지를 만들려는데, 스크롤 기반으로 동작되도록 하고 싶었다.
일단 필요한 기능은
1. 스크롤 시 경로 변경
2. 메뉴 클릭 시 해당 섹션으로 이동
3. 특정 페이지(메인, 마지막)에선 메뉴 숨김
const mainRef = useRef<HTMLElement | null>(null); // 타입 명시
const aboutMeRef = useRef<HTMLElement | null>(null);
const projectRef = useRef<HTMLElement | null>(null);
const experienceRef = useRef<HTMLElement | null>(null);
const contactRef = useRef<HTMLElement | null>(null);
useRef로 각 세션의 DOM을 저장한다.
이걸 이용해서 scrollntoView()로 해당 섹션으로 스크롤 하거나, IntersectionObserver로 감시할 수 있다.
useRef
리액트 컴포넌트에서 DOM 요소나 어떤 값을 기억하고 싶을 때 사용하는 훅
const myRef = useRef(null);
myRef.current를 통해 실제 DOM 요소나 값에 접근이 가능하다.
또한 컴포넌트가 리렌더링되어도 값을 유지한다!
그리고 값이 바뀌어도 리렌더링 X
const boxRef = useRef(null);
useEffect(() => {
boxRef.current.scrollIntoView({ behavior: "smooth" });
}, []);
이렇게 사용할 수 있는데,
boxRef를 특정 HTML 요소에 연결해서 사용한다.
그러면 처음 마운트될 때, boxRef가 연결된 DOM 요소로 부드럽게 스크롤을 이동하라는 뜻이 된다.
useRef는 너무 잘 까먹게 되는 것 같다...
그리고 스크롤 시 경로를 바꾸는 기능을 구현해보자.
IntersectionObserver을 사용하면 된다.
IntersectionObserver는 어떤 섹션이 화면 안에 보이는지 감지하는 함수이다.
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) { // 요소가 화면에 보인다면?
const id = entry.target.id; // 보이는 요소의 id를 가져옴
if (location.pathname !== `/${id}`) { // 경로가 같지 않다면
navigate(`/${id}`, { replace: true }); // 경로 이동
// replace: true -> 브라우저 히스토리가 쌓이지 않게 함.
}
}
}
}, { threshold: 0.6 }); // 0.6만큼 보일 때 경로 변경
const sections = [
mainRef.current,
aboutMeRef.current,
projectRef.current,
experienceRef.current,
contactRef.current,
];
sections.forEach((sec) => sec && observer.observe(sec));
// 각 세션을 observer가 감시!
return () => observer.disconnect(); // 메모리 낭비 방지
}, [navigate, location.pathname]);
여기서 entries는 감시 중인 섹션의 정보가 담긴 배열이다.
그럼 entry는 각 섹션의 정보가 될테고, 보이는 섹션의 id를 가져와서 경로를 변경해 주는 것이다.
다음으로 경로가 바뀔 시 (메뉴 선택) 스크롤 이동이 가능하도록 하는 것이다.
useEffect(() => {
const scrollToSection = () => {
switch (location.pathname) {
case "/":
mainRef.current?.scrollIntoView({ behavior: "smooth" });
break;
case "/aboutMe":
aboutMeRef.current?.scrollIntoView({ behavior: "smooth" });
break;
...
}
};
scrollToSection();
}, [location]);
location.pathname이 바뀔 때마다 scrollIntoView()를 사용해서 부드럽게 이동하도록 한다.
scrollIntoView()은 브라우저 화면에서 어떤 요소가 자동으로 보이도록 스크롤해주는 함수이다.
그리고 특정 경로에서 메뉴를 숨겨야 하니까, location.pathname으로 조건문 처리하면 된다.
{location.pathname !== "/" && location.pathname !== "/contact" && (
<Navbar />
)}
경로가 '/' 또는 '/contact'일 땐 메뉴가 안 보이도록 숨겼다.
<section
ref={mainRef}
id=""
style={{ height: "100vh" }}
>
<Main />
</section>
각 섹션은 이렇게 되어 있다.
IntersectionObserver를 위한 id 속성이 있고, 스크롤 하기 위한 ref 속성이 있다.
'개발 > 프로젝트' 카테고리의 다른 글
[리팩토링] 새로고침 해도 검색 결과가 남아있도록 하기 (5) | 2025.02.28 |
---|---|
Zustand로 API 호출 및 상태 관리하기 (0) | 2025.02.28 |
Tailwind CSS + SCSS로 반응형 구현하기 (0) | 2025.02.25 |
Material UI를 사용하여 페이지네이션 구현하기 (2) | 2025.02.13 |
Swiper.js 라이브러리를 사용해서 슬라이더 구현하기 (Next.js + React) (0) | 2025.02.13 |