개요
웹사이트에서 라우팅을 구현할 때 페이지 전환효과를 주고싶은 경우가 있다. React-Router로 라우팅을 구현하고 있다면 어떻게 해당 효과를 줄 수 있을까? React-Router는 현재 경로가 바뀌게 되면 즉시 DOM에서 제거하기 때문에 history가 바뀌기 전에 애니메이션을 실행시켜야 한다. 이 말은, 제거될 컴포넌트를 기억하고 애니메이션을 실행시킨 뒤에 애니메이션이 끝나면 바뀐 history의 컴포넌트에 애니메이션을 적용한다는 뜻이다. 생각 해보면, 직접 구현하게 될 경우 구현이 까다롭고 비효율적인 부분이 많아진다는 것을 알 수 있다. 따라서, 공식적인 솔루션은 react-transition-group 을 사용할 것을 권장한다. 결과는 아래화면과 같고 이제부터 그 방법을 알아보도록 하자.
react-transition-group
먼저 사용할 패키지의 사용법에 대해 알아야 한다.
-
<TransitionGroup>
- 자식 컴포넌트의 마운트/언마운트를 제어하는 컴포넌트로, 자식으로
<Transition>
과<CSSTransition>
컴포넌트를 가질 수 있다. 실제 애니메이션 효과를 정의하진 않지만 자식 컴포넌트들의 애니메이션 동작시점을 제어한다.
- 자식 컴포넌트의 마운트/언마운트를 제어하는 컴포넌트로, 자식으로
-
<CSSTransition>
- CSS의 transition/animation을 적용할 때 사용하는 컴포넌트로
appear
,enter
,exit
을 접미사로 가진 클래스 이름을 통해 동작한다. - 이 컴포넌트의
classNames
props로fade
라고 했으면 CSS로.fade-appear
,.fade-enter
,.fade-exit
과 같이 스타일을 정의하는 방식이다.
- CSS의 transition/animation을 적용할 때 사용하는 컴포넌트로
-
시점
- appear는 페이지를 로드했을 때
- enter는 컴포넌트를 마운트 했을 때
- exit은 컴포넌트를 언마운트 했을 때
- 각각의 시점에 접미사로
-active
와-done
이 붙는데,-active
는 시점이 활성화되고 있을 때이고-done
은 해당 시점이 끝났을 때이다. 접미사가 안 붙는 경우는 활성화 되기 직전이다.
프로젝트 초기화
CRA를 통해서 프로젝트를 생성한 뒤에 필요한 패키지들을 설치해주자.
npx create-react-app react-transition-example
cd react-transition-example
yarn add react-router-dom react-transition-group
내비게이션 컴포넌트 생성
링크를 누르면 라우팅 전환이 일어날 수 있도록 내비게이션 컴포넌트를 생성하자.
Nav.jsx
import React from "react";
import { Link } from "react-router-dom";
import "./Nav.css";
const Nav = () => {
return (
<ul>
<li>
<Link to="/">아이린</Link>
</li>
<li>
<Link to="/seulgi">슬기</Link>
</li>
<li>
<Link to="/yeri">예리</Link>
</li>
<li>
<Link to="/joy">조이</Link>
</li>
<li>
<Link to="/wendy">웬디</Link>
</li>
</ul>
);
};
export default Nav;
Nav.css
ul {
margin: 0;
display: flex;
justify-content: center;
margin-bottom: 2rem;
}
a {
color: black;
text-decoration: none;
}
li {
list-style-type: none;
margin-right: 1rem;
font-size: 2rem;
font-weight: bold;
}
페이지 컴포넌트 생성
레드벨벳 멤버들의 이미지를 props로 받아서 컴포넌트를 리턴하는 페이지 컴포넌트를 만들자.
Page.jsx
import React from "react";
import "./Page.css";
const Page = ({ image }) => {
return (
<div className="container">
<img src={image} alt="멤버 사진" />
</div>
);
};
export default Page;
Page.css
.container {
position: absolute;
left: 50%;
top: 0;
width: 1000px;
transform: translateX(-50%);
}
img {
width: 100%;
vertical-align: middle;
}
트랜지션 컴포넌트 생성
이제 트랜지션 효과를 적용시킬 컴포넌트를 만들텐데 주의깊게 봐야 한다.
Transition.jsx
import React from "react";
import { TransitionGroup, CSSTransition } from "react-transition-group";
import { Route, Switch, useLocation } from "react-router-dom";
import Page from "./Page";
import "./Transition.css";
const irene =
"https://raw.githubusercontent.com/baeharam/Redvelvet-Fansite/master/images/about-irene.jpg";
const seulgi =
"https://raw.githubusercontent.com/baeharam/Redvelvet-Fansite/master/images/about-seulgi.jpg";
const yeri =
"https://raw.githubusercontent.com/baeharam/Redvelvet-Fansite/master/images/about-yeri.jpg";
const joy =
"https://raw.githubusercontent.com/baeharam/Redvelvet-Fansite/master/images/about-joy.jpg";
const wendy =
"https://raw.githubusercontent.com/baeharam/Redvelvet-Fansite/master/images/about-wendy.jpg";
const Transition = () => {
const PageIrene = <Page image={irene} />;
const PageSeulgi = <Page image={seulgi} />;
const PageYeri = <Page image={yeri} />;
const PageJoy = <Page image={joy} />;
const PageWendy = <Page image={wendy} />;
const location = useLocation();
return (
<TransitionGroup className="transition-group">
<CSSTransition key={location.pathname} classNames="fade" timeout={500}>
<Switch location={location}>
<Route exact path="/" children={PageIrene} />
<Route path="/seulgi" children={PageSeulgi} />
<Route path="/yeri" children={PageYeri} />
<Route path="/joy" children={PageJoy} />
<Route path="/wendy" children={PageWendy} />
</Switch>
</CSSTransition>
</TransitionGroup>
);
};
export default Transition;
먼저 각 이미지로 페이지 컴포넌트를 생성하는 것까지는 이해가 될 것이니 그 이후부터 보자.
- 각 페이지의 마운트/언마운트에 따른 애니메이션을 제어하기 위해
<TransitionGroup>
으로 묶는다. - 애니메이션 효과를 정의하기 위해
<CSSTransition>
으로 묶고, 클래스의 접두사를fade
로 설정한다. 또한 페이지가 바뀔 때마다 애니메이션을 동작할 것이므로key
값으로location.pathname
을 주고 0.5초의 딜레이가 발생하도록 해준다. - 이제 각 라우트들을
<Switch>
로 감싸서 한 페이지만 렌더링되게 하는데 여기서location
을 넘겨준다. 넘겨주는 이유는 언마운트 되는 이전의 페이지 애니메이션을 동작시키기 위함이다. - 마지막으로 각각의 라우트를 설정한다.
Transition.css
body {
overflow: hidden;
}
.transition-group {
position: relative;
}
.fade-enter {
opacity: 0;
transform: translateX(100%) rotateZ(45deg);
}
.fade-enter-active {
opacity: 1;
transform: translateX(-50%) rotateZ(0);
transition: all 1s ease-in;
}
.fade-exit {
opacity: 1;
transform: translateX(-50%) rotateZ(0);
}
.fade-exit-active {
opacity: 0;
transform: translateX(-100%) rotateZ(-45deg);
transition: all 1s ease-in;
}
-
<CSSTransition>
의classNames
로 넘긴fade
를 기준으로enter
와exit
접미사를 붙였고 각 시점에 따라active
를 붙였다. 이걸 통해서 다음 시점의 효과들을 구현할 수 있다.fade-enter
: 마운트 되기 직전fade-enter-active
: 마운트 활성화 될 때fade-exit
: 언마운트 되기 직전fade-exit-active
: 언마운트 활성화 될 때
.transition-group
에position:relative
를 주었는데, 각 페이지 컴포넌트가 겹쳐지지 않으면 층처럼 쌓이는 효과가 일어나기 때문에 각 페이지에position:absolute
를 주기 위해 설정해놓은 것이다. 위에서 본 Page.css 코드의.container
스타일에서position:absolute
를 제거하면 왜 이런 스타일을 정의하는지 이해할 수 있다.- 전환효과로
transform
을 사용하기 때문에 스크롤바가 생기는 경우가 있기 때문에body
에overflow:hidden
을 주어 없앴다.
라우팅 설정
라우팅이 동작할 수 있도록 설정해주자.
App.js
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import Nav from './components/Nav';
import Transition from './components/Transition';
function App() {
return (
<Router>
<Nav />
<Transition />
</Router>
);
}
export default App;
메뉴를 누르면 전환이 일어나는 것을 볼 수 있다. 여기선 enter
와 active
만 적용했지만 <TransitionGroup>
의 props로 appear
를 주면 CSS에서 appear
스타일 또한 적용할 수 있다.
마무리
지금까지 라우팅할 때 전환 효과를 어떻게 구현하는지에 대해 알아보았다. react-transition-group을 사용하면 편하게 그 효과를 구현할 수 있으니 이 작업이 필요할 때 사용하도록 하자. 이 포스팅의 모든 코드는 깃헙 에서 확인할 수 있다.