[javascript] 리액트 라우터에서 페이지 스크롤과 애니메이션 처리

리액트 라우터를 사용하는 웹 애플리케이션에서 페이지 스크롤과 애니메이션을 처리하는 방법에 대해 알아보겠습니다.

페이지 스크롤 처리하기

리액트 라우터는 라우트간의 페이지 전환을 처리하는데 사용되지만, 스크롤 위치는 기억하지 않습니다. 하지만 우리는 페이지 스크롤 위치를 기억하거나 특정 위치로 스크롤 이동하는 방법을 구현할 수 있습니다.

스크롤 위치 기억하기

페이지 전환을 할 때 현재 스크롤 위치를 저장하고, 다음 페이지에서 해당 위치로 스크롤하는 방법을 구현할 수 있습니다. 이를 위해 리액트의 useEffect 훅을 사용하여 페이지 전환 시 스크롤 위치를 저장하고, useLayoutEffect 훅을 사용하여 다음 페이지에서 스크롤 위치를 설정합니다.

아래는 스크롤 위치를 기억하여 페이지 전환 시 해당 위치로 스크롤하는 예제 코드입니다.

import { useEffect, useLayoutEffect } from 'react';
import { useHistory, useLocation } from 'react-router-dom';

function ScrollToTop() {
  const { pathname } = useLocation();
  const history = useHistory();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  useLayoutEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    };
  }, [history]);

  return null;
}

export default ScrollToTop;

위 코드에서 ScrollToTop 컴포넌트는 useEffectuseLayoutEffect 훅을 사용하여 페이지 전환 시 스크롤 위치를 설정합니다. useEffect 훅은 현재 페이지가 렌더링될 때 스크롤 위치를 맨 위로 설정하고, useLayoutEffect 훅은 페이지 전환 시 스크롤 위치를 맨 위로 설정합니다.

특정 위치로 스크롤 이동하기

특정 위치로 스크롤을 이동하는 기능을 구현하기 위해서는 scrollIntoView 메서드를 사용할 수 있습니다. 이 메서드는 지정된 요소가 보일 때까지 항목을 스크롤합니다.

아래는 특정 위치로 스크롤 이동하는 예제 코드입니다.

import { useRef } from 'react';

function ScrollToSection() {
  const sectionRef = useRef();

  const scrollToSection = () => {
    sectionRef.current.scrollIntoView({ behavior: 'smooth' });
  };

  return (
    <div>
      <button onClick={scrollToSection}>Section으로 스크롤 이동</button>
      {/* ... */}
      <section ref={sectionRef}>Section</section>
    </div>
  );
}

export default ScrollToSection;

위 코드에서 ScrollToSection 컴포넌트는 scrollIntoView 메서드를 사용하여 sectionRef에 참조된 요소로 스크롤 이동합니다. 이 때 behavior: 'smooth' 옵션을 통해 부드러운 스크롤 애니메이션을 적용할 수 있습니다.

애니메이션 처리하기

리액트 라우터에서 페이지 전환에 애니메이션 효과를 추가하기 위해 CSS 트랜지션을 사용할 수 있습니다.

import { CSSTransition } from 'react-transition-group';

function PageTransition({ children }) {
  return (
    <CSSTransition
      in={true}
      appear={true}
      classNames="page-transition"
      timeout={500}
      unmountOnExit
    >
      {children}
    </CSSTransition>
  );
}

export default PageTransition;

위 코드에서 CSSTransition 컴포넌트는 페이지 전환에 효과를 적용합니다. in props는 컴포넌트를 맨 처음 렌더링할 때부터 애니메이션을 적용할지 설정합니다. appear props는 페이지에 진입할 때에도 애니메이션을 적용할지 설정합니다. classNames props는 애니메이션에 사용될 CSS 클래스 이름을 지정합니다. timeout props는 애니메이션 지속 시간을 설정합니다. unmountOnExit props는 요소가 사라질 때 컴포넌트를 언마운트할지 결정합니다.

위에서 설정한 classNames 값을 기준으로 페이지 전환에 사용될 CSS 애니메이션을 작성할 수 있습니다.

.page-transition-enter {
  opacity: 0;
  transform: translateX(100%);
}

.page-transition-enter-active {
  opacity: 1;
  transform: translateX(0%);
  transition: opacity 500ms, transform 500ms;
}

.page-transition-exit {
  opacity: 1;
  transform: translateX(0%);
}

.page-transition-exit-active {
  opacity: 0;
  transform: translateX(-100%);
  transition: opacity 500ms, transform 500ms;
}

위 CSS 코드는 페이지 전환 시에 오른쪽으로 슬라이드되면서 나타나고, 왼쪽으로 슬라이드되면서 사라지는 애니메이션을 설정합니다.

이렇게 설정한 PageTransition 컴포넌트를 사용하여 리액트 라우터의 Switch 컴포넌트로 감싸주면 페이지 전환에 애니메이션 효과가 적용됩니다.

import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import PageTransition from './PageTransition';

function App() {
  return (
    <Router>
      <Switch>
        <PageTransition>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />
        </PageTransition>
      </Switch>
    </Router>
  );
}

export default App;

위 코드에서 PageTransition 컴포넌트는 Switch 컴포넌트로 감싸진 라우트 컴포넌트에 애니메이션 효과를 적용합니다.

이제 리액트 라우터에서 페이지 스크롤과 애니메이션을 처리하는 방법에 대해 알아보았습니다. 제가 작성한 예제 코드와 설명을 참고하여 프로젝트에 적용해보세요.