공통 버튼 제작기

sikkzz·2024년 4월 7일
0

Project

목록 보기
3/8

🖐️ 시작하며

현재 진행중인 프로젝트에서 자주 사용하는 컴포넌트들을 공통 컴포넌트들로 만들어서 사용하고 있습니다. 디자인 시스템이 다 정해지고 개발을 시작한게 아니고 디자인도 중간중간 수정되는 부분들도 많았지만 Flex, Box, Heading, Text 등의 자주 사용되는 컴포넌트들은 이미 공통 컴포넌트로 제작을 완료 했었습니다.

개발을 계속 이어나가다가 계속 겹치는 Button 스타일링 코드들이 너무 거슬려서 버튼도 공통으로 제작하고 수정하면서 개발을 이어나가는게 좋겠다고 생각해서 Button 공통 컴포넌트 제작에 들어갔습니다.

버튼 종류 구분하기

프로젝트에서 자주 사용되는 버튼의 종류들은 다음과 같았습니다.

  • Primary(주황) Background, White Text
  • White Background, Gray Text, Gray Border
  • Gray Background, White Text
  • White Background, Primary Text
  • Red Background, White Text

사이즈는 사용되는 곳마다 상이한 부분들이 많았기에 우선은 variant 종류로만 구분을 두고 제작에 들어갔습니다.

코드 작성

Button Props

버튼 Props로는 variant로 종류를 구별해주고 하위 컴포넌트 children과 나머지 속성들을 받아줄 attributes를 받아줍니다. 그리고 useRef가 사용될 여지가 있으므로 ref도 지정해주었습니다.

가장 많이 사용되는 primary를 default값으로 잡고 나머지는 속성에 맞춰서 네이밍을 아래와 같이 해주었습니다.

// ButtonProps
export interface ButtonProps extends ComponentPropsWithRef<"button"> {
	variant?: "default" | "disabled" | "white" | "outline" | "danger";
}

const Button = (
	{ variant = "default", children, ...attributes }: ButtonProps,
	ref: ForwardedRef<HTMLButtonElement>,
) => ...

Button Component

컴포넌트 코드에는 ref와 styling을 위한 css를 작성해주었습니다. 기본 button 스타일에다가 variant 별로 속성을 따로 정해주는 방식으로 작성했습니다.

// Button Component
const Button = (
	{ variant = "default", children, ...attributes }: ButtonProps,
	ref: ForwardedRef<HTMLButtonElement>,
) => {
	return (
		<button
			ref={ref}
			css={[buttonStyle, buttonVariantStyle(variant)]}
			{...attributes}
		>
			{children}
		</button>
	);
};

// Button Style
export const buttonVariantStyle = (variant: Required<ButtonProps>["variant"]) => {
	const style = {
		default: css({
			backgroundColor: Theme.color.brand_primary,
			color: Theme.color.white,
		}),
		disabled: css({
			backgroundColor: Theme.color.disabled_text,
			color: Theme.color.white,
		}),
		outline: css({
			backgroundColor: Theme.color.white,
			color: Theme.color.disabled_text,
			border: `1px solid ${Theme.color.border}`,
		}),
		white: css({
			backgroundColor: Theme.color.white,
			color: Theme.color.brand_primary,
		}),
		danger: css({
			backgroundColor: Theme.color.btn_danger,
			color: Theme.color.white,
		}),
	};

	return style[variant];
};

export const buttonStyle = css({
	display: "flex",
	justifyContent: "center",
	alignItems: "center",
	border: "none",
	borderRadius: "4px",
	outline: "none",
	backgroundColor: Theme.color.white,
	fontWeight: 500,
	fontSize: Theme.text.medium.fontSize,
	cursor: "pointer",
	fontFamily: "Pretendard",

	"&:disabled": {
		opacity: ".4",
	},
});

Story book

팀원이 개발하는 파트까지 Figma를 보면서 제작을 하긴 했지만 빠진 부분이 있을 수 있을까봐 Storybook에 작성을 해서 팀원에게 공유하고 빠진 부분이 있으면 추가하기로 했습니다.

스토리북 코드는 참고용으로 올려놓습니다

// button stories
import type { Meta, StoryObj } from "@storybook/react";

import Button from "@/components/common/Design/Button/Button";

import { containerStyle, informationStyle } from "./styles";

import type { ButtonProps } from "@/components/common/Design/Button/Button";

const meta = {
	title: "Button",
	component: Button,
	argTypes: {
		variant: {
			control: { type: "radio" },
			options: ["default", "disabled", "white", "outline", "danger"],
		},
		size: {
			control: { type: "radio" },
			options: ["small", "medium", "large", "xLarge"],
		},
		children: {
			control: { type: "text" },
		},
	},
	args: {
		variant: "default",
		size: "small",
		children: "Button",
	},
} satisfies Meta<typeof Button>;

export default meta;
type Button = StoryObj<typeof meta>;

const createButtonStory = (variant: ButtonProps["variant"]) => ({
	args: {
		variant,
	},
	argTypes: {
		variant: {
			control: false,
		},
	},
});

export const Playground: Button = {};

export const Variants: Button = {
	render: ({ size, children }) => (
		<ul css={containerStyle}>
			<li css={informationStyle}>
				<h6>Default</h6>
				<Button variant="default" size={size}>
					{children}
				</Button>
			</li>
			<li css={informationStyle}>
				<h6>Disabled</h6>
				<Button variant="disabled" size={size}>
					{children}
				</Button>
			</li>
			<li css={informationStyle}>
				<h6>white</h6>
				<Button variant="white" size={size}>
					{children}
				</Button>
			</li>
			<li css={informationStyle}>
				<h6>outline</h6>
				<Button variant="outline" size={size}>
					{children}
				</Button>
			</li>
			<li css={informationStyle}>
				<h6>danger</h6>
				<Button variant="danger" size={size}>
					{children}
				</Button>
			</li>
		</ul>
	),
	argTypes: {
		variant: {
			control: false,
		},
	},
};

export const Default: Button = createButtonStory("default");
export const Disabled: Button = createButtonStory("disabled");
export const White: Button = createButtonStory("white");
export const Outline: Button = createButtonStory("outline");
export const Danger: Button = createButtonStory("danger");

🔚 마치며

여러 파일에 흩어져 있던 Button styling 코드들을 다 제거하고 공통 컴포넌트로 제작함으로써 버튼 수정시 유지보수에 용이하고 코드 간소화 또한 진행할 수 있었습니다. 버튼 Size별로도 제작이 가능하지만 해당 프로젝트에서는 버튼의 width, height만 다르고 대부분 내부 속성값들은 동일해서 size별 버튼은 팀원과 상의후에 좀 더 제작 예정입니다.

Input, TextArea 등 다른 컴포넌트들도 제작중에 있는데 제작 완료 후 npm 배포로 디자인 시스템만 따로 import해서 사용하는걸 최종 목표 중 하나로 잡고 있으니 남은 컴포넌트들도 모아서 제작을 진행해야 할 거 같습니다.

프로젝트 세팅하면서 디자인 컴포넌트를 만들어놓으면 훨씬 수월한 개발이 가능하지만 그러지 못했기에 중간중간 리팩토링 과정이 생길 수 밖에 없는 거 같습니다. 결과적으로는 코드 라인 200줄 정도 압축이 가능했습니다.

다음에는 관련해서 디자인 시스템 최종 제작 및 배포 관련 포스팅으로 돌아오도록 하겠습니다.

감사합니다.

profile
FE Developer

0개의 댓글