라우터가 없는 Next.js 환경에서 특정 경로(/meeting)에서는 푸터를 sticky로 설정하고, 나머지 경로에서는 fixed로 설정하고 싶어했다. 이를 위해 기존 Footer 컴포넌트를 수정하고, CVA를 사용하여 변형을 적용했다.
<html lang="en">
<body className={inter.className}>
<Header />
{children}
<Footer />
</body>
</html>
position을 sticky
와 fixed
번갈아서 넣고, 모든 페이지에서는 'fixed'를 넣고 '/meeting'만 sticky를 적용해야하는데
컴포넌트를 따로 분리해서 layout.tsx를 따로 만들었는데도 해결할 수 없었고,
컴포넌트마다 footer 적용을 해봤지만 코드의 효율성 문제로 쓰지 않았다.
모든 페이지에서 Footer를 fixed로 설정하고, /meeting 페이지에서는 sticky로 설정하는 요구사항을 충족해야했다. 기존 컴포넌트 구조에서는 이를 동적으로 처리하기 어려웠다.
CVA와 Next.js의 usePathname
훅을 사용하여 경로에 따라 position
속성을 동적으로 변경했다.
CVA를 사용해 fixed
와 sticky
변형을 정의.
usePathname 훅을 사용해 현재 경로를 확인하고, 경로에 따라 position 속성을 설정.
변경된 Footer 컴포넌트에서는 경로에 따라 position 속성을 동적으로 설정했다.
'use client';
import { cva, VariantProps } from 'class-variance-authority';
import { usePathname } from 'next/navigation';
import { ComponentProps, PropsWithChildren } from 'react';
const footerVariants = cva(' bg-header-color text-white text-center py-4 bottom-0 left-0 w-full z-10', {
variants: {
position: {
fixed: 'fixed',
sticky: 'sticky'
}
}
});
type FooterVariant = VariantProps<typeof footerVariants>;
type FooterProps = FooterVariant & ComponentProps<'footer'>;
export default function Footer({ position, ...props }: PropsWithChildren<FooterProps>) {
const pathname = usePathname();
const isMeetingPath = pathname.startsWith('/meeting');
const Position = isMeetingPath && position === undefined ? null : 'fixed';
return (
<footer className={footerVariants({ position: Position })} {...props}>
{' '}
© 2024 독수리오남매. All rights reserved.
</footer>
);
}
특정 경로에서는 sticky 푸터를, 나머지 경로에서는 fixed 푸터를 동적으로 적용할 수 있게 되었다.
class-variance-authority 라이브러리의 약자로 일관성 있는 UI를 정의하고 사용할 수 있도록 도와주는 툴로써 컴포넌트 사용할때 유용하다.
즉, 공통 스타일링
yarn add class-variance-authority
import { cva } from "class-variance-authority";
const button = cva("button", {
variants: {
intent: {
primary: "button--primary",
secondary: "button--secondary",
},
size: {
small: "button--small",
medium: "button--medium",
},
},
compoundVariants: [
{ intent: "primary", size: "medium", class: "button--primary-small" },
],
defaultVariants: {
intent: "primary",
size: "medium",
},
});
button();
// => "button button--primary button--medium"
button({ intent: "secondary", size: "small" });
// => "button button--secondary button--small"
intent
와 size
의 값에 따라 다른 디자인을 보여줄 수 있도록 구성되어 있다.
cva 첫번째 인자에는 모든 경우 공통으로 들어갈 css를 입력하게 된다.
만약 없다면 '' 비워두어도 된다.
두번째 인자에서부터 조건에 따른 객체를 넣어주면 된다.