button 컴포넌트 코드로 공부 - 인터페이스, clsx

·2023년 7월 4일

SUI 프로젝트

목록 보기
2/6

SUI를 진행 중 기본 개념이 부족한 것 같아 button 컴포넌트 코드를 하나하나 뜯어보며 다시 공부해보기로 했다. 얼렁뚱땅 내 몫의 일을 하고있긴 하지만 팀원에게 하는 질문이 너무 많기도하고 기본기가 부족하다는 소리를 들었다ㅠ 정말 맞는 말이라고 생각한다. 시간 안에 내 분량만큼 개발하지 못할까봐 조급함이 들어서 계속 미뤘는데 이번에 확실하게 개념을 잡고 가야겠다😾



import * as React from 'react';
import clsx from 'clsx';

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'contained' | 'outlined' | 'text';
  color?: 'primary' | 'secondary';
  size?: 'small' | 'medium' | 'large';
}

const baseStyles = 'font-medium rounded';

const sizeStyles = {
  small: 'text-xs px-2.5 py-1.5',
  medium: 'text-sm leading-4 px-3 py-2',
  large: 'text-sm px-4 py-2',
};

const variantStyles = {
  contained: 'border border-transparent',
  outlined: 'bg-transparent border',
  text: 'bg-transparent border-transparent',
};

const colorStyles = {
  text: {
    primary:
      'text-black bg-transparent hover:bg-primary-50 active:bg-primary-100',
    secondary:
      'text-black bg-transparent hover:bg-secondary-50 active:bg-secondary-100',
  },
  contained: {
    primary:
      'text-white bg-primary-default hover:bg-primary-600 active:bg-primary-500 rounded-lg',
    secondary:
      'text-white bg-secondary-default hover:bg-secondary-600 active:bg-secondary-500 rounded-lg',
  },
  outlined: {
    primary:
      'text-primary-default border-primary-default hover:bg-primary-50 active:bg-primary-100',
    secondary:
      'text-secondary-500 border-secondary-500 hover:bg-secondary-50 active:bg-secondary-100',
  },
};

const disabledSyles = 'cursor-default pointer-events-none';
const disabledVariantSyles = {
  contained: 'text-gray-300 bg-gray-100',
  outlined: 'text-gray-300 border-gray-300',
  text: 'text-gray-300 bg-gray-100',
};

export function Button({
  className,
  variant = 'contained',
  color = 'primary',
  size = 'medium',
  disabled = false,
  children,
  ...props
}: ButtonProps) {
  const buttonClass = disabled
    ? clsx(
        className,
        baseStyles,
        variantStyles[variant],
        sizeStyles[size],
        disabledSyles,
        disabledVariantSyles[variant]
      )
    : clsx(
        className,
        baseStyles,
        variantStyles[variant],
        sizeStyles[size],
        colorStyles[variant][color]
      );

  return (
    <button className={buttonClass} disabled={disabled} {...props}>
      {children}
    </button>
  );
}

SUI 버튼 컴포넌트 풀 코드다!



interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'contained' | 'outlined' | 'text';
  color?: 'primary' | 'secondary';
  size?: 'small' | 'medium' | 'large';
}

이 코드는 React 컴포넌트의 속성 타입을 정의하는 인터페이스다.



인터페이스(interface)란?

TypeScript의 기능 중 하나로 코드를 작성할 때 특정 객체의 형태를 정의한다.

  • 객체의 스펙(속성과 속성의 타입)
  • 함수의 파라미터
  • 함수의 스펙(파라미터, 반환 타입 등)
  • 배열과 객체를 접근하는 방식
  • 클래스


    일반적으로 위와 같은 것들을 인터페이스가 정의한다.




    다시 코드를 보면
interface 🌸ButtonProps 🌸🌸extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  🌸🌸🌸variant?: 'contained' | 'outlined' | 'text';
  🌸🌸🌸color?: 'primary' | 'secondary';
  🌸🌸🌸size?: 'small' | 'medium' | 'large';
}

🌸 인터페이스의 이름 : ButtonProps
🌸🌸 인터페이스 확장 : React.ButtonHTMLAttributes< HTMLButtonElement >

  • React에서 제공하는 버튼 엘리먼트의 속성을 사용
  • 아래의 코드는 내가 만든 칩 컴포넌트에서 가져온 코드인데 원래의 div 엘리먼트에서 사용되던 size 속성을 제외하고 가져온다는 의미다. size 속성을 새롭게 정의할 것이기 때문에 아래처럼 코드를 작성했다.
   extends Omit<React.HTMLAttributes<HTMLDivElement>, 'size'>

🌸🌸🌸 내가 정의한 객체의 속성 : variant는 contained, outlined, text 중 하나를 지정할 수 있다. color, size도 마찬가지!



const variantStyles = {
  contained: 'border border-transparent',
  outlined: 'bg-transparent border',
  text: 'bg-transparent border-transparent',
};

다음은 객체를 만들어 속성값을 자세하게 지정했다.
코드의 두번째 줄에서는 variant가 contained일 때 가장자리를 투명하게 해주고 있다. 따옴표 안에는 tailwind 문법으로 써줘야하는데 공식 사이트에 css 문법을 검색해서 찾을 수 있다.



export function Button({
  className,🌸
  variant = 'contained', 
  color = 'primary',       🌸🌸
  size = 'medium', 
  disabled = false, 
  children,
  ...props 
}: ButtonProps) {
  const buttonClass = disabled 🌸🌸🌸
    ? clsx(🌸🌸🌸🌸
        className,
        baseStyles,
        variantStyles[variant],
        sizeStyles[size],
        disabledSyles,
        disabledVariantSyles[variant]
      )
    : clsx(
        className,
        baseStyles,
        variantStyles[variant],
        sizeStyles[size],
        colorStyles[variant][color]
      );

🌸 버튼에 적용할 CSS 클래스 이름
🌸🌸 인터페이스에 정의된 속성들로 각각 기본값을 지정해줬다.
🌸🌸🌸 disabled의 경우 boolean 값으로 접근하는데에 어려움이 있어 따로 객체를 만들지 않았다.
🌸🌸🌸🌸 clsx란 CSS 클래스 이름을 조건부로 결합하는 유틸리티 함수다. 위의 코드에서 clsx 함수는 button과 객체들을 결합하여 buttonClass 변수에 할당하는 역할을 한다.



  return (
    <button className={buttonClass} disabled={disabled} {...props}>
      {children}
    </button>
  );
}

위의 코드에서는 우리가 만든 button을 렌더링한다. 사용자는 아래의 코드처럼 작성해서 사용할 수 있다.

<Button color="primary" variant="contained" size="small">


확실히 개념을 잡으니까 그동안 의문이었던 것들이 많이 해결되었다!!! 이제 스토리북 코드도 공부해야한다 바쁘다😾

0개의 댓글