본문 바로가기
웹 프로그래밍/🔮포트폴리오 사이트

[React] 포트폴리오 웹 사이트 제작 일지-5. 상단 메뉴바 구현 (useref 를 이용한 컴포넌트 간 스크롤 이동)

by 청량리 물냉면 2023. 10. 13.
반응형

기존 계획은 사이드 메뉴를 구현하는 것이었는데, 이미 구현해 놓은 화면 위에 사이드 메뉴를 얹으니 이질감이 들어서;; 상단 메뉴바를 구현하기로 했다.

 

MenuBar.js

import "./MenuBar.css";

const MenuBar = () => {
  return (
    <header className="menu-bar">
      <div className="menu-contents">
        <div className="menu-title">
          <a href="#Home">Portfolio</a>
        </div>
        <div className="menu-list">
          <div className="menu-item">
            <a href="#Archiving">Archiving</a>
          </div>
          <div className="menu-item">
            <a href="#Skills">Tech Skills</a>
          </div>
          <div className="menu-item">
            <a href="#Project">Project</a>
          </div>
          <div className="menu-item">
            <a href="#Edu">Edu & Cert</a>
          </div>
          <div className="menu-item">
            <a href="#Contact">Contact</a>
          </div>
        </div>
      </div>
    </header>
  );
};

export default MenuBar;

<a href="#Home"></a> 을 이용해 id="Home"인 divname="Home"인 a 태그로 이동이 가능하도록 하였다.

스타일링은 아래와 같이 진행했다.

 

.menu-bar {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  background-color: beige;
  z-index: 1;
}

.menu-contents {
  width: 100%;
  display: flex;
  padding: 20px 30px;
  margin: 0 auto;
  flex-direction: row;
  justify-content: space-around;
}

.menu-list {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
}

 

추가로 a태그를 스타일링하고 menu-item 항목들에 margin을 주었다.

.menu-title > a {
  color: black;
  font-size: 25px;
  cursor: pointer;
  text-decoration: none;

  font-family: "Dela Gothic One", sans-serif;
}

.menu-item {
  margin: 0 35px;
}

.menu-item > a {
  color: black;
  font-size: 20px;
  cursor: pointer;
  text-decoration: none;
}

 

useref 스크롤 이동

현재 내부링크 이동은 a 태그 href와 id 태그 지정을 통해 구현된 상태이다.

하지만 나는 지금 바닐라 자바스크립트가 아닌 리액트를 사용하고 있기 때문에 태그를 이용하는 대신, useRef 훅을 이용한 내부 링크 이동을 구현해 보고 싶었다.

useRef를 이용하면 스크롤 애니메이션을 주면서 간단하게 함수로 내부링크 이동을 구현할 수가 있다. 

 

먼저 useRef를 이용해 Ref 객체를 생성한다. 

  const scrollRef = useRef(null);	//Ref 객체 생성

  const moveHandler = () => {	//이동 함수
    scrollRef.current.scrollIntoView({ behavior: "smooth" });
  };

moveHandler 함수는 스크롤 이동을 구현하는 함수이다.

 

scrollIntoView() 메서드는 호출되는 요소(위의 코드의 경우 scrollRef.current)가 사용자에게 보이도록 스크롤해 주는 메서드이다.

 

Element: scrollIntoView() method - Web APIs | MDN

The Element interface's scrollIntoView() method scrolls the element's ancestor containers such that the element on which scrollIntoView() is called is visible to the user.

developer.mozilla.org

스크롤을 원하는 요소에 ref를 지정해주면 해당 위치로 이동할 수 있다.

<div ref={scrollRef} />

 

하나의 페이지 안에 함수와 이동할 위치가 모두 존재한다면 위의 방식으로 간단히 스크롤 이동을 구현할 수 있다.

하지만 나는 App.js에 메뉴바와 컴포넌트들을 가져와서 사용하고 있었기에 해당 방식은 사용할 수 없었다.

따라서 App.js에 ref 객체와 스크롤 이동 함수를 구현해 놓고 각 컴포넌트에 props로 넘겨주고, 각 컴포넌트에서 forwardRef를 이용해 ref를 전달받는 식으로 구현했다.

 

Ref 전달하기 – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

forwardRef는 자식 컴포넌트에 ref를 전달할 수 있게 해준다.

 

App.js

...

function App() {
  const HomeRef = useRef(null);

  const moveHandler = () => {
    HomeRef.current.scrollIntoView({ behavior: "smooth" });
  };

  return (
    <div className="App">
      <MenuBar move={moveHandler} />
      <Home />
      <Archiving ref={HomeRef} />
      <Skills />
      <Project />
      <Edu />
      <Contact />
    </div>
  );
}

export default App;

 

Archving.js

...
const Archiving = forwardRef((props, ref) => {
  return (
    ...
  );
});

export default Archiving;

컴포넌트 전체를 forwardRef();로 감싸주면 된다.

'Portfolio' 타이틀을 누르면 Archaving 컴포넌트 시작 부분으로 잘 이동하고 있다.

 

사실 타이틀 클릭 시 Home 화면으로 이동해야 하는데, 우선 forwardRef가 잘 동작하는지 확인해 보느라 아무 컴포넌트에나 HomeRef를 지정해 버려서 계획과 동작이 조금 달라졌다.;; 하지만 정상 동작은 하고 있으니 ref만 잘 지정하면 계획대로 동작할 것이다.

다른 컴포넌트도 동일한 방식으로 코드를 작성해 준다.

 

최종 구현 화면

모든 화면 스크롤 이동이 성공적으로 구현 되었다.

추가로 position: fixed로 지정된 메뉴바 때문에 이동한 화면의 일부가 잘리게 되는데 이 부분은 어떻게 처리할지 고민해 봐야겠다.

반응형