# v7: 대시보드 시트 자동 생성 ## 이번 단계에서 할 일 1. 대시보드 레이아웃 설계 2. KPI 요약 섹션 생성 3. 월별/카테고리별 테이블 생성 4. 셀 서식 및 조건부 서식 적용 ## 1. 대시보드 레이아웃 설계 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Dashboard 시트 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ A1:D6 📊 연간 재무 요약 (KPI 카드) │ │ ┌─────────────────────────────────────┐ │ │ │ 총 수입 ¥5,343,495 │ 총 지출 ¥4,224,666 │ │ │ │ 순 저축 ¥1,118,829 │ 저축률 20.9% │ │ │ │ 월평균수입 ¥445,291 │ 월평균지출 ¥352,055 │ │ │ └─────────────────────────────────────┘ │ │ │ │ A8:G21 📈 월별 추이 I1:M12 📊 카테고리별 지출 │ │ ┌──────────────────────────────────┐ ┌──────────────────────────────┐ │ │ │ 월 │ 수입 │ 지출 │ 저축 │ │ 카테고리 │ 금액 │ 비율 │ │ │ │ 2024-01│358,432│352,456│ 5,976 │ │ 주거비 │1,116K │ 26.4% │ │ │ │ 2024-02│345,678│298,432│47,246 │ │ 식비 │ 924K │ 21.9% │ │ │ │ ... │ │ │ │ │ ... │ │ │ │ │ │ 합계 │5,343K │4,224K │1,118K │ │ │ │ │ │ │ └──────────────────────────────────┘ └──────────────────────────────┘ │ │ │ │ A25:D37 (차트 데이터 영역 - 숨김) I25:J35 (차트 데이터 영역 - 숨김) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ## 2. 대시보드 생성기 (src/dashboard/creator.ts) ```typescript /** * 대시보드 시트 생성기 */ import { SheetsClient } from '../sheets/client'; import { MonthlySummary, CategoryBreakdown } from '../types'; import { DashboardFormatter } from './formatter'; export class DashboardCreator { private client: SheetsClient; private formatter: DashboardFormatter; constructor(client: SheetsClient) { this.client = client; this.formatter = new DashboardFormatter(client); } /** * 대시보드 시트 생성 */ async createDashboard( spreadsheetId: string, data: { monthlySummaries: MonthlySummary[]; categoryBreakdown: CategoryBreakdown[]; annualStats: { totalIncome: number; totalExpense: number; netSavings: number; savingsRate: number; }; } ): Promise { const sheetName = 'Dashboard'; // 1. 시트 준비 const sheetId = await this.client.createOrClearSheet(spreadsheetId, sheetName); // 2. KPI 요약 섹션 await this.writeKPISection(spreadsheetId, sheetName, data.annualStats); // 3. 월별 요약 테이블 await this.writeMonthlySummarySection(spreadsheetId, sheetName, data.monthlySummaries); // 4. 카테고리 분석 섹션 await this.writeCategorySection(spreadsheetId, sheetName, data.categoryBreakdown); // 5. 서식 적용 await this.formatter.applyDashboardFormat(spreadsheetId, sheetId); // 6. 열 너비 조정 await this.formatter.adjustColumnWidths(spreadsheetId, sheetId); } /** * KPI 요약 섹션 작성 */ private async writeKPISection( spreadsheetId: string, sheetName: string, stats: { totalIncome: number; totalExpense: number; netSavings: number; savingsRate: number } ): Promise { const data = [ ['📊 연간 재무 요약', '', '', ''], ['', '', '', ''], ['총 수입', `¥${stats.totalIncome.toLocaleString()}`, '총 지출', `¥${stats.totalExpense.toLocaleString()}`], ['순 저축', `¥${stats.netSavings.toLocaleString()}`, '저축률', `${stats.savingsRate.toFixed(1)}%`], ['', '', '', ''], ['월평균 수입', `¥${Math.round(stats.totalIncome / 12).toLocaleString()}`, '월평균 지출', `¥${Math.round(stats.totalExpense / 12).toLocaleString()}`], ]; await this.client.writeSheet(spreadsheetId, `${sheetName}!A1`, data); } } ``` ## 3. 서식 관리 (src/dashboard/formatter.ts) ```typescript /** * 대시보드 서식 관리 */ export class DashboardFormatter { /** * 대시보드 전체 서식 적용 */ async applyDashboardFormat(spreadsheetId: string, sheetId: number): Promise { const requests = [ // KPI 타이틀 서식 this.createTitleFormat(sheetId, 0, 1, 0, 4), // KPI 카드 배경 this.createKPICardFormat(sheetId), // 월별 테이블 헤더 (파란색) this.createTableHeaderFormat(sheetId, 8, 9, 0, 7, { red: 0.2, green: 0.4, blue: 0.6 }), // 카테고리 테이블 헤더 (녹색) this.createTableHeaderFormat(sheetId, 1, 2, 8, 13, { red: 0.2, green: 0.5, blue: 0.4 }), // 숫자 형식 this.createNumberFormat(sheetId, 9, 21, 1, 4, '#,##0'), // 조건부 서식 ...this.createConditionalFormats(sheetId), // 테두리 this.createBorderFormat(sheetId, 8, 22, 0, 7), ]; await this.client.batchUpdate(spreadsheetId, requests); } /** * 조건부 서식 (상태 아이콘 기반) */ private createConditionalFormats(sheetId: number) { return [ // ✅ (저축률 >= 20%): 녹색 배경 { addConditionalFormatRule: { rule: { ranges: [{ sheetId, startRowIndex: 9, endRowIndex: 21, startColumnIndex: 6, endColumnIndex: 7 }], booleanRule: { condition: { type: 'TEXT_EQ', values: [{ userEnteredValue: '✅' }] }, format: { backgroundColor: { red: 0.85, green: 0.95, blue: 0.85 } }, }, }, }, }, // ⚠️ (저축률 10-20%): 노란색 배경 { addConditionalFormatRule: { rule: { ranges: [{ sheetId, startRowIndex: 9, endRowIndex: 21, startColumnIndex: 6, endColumnIndex: 7 }], booleanRule: { condition: { type: 'TEXT_EQ', values: [{ userEnteredValue: '⚠️' }] }, format: { backgroundColor: { red: 1.0, green: 0.95, blue: 0.8 } }, }, }, }, }, // ❌ (저축률 < 10%): 빨간색 배경 { addConditionalFormatRule: { rule: { ranges: [{ sheetId, startRowIndex: 9, endRowIndex: 21, startColumnIndex: 6, endColumnIndex: 7 }], booleanRule: { condition: { type: 'TEXT_EQ', values: [{ userEnteredValue: '❌' }] }, format: { backgroundColor: { red: 1.0, green: 0.85, blue: 0.85 } }, }, }, }, }, ]; } } ``` ## 4. 완성된 대시보드 예시 ### KPI 요약 섹션 ``` ┌──────────────────────────────────────────────────┐ │ 📊 연간 재무 요약 │ ├──────────────────────────────────────────────────┤ │ 총 수입 ¥5,343,495 총 지출 ¥4,224,666 │ │ 순 저축 ¥1,118,829 저축률 20.9% │ │ 월평균 수입 ¥445,291 월평균 지출 ¥352,055 │ └──────────────────────────────────────────────────┘ ``` ### 월별 추이 테이블 ``` ┌─────────────────────────────────────────────────────────────┐ │ 📈 월별 추이 │ ├──────────┬──────────┬──────────┬──────────┬───────┬────┬────┤ │ 월 │ 수입 │ 지출 │ 저축 │ 저축률│거래│상태│ ├──────────┼──────────┼──────────┼──────────┼───────┼────┼────┤ │ 2024-01 │ 358,432 │ 352,456 │ 5,976 │ 1.7% │ 45 │ ❌ │ │ 2024-02 │ 345,678 │ 298,432 │ 47,246 │ 13.7% │ 42 │ ⚠️ │ │ 2024-03 │ 362,111 │ 315,890 │ 46,221 │ 12.8% │ 48 │ ⚠️ │ │ ... │ │ │ │ │ │ │ │ 2024-12 │ 856,234 │ 412,345 │ 443,889 │ 51.8% │ 52 │ ✅ │ ├──────────┼──────────┼──────────┼──────────┼───────┼────┼────┤ │ 합계 │5,343,495 │4,224,666 │1,118,829 │ 20.9% │544 │ │ └──────────┴──────────┴──────────┴──────────┴───────┴────┴────┘ ``` ### 카테고리별 지출 ``` ┌────────────────────────────────────────────────────┐ │ 📊 카테고리별 지출 │ ├──────────────┬──────────┬───────┬─────────┬────────┤ │ 카테고리 │ 금액 │ 비율 │ 월평균 │ 상태 │ ├──────────────┼──────────┼───────┼─────────┼────────┤ │ 주거비 │1,116,000 │ 26.4% │ 93,000 │ 🔴 │ │ 식비 │ 924,000 │ 21.9% │ 77,000 │ ⚠️ │ │ 유틸리티 │ 456,000 │ 10.8% │ 38,000 │ ✅ │ │ 쇼핑 │ 412,000 │ 9.8% │ 34,333 │ ✅ │ │ 교통비 │ 324,000 │ 7.7% │ 27,000 │ ✅ │ │ ... │ │ │ │ │ └──────────────┴──────────┴───────┴─────────┴────────┘ ``` ## 5. 상태 아이콘 규칙 | 아이콘 | 조건 | 의미 | |--------|------|------| | ✅ | 저축률 >= 20% 또는 비율 <= 15% | 양호 | | ⚠️ | 저축률 10-20% 또는 비율 15-25% | 주의 | | ❌ | 저축률 < 10% | 개선 필요 | | 🔴 | 비율 > 25% | 지출 과다 | ## 6. 테스트 실행 ```typescript // src/test-dashboard.ts import * as dotenv from 'dotenv'; dotenv.config(); import { SheetsClient } from './sheets/client'; import { DataAggregator } from './analysis/aggregator'; import { DashboardCreator } from './dashboard/creator'; async function main() { const spreadsheetId = process.env.TEST_SPREADSHEET_ID!; const client = new SheetsClient(); const aggregator = new DataAggregator(client); const dashboardCreator = new DashboardCreator(client); // 분석 실행 const result = await aggregator.runFullAnalysis(spreadsheetId); // 대시보드 생성 await dashboardCreator.createDashboard(spreadsheetId, result); } main().catch(console.error); ``` ## 현재 프로젝트 구조 ``` project/src/ ├── sheets/ │ ├── client.ts ✅ │ ├── reader.ts ✅ │ ├── writer.ts ✅ │ └── template.ts ✅ ├── analysis/ │ ├── categories.ts ✅ │ ├── calculator.ts ✅ │ ├── aggregator.ts ✅ │ └── validator.ts ✅ ├── dashboard/ │ ├── creator.ts ✅ 대시보드 생성 │ └── formatter.ts ✅ 서식 관리 ├── vertexai/ │ ├── client.ts ✅ │ └── analyzer.ts ✅ └── types/ └── index.ts ✅ ``` ## 다음 단계 v8에서는: - 라인 차트: 월별 수입/지출 추이 - 영역 차트: 누적 저축 추이 - 바 차트: 카테고리별 비교 - 스택 바 차트: 월별 구성 비율 --- **작성일**: 2025-12-01 **상태**: ✅ 완료 **다음**: v8 - 차트 시각화 1 (기본 트렌드 차트)