diff --git a/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardConfig.tsx b/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardConfig.tsx
index 52f18d27..f64e09ae 100644
--- a/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardConfig.tsx
+++ b/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardConfig.tsx
@@ -60,6 +60,7 @@ import type {
JoinConfig,
JoinType,
ItemStyleConfig,
+ AggregationType,
} from "../types";
import {
TEXT_ALIGN_LABELS,
@@ -132,6 +133,50 @@ const JOIN_TYPE_LABELS: Record = {
right: "RIGHT JOIN",
};
+// ===== 집계 함수 유효성 검증 유틸 =====
+
+// 아이템 타입별 사용 가능한 집계 함수
+const SUBTYPE_AGGREGATION_MAP: Record = {
+ "kpi-card": ["count", "sum", "avg", "min", "max"],
+ chart: ["count", "sum", "avg", "min", "max"],
+ gauge: ["count", "sum", "avg", "min", "max"],
+ "stat-card": ["count"],
+};
+
+// 집계 함수 라벨
+const AGGREGATION_LABELS: Record = {
+ count: "건수 (COUNT)",
+ sum: "합계 (SUM)",
+ avg: "평균 (AVG)",
+ min: "최소 (MIN)",
+ max: "최대 (MAX)",
+};
+
+// 숫자 전용 집계 함수 (숫자 컬럼에만 사용 가능)
+const NUMERIC_ONLY_AGGREGATIONS: AggregationType[] = ["sum", "avg"];
+
+// PostgreSQL 숫자 타입 판별용 패턴
+const NUMERIC_TYPE_PATTERNS = [
+ "int", "integer", "bigint", "smallint",
+ "numeric", "decimal", "real", "double",
+ "float", "serial", "bigserial", "smallserial",
+ "money", "number",
+];
+
+/** 컬럼이 숫자 타입인지 판별 */
+function isNumericColumn(col: ColumnInfo): boolean {
+ const t = (col.type || "").toLowerCase();
+ const u = (col.udtName || "").toLowerCase();
+ return NUMERIC_TYPE_PATTERNS.some(
+ (pattern) => t.includes(pattern) || u.includes(pattern)
+ );
+}
+
+/** 현재 집계 함수가 숫자 전용(sum/avg)인지 판별 */
+function isNumericOnlyAggregation(aggType: string | undefined): boolean {
+ return !!aggType && NUMERIC_ONLY_AGGREGATIONS.includes(aggType as AggregationType);
+}
+
const FILTER_OPERATOR_LABELS: Record = {
"=": "같음 (=)",
"!=": "다름 (!=)",
@@ -149,9 +194,11 @@ const FILTER_OPERATOR_LABELS: Record = {
function DataSourceEditor({
dataSource,
onChange,
+ subType,
}: {
dataSource: DataSourceConfig;
onChange: (ds: DataSourceConfig) => void;
+ subType?: DashboardSubType;
}) {
// 테이블 목록 (Combobox용)
const [tables, setTables] = useState([]);
@@ -268,7 +315,15 @@ function DataSourceEditor({
@@ -331,10 +390,15 @@ function DataSourceEditor({
/>
- 컬럼을 찾을 수 없습니다.
+ {isNumericOnlyAggregation(dataSource.aggregation?.type)
+ ? "숫자 타입 컬럼이 없습니다."
+ : "컬럼을 찾을 수 없습니다."}
- {columns.map((col) => (
+ {(isNumericOnlyAggregation(dataSource.aggregation?.type)
+ ? columns.filter(isNumericColumn)
+ : columns
+ ).map((col) => (
차트에서 X축 카테고리로 사용됩니다
+ {subType === "chart" && !dataSource.aggregation?.groupBy?.length && (
+
+ 차트 모드에서는 그룹핑(X축)을 설정해야 의미 있는 차트가 표시됩니다
+
+ )}
)}
@@ -1144,9 +1213,30 @@ function ItemEditor({