# 코드 컨벤션

프론트엔드 디자인 시스템 적용을 위한 코드 작성 규칙.

---

## 파일 네이밍

| 유형 | 규칙 | 예시 |
|------|------|------|
| 컴포넌트 | PascalCase | `Button.tsx`, `UserCard.tsx` |
| 훅 | camelCase, `use` 접두사 | `useModal.ts`, `useTheme.ts` |
| 유틸리티 | camelCase | `cn.ts`, `formatDate.ts` |
| 상수 | camelCase | `chart.ts`, `routes.ts` |
| 타입 | camelCase | `user.ts`, `apihub.ts` |
| 스타일 | camelCase | `index.css` |

컴포넌트 파일 당 하나의 `export default` 컴포넌트.

---

## Tailwind 클래스 순서

가독성을 위해 다음 순서로 클래스를 작성한다. Prettier + prettier-plugin-tailwindcss로 자동 정렬 권장.

1. **레이아웃** — `flex`, `grid`, `block`, `hidden`, `relative`, `fixed`, `z-*`
2. **크기** — `w-*`, `h-*`, `min-w-*`, `max-w-*`, `size-*`
3. **여백** — `m-*`, `mx-*`, `my-*`, `p-*`, `px-*`, `py-*`, `gap-*`
4. **테두리** — `border*`, `rounded-*`, `ring-*`, `outline-*`
5. **배경** — `bg-*`, `shadow-*`, `backdrop-*`
6. **텍스트** — `text-*`, `font-*`, `leading-*`, `tracking-*`, `truncate`
7. **색상** — `text-[var(*)]`, `bg-[var(*)]`
8. **상호작용** — `cursor-*`, `select-*`, `pointer-events-*`
9. **트랜지션** — `transition-*`, `duration-*`, `ease-*`, `animate-*`
10. **반응형** — `sm:*`, `md:*`, `lg:*`, `xl:*`
11. **다크모드** — `dark:*`
12. **상태** — `hover:*`, `focus:*`, `active:*`, `disabled:*`, `aria-*:`

```tsx
// 올바른 예
<div className="flex items-center justify-between w-full p-4 border border-[var(--color-border)] rounded-lg bg-[var(--color-bg-surface)] text-sm text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-elevated)]">
```

---

## 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
// 기본 사용
<div className={cn('p-4 rounded-lg', className)} />

// 조건부 클래스
<button
  className={cn(
    'px-4 py-2 rounded-lg font-medium transition-colors',
    variant === 'primary' && 'bg-[var(--color-primary)] text-white',
    variant === 'ghost' && 'text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-surface)]',
    disabled && 'opacity-50 cursor-not-allowed',
    className,
  )}
/>

// 클래스 충돌 해결 (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;

<div className={cn(baseClass, variants[variant], className)} />
```

---

## 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<HTMLInputElement> {
  label?: string;
  error?: string;
}

const Input = forwardRef<HTMLInputElement, InputProps>(
  ({ label, error, className, ...props }, ref) => {
    return (
      <div className="flex flex-col gap-1.5">
        {label && (
          <label className="text-sm font-medium text-[var(--color-text-primary)]">
            {label}
          </label>
        )}
        <input
          ref={ref}
          className={cn(
            'w-full rounded-lg border border-[var(--color-border)] bg-[var(--color-bg-surface)]',
            'px-3 py-2 text-base text-[var(--color-text-primary)]',
            'placeholder:text-[var(--color-text-tertiary)]',
            'focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:border-transparent',
            'disabled:opacity-50 disabled:cursor-not-allowed',
            error && 'border-[var(--color-danger)] focus:ring-[var(--color-danger)]',
            className,
          )}
          {...props}
        />
        {error && (
          <p className="text-xs text-[var(--color-danger)]">{error}</p>
        )}
      </div>
    );
  },
);

Input.displayName = 'Input';

export default Input;
```

---

## Props 타입 정의

- Props 타입은 `interface`로 정의. 이름은 `{ComponentName}Props`.
- HTML 속성 확장 시 `extends React.HTMLAttributes<HTMLElement>` 사용.
- 선택적 `className` prop 항상 포함.

```tsx
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
  title: string;
  description?: string;
  variant?: 'default' | 'elevated' | 'flat';
}
```

---

## CSS 변수 사용

```tsx
// DO: CSS 변수 토큰 사용
<div className="bg-[var(--color-bg-surface)] text-[var(--color-text-primary)]" />

// DO NOT: HEX 직접 사용
<div className="bg-white text-gray-900" />

// DO NOT: Tailwind 기본 색상으로 브랜드 컬러 표현
<div className="bg-blue-500 text-blue-900" />
```

---

## 컴포넌트 파일 구조

```tsx
// 1. imports
// 2. 타입 정의
// 3. 상수 (variants 맵 등)
// 4. 컴포넌트 함수
// 5. export default
```
