Files
sam-react-prod/claudedocs/dashboard/[PLAN] ceo-dashboard-refactoring.md
유병철 b59150551e chore(WEB): PermissionManagement 오류 수정 및 claudedocs 폴더 정리
- PermissionManagement externalSelection 콜백 함수 오류 수정
  - setSelectedItems → onToggleSelection, onToggleSelectAll, getItemId 변경
- claudedocs 문서 폴더별 정리 (26개 파일)
  - dashboard/, guides/, settings/, construction/, sales/ 등

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-17 13:11:35 +09:00

9.7 KiB

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 (신규)

// 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 (신규)

// 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 파일 예시

// 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 (리팩토링 후)

// 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

// 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

// 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 섹션 공통 변경

// 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 패딩

// 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. 시작 조건

  • 계획서 작성 완료
  • 모바일 가이드 작성 완료
  • 사용자 승인

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