```
---
## cn() 유틸리티
`clsx` + `tailwind-merge` 조합. 조건부 클래스와 클래스 충돌 해결에 사용한다.
```ts
// src/utils/cn.ts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
```
### 사용 패턴
```tsx
// 기본 사용
// 조건부 클래스
// 클래스 충돌 해결 (twMerge)
cn('p-4', 'p-6') // → 'p-6'
cn('text-sm', 'text-base') // → 'text-base'
```
### 규칙
- 모든 컴포넌트의 루트 엘리먼트에 `className` prop을 전달할 수 있도록 `cn()`으로 병합.
- variant별 클래스는 객체 맵으로 분리.
```tsx
const variants = {
primary: 'bg-[var(--color-primary)] text-white',
ghost: 'text-[var(--color-text-secondary)]',
} as const;
type Variant = keyof typeof variants;
```
---
## Import 순서
ESLint `import/order` 규칙 기준.
```tsx
// 1. React
import { useState, useCallback } from 'react';
// 2. 외부 라이브러리
import { ChevronRight } from 'lucide-react';
// 3. 내부 모듈 (절대 경로)
import { cn } from '@/utils/cn';
import { CHART_COLORS } from '@/constants/chart';
import type { ButtonProps } from '@/types/ui';
// 4. 상대 경로 컴포넌트
import Badge from './Badge';
// 5. 스타일 (필요 시)
import styles from './Button.module.css';
```
---
## forwardRef
외부에서 DOM 요소에 직접 접근이 필요한 컴포넌트(Input, Button 등)에 적용.
```tsx
import { forwardRef } from 'react';
import { cn } from '@/utils/cn';
interface InputProps extends React.InputHTMLAttributes {
label?: string;
error?: string;
}
const Input = forwardRef(
({ label, error, className, ...props }, ref) => {
return (
{label && (
)}
{error && (
{error}
)}
);
},
);
Input.displayName = 'Input';
export default Input;
```
---
## Props 타입 정의
- Props 타입은 `interface`로 정의. 이름은 `{ComponentName}Props`.
- HTML 속성 확장 시 `extends React.HTMLAttributes` 사용.
- 선택적 `className` prop 항상 포함.
```tsx
interface CardProps extends React.HTMLAttributes {
title: string;
description?: string;
variant?: 'default' | 'elevated' | 'flat';
}
```
---
## CSS 변수 사용
```tsx
// DO: CSS 변수 토큰 사용
// DO NOT: HEX 직접 사용
// DO NOT: Tailwind 기본 색상으로 브랜드 컬러 표현
```
---
## 컴포넌트 파일 구조
```tsx
// 1. imports
// 2. 타입 정의
// 3. 상수 (variants 맵 등)
// 4. 컴포넌트 함수
// 5. export default
```