diff --git a/package-lock.json b/package-lock.json
index 4ba4974..a735708 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,8 @@
"@hookform/resolvers": "^3.10.0",
"@tanstack/react-query": "^5.65.1",
"axios": "^1.7.9",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
"es-toolkit": "^1.32.0",
"lenis": "^1.1.20",
"lottie-react": "^2.4.1",
@@ -20,6 +22,7 @@
"react-datepicker": "^7.6.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
+ "tailwind-merge": "^3.0.1",
"zod": "^3.24.1",
"zustand": "^5.0.3"
},
@@ -1702,6 +1705,18 @@
"node": ">= 6"
}
},
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@@ -1711,6 +1726,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -5450,6 +5466,16 @@
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
"integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
},
+ "node_modules/tailwind-merge": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.1.tgz",
+ "integrity": "sha512-AvzE8FmSoXC7nC+oU5GlQJbip2UO7tmOhOfQyOmPhrStOGXHU08j8mZEHZ4BmCqY5dWTCo4ClWkNyRNx1wpT0g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
"node_modules/tailwindcss": {
"version": "3.4.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
diff --git a/package.json b/package.json
index dd7092c..2ca3bd8 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,8 @@
"@hookform/resolvers": "^3.10.0",
"@tanstack/react-query": "^5.65.1",
"axios": "^1.7.9",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
"es-toolkit": "^1.32.0",
"lenis": "^1.1.20",
"lottie-react": "^2.4.1",
@@ -22,6 +24,7 @@
"react-datepicker": "^7.6.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
+ "tailwind-merge": "^3.0.1",
"zod": "^3.24.1",
"zustand": "^5.0.3"
},
diff --git a/src/components/ui/Button/Button.tsx b/src/components/ui/Button/Button.tsx
new file mode 100644
index 0000000..401a49f
--- /dev/null
+++ b/src/components/ui/Button/Button.tsx
@@ -0,0 +1,74 @@
+import { ButtonHTMLAttributes } from 'react';
+import { cva, type VariantProps } from 'class-variance-authority';
+import { cn } from '@/utils/helper';
+
+/**
+ * 커스터마이징 가능한 기본 공용 Button 컴포넌트입니다.
+ *
+ * ### Variants:
+ * - `variant`:
+ * - `default`: 보라색 배경과 흰색 텍스트.
+ * - `outline`: 흰색 배경과 보라색 텍스트, 회색 테두리.
+ *
+ * - `size`:
+ * - `default`: (PC 기준) : height:48px, font-size:16px
+ * (Mobile 기준) : height:42px, font-size:14px
+ *
+ * - `sm`: (PC 기준) : height:32px, font-size:14px;
+ * (Mobile 기준) : height:32px, font-size:12px;
+ *
+ * 단독으로 사용 시 최소 너비 84px을 가지며,
+ * flex 컨테이너 안에서 사용되면 `flex-1`로 늘어나고, 컨테이너가 전체너비를 통해서 컨트롤.
+ *
+ * - `lg`: height:48px, font-size:16px
+ *
+ * ### Compound Variants (조합된 스타일):
+ * - `size: lg` + `variant: outline`: 텍스트 색상이 회색으로 변경.
+ *
+ * ### Default Variants (기본 스타일):
+ * - `variant`: `default`
+ * - `size`: `default`
+ *
+ *
+ * @example
+ *
+ */
+
+const buttonVariants = cva(
+ //prettier-ignore
+ [
+ 'inline-flex items-center justify-center gap-2 flex-1',
+ 'whitespace-nowrap font-semibold',
+ 'rounded-[4px] px-4 py-2',
+ 'disabled:cursor-not-allowed',
+ ].join(' '),
+ {
+ variants: {
+ variant: {
+ default: 'bg-violet-20 text-white disabled:bg-gray-40',
+ outline: 'bg-white text-violet-20 border border-gray-30 disabled:opacity-50',
+ },
+ size: {
+ default: 'h-[42px] text-md md:text-lg md:h-12',
+ sm: 'h-8 text-xs md:text-md w-auto min-w-[84px]',
+ lg: 'h-[54px] text-lg rounded-lg',
+ },
+ },
+ compoundVariants: [
+ {
+ size: 'lg',
+ variant: 'outline',
+ className: 'text-gray-50',
+ },
+ ],
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ },
+);
+interface ButtonProps extends ButtonHTMLAttributes, VariantProps {}
+
+export default function Button({ variant, size, className, ...props }: ButtonProps) {
+ return ;
+}
diff --git a/src/utils/helper.ts b/src/utils/helper.ts
new file mode 100644
index 0000000..2819a83
--- /dev/null
+++ b/src/utils/helper.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}