Next.js에서 페이지 전환 애니메이션 구현
웹사이트에서 페이지가 전환될 때, 단순한 화면 변경보다 부드러운 애니메이션이 적용되면
사용자 경험에 있어 긍정적인 영향을 미친다고 생각합니다.
Next.js에서는 기본적으로 페이지 이동 시 애니메이션이 없지만,
이를 직접 구현하여 자연스러운 페이지 전환을 만들 수 있습니다.
이번 글에서는 좌우 슬라이드 전환 애니메이션을 적용하는 방법을 공유하겠습니다.
이를 위해 Context API를 활용하여 애니메이션 상태를 관리하고,
커스텀 훅을 만들어 애니메이션을 쉽게 적용할 수 있도록 해보겠습니다.
본 예시에서는 Next.js Tailwind CSS를 사용하였습니다.
| Context 생성하기
먼저, 페이지 전환 애니메이션을 관리할 Context를 생성합니다.
이 Context는 애니메이션을 위한 className과 애니메이션 타입(left 또는 right),
그리고 className을 변경하는 함수를 포함하고 있습니다.
"use clinet";
import { createContext } from "react";
export type PageAnimation = "left" | "right";
interface PageTransitionContext {
animation: React.RefObject<PageAnimation>;
className: string;
setClassName: React.Dispatch<React.SetStateAction<string>>;
}
const PageTransitionContext = createContext<PageTransitionContext | null>(null);
export default PageTransitionContext;
여기서 Context의 역할은 다음과 같습니다.
- animation : 현재 적용할 애니메이션 방향 (left 또는 right)
- className : 애니메이션이 적용될 페이지의 클래스
- setClassName : 클래스명을 변경하는 함수
| Provider 생성하기
이제 생성한 Context를 하위 컴포넌트에 공급할 Provider를 만들어줍니다.
이 Provider는 기본적인 페이지 레이아웃(헤더, 푸터 등)을 포함하고,
애니메이션을 관리하는 값을 Context에 설정합니다.
또한, 애니메이션이 적용될 페이지를 children으로 받아 div 박스로 감싸 애니메이션을 적용합니다.
"use client";
import PageTransitionContext, {
PageAnimation,
} from "@/app/context/page-transition-context";
import cn from "@/utils/cn";
import { useRef, useState } from "react";
import Navbar from "../components/navbar/navbar";
const PageTransitionProvider = ({
className,
children,
}: React.PropsWithChildren<{
className?: React.ComponentProps<"div">["className"];
}>) => {
const [animationClass, setAnimationClass] = useState("");
const animation = useRef<PageAnimation>("left");
return (
<PageTransitionContext.Provider
value={{
animation,
className: animationClass,
setClassName: setAnimationClass,
}}
>
<Navbar />
<div className={cn("relative p-16", className, animationClass)}>
{children}
</div>
</PageTransitionContext.Provider>
);
};
export default PageTransitionProvider;
여기서 cn 함수는 tailwind CSS 클래스를 안전하게 합쳐주는 유틸 함수입니다.
이제 Provider를 Next.js의 layout.tsx에서 감싸주면 모든 페이지에서 애니메이션을 사용할 수 있습니다.
<PageTransitionProvider>{children}</PageTransitionProvider>
| 커스텀 훅 만들기
이제 슬라이드 애니메이션을 위한 커스텀 훅을 만들겠습니다.
이 훅은 애니메이션 타입에 따라 Context값을 변경하여 페이지가 적절히 슬라이드 되도록 합니다.
애니메이션 클래스 반환 함수
const getOutAnimation = (animation: PageAnimation) => {
return animation === "left" ? "animate-slide-left-out" : "animate-slide-right-out";
};
const getInAnimation = (animation: PageAnimation) => {
return animation === "left" ? "animate-slide-left-in" : "animate-slide-right-in";
};
애니메이션 적용 함수
const animate = (animation: PageAnimation, context: PageTransitionContext) => {
return new Promise((resolve) => {
const className = getOutAnimation(animation);
context.setClassName(className);
context.animation.current = animation;
setTimeout(resolve, SLIDE_ANIMATION_DURATION);
});
};
커스텀 훅 완성
const usePageTransition = () => {
const pageTransitionContext = useContext(PageTransitionContext);
if (!pageTransitionContext) {
throw new Error(
"usePageTransition은 PageTransitionContext 내부에서 사용해야 합니다."
);
}
const context = pageTransitionContext;
const slideLeft = () => animate("left", context);
const slideRight = () => animate("right", context);
const slideIn = () => {
if (context.animation.current) {
const animation = getInAnimation(context.animation.current);
context.setClassName(animation);
}
};
return { slideLeft, slideRight, slideIn };
};
| 애니메이션 적용하기
기본적으로 페이지 전환 기능은 다음과 같이 동작합니다.
- 링크를 클릭하면 애니메이션 className을 붙여서 페이지가 슬라이드 되도록 한다.
- 슬라이드가 완료되면 페이지를 이동시킨다.
- 이동이 완료되면 페이지가 슬라이드 되어 들어오도록 한다.
페이지 입장 시 애니메이션 적용
먼저 페이지에 입장할 때 해당 페이지가 적절히 슬라이드 되어 오도록 초기화해 줘야 합니다.
따라서, 애니메이션이 적용될 페이지에 커스텀 훅에 있는 slideIn 함수를 실행시켜 줍니다.
export default function Home() {
const { slideIn } = usePageTransition();
useEffect(() => {
slideIn();
}, []);
return (
<div>
<h1>Main Page</h1>
</div>
);
}
페이지 이동 시 애니메이션 적용
이제 페이지에서 나갈 때 슬라이드 되어 사라지도록 해야 합니다.
이를 구현하려면 링크를 클릭하면 커스텀 훅에 있는 slideLeft나 slideRight를 사용하여 페이지가 슬라이드 되도록 한 후 해당 애니메이션이 완료되면 Next.js의 useRouter를 사용해 페이지를 이동시켜 줍니다.
링크를 클릭하면 실행될 navigation 함수를 다음과 같이 만들어 줍니다.
// 링크 데이터
const navLink = [
{ title: "Home", url: "/" },
{ title: "About", url: "/about" },
{ title: "Services", url: "/services" },
{ title: "Contact", url: "/contact" },
];
// 함수
const navigation = (url: string) => {
if (pathname === url) return;
const curIndex = navLink.findIndex((link) => link.url === pathname);
const clickIndex = navLink.findIndex((link) => link.url === `${url}`);
if (curIndex > clickIndex) {
slideRight().then(() => {
router.push(url);
});
} else {
slideLeft().then(() => {
router.push(url);
});
}
};
여기서 usePathname을 활용해 같은 url에서는 동작하지 않게 합니다.
또한, 링크 순서에 따라 적절하게 오른쪽, 왼쪽 애니메이션이 구분되어 작동되도록 하면
더욱 보기 좋은 애니메이션이 구현될 수 있습니다.
| 끝
이제 Next.js에서 페이지 이동 시 자연스러운 슬라이드 애니메이션이 적용되었습니다.
해당 애니메이션은 transform: translateX(...)를 활용하여 구현되었으며,
이는 브라우저의 GPU에서 처리되므로 reflow나 repaint 없이 성능적으로 매우 효율적인 방식입니다.
하지만 과도한 애니메이션 사용이나, 여러 애니메이션이 동시에 실행되는 경우
GPU 리소스가 증가하면서 성능 저하가 발생할 수 있습니다.
또한 애니메이션이 항상 사용자 경험을 향상시키는 것은 아닙니다.
지나치게 길거나 불필요한 애니메이션은 오히려 사용자의 피로감을 유발할 수 있습니다.
따라서 페이지 전환 애니메이션을 적용할 때는 연출의 목적과 사용자 경험을 종합적으로 고려하여
적절한 타이밍과 강도로 적용하는 것이 중요합니다.