# CEO 대시보드 리팩토링 계획

> 작성일: 2026-01-10
> 대상 파일: `src/components/business/CEODashboard/`
> 목표: 파일 분리 + 모바일(344px) 대응

---

## 1. 현재 상태 분석

### 1.1 파일 구조

```
CEODashboard/
├── CEODashboard.tsx      # 1,648줄 ⚠️ 분리 필요
├── components.tsx        # 312줄 ✅ 적정
├── types.ts              # ~100줄 ✅ 적정
├── sections/
│   ├── index.ts
│   ├── TodayIssueSection.tsx      # 73줄 ✅
│   ├── DailyReportSection.tsx     # 37줄 ✅
│   ├── MonthlyExpenseSection.tsx  # 38줄 ✅
│   ├── CardManagementSection.tsx  # ~50줄 ✅
│   ├── EntertainmentSection.tsx   # ~50줄 ✅
│   ├── WelfareSection.tsx         # ~50줄 ✅
│   ├── ReceivableSection.tsx      # ~50줄 ✅
│   ├── DebtCollectionSection.tsx  # ~50줄 ✅
│   ├── VatSection.tsx             # ~50줄 ✅
│   └── CalendarSection.tsx        # ~100줄 ✅
├── modals/
│   ├── ScheduleDetailModal.tsx    # ~200줄 ✅
│   └── DetailModal.tsx            # ~300줄 ✅
└── dialogs/
    └── DashboardSettingsDialog.tsx # ~200줄 ✅
```

### 1.2 CEODashboard.tsx 내부 분석 (1,648줄)

| 줄 범위 | 내용 | 줄 수 | 분리 대상 |
|---------|------|-------|----------|
| 1-26 | imports | 26 | - |
| 27-370 | mockData 객체 | **344** | ✅ 분리 |
| 371-748 | handleMonthlyExpenseCardClick (모달 config) | **378** | ✅ 분리 |
| 749-1019 | handleCardManagementCardClick (모달 config) | **271** | ✅ 분리 |
| 1020-1247 | handleEntertainmentCardClick (모달 config) | **228** | ✅ 분리 |
| 1248-1375 | handleWelfareCardClick (모달 config) | **128** | ✅ 분리 |
| 1376-1465 | handleVatClick (모달 config) | **90** | ✅ 분리 |
| 1466-1509 | 캘린더 관련 핸들러 | 44 | - |
| 1510-1648 | 컴포넌트 렌더링 | 139 | - |

**분리 대상 총합**: ~1,439줄 (87%)
**분리 후 예상**: ~210줄

---

## 2. 분리 계획

### 2.1 목표 구조

```
CEODashboard/
├── CEODashboard.tsx      # ~250줄 (컴포넌트 + 핸들러)
├── components.tsx        # 312줄 (유지)
├── types.ts              # ~100줄 (유지)
├── mockData.ts           # 🆕 ~350줄 (목데이터)
├── modalConfigs/         # 🆕 모달 설정 분리
│   ├── index.ts
│   ├── monthlyExpenseConfigs.ts   # ~380줄
│   ├── cardManagementConfigs.ts   # ~280줄
│   ├── entertainmentConfigs.ts    # ~230줄
│   ├── welfareConfigs.ts          # ~130줄
│   └── vatConfigs.ts              # ~100줄
├── sections/             # (유지)
├── modals/               # (유지)
└── dialogs/              # (유지)
```

### 2.2 분리 파일 상세

#### A. mockData.ts (신규)

```typescript
// mockData.ts
import type { CEODashboardData } from './types';

export const mockData: CEODashboardData = {
  todayIssue: [...],
  dailyReport: {...},
  monthlyExpense: {...},
  cardManagement: {...},
  entertainment: {...},
  welfare: {...},
  receivable: {...},
  debtCollection: {...},
  vat: {...},
  calendarSchedules: [...],
};
```

#### B. modalConfigs/index.ts (신규)

```typescript
// modalConfigs/index.ts
export { getMonthlyExpenseModalConfig } from './monthlyExpenseConfigs';
export { getCardManagementModalConfig } from './cardManagementConfigs';
export { getEntertainmentModalConfig } from './entertainmentConfigs';
export { getWelfareModalConfig } from './welfareConfigs';
export { getVatModalConfig } from './vatConfigs';
```

#### C. 개별 모달 config 파일 예시

```typescript
// modalConfigs/monthlyExpenseConfigs.ts
import type { DetailModalConfig } from '../types';

export function getMonthlyExpenseModalConfig(cardId: string): DetailModalConfig | null {
  const configs: Record<string, DetailModalConfig> = {
    me1: { title: '당월 매입 상세', ... },
    me2: { title: '당월 카드 상세', ... },
    me3: { title: '당월 발행어음 상세', ... },
    me4: { title: '당월 지출 예상 상세', ... },
  };
  return configs[cardId] || null;
}
```

#### D. CEODashboard.tsx (리팩토링 후)

```typescript
// CEODashboard.tsx (리팩토링 후 ~250줄)
import { mockData } from './mockData';
import {
  getMonthlyExpenseModalConfig,
  getCardManagementModalConfig,
  getEntertainmentModalConfig,
  getWelfareModalConfig,
  getVatModalConfig,
} from './modalConfigs';

export function CEODashboard() {
  // 상태 관리
  const [data] = useState<CEODashboardData>(mockData);
  const [detailModalConfig, setDetailModalConfig] = useState<DetailModalConfig | null>(null);
  // ...

  // 간소화된 핸들러
  const handleMonthlyExpenseCardClick = useCallback((cardId: string) => {
    const config = getMonthlyExpenseModalConfig(cardId);
    if (config) {
      setDetailModalConfig(config);
      setIsDetailModalOpen(true);
    }
  }, []);

  // 렌더링
  return (...);
}
```

---

## 3. 모바일 대응 계획

### 3.1 적용 대상 컴포넌트

| 컴포넌트 | 현재 상태 | 변경 필요 |
|----------|----------|----------|
| TodayIssueSection | `grid-cols-2 md:grid-cols-4` | ✅ `grid-cols-1 xs:grid-cols-2 md:grid-cols-4` |
| DailyReportSection | `grid-cols-2 md:grid-cols-4` | ✅ 동일 |
| MonthlyExpenseSection | `grid-cols-2 md:grid-cols-4` | ✅ 동일 |
| CardManagementSection | `grid-cols-2 md:grid-cols-4` | ✅ 동일 |
| EntertainmentSection | `grid-cols-2 md:grid-cols-4` | ✅ 동일 |
| WelfareSection | `grid-cols-2 md:grid-cols-4` | ✅ 동일 |
| ReceivableSection | `grid-cols-2 md:grid-cols-4` | ✅ 동일 |
| DebtCollectionSection | `grid-cols-2 md:grid-cols-4` | ✅ 동일 |
| VatSection | `grid-cols-2 md:grid-cols-4` | ✅ 동일 |
| AmountCardItem (공통) | 고정 텍스트 크기 | ✅ 반응형 텍스트 |
| IssueCardItem (공통) | 고정 텍스트 크기 | ✅ 반응형 텍스트 |
| PageHeader | 가로 배치 | ✅ 세로/가로 반응형 |

### 3.2 components.tsx 변경 사항

#### AmountCardItem

```tsx
// Before
<p className="text-2xl md:text-3xl font-bold">
  {formatCardAmount(card.amount)}
</p>

// After
<p className="text-lg xs:text-xl md:text-2xl lg:text-3xl font-bold truncate">
  {formatCardAmount(card.amount)}
</p>
<p className="text-xs xs:text-sm font-medium mb-1 xs:mb-2 break-keep">
  {card.label}
</p>
```

#### IssueCardItem

```tsx
// Before
<p className="text-2xl md:text-3xl font-bold">
  {typeof count === 'number' ? `${count}건` : count}
</p>

// After
<p className="text-lg xs:text-xl md:text-2xl lg:text-3xl font-bold">
  {typeof count === 'number' ? `${count}건` : count}
</p>
```

### 3.3 섹션 공통 변경

```tsx
// Before (모든 섹션)
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">

// After
<div className="grid grid-cols-1 xs:grid-cols-2 md:grid-cols-4 gap-3 xs:gap-4">
```

### 3.4 CardContent 패딩

```tsx
// Before
<CardContent className="p-6">

// After
<CardContent className="p-3 xs:p-4 md:p-6">
```

---

## 4. 실행 계획

### Phase 1: 파일 분리 (예상 30분)

- [ ] **1.1** `mockData.ts` 생성 및 데이터 이동
- [ ] **1.2** `modalConfigs/` 폴더 생성
- [ ] **1.3** `monthlyExpenseConfigs.ts` 생성
- [ ] **1.4** `cardManagementConfigs.ts` 생성
- [ ] **1.5** `entertainmentConfigs.ts` 생성
- [ ] **1.6** `welfareConfigs.ts` 생성
- [ ] **1.7** `vatConfigs.ts` 생성
- [ ] **1.8** `modalConfigs/index.ts` 생성
- [ ] **1.9** `CEODashboard.tsx` 리팩토링
- [ ] **1.10** import 정리 및 동작 확인

### Phase 2: 모바일 대응 (예상 30분)

- [ ] **2.1** `components.tsx` - AmountCardItem 반응형 적용
- [ ] **2.2** `components.tsx` - IssueCardItem 반응형 적용
- [ ] **2.3** `sections/*.tsx` - 그리드 반응형 적용 (일괄)
- [ ] **2.4** `sections/*.tsx` - CardContent 패딩 반응형 적용
- [ ] **2.5** PageHeader 반응형 확인
- [ ] **2.6** 344px 테스트 및 미세 조정

### Phase 3: 검증 (예상 15분)

- [ ] **3.1** 빌드 확인 요청
- [ ] **3.2** 데스크탑(1280px) 동작 확인
- [ ] **3.3** 태블릿(768px) 동작 확인
- [ ] **3.4** 모바일(375px) 동작 확인
- [ ] **3.5** Galaxy Fold(344px) 동작 확인

---

## 5. 예상 결과

### 5.1 파일 크기 변화

| 파일 | Before | After |
|------|--------|-------|
| CEODashboard.tsx | 1,648줄 | ~250줄 |
| mockData.ts | - | ~350줄 |
| modalConfigs/*.ts | - | ~1,100줄 (5개 파일) |

### 5.2 장점

1. **유지보수성**: 각 파일이 단일 책임 원칙 준수
2. **재사용성**: 모달 config를 다른 곳에서 재사용 가능
3. **확장성**: 새 모달 추가 시 별도 파일로 분리
4. **가독성**: 핵심 로직만 CEODashboard.tsx에 유지
5. **API 전환 용이**: mockData.ts만 교체하면 됨

### 5.3 모바일 개선 효과

| 항목 | Before (344px) | After (344px) |
|------|----------------|---------------|
| 카드 배치 | 2열 (160px/카드) | 1열 (320px/카드) |
| 금액 표시 | 잘림 가능 | 완전 표시 |
| 라벨 표시 | 잘림 가능 | 줄바꿈/truncate |
| 패딩 | 과다 (24px) | 적정 (12px) |

---

## 6. 참고 문서

- **모바일 대응 가이드**: `claudedocs/guides/[GUIDE] mobile-responsive-patterns.md`
- **기존 테스트 계획**: `claudedocs/[PLAN] mobile-overflow-testing.md`

---

## 7. 의사결정 사항

### Q1: mockData를 별도 파일로?
- **결정**: ✅ 분리
- **이유**: 향후 API 연동 시 교체 용이

### Q2: 모달 config를 폴더로?
- **결정**: ✅ 폴더로 분리
- **이유**: 각 config가 100줄 이상, 단일 파일은 여전히 큼

### Q3: 모바일에서 1열 vs 2열?
- **결정**: 344px 이하 1열, 375px 이상 2열
- **이유**: Galaxy Fold 160px 카드는 너무 좁음

---

## 8. 시작 조건

- [x] 계획서 작성 완료
- [x] 모바일 가이드 작성 완료
- [ ] 사용자 승인

---

> **다음 단계**: 계획 승인 후 Phase 1 (파일 분리) 시작