# v8: 차트 시각화 1 - 기본 트렌드 차트 ## 이번 단계에서 할 일 1. 라인 차트: 월별 수입/지출 추이 2. 영역 차트: 누적 저축 추이 3. 바 차트: 카테고리별 비교 4. 스택 바 차트: 월별 구성 비율 ## 1. 차트 개요 ### 생성할 차트 목록 | 차트 타입 | 용도 | 데이터 | 위치 | |-----------|------|--------|------| | 라인 (LINE) | 월별 수입/지출 추이 | 12개월 데이터 | O1:T16 | | 영역 (AREA) | 누적 저축 추이 | 누적 합계 | O17:T32 | | 컬럼 (COLUMN) | 카테고리별 비교 | 연간 합계 | O33:T48 | | 스택 바 (STACKED BAR) | 월별 구성 비율 | 카테고리×월 | O49:T64 | ### 차트 배치 레이아웃 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ A-M: 데이터 테이블 N O-T: 차트 영역 │ ├─────────────────────────────────────────┬───────────────────────────────────┤ │ │ │ │ KPI 요약 (A1:D6) │ 📈 라인 차트 │ │ │ 월별 수입/지출 추이 │ │ 월별 추이 (A8:G21) 카테고리 (I1:M12) │ │ │ │ ───────────────────────────── │ │ │ │ │ │ 📊 영역 차트 │ │ │ 누적 저축 추이 │ │ │ │ │ 차트 데이터 영역 (A25:R37) │ ───────────────────────────── │ │ (숨김 처리) │ │ │ │ 📊 바 차트 │ │ │ 카테고리별 연간 지출 │ │ │ │ │ │ ───────────────────────────── │ │ │ │ │ │ 📊 스택 바 차트 │ │ │ 월별 지출 구성 │ │ │ │ └─────────────────────────────────────────┴───────────────────────────────────┘ ``` ## 2. 차트 데이터 준비 차트 생성 전에 데이터 영역을 준비합니다: ```typescript /** * 차트 데이터 영역 준비 */ private async prepareChartData( spreadsheetId: string, sheetName: string, summaries: MonthlySummary[], breakdown: CategoryBreakdown[] ): Promise { // 월별 트렌드 데이터 (A25:E37) let cumulativeSavings = 0; const trendData = [ ['월', '수입', '지출', '저축', '누적저축'], ...summaries.map((s) => { cumulativeSavings += s.netSavings; return [ s.month.substring(5), // MM만 표시 s.totalIncome, s.totalExpense, s.netSavings, cumulativeSavings, ]; }), ]; await this.client.writeSheet(spreadsheetId, `${sheetName}!A25`, trendData); // 카테고리별 데이터 (G25:H35) const categoryData = [ ['카테고리', '금액'], ...breakdown.slice(0, 9).map((b) => [b.category, b.amount]), ]; await this.client.writeSheet(spreadsheetId, `${sheetName}!G25`, categoryData); } ``` ## 3. 라인 차트 구현 ### 월별 수입/지출 추이 ```typescript /** * 라인 차트: 월별 수입/지출 추이 */ private createLineChart(sheetId: number, dataRows: number): sheets_v4.Schema$Request { return { addChart: { chart: { spec: { title: '월별 수입/지출 추이', basicChart: { chartType: 'LINE', legendPosition: 'BOTTOM_LEGEND', axis: [ { position: 'BOTTOM_AXIS', title: '월' }, { position: 'LEFT_AXIS', title: '금액 (¥)' }, ], domains: [{ domain: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + dataRows, startColumnIndex: 0, // A열: 월 endColumnIndex: 1, }], }, }, }], series: [ { // 수입 (녹색) series: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + dataRows, startColumnIndex: 1, // B열: 수입 endColumnIndex: 2, }], }, }, targetAxis: 'LEFT_AXIS', color: { red: 0.2, green: 0.6, blue: 0.2 }, }, { // 지출 (빨간색) series: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + dataRows, startColumnIndex: 2, // C열: 지출 endColumnIndex: 3, }], }, }, targetAxis: 'LEFT_AXIS', color: { red: 0.8, green: 0.2, blue: 0.2 }, }, ], headerCount: 1, }, }, position: { overlayPosition: { anchorCell: { sheetId, rowIndex: 0, columnIndex: 14 }, widthPixels: 500, heightPixels: 300, }, }, }, }, }; } ``` ### 시각적 결과 ``` 월별 수입/지출 추이 ¥ 500K ┤ ╭──────── 수입 │ ╭────────╯ 400K ┤ ╭────╯ │ ╭────╯ 350K ┤ ╭────╯ ╭──────── 지출 │╭─╯ ╭──────────────╯ 300K ┼──────────────╯ └──┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬── 01 02 03 04 05 06 07 08 09 10 11 12 ``` ## 4. 영역 차트 구현 ### 누적 저축 추이 ```typescript /** * 영역 차트: 누적 저축 추이 */ private createAreaChart(sheetId: number, dataRows: number): sheets_v4.Schema$Request { return { addChart: { chart: { spec: { title: '누적 저축 추이', basicChart: { chartType: 'AREA', legendPosition: 'BOTTOM_LEGEND', axis: [ { position: 'BOTTOM_AXIS', title: '월' }, { position: 'LEFT_AXIS', title: '금액 (¥)' }, ], domains: [{ domain: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + dataRows, startColumnIndex: 0, // A열: 월 endColumnIndex: 1, }], }, }, }], series: [{ series: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + dataRows, startColumnIndex: 4, // E열: 누적저축 endColumnIndex: 5, }], }, }, targetAxis: 'LEFT_AXIS', color: { red: 0.2, green: 0.5, blue: 0.8 }, }], headerCount: 1, }, }, position: { overlayPosition: { anchorCell: { sheetId, rowIndex: 16, columnIndex: 14 }, widthPixels: 500, heightPixels: 300, }, }, }, }, }; } ``` ## 5. 바 차트 구현 ### 카테고리별 연간 지출 ```typescript /** * 컬럼 차트: 카테고리별 비교 */ private createColumnChart(sheetId: number, dataRows: number): sheets_v4.Schema$Request { return { addChart: { chart: { spec: { title: '카테고리별 연간 지출', basicChart: { chartType: 'COLUMN', legendPosition: 'NO_LEGEND', axis: [ { position: 'BOTTOM_AXIS', title: '카테고리' }, { position: 'LEFT_AXIS', title: '금액 (¥)' }, ], domains: [{ domain: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + Math.min(dataRows, 9), startColumnIndex: 6, // G열: 카테고리명 endColumnIndex: 7, }], }, }, }], series: [{ series: { sourceRange: { sources: [{ sheetId, startRowIndex: 25, endRowIndex: 26 + Math.min(dataRows, 9), startColumnIndex: 7, // H열: 금액 endColumnIndex: 8, }], }, }, targetAxis: 'LEFT_AXIS', color: { red: 0.4, green: 0.6, blue: 0.8 }, }], headerCount: 1, }, }, position: { overlayPosition: { anchorCell: { sheetId, rowIndex: 32, columnIndex: 14 }, widthPixels: 500, heightPixels: 300, }, }, }, }, }; } ``` ## 6. 스택 바 차트 구현 ### 월별 지출 구성 ```typescript /** * 스택 바 차트: 월별 구성 비율 */ private createStackedBarChart(sheetId: number, dataRows: number): sheets_v4.Schema$Request { return { addChart: { chart: { spec: { title: '월별 지출 구성', basicChart: { chartType: 'BAR', legendPosition: 'RIGHT_LEGEND', stackedType: 'STACKED', // 스택 모드 axis: [ { position: 'BOTTOM_AXIS', title: '금액 (¥)' }, { position: 'LEFT_AXIS', title: '월' }, ], // 도메인 및 시리즈 설정 // Top 5 카테고리를 각각 시리즈로 추가 }, }, position: { overlayPosition: { anchorCell: { sheetId, rowIndex: 48, columnIndex: 14 }, widthPixels: 600, heightPixels: 350, }, }, }, }, }; } ``` ## 7. 전체 차트 생성 메서드 ```typescript /** * 모든 트렌드 차트 생성 */ async createAllTrendCharts( spreadsheetId: string, sheetId: number, summaries: MonthlySummary[], breakdown: CategoryBreakdown[] ): Promise { console.log('\n📈 트렌드 차트 생성 시작...\n'); // 차트 데이터 영역 준비 await this.prepareChartData(spreadsheetId, 'Dashboard', summaries, breakdown); const requests: sheets_v4.Schema$Request[] = []; // 1. 라인 차트 requests.push(this.createLineChart(sheetId, summaries.length)); console.log(' ✅ 라인 차트 (월별 수입/지출 추이)'); // 2. 영역 차트 requests.push(this.createAreaChart(sheetId, summaries.length)); console.log(' ✅ 영역 차트 (누적 저축 추이)'); // 3. 바 차트 requests.push(this.createColumnChart(sheetId, breakdown.length)); console.log(' ✅ 바 차트 (카테고리별 비교)'); // 4. 스택 바 차트 requests.push(this.createStackedBarChart(sheetId, summaries.length)); console.log(' ✅ 스택 바 차트 (월별 구성 비율)'); await this.client.batchUpdate(spreadsheetId, requests); console.log('\n✅ 트렌드 차트 생성 완료!\n'); } ``` ## 8. 차트 옵션 정리 ### 공통 옵션 | 옵션 | 설명 | 값 | |------|------|-----| | `chartType` | 차트 종류 | LINE, AREA, COLUMN, BAR | | `legendPosition` | 범례 위치 | BOTTOM_LEGEND, RIGHT_LEGEND, NO_LEGEND | | `stackedType` | 스택 모드 | STACKED, PERCENT_STACKED | | `headerCount` | 헤더 행 수 | 1 (첫 행이 헤더) | ### 색상 설정 | 항목 | RGB 값 | 용도 | |------|--------|------| | 수입 | (0.2, 0.6, 0.2) | 녹색 | | 지출 | (0.8, 0.2, 0.2) | 빨간색 | | 누적저축 | (0.2, 0.5, 0.8) | 파란색 | | 카테고리 | (0.4, 0.6, 0.8) | 청회색 | ## 현재 프로젝트 구조 ``` project/src/ ├── charts/ │ └── trend-charts.ts ✅ 트렌드 차트 생성 ├── dashboard/ │ ├── creator.ts ✅ │ └── formatter.ts ✅ └── ... ``` ## 다음 단계 v9에서는: - 파이 차트: 지출 카테고리 비율 - 도넛 차트: 수입원 비율 - 콤보 차트: 수입/지출 + 저축률 - 워터폴 차트: 월간 순자산 변동 --- **작성일**: 2025-12-01 **상태**: ✅ 완료 **다음**: v9 - 차트 시각화 2 (비율 및 비교 차트)