[React] 버튼 컴포넌트 재사용하기
1️⃣ 기존에 사용한 방식
기존에 프로젝트에서 버튼 컴포넌트를 구성할 때는 props를 사용하여 컴포넌트를 생성했다.
// ButtonContainer.jsx
export default function ButtonContainer({
children,
type,
color,
bgColor,
width,
height,
disabled = false,
onClick,
rmargin,
fontSize,
border,
hoverFilter,
}) {
return (
<Button
type={type || "button"}
onClick={onClick}
$color={color}
$bgColor={bgColor}
$width={width}
$height={height}
$disabled={disabled}
$rmargin={rmargin}
$fontSize={fontSize}
$border={border}
$hoverFilter={hoverFilter}
>
{children}
</Button>
);
}
// ButtonContainerStyle.jsx
export const Button = styled.button`
width: ${(props) => (props.$width ? props.$width : "120px")};
height: ${(props) => (props.$height ? props.$height : "44px")};
background-color: ${(props) =>
props.$bgColor ? props.$bgColor : props.theme.colors.mainColor};
color: ${(props) => (props.$color ? props.$color : "#ffffff")};
border-radius: 30px;
margin-right: ${(props) => (props.$rmargin ? props.$rmargin : "")};
font-size: ${(props) =>
props.$fontSize ? props.$fontSize : props.theme.fontSize.small};
border: ${(props) => (props.$border ? props.$border : null)};
&:hover {
filter: ${(props) => (props.$hoverFilter ? "brightness(0.9)" : null)};
transition: all 0.2s ease-in-out;
}
`;
기본 스타일을 styled-component을 사용하여 지정하고, 스타일링에 필요한 요소들을 props로 받아온 후 필요한 스타일을 버튼마다 적용시켰다.
하지만, 이런식으로 기본 스타일을 지정해주니 버튼마다 크기도 스타일도 다른 경우가 많아 추가되는 코드가 오히려 더 많아지고 너무 많아진 props 때문에 한번에 알아보기가 어려웠다… 🤧
리팩토링을 고민하던 중 한 블로그에서 css 함수를 사용한 방법을 보고 방식을 바꿔봤다.
2️⃣ CSS 함수 사용
✔️ 기본 스타일링
버튼 컴포넌트에 공통적으로 필요한 스타일을 적용시켜준다. 이때 버튼 종류에 따라 바뀔 수 있는 속성값은 CSS 변수
를 사용해주었다.
function Button({ disabled, children, ...props }) {
return (
<StyledButton disabled={disabled} {...props}>
{children}
</StyledButton>
);
}
const StyledButton = styled.button`
margin: 0;
font: inherit;
cursor: pointer;
background: var(--button-background);
border: var(--button-border);
color: var(--button-color);
padding: var(--button-padding);
height: var(--button-height);
line-height: var(--button-height);
font-size: var(--button-font-size);
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: 2.75rem;
transition: all 0.3s ease-out;
&:disabled {
--button-background: ${(props) => props.theme.colors.disabledColor};
--button-color: ${(props) => props.theme.colors.whiteColor};
cursor: not-allowed;
}
&:hover {
filter: brightness(0.9);
}
`;
✔️ 크기 스타일링
크기에 따른 버튼 컴포넌트를 생성하기 위해 size
라는 prop을 추가해준다. 이때 styled-components의 css 함수를 사용하여 SIZES
라는 자바스크립트 객체에 저장해줬다.
import styled, { css } from "styled-components";
const SIZES = {
sm: css`
--button-font-size: ${(props) => props.theme.fontSize.xsmall};
--button-padding: 0 0.85rem;
--button-height: 1.75rem;
`,
md: css`
--button-font-size: ${(props) => props.theme.fontSize.small};
--button-padding: 0.5rem;
--button-height: 2.15rem;
`,
lg: css`
--button-font-size: ${(props) => props.theme.fontSize.medium};
--button-padding: 0 1.15rem;
--button-height: 2.75rem;
`,
};
✔️ 변형 스타일링
성공, 오류, 경고 등 여러 상황에 따라 활용하거나 버튼 UI가 여러 타입일 때 사용할 수 있는 버튼 컴포넌트다. variant
라는 prop을 추가해준 뒤 css 함수를 사용하여 VARIANTS
라는 자바스크립트 객체에 저장해준다.
import styled, { css } from "styled-components";
const VARIANTS = {
primary: css`
--button-color: ${(props) => props.theme.colors.whiteColor};
--button-background: ${(props) => props.theme.colors.mainColor};
--button-border: 1px solid ${(props) => props.theme.colors.mainColor};
`,
white: css`
--button-color: ${(props) => props.theme.colors.blackColor};
--button-background: ${(props) => props.theme.colors.whiteColor};
--button-border: 1px solid ${(props) => props.theme.colors.placeHolderColor};
`,
};
size, variant prop을 받아서 저장해둔 객체에 따라 해당하는 스타일을 적용하기 위해 기본 StyledButton
컴포넌트에 아래처럼 추가해준다.
function Button({ disabled, size, variant, children, ...props }) {
return (
<StyledButton disabled={disabled} size={size} variant={variant} {...props}>
{children}
</StyledButton>
);
}
const StyledButton = styled.button`
${({ size }) => SIZES[size]}
${({ variant }) => VARIANTS[variant]}
(이하 생략)
`;
3️⃣ 사용하기
<Button type="button" size="lg" variant="white">
회원가입
</Button>
이렇게 사이즈, 상황 별로 변수를 지정해주니 props만 보고 어떤 버튼인지 한눈에 파악할 수 있고, 중복되는 코드 또한 줄일 수 있었다. 👍
위의 분류 말고도 필요한 상황에 따라 변수를 추가하여 버튼 컴포넌트를 분류해주면 좋을 것 같다.
전체 코드 (Button.jsx)
import styled, { css } from "styled-components";
function Button({ disabled, size, variant, children, ...props }) {
return (
<StyledButton disabled={disabled} size={size} variant={variant} {...props}>
{children}
</StyledButton>
);
}
const SIZES = {
sm: css`
--button-font-size: ${(props) => props.theme.fontSize.xsmall};
--button-padding: 0.2rem 0.3rem;
--button-height: 1.75rem;
`,
md: css`
--button-font-size: ${(props) => props.theme.fontSize.small};
--button-padding: 0.5rem 1.95rem;
--button-height: 2rem;
`,
lg: css`
--button-font-size: ${(props) => props.theme.fontSize.medium};
--button-padding: 0.7rem 2.15rem;
--button-height: 2.75rem;
`,
};
const VARIANTS = {
primary: css`
--button-color: ${(props) => props.theme.colors.whiteColor};
--button-background: ${(props) => props.theme.colors.mainColor};
--button-border: 1px solid ${(props) => props.theme.colors.mainColor};
`,
white: css`
--button-color: ${(props) => props.theme.colors.blackColor};
--button-background: ${(props) => props.theme.colors.whiteColor};
--button-border: 1px solid ${(props) => props.theme.colors.placeHolderColor};
`,
disabled: css`
--button-color: ${(props) => props.theme.colors.whiteColor};
--button-background: ${(props) => props.theme.colors.disabledColor};
--button-border: 1px solid ${(props) => props.theme.colors.disabledColor};
`,
};
const StyledButton = styled.button`
${({ size }) => SIZES[size]}
${({ variant }) => VARIANTS[variant]}
margin: 0;
font: inherit;
cursor: pointer;
background: var(--button-background);
border: var(--button-border);
color: var(--button-color);
padding: var(--button-padding);
height: var(--button-height);
line-height: var(--button-height);
font-size: var(--button-font-size);
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: 2.75rem;
transition: all 0.3s ease-out;
&:disabled {
--button-background: ${(props) => props.theme.colors.disabledColor};
--button-color: ${(props) => props.theme.colors.whiteColor};
cursor: not-allowed;
}
&:not(:disabled):hover {
filter: brightness(0.9);
}
`;
export default Button;
Leave a comment