๐Ÿšจ Tailwind + cva + clsx + twMerge ์กฐํ•ฉ์—์„œ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์„ ๋•Œ

2025. 9. 25. 21:17ยท๐Ÿ‘€ ํ”„๋กœ์ ํŠธ ๋ฐ ํ™œ๋™/ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… + ๋ฆฌํŒฉํ† ๋ง

๐Ÿ”— ํ”„๋กœ์ ํŠธ - ๋กค๋ง

๋”๋ณด๊ธฐ
๋”๋ณด๊ธฐ

1. ๐Ÿšจ Tailwind + cva + clsx + twMerge ์กฐํ•ฉ์—์„œ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์„ ๋•Œ

2. ๐Ÿšจ PropTypes์—์„œ JSDoc์œผ๋กœ — React 19 ํƒ€์ž… ๊ด€๋ฆฌ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

3. ๐Ÿšจ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ — ์ด๋ชจ์ง€ ๋ฐ˜์‘ UI ๋ฏธ๋ฐ˜์˜ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

4. [์ฝ”๋“œ์ž‡ ์Šคํ”„๋ฆฐํŠธ FE 19๊ธฐ] ๊ธฐ์ดˆ ํ”„๋กœ์ ํŠธ - ๋กค๋ง ํšŒ๊ณ 


ํ”„๋กœ์ ํŠธ์—์„œ Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๋˜ ์ค‘

Tailwind, cva, clsx, twMerge๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

1๏ธโƒฃ ๋ฌธ์ œ ์ƒํ™ฉ

import { cva } from 'class-variance-authority';
import { cn } from '@/utils/style';

const Button = ({
  children,
  type = 'button',
  style = 'primary',
  size,
  full = false,
  ...props
}) => {
  const buttonVariants = cva(
    'flex items-center justify-center rounded-md cursor-pointer',
    {
      variants: {
        style: {
          primary: 'bg-purple-600 text-white',
          secondary:
            'bg-white border border-solid border-purple-600 text-purple-700',
          outlined:
            'bg-white border border-solid border-gray-300 text-gray-900',
        },
        size: {
          s: 'h-[28px]',
          m: 'h-[36px]',
          l: 'h-[40px]',
          xl: 'w-[280px] h-[56px] rounded-2xl text-18-bold',
        },
        full: {
          true: 'w-full',
        },
      },
    }
  );

  return (
    <button
      className={cn(buttonVariants({ size, full, style }))}
      type={type}
      {...props}>
      {children}
    </button>
  );
};

export default Button;
  • text-18-bold → ์ปค์Šคํ…€ ํฐํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ
  • text-purple-700 → Tailwind ์ƒ‰์ƒ ์œ ํ‹ธ๋ฆฌํ‹ฐ
  • cn() → ๋‚ด๋ถ€์ ์œผ๋กœ clsx + twMerge ์กฐํ•ฉ ์‚ฌ์šฉ

์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋Š”๋ฐ, ํ™”๋ฉด์—์„œ ํฐํŠธ ํฌ๊ธฐ๋Š” ์ ์šฉ๋˜์ง€๋งŒ ์ƒ‰์ƒ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค.

 

2๏ธโƒฃ ์›์ธ ๋ถ„์„

์ด ๋ฌธ์ œ๋Š” cn ์œ ํ‹ธ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ ์ค‘์ธ twMerge() ๋•Œ๋ฌธ์ด์—ˆ๋‹ค.

์ปฌ๋Ÿฌ์™€ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ๋ชจ๋‘ text๋ผ๋Š” ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—,

๋งˆ์ง€๋ง‰์— ์ ์šฉ๋œ text-18-bold๊ฐ€ text-purple-700์„ ๋ฎ์–ด์จ๋ฒ„๋ฆฐ ๊ฒƒ์ด ์›์ธ์ด์—ˆ๋‹ค.

 

3๏ธโƒฃ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ์˜ ์ ‘๋‘์‚ฌ๋ฅผ text ๋Œ€์‹  font๋กœ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.

@utility font-28-bold {
  font-size: var(--text-28);
  line-height: var(--leading-28);
  font-weight: 700;
}

๋ณ€๊ฒฝ ํ›„์˜ Button ์ปดํฌ๋„ŒํŠธ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

import { cva } from 'class-variance-authority';
import { cn } from '@/utils/style';

const Button = ({
  children,
  type = 'button',
  style = 'primary',
  size,
  full = false,
  ...props
}) => {
  const buttonVariants = cva(
    'flex items-center justify-center rounded-md cursor-pointer',
    {
      variants: {
        style: {
          primary: 'bg-purple-600 text-white',
          secondary:
            'bg-white border border-solid border-purple-600 text-purple-700',
          outlined:
            'bg-white border border-solid border-gray-300 text-gray-900',
        },
        size: {
          s: 'h-[28px] font-14-regular',
          m: 'h-[36px] font-16-medium',
          l: 'h-[40px] font-16-regular',
          xl: 'w-[280px] h-[56px] rounded-2xl text-18-bold',
        },
        full: {
          true: 'w-full',
        },
      },
    }
  );

  return (
    <button
      className={cn(buttonVariants({ size, full, style }))}
      type={type}
      {...props}>
      {children}
    </button>
  );
};

export default Button;

์ด์ œ๋Š” ์›ํ•˜๋Š” ๋Œ€๋กœ ์Šคํƒ€์ผ์ด ์ž˜ ์ ์šฉ๋˜์—ˆ๋‹ค. ๐ŸŽ‰

 

4๏ธโƒฃ ์ •๋ฆฌ

  • clsx + twMerge ์กฐํ•ฉ์„ ์‚ฌ์šฉํ•  ๋•Œ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ์ ‘๋‘์‚ฌ๊ฐ€ Tailwind ๊ธฐ๋ณธ ํด๋ž˜์Šค์™€ ๊ฒน์น˜๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฎ์–ด์“ฐ๊ธฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
728x90
'๐Ÿ‘€ ํ”„๋กœ์ ํŠธ ๋ฐ ํ™œ๋™/ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… + ๋ฆฌํŒฉํ† ๋ง' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • ๐Ÿšจ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ — ์ด๋ชจ์ง€ ๋ฐ˜์‘ UI ๋ฏธ๋ฐ˜์˜ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…
  • ๐Ÿšจ PropTypes์—์„œ JSDoc์œผ๋กœ — React 19 ํƒ€์ž… ๊ด€๋ฆฌ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…
_์ด์•Ž
_์ด์•Ž
๊ณต๋ถ€ ๊ธฐ๋ก ๋ธ”๋กœ๊ทธ
  • _์ด์•Ž
    ๊ณต๋ถ€
    _์ด์•Ž
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • โœ๏ธ ๋ชจ๋“  ๊ธ€ (79)
      • ๐Ÿ“š ํ”„๋ก ํŠธ์—”๋“œ (48)
        • Web (6)
        • React (11)
        • Next.js (2)
        • JavaScript (13)
        • TypeScript (2)
        • Dev Setup (1)
        • git+github (4)
        • HTML+CSS (9)
      • ๐Ÿ‘€ ํ”„๋กœ์ ํŠธ ๋ฐ ํ™œ๋™ (24)
        • ์ฝ”๋“œ์ž‡ ์Šคํ”„๋ฆฐํŠธ 19๊ธฐ (19)
        • ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ (2)
        • ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… + ๋ฆฌํŒฉํ† ๋ง (3)
      • ๐Ÿ“ ์ž๋ฃŒ ์ •๋ฆฌ (3)
        • React ๊ณต์‹๋ฌธ์„œ (3)
      • โš™๏ธ etc. (4)
  • ๋งํฌ

    • ๐Ÿ‘‹ GitHub
    • ๐Ÿ“ GitHub/Study
    • ๐Ÿง ์ฝ”๋”ฉํ…Œ์ŠคํŠธ ๊ธฐ๋ก
  • ์ธ๊ธฐ ๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.4
_์ด์•Ž
๐Ÿšจ Tailwind + cva + clsx + twMerge ์กฐํ•ฉ์—์„œ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์„ ๋•Œ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”