๋ผ์ฐํ
ํธ๋์ง์
์ ์ ์ฉํ๋ฉด์ ๊ณต์๋ฌธ์๋ ์ฐพ์๋ณด๊ณ ์ฌ๋ฌ ๊ธ๋ค์ ์ฐธ๊ณ ํ๋๋ฐ, ๋ฌด์ธ๊ฐ ์ค๋ฌด์ ์ธ ์ ์์ ์ ๋๋ก ์์ํ๊ฒ ํด๋ฒ์ ์ ์ํ ๊ณณ์ด ์์๋ค. ๋๋ถ์ ๊ณ ๋ฏผ์ ๋ง์ด ํ๊ฒ ๋์๋ ๊ฒฝํ์ด์๋ค. ๊ทธ๋์ ์ด๋ฒ ํฌ์คํ
์ ํ ๋ react-transition-group์ ๋์
ํ๋ ค๋ ์ฌ๋์ ๊ถ๊ธ์ฆ์ ์ ๋๋ก ํด์ํด์ค ์ ์๋๋ก ํ์๋ณด๋ค ์กฐ๊ธ ๋ ์น์ ํ๊ฒ ์ ์ด๋๊ฐ๋ค. ์ด ๊ธ์ ์ฝ์ ๋๊ตฌ๋ผ๋ ๋ด ๊ณ ๋ฏผ์ ๊ณต๊ฐํ๊ณ ํด๊ฒฐ๋ฐฉ์์ ์ฐพ์๊ฐ ์ ์๋ ๊ธ์ด ๋๊ธฐ๋ฅผ!!
[๋ฑ
ํค์ฆ] 6. React-transition-group ๋ผ์ฐํ
ํธ๋์ง์
(1) - ๋์
ํ๊ธฐ ์์ ๊ณต์ ํ๊ณ ์๋ ๋ด์ฉ
- react-transition-group์ ์ด์ฉํด ๋ผ์ฐํธ๊ฐ ์ ๋๋ฉ์ด์ ๋ฃ๊ธฐ
- ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ ์ง์ (์๋ฐฉํฅ ์ฌ๋ผ์ด๋)
์ฐ๊ฒฐ๋ ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋ํ ๋์๋ ์ค๋ฅธ์ชฝ์์ ์๋ก์ด ํ์ด์ง๊ฐ ๋ค์ด์ค๋ฉด์ ์คํ์ด ์์ด๋ ๋ฏํ ๋๋์ ์ฃผ๊ณ , ๋ค์ ๋ฉ์ธํ๋ฉด์ ํฅํด ๋ค๋ก ๋์๊ฐ ๋๋ ์์ ์์๋ ํ์ด์ง๊ฐ ๋ค์ ์ค๋ฅธ์ชฝ์ผ๋ก ๋๊ฐ๋ ์ ๋๋ฉ์ด์ ์ ๊ตฌํํ๊ณ ์ถ์๋ค. ์ฆ, ๋ผ์ฐํ ์ ํ ๋ ํธ๋์ง์ ์ ์ค๋ค.
react-transition-group
์ด๋ผ๋ ๊ณต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค.
1. react-transition-group ๋์ ํ๊ธฐ
๋ฌธ์๋ฅผ ์ฑ ๋ณธ๋ค. ๊ทธ๋ฆฌ ์น์ ํ์ง๋ ์๋ค. CSSTransition
์ด๋ผ๋ ์ปดํฌ๋ํธ๋ฅผ ํตํด ์ ๋๋ฉ์ด์
์ ์ค ์ ์๋ค. ์ฌ๋ฌ ์์์์์ ๋์์ ์ ๋๋ฉ์ด์
์ ์ฃผ๊ณ ์ถ๋ค๋ฉด CSSTransition ์ปดํฌ๋ํธ๋ฅผ TransitionGroup ์ปดํฌ๋ํธ๋ก ํ๋ฒ ๊ฐ์ผ๋ค.
RouteTransition.tsx
const RouteTransition = ({ location, children }: RouteTransitionProps) => {
const pathname = location.pathname;
return (
<TransitionGroup className={'transition-wrapper'}>
<CSSTransition
key={pathname}
timeout={300}
classNames={'navigate-push'}
>
{children}
</CSSTransition>
</TransitionGroup>
);
};
export default RouteTransition;
CSSTransiton ์ปดํฌ๋ํธ์ props์ผ๋ก ์๋์ ๊ฐ๋ค์ ์ค๋ค.
- TransitionGroup ๋ด์์ ์ฌ์ฉํ ๋ map ํจ์์์ ์ฐ์ด๋ key์ฒ๋ผ ์ฐ์ธ๋ค. ๊ตฌ๋ถ์ฉ.
- ์ ๋๋ฉ์ด์ ์ด ๋ํ๋๋ ์๊ฐ์ timeout์ผ๋ก ์ค์ ํด์ค๋ค.
- classNames์ ์ง์ ํด์ค๋ค. ์๋์์ ๋ค์ ์์ธํ ๋ณด๊ฒ ๋ค.
CSSTransition์ ์์ ์์๋ค์ ํด๋์ค๋ฅผ ์ํ์ ๋ฐ๋ผ ๋ฐ๊ฟ์ฃผ๋ ์ญํ ์ ํ๋ค.
- ์๋ก ๋ค์ด์ค๋ ํ์ด์ง์๋
{classNames}-enter
, ๋๊ฐ๋ ํ์ด์ง์๋{classNames}-exit
- ํธ๋์ง์
์ด ์งํ์ค์ผ๋๋ ๊ฐ๊ฐ
enter-active
,exit-active
- ์๋ฃ๋๋ฉด
enter-done
,exit-done
์ ์ด๋ฆ์ผ๋ก ๋ถ๋๋ค.
Transiton.css
.navigate-push-enter {
transform: translateX(100%);
}
.navigate-push-enter-active {
z-index: 1;
transform: translateX(0);
transition: transform 300ms ease-in-out;
box-shadow: -5px 0px 25px rgba(0, 0, 0, 0.05);
}
.navigate-push-exit {
transform: translateX(0);
}
.navigate-push-exit-active {
transform: translateX(-20%);
transition: transform 300ms ease-in-out;
}
.transition-wrapper {
position: relative;
width: 100vw;
}
ํด๋์ค๋ง๋ค css ์คํ์ผ์ ์ง์ ์์ฑํ๋ค. ์๋ก ๋ค์ด์ค๋ ํ์ด์ง๋ ์คํ์ด ์์ด๋ ์ค๋ฅธ์ชฝ์์ ๋ค์ด์ค๊ธฐ ๋๋ฌธ์ z-index๋ฅผ ์ถ๊ฐ๋ก ์ฃผ์๋ค. ๋ค๋ฅธ ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ๋ค๊ณผ ๊ฑฐ์ ๋น์ทํ๊ฒ ๋ณด์ธ๋ค. ๊ฒฐ๊ตญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ํฉ์ ๋ง๊ฒ ์์๋ค์ ํด๋์ค ์ด๋ฆ์ ๋ฐ๊ฟ์ฃผ๋๊ฒ ์ ๋ถ์ด๊ณ , ๊ทธ์ ๋ง๋ ์ ๋๋ฉ์ด์ ์ css๋ฅผ ํตํด ์ง์ ์คํ์ผ์ ์ฃผ์ด์ผ ํ๋ ๊ฒ.
TransitionGroup ์ปดํฌ๋ํธ์ position : relative;
์์ฑ์ ์ฃผ์๋ค. ๊ทธ๋ฆฌ๊ณ ForegroundTemplate๊ณผ BackgroundTemplate ์ปดํฌ๋ํธ์ Wrapper์ position : absolute;
์์ฑ์ ์ค๋ค.. ํด๋น ํฌํ๋ฆฟ ๋ ์ด์์์ ์ฌ์ฉํ์ง ์๋๋ผ๊ณ ๊ฐ ํ์ด์ง์ ์ต์์ ์ปดํฌ๋ํธ์ ๋ชจ๋ absolute
์์ฑ์ ์ฃผ์ด์ผ ํ๋ค. ๊ทธ๋์ผ ์ ๋๋ฉ์ด์
์ด ์ ์์ ์ผ๋ก ์๋ํ๋ค.
CSSTransiton์ ์์์ผ๋ก๋ ์ปดํฌ๋ํธ ํ๋๋ง ๋ค์ด์ฌ ์ ์๋ค. ํธ๋์ง์
๊ด๋ จ ๋ก์ง์ ๋ฐ๋ก ๋ถ๋ฆฌํ๊ณ ๋ผ์ฐํ
์ปดํฌ๋ํธ๋ฅผ children์ผ๋ก ๋ฐ์์ ์ฌ์ฉํ๋ค.
ServiceRouter.tsx
// ServiceRouter.tsx
const ServiceRouter = () => {
const location = useLocation();
return (
<Wrapper>
<TabBar />
<Screen>
<RouteTransition location={location}>
<Routes location={location}>
<Route path="/*" element={<HomeRouter location={location} />} />
<Route path="/walk/*" element={<WalkRouter />} />
<Route
path="/mypage/*"
element={<MypageRouter location={location} />}
/>
<Route path="/interest/*" element={<InterestRouter />} />
</Routes>
</RouteTransition>
</Screen>
</Wrapper>
);
};
export default ServiceRouter;
ํธ๋์ง์
์ ์ ์ฉํ Routes ๋ฐ๊นฅ์ ์๊น ๋ง๋ RouteTransition ์ปดํฌ๋ํธ๋ก ๊ฐ์ธ์ฃผ์๋ค.
์ฌ์ค ์์์ ์ค๋ช
ํ์ง ์๊ณ ๋์ด๊ฐ ๋ถ๋ถ์ด ์๋ค. ์ฌ๊ธฐ์ ์ค์ํ๊ฑด Routes์ location ๊ฐ์ฒด๋ฅผ ๋๊ฒจ์ค๋ค๋ ๊ฒ. ์ด ์์
์ด ์์ผ๋ฉด, Routes ์๋์ ๋ชจ๋ Route๋ค์ ํญ์ ํ์ฌ ์ํ์ location ๊ฐ์ฒด๋ฅผ ๊ฐ๋๋ค.
๊ทธ๋ ๊ฒ ๋๋ฉด ์ด๋ฐ ๋์ฐธ์ฌ๊ฐ ์ผ์ด๋๋ค. enterํ๋ ํ์ด์ง์ exitํ๋ ํ์ด์ง, ์ด๋ ๊ฒ ๋๊ฐ๋์๊ณ ๋ณด์ฌ์ฃผ๊ธด ํ๋ค. ํ์ง๋ง ๊ฐ์ ํ์ด์ง ๋๊ฐ๊ฐ ๋ณด์ฌ์ง๋ค. ์๊น CSSTransition ์ปดํฌ๋ํธ์ key props๋ก location.pathname
์ ๋ฃ์ด์ฃผ์๊ณ , ๊ฐ์ location ๊ฐ์ฒด์ด๊ธฐ ๋๋ฌธ์ ๊ฐ์ key๊ฐ์ ๊ฐ๋๋ค.
๋งค๋ฒ Route๋ก location ๊ฐ์ฒด๋ฅผ ๋๊ฒจ์ค๋ค๋ฉด, ๋ชจ๋ Route๋ ํ์ฌ์ location ์ ๋ณด๊ฐ ์๋ ๊ฐ์ ์์ ์ด ๋ฐ์๋ location ์ ๋ณด๋ฅผ ๊ฐ์ง ์ ์๊ฒ ๋๋ค.
์๊ฐ๋ณด๋ค ๊ฐ๋จํ๋ค. ํ์ง๋ง ๋ฌธ์ ๋ ์ด๊ฒ ๋์ด ์๋๋ค. ๋ฐ๋ก ์ ์์งค์ ๋ค์ ๋ณด์. ์๋ฆผ ๋ด์ญ ํ์ด์ง์์ ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ ์ ๋๊ฐ ์๋นํ ์ด์ํ๋ค. '๋ค๋ก๊ฐ๊ธฐ'์ด๋ฉด ๋ฐ๋ ๋ฐฉํฅ์ ์ ๋๋ฉ์ด์
์ ๋ฐ๋ก ์ค ์ ์์ด์ผ ํ๋ค.
2. ์๋ฐฉํฅ์ผ๋ก ํธ๋์ง์ ์ฃผ๊ธฐ (๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ)
๋ชจ๋ ์ ๋๋ฉ์ด์ ์ ํด๋์ค๋ช ์ ๋ฐ๋ผ ์ง์ ํด๋ ์คํ์ผ๋๋ก ์ผ์ด๋๋ค. path๊ฐ ๋ฌ๋ผ์ง ๋ ๊ทธ๊ฒ '๋ค๋ก๊ฐ๊ธฐ'์์ ์์๋ด๋ฉด ํด๋์ค ์ด๋ฆ์ ๋ค๋ฅด๊ฒ ์ค์ผ๋ก์ ์ ๋๋ฉ์ด์ ์ ๋ค๋ฅด๊ฒ ๋ฃ์ด์ค ์ ์๊ฒ ๋ค.
AppBar.tsx
interface AppBarProps {
// ์ด์ ํ์ด์ง๋ช
label?: string;
// ์ด์ ํ์ด์ง ๋งํฌ
to?: string;
// ์ปค์คํ
์ด๋ฒคํธ
customEvent?: () => void;
}
function AppBar({ label, to, customEvent }: AppBarProps) {
const navigate = useNavigate();
const onClickAppBar = () => {
if (customEvent) {
customEvent();
} else {
to
? navigate(to, {
state: { direction: 'navigate-pop' },
})
: navigate(-1);
}
};
return (
<Wrapper>
<div onClick={onClickAppBar}>
<Arrow />
</div>
<p>{label}</p>
</Wrapper>
);
}
export default AppBar;
๊ฐ๋ตํ๊ฒ ์ถ๋ ค๋ธ ์๋จ ์ฑ๋ฐ ์ปดํฌ๋ํธ ์ฝ๋์ด๋ค. customEvent๋ ๋ค์ํธ์ ์ธ๊ธํ ์์ ์ด๋ฏ๋ก ์ ๊น ๋ฌด์ํด์ค๋ค. ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ ์ ๋ location์ state์ direction์ด๋ผ๋ ๊ฐ์ฒด๋ฅผ ์ ํด์ค๋ค. navigate-pop์ด๋ผ๋ ๋ฌธ์์ด์ ๋ด์์.
์ด์ ์๋ ๊ทธ๋ฅ navigate(-1)
๋ก ํ์คํ ๋ฆฌ์์ pop์ ํด์ฃผ๋ ์์ด์์ง๋ง ์ ์ฌ์ง๋๋ก navigate ํจ์์ ์ต์
์ ๋ฃ์ ์ ์๋ค. ๊ทธ๋์ ์ผ์ผ์ด ๋ค๋ก๊ฐ ์ฃผ์๋ฅผ ์ ์ด์ฃผ์ด์ผ ํ๋ ์ ์ด ์์ฌ์ ๋ค. ์๋ก ๋ฐ๋ ์ฝ๋์์๋ ์ธ๋ถ์์ ๋์๊ฐ ์ฃผ์(to
)๋ฅผ ์ธ์๋ก ๋ฐ์ ์ ์๊ณ , ๋ผ์ฐํ
์์ state๋ฅผ ๋ด์ ์ ์๋๋ก ํ๋ค.
<TransitionGroup className={'transition-wrapper'}>
<CSSTransition
key={pathname}
timeout={300}
classNames={location.state?.direction || 'navigate-push'}
>
{children}
</CSSTransition>
</TransitionGroup>;
CSSTransiton์์ classNames๋ฅผ ์ฝ๋์ ๊ฐ์ด ์์ฑํ๋ค. direction state๊ฐ ์์ผ๋ฉด ์๊น ์ ์ด์ค๋๋ก 'navigate-pop'์, ์๋ค๋ฉด 'navigate-push'๋ฅผ. Transition.css์ -pop์ ํด๋น๋๋ ์คํ์ผ๋ ์ถ๊ฐํด์ฃผ์๋ค. translate ๋ฐฉํฅ๊ณผ ๊ทธ๋ฆผ์์ ๋ฐฉํฅ์ ๋ฐ๋๋ก ์ ์กฐ์ ํด์ค๋ค. ์ผ์ฌ์ฐจ๊ฒ ์คํํด๋ณด์.
์ธ์์ ์ด๊ฒ ๋ฌด์จ์ผ์ด์ผ. ๋ฌธ์ ๋ ๊ฐ ๋ผ์ฐํธ์ ๋จ๊ฒจ๋์๋ ๊ฐ์ ์์ ์ location๊ฐ์ฒด์ state ๋๋ฌธ์ด์๋ค. ์๋ฆผ๋ด์ญ ํ์ด์ง์์ ๊ฐ๊ณ ์๋ location.state
๋ null
์ด๋ค. ๋ฐ๋ฉด ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ํตํด ๋ผ์ฐํ
๋ ๋ฉ์ธ ํ์ด์ง์ location.state
์๋ direction ๊ฐ์ฒด๊ฐ ์๋ค. ๊ทธ๋ก ์ธํด์ ๋ ํ์ด์ง์ className์ด -pop๊ณผ -push๋ก ๋ค๋ฅด๊ฒ ๋ค์ด๊ฐ๊ฒ ๋๋ ๊ฒ.
๋ ์์๊ฐ ๋๊ฐ์ className์ ๋ฐ์์ผ ํ ํ์๊ฐ ์๋ค. ์ด ๋, TransitionGroup์ childFactory props๊ฐ ๋ฑ์ฅํ๋ค. childFactory๋ exiting ํ๋ ์๋
์์๋ฅผ ์
๋ฐ์ดํธํ ๋ ์ฌ์ฉํ ์ ์๋ค (๋ผ๊ณ ๋ฌธ์์์ ๊ทธ๋ผ).
๊ฐ์ ๋ RouteTransiton.tsx
const RouteTransition = ({ location, children }: RouteTransitionProps) => {
const pathname = location.pathname;
const state = location.state;
return (
<TransitionGroup
className={'transition-wrapper'}
childFactory={(child) => {
return React.cloneElement(child, {
classNames: location.state?.direction || 'navigate-push',
});
}}
>
<CSSTransition exact key={pathname} timeout={300}>
{children}
</CSSTransition>
</TransitionGroup>
);
};
export default RouteTransition;
React.cloneElement๋ ์ธ์๋ก ๋ฐ์ ์๋ element๋ฅผ ๊ธฐ์ค์ผ๋ก ์๋ก์ด element๋ฅผ ๋ณต์ฌํ๊ณ ๋ฐํํ๋ค. ๋ฐํ๋ ๋๋ ์๋ ์์๊ฐ ๊ฐ๊ณ ์๋ props๊ฐ ์๋ก์ด props์ ์๊ฒ ํฉ์ณ์ง๋ค๊ณ ํ๋ค. ๊ฐ๊ฐ์ child๋ง๋ค classNames์ ์ผ๊ด์ ์ผ๋ก ๋ค์ ์ ํด ๋ฆฌํดํ๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ด๋ํ ๋ผ์ฐํธ์ location.state๋ฅผ ๋ชจ๋ ์๋
๊ฐ ๊ณตํต์ผ๋ก ํด๋์ค๋ช
์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
'๋ค๋ก๊ฐ๊ธฐ'๋ฅผ ํตํด ํ์ด์ง๊ฐ ๋ฐ๋ ๋๋ enter์ exit ๋ ์์์ ํด๋์ค๋ช ์ด ๋ชจ๋ 'navigate-pop'์ผ๋ก ์ ์ฉ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ฝค ๋ง์กฑ์ค๋ฌ์ด ๊ฒฐ๊ณผ๋ฌผ์ด๋ค. ํ์ง๋ง ํ์ค์ ๊ทธ๋งํผ ๋ น๋ก์น ์๋ค. ๋์ฑ ์๋ฒฝํ ๊ฒฝํ์ ์ํด์ ์๋์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด์ผ ํ๋ค.
- ๊ธฐ์กด์ ๋ผ์ฐํ ๊ตฌ์กฐ ๋๋ฌธ์ ์๊ฒผ๋ ๋ฌธ์
- ํ์ด์ง ์์์ ๋ฐ๋ผ ๋ค๋ฅธ ์ ๋๋ฉ์ด์ ์ ๋ณด์ฌ์ฃผ๊ธฐ
- ๋ผ์ฐํ ์ด ์๋, state ๋ณ๊ฒฝ์ ๋ฐ๋ฅธ ์ ๋๋ฉ์ด์ ๋ฃ๊ธฐ
[๋ฑ
ํค์ฆ] 7. React-transition-group ๋ผ์ฐํ
์ ๋๋ฉ์ด์
(2) - ๋ํ
์ผ ์ก๊ธฐ ์์ ํด๊ฒฐํด๋ณด๊ฒ ์ต๋๋ค. ์ปค๋ฐ์จ.
์ฐธ๊ณ 1
์ฐธ๊ณ 2