20 KiB
20 KiB
📊 차트 시스템 구현 계획
개요
D3.js 기반의 강력한 차트 시스템을 구축합니다. 사용자는 데이터를 두 가지 방법(DB 쿼리 또는 REST API)으로 가져와 다양한 차트로 시각화할 수 있습니다.
🎯 핵심 요구사항
1. 데이터 소스 (2가지 방식)
A. 데이터베이스 커넥션
- 현재 DB: 애플리케이션의 기본 PostgreSQL 연결
- 외부 DB: 기존 "외부 커넥션 관리" 메뉴에서 등록된 커넥션만 사용
- 신규 커넥션 생성은 외부 커넥션 관리 메뉴에서만 가능
- 차트 설정에서는 등록된 커넥션 목록에서 선택만 가능
- 쿼리 제한: SELECT 문만 허용 (INSERT, UPDATE, DELETE, DROP 등 금지)
- 쿼리 검증: 서버 측에서 SQL Injection 방지 및 쿼리 타입 검증
B. REST API 호출
- HTTP Methods: GET (권장) - 데이터 조회에 충분
- 데이터 형식: JSON 응답만 허용
- 헤더 설정: Authorization, Content-Type 등 커스텀 헤더 지원
- 쿼리 파라미터: URL 파라미터로 필터링 조건 전달
- 응답 파싱: JSON 구조에서 차트 데이터 추출
- 에러 처리: HTTP 상태 코드 및 타임아웃 처리
참고: POST는 향후 확장 (GraphQL, 복잡한 필터링)을 위해 선택적으로 지원 가능
2. 차트 타입 (D3.js 기반)
현재 지원 예정:
- Bar Chart (막대 차트): 수평/수직 막대
- Line Chart (선 차트): 단일/다중 시리즈
- Area Chart (영역 차트): 누적 영역 지원
- Pie Chart (원 차트): 도넛 차트 포함
- Stacked Bar (누적 막대): 다중 시리즈 누적
- Combo Chart (혼합 차트): 막대 + 선 조합
3. 축 매핑 설정
- X축: 카테고리/시간 데이터 (문자열, 날짜)
- Y축: 숫자 데이터 (단일 또는 다중 선택 가능)
- 다중 Y축: 여러 시리즈를 한 차트에 표시 (예: 갤럭시 vs 아이폰 매출)
- 자동 감지: 데이터 타입에 따라 축 자동 추천
- 데이터 변환: 문자열 날짜를 Date 객체로 자동 변환
4. 차트 스타일링
- 색상 팔레트: 사전 정의된 색상 세트 선택
- 커스텀 색상: 사용자 지정 색상 입력
- 범례: 위치 설정 (상단, 하단, 좌측, 우측, 숨김)
- 애니메이션: 차트 로드 시 부드러운 전환 효과
- 툴팁: 데이터 포인트 호버 시 상세 정보 표시
- 그리드: X/Y축 그리드 라인 표시/숨김
📁 파일 구조
frontend/components/admin/dashboard/
├── CHART_SYSTEM_PLAN.md # 이 파일
├── types.ts # ✅ 기존 (타입 확장 필요)
├── ElementConfigModal.tsx # ✅ 기존 (리팩토링 필요)
│
├── data-sources/ # 🆕 데이터 소스 관련
│ ├── DataSourceSelector.tsx # 데이터 소스 선택 UI (DB vs API)
│ ├── DatabaseConfig.tsx # DB 커넥션 설정 UI
│ ├── ApiConfig.tsx # REST API 설정 UI
│ └── dataSourceUtils.ts # 데이터 소스 유틸리티
│
├── chart-config/ # 🔄 차트 설정 관련 (리팩토링)
│ ├── QueryEditor.tsx # ✅ 기존 (확장 필요)
│ ├── ChartConfigPanel.tsx # ✅ 기존 (확장 필요)
│ ├── AxisMapper.tsx # 🆕 축 매핑 UI
│ ├── StyleConfig.tsx # 🆕 스타일 설정 UI
│ └── ChartPreview.tsx # 🆕 실시간 미리보기
│
├── charts/ # 🆕 D3 차트 컴포넌트
│ ├── ChartRenderer.tsx # 차트 렌더러 (메인)
│ ├── BarChart.tsx # 막대 차트
│ ├── LineChart.tsx # 선 차트
│ ├── AreaChart.tsx # 영역 차트
│ ├── PieChart.tsx # 원 차트
│ ├── StackedBarChart.tsx # 누적 막대 차트
│ ├── ComboChart.tsx # 혼합 차트
│ ├── chartUtils.ts # 차트 유틸리티
│ └── d3Helpers.ts # D3 헬퍼 함수
│
└── CanvasElement.tsx # ✅ 기존 (차트 렌더링 통합)
🔧 타입 정의 확장
기존 타입 업데이트
// types.ts
// 데이터 소스 타입 확장
export interface ChartDataSource {
type: "database" | "api"; // 'static' 제거
// DB 커넥션 관련
connectionType?: "current" | "external"; // 현재 DB vs 외부 DB
externalConnectionId?: string; // 외부 DB 커넥션 ID
query?: string; // SQL 쿼리 (SELECT만)
// API 관련
endpoint?: string; // API URL
method?: "GET"; // HTTP 메서드 (GET만 지원)
headers?: Record<string, string>; // 커스텀 헤더
queryParams?: Record<string, string>; // URL 쿼리 파라미터
jsonPath?: string; // JSON 응답에서 데이터 추출 경로 (예: "data.results")
// 공통
refreshInterval?: number; // 자동 새로고침 (초)
lastExecuted?: string; // 마지막 실행 시간
lastError?: string; // 마지막 오류 메시지
}
// 외부 DB 커넥션 정보 (기존 외부 커넥션 관리에서 가져옴)
export interface ExternalConnection {
id: string;
name: string; // 사용자 지정 이름 (표시용)
type: "postgresql" | "mysql" | "mssql" | "oracle";
// 나머지 정보는 외부 커넥션 관리에서만 관리
}
// 차트 설정 확장
export interface ChartConfig {
// 축 매핑
xAxis: string; // X축 필드명
yAxis: string | string[]; // Y축 필드명 (다중 가능)
// 데이터 처리
groupBy?: string; // 그룹핑 필드
aggregation?: "sum" | "avg" | "count" | "max" | "min";
sortBy?: string; // 정렬 기준 필드
sortOrder?: "asc" | "desc"; // 정렬 순서
limit?: number; // 데이터 개수 제한
// 스타일
colors?: string[]; // 차트 색상 팔레트
title?: string; // 차트 제목
showLegend?: boolean; // 범례 표시
legendPosition?: "top" | "bottom" | "left" | "right"; // 범례 위치
// 축 설정
xAxisLabel?: string; // X축 라벨
yAxisLabel?: string; // Y축 라벨
showGrid?: boolean; // 그리드 표시
// 애니메이션
enableAnimation?: boolean; // 애니메이션 활성화
animationDuration?: number; // 애니메이션 시간 (ms)
// 툴팁
showTooltip?: boolean; // 툴팁 표시
tooltipFormat?: string; // 툴팁 포맷 (템플릿)
// 차트별 특수 설정
barOrientation?: "vertical" | "horizontal"; // 막대 방향
lineStyle?: "smooth" | "straight"; // 선 스타일
areaOpacity?: number; // 영역 투명도
pieInnerRadius?: number; // 도넛 차트 내부 반지름 (0-1)
stackMode?: "normal" | "percent"; // 누적 모드
}
// API 응답 구조
export interface ApiResponse<T = any> {
success: boolean;
data: T;
message?: string;
error?: string;
}
// 차트 데이터 (변환 후)
export interface ChartData {
labels: string[]; // X축 레이블
datasets: ChartDataset[]; // Y축 데이터셋 (다중 시리즈)
}
export interface ChartDataset {
label: string; // 시리즈 이름
data: number[]; // 데이터 값
color?: string; // 색상
}
📝 구현 단계
Phase 1: 데이터 소스 설정 UI (4-5시간)
Step 1.1: 데이터 소스 선택기
DataSourceSelector.tsx생성- DB vs API 선택 라디오 버튼
- 선택에 따라 하위 UI 동적 렌더링
- 상태 관리 (현재 선택된 소스 타입)
Step 1.2: 데이터베이스 설정
DatabaseConfig.tsx생성- 현재 DB / 외부 DB 선택 라디오 버튼
- 외부 DB 선택 시:
- 기존 외부 커넥션 관리에서 등록된 커넥션 목록 불러오기
- 드롭다운으로 커넥션 선택 (ID, 이름, 타입 표시)
- "외부 커넥션 관리로 이동" 링크 제공
- 선택된 커넥션 정보 표시 (읽기 전용)
- SQL 에디터 통합 (기존
QueryEditor재사용) - 쿼리 테스트 버튼 (선택된 커넥션으로 실행)
Step 1.3: REST API 설정
ApiConfig.tsx생성- API 엔드포인트 URL 입력
- HTTP 메서드: GET 고정 (UI에서 표시만)
- URL 쿼리 파라미터 추가 UI (키-값 쌍)
- 동적 파라미터 추가/제거 버튼
- 예시:
?category=electronics&limit=10
- 헤더 추가 UI (키-값 쌍)
- Authorization 헤더 빠른 입력
- 일반적인 헤더 템플릿 제공
- JSON Path 설정 (데이터 추출 경로)
- 예시:
data.results,items,response.data
- 예시:
- 테스트 요청 버튼
- 응답 미리보기 (JSON 구조 표시)
Step 1.4: 데이터 소스 유틸리티
dataSourceUtils.ts생성- DB 커넥션 검증 함수
- API 요청 실행 함수
- JSON Path 파싱 함수
- 데이터 정규화 함수 (DB/API 결과를 통일된 형식으로)
Phase 2: 서버 측 API 구현 (2-3시간)
Step 2.1: 외부 커넥션 목록 조회 API
GET /api/external-connections- 기존 외부 커넥션 관리의 커넥션 목록 조회- 응답:
{ id, name, type }최소 정보만 반환 (보안) - 인증된 사용자만 접근 가능
Step 2.2: 쿼리 실행 API (확장)
- 기존
POST /api/dashboards/execute-query확장 - 외부 DB 연결 지원
- SELECT 쿼리 검증 (정규식 + SQL 파서)
- SQL Injection 방지
- 쿼리 타임아웃 설정
- 결과 행 수 제한 (최대 1000행)
- 에러 핸들링 및 로깅
Step 2.3: REST API 프록시
GET /api/dashboards/fetch-api- API 호출 프록시 (GET 프록시)- 쿼리 파라미터로 대상 URL, 헤더, JSON Path 전달
- CORS 우회
- 요청 헤더 전달 (Authorization 등)
- 응답 캐싱 (선택적, 5분)
- 타임아웃 설정 (30초)
- JSON Path 적용 (서버 측에서 데이터 추출)
- 에러 핸들링 및 상태 코드 변환
Phase 3: 차트 설정 UI 개선 (3-4시간)
Step 3.1: 축 매퍼
AxisMapper.tsx생성- X축 필드 선택 드롭다운
- Y축 필드 다중 선택 (체크박스)
- 데이터 타입 자동 감지 및 표시
- 샘플 데이터 미리보기 (첫 3행)
- 축 라벨 커스터마이징
Step 3.2: 스타일 설정
StyleConfig.tsx생성- 색상 팔레트 선택 (사전 정의 + 커스텀)
- 범례 위치 선택
- 그리드 표시/숨김
- 애니메이션 설정
- 차트별 특수 옵션
- 막대 차트: 수평/수직
- 선 차트: 부드러움 정도
- 원 차트: 도넛 모드
Step 3.3: 실시간 미리보기
ChartPreview.tsx생성- 축소된 차트 미리보기 (300x200)
- 설정 변경 시 실시간 업데이트
- 로딩 상태 표시
- 에러 표시
Phase 4: D3 차트 컴포넌트 (6-8시간)
Step 4.1: 차트 렌더러 (공통)
ChartRenderer.tsx생성- 차트 타입에 따라 적절한 컴포넌트 렌더링
- 데이터 정규화 및 변환
- 공통 레이아웃 (제목, 범례)
- 반응형 크기 조절
- 에러 바운더리
Step 4.2: 막대 차트
BarChart.tsx생성- D3 스케일 설정 (x: 범주형, y: 선형)
- 막대 렌더링 (rect 요소)
- 축 렌더링 (d3-axis)
- 툴팁 구현
- 애니메이션 (높이 전환)
- 수평/수직 모드 지원
- 다중 시리즈 (그룹화)
Step 4.3: 선 차트
LineChart.tsx생성- D3 라인 제너레이터 (d3.line)
- 부드러운 곡선 (d3.curveMonotoneX)
- 데이터 포인트 표시 (circle)
- 툴팁 구현
- 애니메이션 (path 길이 전환)
- 다중 시리즈 (여러 선)
- 누락 데이터 처리
Step 4.4: 영역 차트
AreaChart.tsx생성- D3 영역 제너레이터 (d3.area)
- 투명도 설정
- 누적 모드 지원 (d3.stack)
- 선 차트 기능 재사용
- 애니메이션
Step 4.5: 원 차트
PieChart.tsx생성- D3 파이 레이아웃 (d3.pie)
- 아크 제너레이터 (d3.arc)
- 도넛 모드 (innerRadius)
- 라벨 배치 (중심 또는 외부)
- 툴팁 구현
- 애니메이션 (회전 전환)
- 퍼센트 표시
Step 4.6: 누적 막대 차트
StackedBarChart.tsx생성- D3 스택 레이아웃 (d3.stack)
- 다중 시리즈 누적
- 일반 누적 vs 퍼센트 모드
- 막대 차트 로직 재사용
- 범례 색상 매핑
Step 4.7: 혼합 차트
ComboChart.tsx생성- 막대 + 선 조합
- 이중 Y축 (좌측: 막대, 우측: 선)
- 스케일 독립 설정
- 막대/선 차트 로직 결합
- 복잡한 툴팁 (두 데이터 표시)
Step 4.8: 차트 유틸리티
chartUtils.ts생성- 데이터 변환 함수 (QueryResult → ChartData)
- 날짜 파싱 및 포맷팅
- 숫자 포맷팅 (천 단위 콤마, 소수점)
- 색상 팔레트 정의
- 반응형 크기 계산
Step 4.9: D3 헬퍼
d3Helpers.ts생성- 공통 스케일 생성
- 축 생성 및 스타일링
- 그리드 라인 추가
- 툴팁 DOM 생성/제거
- SVG 마진 계산
Phase 5: 차트 통합 및 렌더링 (2-3시간)
Step 5.1: CanvasElement 통합
CanvasElement.tsx수정- 차트 요소 감지 (element.type === 'chart')
ChartRenderer컴포넌트 임포트 및 렌더링- 데이터 로딩 상태 표시
- 에러 상태 표시
- 자동 새로고침 로직
Step 5.2: 데이터 페칭
- 차트 마운트 시 초기 데이터 로드
- 자동 새로고침 타이머 설정
- 수동 새로고침 버튼
- 로딩/에러/성공 상태 관리
- 캐싱 (선택적)
Step 5.3: ElementConfigModal 리팩토링
- 데이터 소스 선택 UI 통합
- 3단계 플로우 구현
- 데이터 소스 선택 및 설정
- 데이터 가져오기 및 검증
- 축 매핑 및 스타일 설정
- 진행 표시기 (스텝 인디케이터)
- 뒤로/다음 버튼
Phase 6: 테스트 및 최적화 (2-3시간)
Step 6.1: 기능 테스트
- 각 차트 타입 렌더링 확인
- DB 쿼리 실행 및 차트 생성
- API 호출 및 차트 생성
- 다중 시리즈 차트 확인
- 자동 새로고침 동작 확인
- 에러 처리 확인
Step 6.2: UI/UX 개선
- 로딩 스피너 추가
- 빈 데이터 상태 UI
- 에러 메시지 개선
- 툴팁 스타일링
- 범례 스타일링
- 반응형 레이아웃 확인
Step 6.3: 성능 최적화
- D3 렌더링 최적화 (불필요한 재렌더링 방지)
- 대용량 데이터 처리 (샘플링, 페이징)
- 메모이제이션 (useMemo, useCallback)
- SVG 최적화
- 차트 데이터 캐싱
🔒 보안 고려사항
SQL Injection 방지
- 서버 측에서 쿼리 타입 엄격 검증 (SELECT만 허용)
- 정규식 + SQL 파서 사용
- Prepared Statement 사용 (파라미터 바인딩)
- 위험한 키워드 차단 (DROP, DELETE, UPDATE, INSERT, EXEC 등)
외부 DB 커넥션 보안
- 기존 "외부 커넥션 관리"에서 보안 처리됨
- 차트 시스템에서는 커넥션 ID만 사용
- 민감 정보(비밀번호, 호스트 등)는 차트 설정에 노출하지 않음
- 타임아웃 설정 (30초)
API 보안
- CORS 정책 확인
- 민감한 헤더 로깅 방지 (Authorization 등)
- 요청 크기 제한
- Rate Limiting (API 호출 빈도 제한)
🎨 UI/UX 개선 사항
설정 플로우
-
데이터 소스 선택
- 큰 아이콘과 설명으로 DB vs API 선택
- 각 방식의 장단점 안내
-
데이터 구성
- DB: SQL 에디터 + 실행 버튼
- API: URL, 메서드, 헤더, 본문 입력
- 테스트 버튼으로 즉시 확인
-
데이터 미리보기
- 쿼리/API 실행 결과를 테이블로 표시 (최대 10행)
- 컬럼명과 샘플 데이터 표시
-
차트 설정
- X/Y축 드래그 앤 드롭 매핑
- 실시간 미리보기 (작은 차트)
- 스타일 프리셋 선택
피드백 메시지
- ✅ 성공: "데이터를 성공적으로 불러왔습니다 (45행)"
- ⚠️ 경고: "쿼리 실행이 오래 걸리고 있습니다"
- ❌ 오류: "데이터베이스 연결에 실패했습니다: 잘못된 비밀번호"
로딩 상태
- 스켈레톤 UI (차트 윤곽)
- 진행률 표시 (대용량 데이터)
- 취소 버튼 (장시간 실행 쿼리)
📊 샘플 데이터 및 시나리오
시나리오 1: 월별 매출 추이 (DB 쿼리)
SELECT
TO_CHAR(order_date, 'YYYY-MM') as month,
SUM(total_amount) as sales
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY TO_CHAR(order_date, 'YYYY-MM')
ORDER BY month;
- 차트 타입: Line Chart
- X축: month
- Y축: sales
시나리오 2: 제품 비교 (다중 시리즈)
SELECT
DATE_TRUNC('month', order_date) as month,
SUM(CASE WHEN product_category = '갤럭시' THEN amount ELSE 0 END) as galaxy,
SUM(CASE WHEN product_category = '아이폰' THEN amount ELSE 0 END) as iphone
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month;
- 차트 타입: Combo Chart (Bar + Line)
- X축: month
- Y축: [galaxy, iphone] (다중)
시나리오 3: 카테고리별 매출 (원 차트)
SELECT
category,
SUM(amount) as total
FROM sales
WHERE sale_date >= CURRENT_DATE - INTERVAL '1 month'
GROUP BY category
ORDER BY total DESC
LIMIT 10;
- 차트 타입: Pie Chart (Donut)
- X축: category
- Y축: total
시나리오 4: REST API (실시간 환율)
- API:
https://api.exchangerate-api.com/v4/latest/USD - JSON Path:
rates - 변환: Object를 배열로 변환 (통화: 환율)
- 차트 타입: Bar Chart
- X축: 통화 코드 (KRW, JPY, EUR 등)
- Y축: 환율
✅ 완료 기준
Phase 1: 데이터 소스 설정
- DB 커넥션 설정 UI 작동
- 외부 DB 커넥션 저장 및 불러오기
- API 설정 UI 작동
- 테스트 버튼으로 즉시 확인 가능
Phase 2: 서버 API
- 외부 DB 커넥션 CRUD API 작동
- 쿼리 실행 API (현재/외부 DB)
- SELECT 쿼리 검증 및 SQL Injection 방지
- API 프록시 작동
Phase 3: 차트 설정 UI
- 축 매핑 UI 직관적
- 다중 Y축 선택 가능
- 스타일 설정 UI 작동
- 실시간 미리보기 표시
Phase 4: D3 차트
- 6가지 차트 타입 모두 렌더링
- 툴팁 표시
- 애니메이션 부드러움
- 반응형 크기 조절
- 다중 시리즈 지원
Phase 5: 통합
- 캔버스에서 차트 표시
- 자동 새로고침 작동
- 설정 모달 3단계 플로우 완료
- 데이터 로딩/에러 상태 표시
Phase 6: 테스트
- 모든 차트 타입 정상 작동
- DB/API 데이터 소스 모두 작동
- 에러 처리 적절
- 성능 이슈 없음 (1000행 데이터)
🚀 향후 확장 계획
- 실시간 스트리밍: WebSocket 데이터 소스 추가
- 고급 차트: Scatter Plot, Heatmap, Radar Chart
- 데이터 변환: 필터링, 정렬, 계산 필드 추가
- 차트 상호작용: 클릭/드래그로 데이터 필터링
- 내보내기: PNG, SVG, PDF 저장
- 템플릿: 사전 정의된 차트 템플릿 (업종별)
📅 예상 일정
- Phase 1: 1일 (데이터 소스 UI)
- Phase 2: 0.5일 (서버 API) - 기존 외부 커넥션 관리 활용으로 단축
- Phase 3: 1일 (차트 설정 UI)
- Phase 4: 2일 (D3 차트 컴포넌트)
- Phase 5: 0.5일 (통합)
- Phase 6: 0.5일 (테스트)
총 예상 시간: 5.5일 (44시간)
구현 시작일: 2025-10-14 목표 완료일: 2025-10-20 현재 진행률: 0% (계획 수립 완료)
🎯 다음 단계
- Phase 1 시작:
DataSourceSelector.tsx생성 - 타입 정의 확장:
types.ts업데이트 - 서버 API 엔드포인트 설계 및 구현
- D3.js 라이브러리 설치 및 기본 차트 PoC