# v9: 차트 시각화 2 - 비율 및 비교 차트 ## 이번 단계에서 할 일 1. 파이 차트: 지출 카테고리 비율 2. 도넛 차트: 수입원 비율 3. 콤보 차트: 수입/지출 + 저축률 4. 워터폴 차트: 월간 순자산 변동 ## 1. 차트 개요 ### 생성할 차트 목록 | 차트 타입 | 용도 | 특징 | |-----------|------|------| | 파이 (PIE) | 지출 카테고리 비율 | 전체 대비 각 카테고리 비중 | | 도넛 (DONUT) | 수입원 비율 | 급여/보너스/부수입 비중 | | 콤보 (COMBO) | 수입/지출 + 저축률 | 바 + 라인 복합 차트 | | 워터폴 (WATERFALL) | 순자산 변동 | 증감 누적 시각화 | ### 시각적 결과 미리보기 ``` ┌────────────────────────────────────────────────────────────────┐ │ │ │ 🥧 파이 차트 🍩 도넛 차트 │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ ██████ │ │ ████████ │ │ │ │ ██ ██ │ 주거비 26% │ ██ ██ │ 급여 │ │ │ ████████████ │ 식비 22% │ ██ ○○ ██ │ 78% │ │ │ ██ ██ │ 기타 52% │ ██ ██ │ 보너스│ │ │ ██████ │ │ ████████ │ 19% │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ 📊 콤보 차트 (수입/지출 바 + 저축률 라인) │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ ¥ % │ │ │ │ 400K │ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ │ 50% │ │ │ │ │ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ │ │ │ │ │ 300K │ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░──┼─────── │ │ │ │ │ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ │ 20% │ │ │ │ └────────────────────────────────────────┘ │ │ │ │ 01 02 03 04 05 06 07 08 09 10 11 12 │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ 📊 워터폴 차트 (순자산 누적 변동) │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ ¥1M ┌───┐ ┌───┐ │ │ │ │ │ │ ┌───┐ ┌───┐ │ │ │ │ │ │ ¥500K │ │ │ │ ┌───┐ │ │ ┌───┐ │ │ │ │ │ │ ¥0 ───┴───┴─┴───┴─┴───┴─┴───┴─────┴───┴─┴───┴─── │ │ │ │ 01 02 03 04 05 11 12 │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────┘ ``` ## 2. 파이 차트 구현 ### 지출 카테고리 비율 ```typescript /** * 파이 차트: 지출 카테고리 비율 */ private createPieChart(sheetId: number, dataRows: number): sheets_v4.Schema$Request { return { addChart: { chart: { spec: { title: '지출 카테고리 비율', pieChart: { legendPosition: 'RIGHT_LEGEND', domain: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + Math.min(dataRows, 9), startColumnIndex: 26, // AA열: 카테고리명 endColumnIndex: 27, }], }, }, series: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + Math.min(dataRows, 9), startColumnIndex: 27, // AB열: 금액 endColumnIndex: 28, }], }, }, pieHole: 0, // 0 = 완전한 파이 }, }, position: { overlayPosition: { anchorCell: { sheetId, rowIndex: 0, columnIndex: 20 }, widthPixels: 450, heightPixels: 280, }, }, }, }, }; } ``` ### 파이 차트 옵션 | 옵션 | 값 | 설명 | |------|-----|------| | `pieHole` | 0 | 완전한 파이 (0.5 = 도넛) | | `legendPosition` | RIGHT_LEGEND | 범례 오른쪽 배치 | ## 3. 도넛 차트 구현 ### 수입원 비율 ```typescript /** * 도넛 차트: 수입원 비율 */ private createDonutChart(sheetId: number, dataRows: number): sheets_v4.Schema$Request { return { addChart: { chart: { spec: { title: '수입원 비율', pieChart: { legendPosition: 'BOTTOM_LEGEND', domain: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + dataRows, startColumnIndex: 29, // AD열 endColumnIndex: 30, }], }, }, series: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + dataRows, startColumnIndex: 30, // AE열 endColumnIndex: 31, }], }, }, pieHole: 0.5, // 도넛 스타일! }, }, position: { overlayPosition: { anchorCell: { sheetId, rowIndex: 15, columnIndex: 20 }, widthPixels: 350, heightPixels: 250, }, }, }, }, }; } ``` ### 파이 vs 도넛 | 항목 | 파이 차트 | 도넛 차트 | |------|----------|----------| | `pieHole` | 0 | 0.4 ~ 0.6 | | 중앙 공간 | 없음 | 있음 (KPI 표시 가능) | | 용도 | 단순 비율 | 비율 + 중앙 정보 | ## 4. 콤보 차트 구현 ### 수입/지출 바 + 저축률 라인 ```typescript /** * 콤보 차트: 수입/지출 바 + 저축률 라인 */ private createComboChart(sheetId: number, dataRows: number): sheets_v4.Schema$Request { return { addChart: { chart: { spec: { title: '수입/지출 및 저축률 추이', basicChart: { chartType: 'COMBO', // 콤보 차트! legendPosition: 'BOTTOM_LEGEND', axis: [ { position: 'BOTTOM_AXIS', title: '월' }, { position: 'LEFT_AXIS', title: '금액 (¥)' }, { position: 'RIGHT_AXIS', title: '저축률 (%)' }, // 이중 축 ], series: [ { // 수입 (녹색 바) type: 'COLUMN', targetAxis: 'LEFT_AXIS', color: { red: 0.2, green: 0.7, blue: 0.3 }, }, { // 지출 (빨간색 바) type: 'COLUMN', targetAxis: 'LEFT_AXIS', color: { red: 0.9, green: 0.3, blue: 0.3 }, }, { // 저축률 (파란색 라인) type: 'LINE', targetAxis: 'RIGHT_AXIS', // 오른쪽 축 사용 color: { red: 0.1, green: 0.4, blue: 0.8 }, }, ], headerCount: 1, }, }, position: { overlayPosition: { anchorCell: { sheetId, rowIndex: 30, columnIndex: 20 }, widthPixels: 550, heightPixels: 320, }, }, }, }, }; } ``` ### 콤보 차트 특징 - **이중 축**: 왼쪽(금액) + 오른쪽(비율) 축 - **혼합 타입**: COLUMN + LINE 조합 - **용도**: 절대값과 비율을 동시에 표현 ## 5. 워터폴 차트 구현 Google Sheets는 네이티브 워터폴 차트를 지원하지 않으므로 스택 컬럼 차트로 시뮬레이션합니다. ### 데이터 구조 | 월 | 시작값 (투명) | 변동값 | 종료값 | |----|--------------|-------|-------| | 01 | 0 | +5,976 | 5,976 | | 02 | 5,976 | +47,246 | 53,222 | | 03 | 53,222 | +46,221 | 99,443 | | ... | | | | ### 구현 방식 ```typescript /** * 워터폴 차트 (스택바로 시뮬레이션) */ private createWaterfallChart(sheetId: number, dataRows: number): sheets_v4.Schema$Request { return { addChart: { chart: { spec: { title: '월별 순자산 변동 (워터폴)', basicChart: { chartType: 'COLUMN', stackedType: 'STACKED', series: [ { // 시작값 (투명 - 바닥 역할) color: { red: 1, green: 1, blue: 1, alpha: 0 }, }, { // 변동값 (실제 표시되는 바) // 양수: 녹색, 음수: 빨간색 }, ], }, }, }, }, }; } ``` ## 6. 차트 데이터 준비 ```typescript /** * 비율 차트 데이터 준비 */ private async prepareRatioChartData( spreadsheetId: string, sheetName: string, summaries: MonthlySummary[], breakdown: CategoryBreakdown[], incomeBreakdown: { category: string; amount: number }[] ): Promise { // 1. 파이 차트 데이터 (AA25:AB35) const pieData = [ ['카테고리', '금액'], ...breakdown.slice(0, 9).map(b => [b.category, b.amount]), ]; await this.client.writeSheet(spreadsheetId, `${sheetName}!AA25`, pieData); // 2. 도넛 차트 데이터 (AD25:AE28) const donutData = [ ['수입원', '금액'], ...incomeBreakdown.map(i => [i.category, i.amount]), ]; await this.client.writeSheet(spreadsheetId, `${sheetName}!AD25`, donutData); // 3. 콤보 차트 데이터 (AG25:AJ37) const comboData = [ ['월', '수입', '지출', '저축률'], ...summaries.map(s => [ s.month.substring(5), s.totalIncome, s.totalExpense, ((s.netSavings / s.totalIncome) * 100).toFixed(1), ]), ]; await this.client.writeSheet(spreadsheetId, `${sheetName}!AG25`, comboData); // 4. 워터폴 데이터 (AL25:AO37) let cumulative = 0; const waterfallData = [ ['월', '시작', '변동', '종료'], ...summaries.map(s => { const start = cumulative; cumulative += s.netSavings; return [s.month.substring(5), start, s.netSavings, cumulative]; }), ]; await this.client.writeSheet(spreadsheetId, `${sheetName}!AL25`, waterfallData); } ``` ## 7. 차트 타입 비교 | 차트 | 장점 | 단점 | 사용 시기 | |------|------|------|----------| | 파이 | 직관적인 비율 | 항목 많으면 복잡 | 5~7개 이하 항목 | | 도넛 | 중앙 공간 활용 | 파이보다 비율 비교 어려움 | KPI + 비율 동시 표시 | | 콤보 | 다른 단위 동시 표현 | 복잡할 수 있음 | 금액 + 비율 함께 | | 워터폴 | 증감 추이 명확 | 구현 복잡 | 누적 변화 표현 | ## 현재 프로젝트 구조 ``` project/src/ ├── charts/ │ ├── trend-charts.ts ✅ 트렌드 차트 (v8) │ └── ratio-charts.ts ✅ 비율 차트 (v9) └── ... ``` ## 다음 단계 v10에서는: - 스파크라인: 셀 내 미니 차트 - 게이지 차트: 목표 달성률 - 히트맵: 요일별 지출 패턴 - 스코어카드: KPI 하이라이트 - 진행바: 예산 소진율 --- **작성일**: 2025-12-01 **상태**: ✅ 완료 **다음**: v10 - 차트 시각화 3 (고급 KPI 위젯)