패스트캠퍼스 데브캠프

토이3 프로젝트 - 작성한 코드 복기 (2) - 공통 컴포넌트 버튼

vitamin3000 2025. 1. 17. 22:28

이번 시간에는 팀원분이 구현해주신 버튼에 대한 코드 복기를 해보고자 한다.

예를 들면 이런?

하나의 Button 컴포넌트에서 다양한 size와 background-color를 적용하기 위해서는 props로 값을 받아서 동적으로 처리해야한다.

 

 

1. 따라서 먼저 Props에 대한 타입을 지정한다.

type ButtonProps = {
  children: React.ReactNode;
  onClick?: () => void;
  type?: 'button' | 'submit' | 'reset';
  variant?:
    | 'primary'
    | 'secondary'
    | 'danger'
    | 'success'
    | 'tertiary'
    | 'outline'
    | 'kakao'
    | 'google';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  className?: string;
};

 

type, variant, size는 리터럴 타입으로 입력받아 정해놓은 값만 입력받도록 하였다.

2. 해당 props을 type으로 입력받는다

const Button = ({
  children,
  onClick,
  type = 'button',
  variant = 'secondary',
  size = 'medium',
  disabled = false,
  className = '',
}: ButtonProps) => {

 

이때 type = 'button'과 같이 기본값을 설정할 수 있다.

 

3. 각 스타일에 맞는 디자인 토큰을 정의한다.

const baseStyles = 'rounded-md transition-all duration-300';
  const variantStyles = {
    primary: 'bg-primary text-light hover:bg-hover-primary',
    secondary:
      'bg-secondary text-light hover:bg-hover-secondary hover:text-black',
    danger: 'bg-danger text-light hover:bg-hover-danger',
    success: 'bg-success text-light hover:bg-hover-success',
    tertiary: 'bg-tertiary text-black hover:bg-hover-tertiary',
    outline:
      'border border-primary text-black hover:bg-primary hover:text-light',
    kakao: 'bg-[#FEE500] text-black hover:bg-[#FFEB3B]',
    google: 'bg-transparent border border-gray-300 text-gray hover:bg-gray-100',
  };
  const sizeStyles = {
    small: 'px-4 py-1 text-xs',
    medium: 'px-6 py-1.5 text-sm',
    large: 'px-8 py-1.5 text-base',
  };

 

4. 이러한 디자인 토큰들을 cn 함수를 이용하여 하나로 묶는다

  const combinedStyles = cn(
    baseStyles,
    variantStyles[variant],
    sizeStyles[size],
    className,
  );

4-1 여기서 cn 함수란?

import { twMerge } from 'tailwind-merge';
import { ClassValue, clsx } from 'clsx';

const cn = (...inputs: ClassValue[]): string => {
  return twMerge(clsx(...inputs));
};

export { cn };

 tailwind-merge와 clsx를 사용하여 Tailwind CSS 클래스 이름을 결합하는 기능이다

 

4-2 Button 컴포넌트에 대한 return 작성

  return (
    <button
      type={type}
      onClick={onClick}
      disabled={disabled}
      className={cn(
        combinedStyles,
        disabled && 'cursor-not-allowed opacity-60',
      )}
    >
      {children}
    </button>
  );

 

여기서 disabled && 와 같이 조건 연산을 적용할 때에도 cn함수를 사용할 수 있다.

 

5. .ts 파일의 이름으로 다음과 같이 버튼 목록을 생성한다

const bookmarkCategories = ['전체', '북마크만 보기', '카테고리'];

export { bookmarkCategories };

 

6. 그다음 이 값을 저장할 State값을 선언하고

const [selectedCategory, setSelectedCategory] = useState('전체');

 

7. 그 컴포넌트에서 값을 내려준다

      <BookmarkCategory
        selectedCategory={selectedCategory}
        onCategoryChange={setSelectedCategory}
      />

 

8.  버튼 목록의 컴포넌트를 구성한다

const BookmarkCategory = ({
  selectedCategory,
  onCategoryChange,
}: VideoCategoryProps) => {
  return (
    <ul className="flex flex-wrap gap-2">
      {bookmarkCategories.map((category, index) => (
        <li key={index}>
          <Button
            variant={selectedCategory === category ? 'primary' : 'outline'}
            size="small"
            onClick={() => onCategoryChange(category)}
          >
            {category}
          </Button>
        </li>
      ))}
    </ul>
  );
};