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 컴포넌트의 속성 타입을 정의하는 인터페이스다.
TypeScript의 기능 중 하나로 코드를 작성할 때 특정 객체의 형태를 정의한다.
interface 🌸ButtonProps 🌸🌸extends React.ButtonHTMLAttributes<HTMLButtonElement> {
🌸🌸🌸variant?: 'contained' | 'outlined' | 'text';
🌸🌸🌸color?: 'primary' | 'secondary';
🌸🌸🌸size?: 'small' | 'medium' | 'large';
}
🌸 인터페이스의 이름 : ButtonProps
🌸🌸 인터페이스 확장 : React.ButtonHTMLAttributes< HTMLButtonElement >
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">

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