PivotCube
Properties
columns: Array (opens in a new tab)<ColumnMeta> readonly
컬럼 메타데이터 배열을 반환한다.
default: undefined
listenerCount: number (opens in a new tab) readonly
등록된 리스너 개수를 반환한다.
default: undefined
name: string (opens in a new tab)
default: undefined
rowCount: number (opens in a new tab) readonly
필터 적용된 행 개수를 반환한다.
addFilter(), filterAll(), applySlicers() 등으로 설정된 필터를 반영한다.
필터가 없으면 전체 행 수(totalRowCount)와 동일한 값을 반환한다.
default: undefined
See Also
totalRowCount원본 전체 행 개수addFilter필터 추가filterAll필터 일괄 적용
schema: CubeSchema readonly
큐브 스키마를 반환한다.
default: undefined
source: CubeDataSource readonly
큐브 데이터 소스를 반환한다.
ColumnStore 또는 DataViewSource 인스턴스일 수 있다.
default: undefined
table: DataTable readonly
default: undefined
totalRowCount: number (opens in a new tab) readonly
필터와 무관한 원본 전체 행 개수를 반환한다.
default: undefined
See Also
rowCount 필터 적용 후 행 개수
Methods
addExpressionFilter(name, expression): void (opens in a new tab)
수식 기반 필터를 추가한다.
| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 필터 이름 (고유해야 함) |
| expression | string (opens in a new tab) | 필터 수식 (예: 'sales > 1000 AND profit > 100') |
addFilter(filter): void (opens in a new tab)
단일 필터를 추가하거나 업데이트한다.
CubeFilter 형식의 필터를 추가한다. 같은 dimension 필터가 있으면 대체된다.
| Parameter | Type | Description |
|---|---|---|
| filter | CubeFilter | 추가할 CubeFilter |
addListener(listener): void (opens in a new tab)
이벤트 리스너를 추가한다.
| Parameter | Type | Description |
|---|---|---|
| listener | any (opens in a new tab) | 이벤트 리스너 |
addMeasure(name, options): this
동적 계산 측정값(calculated measure)을 DataCube에 등록한다.
등록된 측정값은 aggregate()에서 이름으로 참조할 수 있다.
이 메서드는 row-level 계산을 정의한다.
각 행에서 계산된 값을 지정된 집계 함수(aggregate)로 집계한다.
Row-level 계산
- 각 데이터 행에서 먼저 값을 계산
- 계산된 값들을 aggregate로 집계 (sum, avg, min, max, count)
// Expression string 사용
cube.addMeasure('margin', { source: 'profit / sales * 100', aggregate: 'avg' });
cube.addMeasure('total_revenue', { source: 'price * quantity', aggregate: 'sum' });
// 함수 사용
cube.addMeasure('margin_pct', {
source: (row) => row.sales > 0 ? (row.profit / row.sales * 100) : 0,
aggregate: 'avg'
});
// aggregate에서 이름으로 참조
const agg = cube.aggregate(['region'], ['sales', 'margin', 'total_revenue']);| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 측정값 이름 (고유해야 함) |
| options | { aggregate: 'sum' | 'avg' | 'min' | 'max' | 'count' | 'distinct' | 'product' | 'stdev' | 'stdevp' | 'var' | 'varp' | 'first' | 'last' | 'p25' | 'p50' | 'p75';source: string | (row: any) => any;type: 'i32' | 'f64' | 'str' | 'date'; } | 옵션 객체 |
- source: 계산 수식(expression 문자열) 또는 계산 함수 (필수)
- 문자열: 'profit / sales * 100' 같은 expression
- 함수: (row: any) => row.profit / row.sales * 100
- aggregate: 집계 함수 (기본값: 'sum')
- type: 결과 타입 'f64' | 'i32' (기본값: 'f64') |
return this (메서드 체이닝 가능)
addMetric(name, expression): this
집계 후 계산할 지표(metric)을 DataCube에 등록한다.
이미 집계된 다른 measure들의 값으로부터 계산되는 post-aggregate 지표이다.
Metric은 반드시 적어도 하나의 차원을 가지고 있어야 하며,
차원이 없는 전체 집계에서는 계산되지 않는다.
Row-level Measure vs Metric
- Measure: 각 row에서 계산 후 집계
- 예:
profit / qty(각 row에서 계산)
- 예:
- Metric: 집계 후 계산
- 예:
profit / sales * 100(sum(profit) / sum(sales) * 100)
- 예:
// Measure 등록
cube.addMeasure('profit', { source: 'profit', aggregate: 'sum' });
cube.addMeasure('sales', { source: 'sales', aggregate: 'sum' });
cube.addMeasure('qty', { source: 'qty', aggregate: 'sum' });
// Metric 등록
cube.addMetric('profit_margin', 'profit / sales * 100');
cube.addMetric('profit_per_qty', 'profit / qty');
// aggregate에서 함께 사용
const agg = cube.aggregate(['region'], [
'profit', // Measure
'sales', // Measure
'profit_margin', // Metric (자동으로 차원 필요)
'profit_per_qty' // Metric
]);| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 지표 이름 (고유해야 함) |
| expression | string (opens in a new tab) | 계산 수식 (다른 measure 이름 참조) |
- 예: 'profit / sales * 100'
- 참조 가능: 등록된 모든 measure와 다른 metric |
return this (메서드 체이닝 가능)
addSlicer(options): Slicer
Slicer를 추가한다.
const slicer = cube.addSlicer({ name: 'region', dimension: 'region' });
slicer.select(['Seoul', 'Busan']);
cube.applySlicers();| Parameter | Type | Description |
|---|---|---|
| options | SlicerOptions | Slicer 옵션 (name 필수) |
return 생성된 Slicer 인스턴스
aggregate(dimensionNames, measureNames?, options?): AggTable
지정된 차원과 측정값으로 집계 테이블을 생성한다.
일반 차트(막대, 선, 파이 등)는 이 메서드로 충분하며,
피벗 테이블(crosstab)이 필요한 경우에만 pivot()을 사용한다.
디자인 타임 vs 런타임
- mutable: false (기본값): 불변 AggTable 생성, 캐시 가능
- 런타임용, 프로덕션 차트에 최적
- 동일 조합 요청 시 캐시된 인스턴스 재사용
- mutable: true: 가변 AggTable 생성, 캐시 불가
- 디자인 타임용, 차트 설정 중 차원/측정값 변경 가능
- 확정 후 freeze() 호출 필요
Aggregate 오버라이드
Schema에 정의된 기본 aggregate를 특정 AggTable에서만 변경할 수 있다.
Metric과의 관계:
- Metric(post-aggregate 지표)은 집계된 measure 값을 참조하여 계산됨
- aggregate 오버라이드 시, 해당 AggTable 내의 metric도 오버라이드된 집계값을 사용
- 예:
profit_rate = profit / sales * 100에서 sales를 avg로 오버라이드하면, profit_rate는profit(sum) / sales(avg) * 100으로 계산됨 - 각 AggTable은 독립적이므로, 다른 AggTable의 metric 계산에는 영향 없음
집계 결과 구조
- Dimension 컬럼: 그룹화 기준이 되는 차원들의 고유 조합
- Measure 컬럼: 각 그룹별로 집계된 측정값 (sum, avg, min, max, count)
- Metric 컬럼: 집계된 measure를 기반으로 계산된 지표
// 동적 측정값 등록
cube.addMeasure('margin', { source: 'profit / sales * 100', aggregate: 'avg' });
// 런타임: immutable AggTable (캐시됨)
const agg1 = cube.aggregate(['region'], ['sales', 'margin']);
const agg2 = cube.aggregate(['region'], ['sales', 'margin']); // 캐시에서 재사용 ✓
// 디자인 타임: mutable AggTable (캐시 안됨)
const draft = cube.aggregate(['region'], ['sales'], { mutable: true });
draft.addDimension(productCol); // 차원 추가
draft.removeMeasure('quantity'); // 측정값 제거
draft.freeze(); // 확정 → 이후 캐시 가능
// aggregate 오버라이드: 기본 sum → avg로 변경
// metric (profit_rate)도 오버라이드된 sales(avg) 값을 사용
const aggAvg = cube.aggregate(['region'], ['sales', 'profit_rate'], {
aggregates: { sales: 'avg' }
});
// sales: avg로 집계
// profit_rate = profit(sum) / sales(avg) * 100 ← 오버라이드된 값 사용
// 다중 오버라이드
const aggMulti = cube.aggregate(['region'], ['sales', 'quantity', 'avg_price'], {
aggregates: { sales: 'avg', quantity: 'max' }
});
// avg_price = sales(avg) / quantity(max)| Parameter | Type | Description |
|---|---|---|
| dimensionNames | Array (opens in a new tab)<string (opens in a new tab)> | 집계할 차원 이름 배열 (예: ['region', 'product']) |
| measureNames | Array (opens in a new tab)<string (opens in a new tab) | MeasureAlias> | 집계할 측정값/지표 이름 배열 (생략 시 스키마의 모든 측정값 사용). |
| measure와 metric을 함께 지정 가능. | ||
| options | { aggregates: Record<string, CubeAggregateType>;filters: Array<CubeFilter>;force: boolean;mutable: boolean;userKey: string; } | 집계 옵션 |
return 집계 결과 테이블 (AggTable)
aggregateAll(): AggTable
모든 차원과 모든 측정값을 사용하여 집계를 수행한다.
내부적으로 aggregate()를 호출하여 전체 데이터에 대한 집계 테이블을 생성한다.
동작 방식
- 스키마에서 모든 차원(dimension)을 조회한다.
- 스키마에서 모든 측정값(measure)과 메트릭(metric)을 조회한다.
- aggregate(allDimensions, allMeasuresAndMetrics)를 호출한다.
// 스키마: dimensions = ['region', 'product'], measures = ['sales', 'quantity']
const agg = cube.aggregateAll();
// 동일: cube.aggregate(['region', 'product'], ['sales', 'quantity'])return 모든 차원으로 그룹화하고 모든 측정값을 집계한 불변 AggTable
applySlicers(): void (opens in a new tab)
등록된 모든 Slicer의 활성 필터를 수집하여 DataCube에 일괄 적용한다.
내부적으로 filterAll()을 호출한다.
// 개별 변경: select()가 자동으로 applySlicers() 호출
cube.getSlicer('region')?.select(['Seoul']);
// 배치 변경: apply=false로 지연 후 직접 호출
cube.getSlicer('region')?.select(['Seoul'], false);
cube.getSlicer('category')?.select('Electronics', false);
cube.applySlicers(); // 한 번만 filterAll() 호출beginUpdate(): void (opens in a new tab)
이벤트 발생을 일시적으로 중단한다.
beginUpdate()와 endUpdate()는 중첩 호출을 지원한다.
endUpdate() 호출 횟수가 beginUpdate() 호출 횟수와 같아지면 이벤트 발생이 재개된다.
중요: 예외 발생 시에도 endUpdate()가 반드시 호출되도록 try-finally 패턴을 사용해야 한다.
table.beginUpdate();
try {
table.setValue(0, 0, 'value1');
table.setValue(0, 1, 'value2');
table.updateRow(1, ['a', 'b', 'c']);
// ... 여러 작업들
} finally {
table.endUpdate();
}canDimension(name): boolean (opens in a new tab)
| Parameter | Type |
|---|---|
| name | string (opens in a new tab) |
canMeasure(name): boolean (opens in a new tab)
| Parameter | Type |
|---|---|
| name | string (opens in a new tab) |
clearCache(): void (opens in a new tab)
캐시된 모든 집계 테이블과 피벗 매트릭스를 제거한다.
메모리를 확보하거나 데이터 변경 후 재계산이 필요할 때 사용한다.
clearFilters(): void (opens in a new tab)
모든 필터를 제거한다 (API 필터 + Slicer 필터 모두).
clearListeners(): void (opens in a new tab)
모든 이벤트 리스너를 제거한다.
dice(filters): DataCube
지정된 차원의 특정 범위로 데이터를 다이싱한다.
여러 차원에 대한 필터 조건을 동시에 적용하여 부분 큐브를 생성하는 OLAP 다이싱 연산이다.
슬라이싱(slice)은 단일 차원을 필터링하는 반면, 다이싱(dice)은 다중 차원을 한 번에 필터링한다.
동작 방식
- 필터 맵의 각 차원별 조건을 검증한다.
- 모든 필터 조건을 AND로 결합하여 적용한다.
- 필터링된 데이터를 새로운 DataTableView로 래핑한다.
- 래핑된 뷰를 DataViewSource로 변환한다.
- 새로운 DataCube 인스턴스를 생성하여 반환한다.
slice()와의 차이점
| 기능 | slice() | dice() |
|---|---|---|
| 필터 개수 | 1개 차원 | 여러 차원 |
| 호출 방식 | 단일 호출 | 단일 호출 |
| 적용 방식 | 순차적 (체이닝 필요) | 동시 적용 |
| 사용 사례 | 단계별 드릴다운 | 한번에 다중 조건 |
필터 조건 형식
필터 맵의 각 값은 다음 두 가지 형식을 지원한다:
1. 정확한 값 필터 (Exact Match)
{ region: 'Seoul', quarter: 'Q1' }
// region == 'Seoul' AND quarter == 'Q1'2. 범위 필터 (Range)
{ sales: [1000, 5000] } // 1000 <= sales <= 5000
{ quantity: [0, 100] } // 0 <= quantity <= 100성능 고려사항
- 장점: 여러 조건을 한 번에 적용하므로 필터링 비용 최소화
- 비용: 모든 필터 조건을 각 행에서 평가해야 함
- 권장: 3개 이상의 차원 필터가 필요한 경우 dice() 사용
// 기본 사용: 다중 정확한 값 필터
const cube = new DataCube();
cube.setSource(dataViewSource);
// Seoul 지역, Q1 분기의 데이터만 추출
const dicedCube = cube.dice(
new Map([
['region', 'Seoul'],
['quarter', 'Q1']
])
);
// 결과: region == 'Seoul' AND quarter == 'Q1'인 행만 포함
const agg = dicedCube.aggregate(['product'], ['sales']);// 범위 필터 사용: 매출액 범위 제한
const cube = new DataCube();
cube.setSource(dataViewSource);
// 매출이 10,000 ~ 50,000 범위인 데이터만
const dicedCube = cube.dice(
new Map([
['sales', [10000, 50000]] // 10000 <= sales <= 50000
])
);
// 결과: 매출 범위에 해당하는 행만 포함
const agg = dicedCube.aggregate(['region'], ['quantity']);// 혼합 필터: 정확한 값 + 범위 필터
const cube = new DataCube();
cube.setSource(dataViewSource);
// Seoul 지역 + Q1 분기 + 판매량 50~100
const dicedCube = cube.dice(
new Map([
['region', 'Seoul'], // 정확한 값
['quarter', 'Q1'], // 정확한 값
['quantity', [50, 100]] // 범위 필터
])
);
// 결과: 모든 조건을 만족하는 행만 포함
const agg = dicedCube.aggregate(['product'], ['sales']);// 복잡한 다중 조건 필터
const cube = new DataCube();
cube.setSource(dataViewSource);
// 여러 지역 + 가격대 + 시간 범위 조합
const dicedCube = cube.dice(
new Map([
['region', 'Seoul'], // 특정 지역
['product', 'A'], // 특정 제품
['price', [1000, 5000]], // 가격대
['date', ['2024-01-01', '2024-03-31']] // 날짜 범위
])
);
// 모든 조건을 만족하는 상세 분석
const pivot = dicedCube.pivot(
['region', 'product'],
['quarter'],
'sales'
);// 빈 필터 맵 (필터링 없음)
const cube = new DataCube();
cube.setSource(dataViewSource);
// 필터 없이 전체 데이터
const dicedCube = cube.dice(new Map()); // 또는 new Map([])
// 원본 큐브와 동일한 결과
const agg1 = cube.aggregate(['region'], ['sales']);
const agg2 = dicedCube.aggregate(['region'], ['sales']);
// agg1과 agg2는 동일한 결과// slice()와의 비교
const cube = new DataCube();
cube.setSource(dataViewSource);
// 방법 1: slice() 체이닝 (3번의 호출)
const result1 = cube
.slice('region', 'Seoul')
.slice('quarter', 'Q1')
.slice('product', 'A')
.aggregate(['date'], ['sales']);
// 방법 2: dice() 단일 호출
const result2 = cube
.dice(new Map([
['region', 'Seoul'],
['quarter', 'Q1'],
['product', 'A']
]))
.aggregate(['date'], ['sales']);
// 두 결과는 동일하지만 dice()가 더 효율적// Excel pivot 스타일: 다중 필터와 피벗을 한 번에
const cube = new DataCube();
cube.setSource(dataViewSource);
// Seoul 지역 + Q1 분기의 제품x날짜 피벗을 한 번에 생성
const pivot = cube.dice(
new Map([
['region', 'Seoul'],
['quarter', 'Q1']
]),
['product'], // 행 차원
['date'], // 열 차원
'sales' // 측정값
);
// 결과는 PivotMatrix
console.log(pivot.rowLabels); // [['A'], ['B']]
console.log(pivot.columnLabels); // [['2024-01-01'], ['2024-01-02'], ...]| Parameter | Type | Description |
|---|---|---|
| filters | Map (opens in a new tab)<string (opens in a new tab), any (opens in a new tab)> | 차원별 필터 조건 맵 |
| 키: 차원 이름 (string (opens in a new tab)) | ||
| 값: 필터 값 (any) 또는 범위 [최소값, 최대값] | ||
| 빈 맵은 필터링 없음 (전체 데이터) |
return 다이싱된 새로운 DataCube 인스턴스
원본 큐브의 데이터 소스와 동일한 스키마를 가지며,
지정된 모든 조건을 만족하는 데이터만 포함
See Also
slice단일 차원 필터링drillDown계층적 드릴다운aggregate다이싱된 데이터 집계pivot다이싱된 데이터 피벗
dispose(): null
객체를 해제하고 null을 반환한다.
사용 예: this._obj = this._obj.dispose();
return null
drillDown(fromDimension, toDimension): AggTable
차원 계층을 따라 드릴다운한다.
상위 차원에서 하위 차원으로 세부 수준을 낮춰 분석하는 OLAP 연산이다.
상위 수준의 요약 데이터에서 세부 수준으로 분해하며, 롤업의 역 연산이다.
| Parameter | Type | Description |
|---|---|---|
| fromDimension | string (opens in a new tab) | 현재(상위) 차원 이름 |
| toDimension | string (opens in a new tab) | 드릴다운할 하위 차원 이름 |
return 드릴다운된 집계 테이블
endUpdate(): void (opens in a new tab)
이벤트 발생을 재개한다.
beginUpdate() 호출 횟수만큼 endUpdate()를 호출해야 이벤트 발생이 재개된다.
중요: try-finally 블록의 finally에서 호출하여 예외 발생 시에도 실행되도록 해야 한다.
filter(expressionString): DataCube
Expression을 사용하여 필터링한다.
@realgrid/expression 라이브러리를 사용하여 복잡한 필터 조건을 적용한다.
지원하는 연산자
- 비교:
>,>=,<,<=,==,!= - 논리:
AND,OR,NOT - 문자열:
CONTAINS,STARTSWITH,ENDSWITH - IN:
IN (value1, value2, ...) - 범위:
BETWEEN min AND max - NULL:
IS NULL,IS NOT NULL
// 단순 비교
const cube1 = cube.filter('sales > 1000');
const cube2 = cube.filter('region == "Seoul"');
// 논리 연산
const cube3 = cube.filter('sales > 1000 AND region == "Seoul"');
const cube4 = cube.filter('product == "A" OR product == "B"');
// IN 연산자
const cube5 = cube.filter('region IN ("Seoul", "Busan", "Daegu")');
// 범위 필터
const cube6 = cube.filter('sales BETWEEN 1000 AND 5000');
// 문자열 패턴
const cube7 = cube.filter('productName CONTAINS "Phone"');
const cube8 = cube.filter('productCode STARTSWITH "P-"');
// NULL 체크
const cube9 = cube.filter('discount IS NOT NULL');
// 복잡한 조합
const cube10 = cube.filter(
'(region == "Seoul" OR region == "Busan") AND sales > 1000'
);| Parameter | Type | Description |
|---|---|---|
| expressionString | string (opens in a new tab) | Expression 문자열 |
return 필터링된 새로운 DataCube
filterAll(filters): void (opens in a new tab)
여러 Slicer 필터를 일괄 적용한다.
각 필터를 현재 큐브의 _filters Map에 저장하고, 등록된 AggTable 리스너들에게 알림을 보낸다.
// 직접 사용 (현재 큐브가 변경됨)
cube.filterAll([
{ dimension: 'region', type: 'list', values: ['Seoul', 'Busan'] },
{ dimension: 'sales', type: 'range', range: [1000, 5000] }
]);
// 필터 초기화
cube.filterAll([]);| Parameter | Type | Description |
|---|---|---|
| filters | Array (opens in a new tab)<CubeFilter> | 적용할 CubeFilter 배열 |
See Also
slice단일 차원 필터링 (새 DataCube 반환, immutable)dice다중 차원 필터링 (새 DataCube 반환, immutable)
filterHierarchical(dimensionName, paths): DataCube
계층적 필터링을 수행한다.
날짜(year/month/day)같은 계층 구조에서 상위 수준을 선택하면
하위 수준이 자동으로 포함된다.
// year/month/day 계층의 경우
// 2024년만 필터 → 2024년의 모든 월, 일 자동 포함
cube.filterHierarchical('order_date', [2024])
// 명시적으로 특정 월만 선택
cube.filterHierarchical('order_date', [['2024', '01'], ['2024', '02']])
// 여러 연도의 특정 월
cube.filterHierarchical('order_date', [
['2024', '01'],
['2025', '01']
])| Parameter | Type | Description |
|---|---|---|
| dimensionName | string (opens in a new tab) | 계층 구조가 있는 차원 이름 (e.g., 'order_date') |
| paths | Array (opens in a new tab)<any (opens in a new tab)> | 포함할 경로 배열 |
- 단일 값 배열: [2024, '01', '15'] 또는 [2024, '01']
- 이중 배열: [[2024, '01'], [2024, '02']] |
return 필터링된 새로운 DataCube 인스턴스
getActiveSlicerFilters(): Array (opens in a new tab)<CubeFilter>
활성화된 Slicer 필터 목록을 반환한다.
getApiFilters(): Map (opens in a new tab)<string (opens in a new tab), any (opens in a new tab)>
API 필터만 반환한다.
getCardinalities(): Record (opens in a new tab)<string (opens in a new tab), number (opens in a new tab)>
모든 차원의 카디날리티(고유값 개수)를 한 번에 반환한다.
피벗 UI에서 row/column 필드 배치 가능 여부를 판단할 때 유용하다.
카디날리티가 높은 차원을 축에 배치하면 UI 폭발이 발생할 수 있다.
const cards = cube.getCardinalities();
// { region: 3, product: 5, quarter: 2, customer_id: 100000 }
// 카디날리티 100 이하인 차원만 피벗 축에 배치 가능
const pivotableDims = Object.entries(cards)
.filter(([_, card]) => card <= 100)
.map(([name]) => name);
// ['region', 'product', 'quarter']return 차원 이름을 키로, 카디날리티를 값으로 하는 객체
getCardinality(dimensionName): number (opens in a new tab)
지정된 차원의 카디날리티(고유값 개수)를 반환한다.
Dictionary Encoding된 컬럼은 O(1)로 즉시 반환하며,
그렇지 않은 경우 전체 컬럼을 순회하여 계산한다.
주의: 필터가 적용된 경우에도 전체 데이터의 카디날리티를 반환한다.
필터링된 데이터의 카디날리티가 필요하면 getColumnValues(name).length를 사용한다.
const cube = new DataCube({source, schema});
// 지역 차원의 카디날리티
const regionCount = cube.getCardinality('region'); // 4 (O(1) - Dictionary 활용)
// 전체 차원의 카디날리티 확인
cube.schema.dimensions.forEach(dim => {
console.log(`${dim.name}: ${cube.getCardinality(dim.name)}`);
});| Parameter | Type | Description |
|---|---|---|
| dimensionName | string (opens in a new tab) | 차원 이름 |
return 고유값 개수
getChildDimensions(dimensionName): Array (opens in a new tab)<string (opens in a new tab)>
지정된 차원의 자식 차원들을 반환한다.
계층 관계를 탐색할 때 사용한다.
cube.getChildDimensions('order_date.year'); // ['order_date.month']
cube.getChildDimensions('order_date.month'); // ['order_date.day']| Parameter | Type | Description |
|---|---|---|
| dimensionName | string (opens in a new tab) | 차원 이름 |
return 자식 차원 이름 배열
getColumn(column): ColumnMeta
| Parameter | Type |
|---|---|
| column | string (opens in a new tab) |
getColumnType(column): CubeColumnDataType
| Parameter | Type |
|---|---|
| column | string (opens in a new tab) |
getColumnValues(columnName, filtered?): Array (opens in a new tab)<any (opens in a new tab)>
컬럼의 고유값 목록을 리턴한다.
dimension, measure 구분 없이 모든 컬럼에 대해 사용 가능하다.
반환값 특성
- 중복 제거된 고유값만 포함
- null/undefined 값은 필터링됨
- 정렬: 숫자는 오름차순, 문자는 사전순
- 원본 데이터 타입 유지
// 원본 전체 고유값
const regions = cube.getColumnValues('region');
// 필터 적용된 고유값
const filtered = cube.getColumnValues('region', true);| Parameter | Type | Description |
|---|---|---|
| columnName | string (opens in a new tab) | 컬럼 이름 |
| filtered | boolean (opens in a new tab) | true이면 slicer 필터가 적용된 데이터에서 추출, false(기본)이면 원본 전체에서 추출 |
return 고유값 배열 (정렬됨), 컬럼이 없으면 undefined
getDimensionRange(dimensionName): { max: any;min: any; }
지정된 차원의 수치 범위(최소값, 최대값)를 반환한다.
필터가 적용되어 있으면 필터링된 데이터 범위 내에서의 min/max를 반환한다.
수치형 차원에서만 의미있는 값을 반환하며, 문자형 차원에서는 null을 반환한다.
용도
- 범위 필터 UI의 슬라이더 최소/최대값 설정
- 데이터 분포 확인 (최소값, 최대값)
- 차트의 축(axis) 범위 설정
- 동적 범위 검증
반환값 특성
- 수치형 차원: {min: number (opens in a new tab), max: number (opens in a new tab)} 반환
- 문자형 차원: null 반환
- 빈 데이터: null 반환
- 필터된 범위 내에서 계산 (필터 자동 반영)
필터 적용
- 현재 큐브에 적용된 필터가 있으면 자동으로 반영
- 필터링된 데이터 범위 내에서만 min/max를 계산
const cube = new DataCube({source, schema});
// 판매량의 범위 확인
const salesRange = cube.getDimensionRange('sales');
// 반환: { min: 100, max: 50000 }
// 슬라이더 UI 구성
const range = cube.getDimensionRange('quantity');
if (range) {
setupSlider({
min: range.min,
max: range.max,
step: 10
});
}
// 필터 후 범위 확인
const filtered = cube.slice('region', 'Seoul');
const seoulRange = filtered.getDimensionRange('sales');
// 반환: Seoul 지역의 판매량 범위| Parameter | Type | Description |
|---|---|---|
| dimensionName | string (opens in a new tab) | 차원 이름 (수치형이어야 함) |
return 범위 객체 {min: number (opens in a new tab), max: number (opens in a new tab)} 또는 null
- 수치형 차원: 범위 객체
- 문자형/날짜형 차원: null
- 빈 데이터: null
getDimensions(): Array<{ name: string;parentDimension: string;type: string; }>
큐브의 모든 차원(dimension) 정보를 반환한다.
각 차원의 이름, 타입, 상위 차원(parentDimension) 정보를 포함한다.
UI에서 계층 구조를 표시할 때 사용한다.
const dimensions = cube.getDimensions();
// [
// { name: 'order_date.year', type: 'i32', parentDimension: undefined },
// { name: 'order_date.month', type: 'str', parentDimension: 'order_date.year' },
// { name: 'order_date.day', type: 'str', parentDimension: 'order_date.month' },
// { name: 'region', type: 'str', parentDimension: undefined }
// ]return 차원 정보 배열
getFilters(): Map (opens in a new tab)<string (opens in a new tab), any (opens in a new tab)>
현재 적용된 필터들을 반환한다 (API + Slicer 병합).
return 필터 맵 (필터명/dimension → 값/수식)
getMeasure(name): MeasureMeta
| Parameter | Type |
|---|---|
| name | string (opens in a new tab) |
getMeasures(): Array (opens in a new tab)<MeasureMeta>
현재 등록된 모든 measure 목록을 반환한다.
const measures = cube.getMeasures();
console.log(measures.map(m => m.name)); // ['sales', 'quantity', 'cost']return measure 필드 목록
getMeasuresAndMetrics(): Array<{ aggregate: string;name: string;role: 'measure' | 'metric';source: string | (row: any) => any;type: 'i32' | 'f64' | 'i64' | 'str' | 'date'; }>
현재 등록된 모든 measure와 metric 목록을 반환한다.
UI에서 동적으로 필드 목록을 관리할 때 유용하다.
const fields = cube.getMeasuresAndMetrics();
// [
// { name: 'sales', type: 'f64', role: 'measure', aggregate: 'sum', source: 'sales' },
// { name: 'quantity', type: 'i32', role: 'measure', aggregate: 'sum', source: 'qty' },
// { name: 'margin', type: 'f64', role: 'metric', source: 'profit / sales * 100' },
// ]
// UI에서 드롭다운 구성
const options = fields.map(f => ({ label: f.name, value: f.name }));return measure와 metric 필드 목록
- name: 필드 이름
- type: 필드 타입 ('f64', 'i32')
- role: 'measure' 또는 'metric'
- aggregate?: measure의 경우 집계 함수 (metric은 undefined)
- source?: expression 또는 source 컬럼명
getMemoryUsage(): { aggTables: number;source: number;total: number; }
큐브의 메모리 사용량 정보를 반환한다.
큐브가 사용 중인 전체 메모리를 세 부분으로 나누어 추정한다:
- source: 원본 데이터(ColumnStore/DataViewSource)의 메모리
- aggTables: 캐시된 집계 테이블들의 총 메모리
- pivotMatrices: 캐시된 피벗 매트릭스들의 총 메모리
메모리 추정 방식
1. 데이터 소스 메모리 (source)
데이터 소스의 메모리는 각 컬럼의 데이터 타입을 기반으로 추정된다:
- 숫자형 (i32, f64): 행 개수 × 바이트/값
- i32: 4바이트/값
- f64: 8바이트/값
- 문자열 (str): 행 개수 × (평균 문자열 길이 × 2 + 메타데이터)
- 평균 문자열 길이 추정: 50자
- 메타데이터 오버헤드: 16바이트/값
- 객체 오버헤드: 약 1KB
2. 집계 테이블 메모리 (aggTables)
각 캐시된 AggTable의 메모리는 차원 컬럼과 측정값 컬럼의 합으로 계산된다:
- 행 개수 × (컬럼별 바이트 크기)
- AggTable 객체 오버헤드: 약 2KB
예시:
차원 2개 (문자열) + 측정값 3개 (숫자)
100행의 경우:
- 문자열 차원: 100 × (30 × 2 + 16) × 2 = 15,200바이트
- 숫자 측정값: 100 × 8 × 3 = 2,400바이트
- 오버헤드: 2,048바이트
- 합계: 약 19.6KB3. 피벗 매트릭스 메모리 (pivotMatrices)
피벗 매트릭스의 메모리는 레이블과 셀 데이터로 구성된다:
- 행 레이블: 행 개수 × 레이블당 평균 바이트
- 열 레이블: 열 개수 × 레이블당 평균 바이트
- 셀 데이터: 행 개수 × 열 개수 × 8바이트 (Float64Array)
- 객체 오버헤드: 약 2KB
예시:
5행(지역) × 4열(분기)의 피벗:
- 행 레이블: 5 × (10 × 2 + 16) = 180바이트
- 열 레이블: 4 × (5 × 2 + 16) = 104바이트
- 셀 데이터: 5 × 4 × 8 = 160바이트
- 오버헤드: 2,048바이트
- 합계: 약 2.5KB메모리 최적화 팁
1. 불필요한 캐시 제거
const cube = new DataCube();
cube.setSource(dataViewSource);
// 여러 집계 작업 수행
const agg1 = cube.aggregate(['region'], ['sales']);
const agg2 = cube.aggregate(['product'], ['sales']);
const agg3 = cube.aggregate(['date'], ['sales']);
// 캐시 메모리 확인
const usage = cube.getMemoryUsage();
console.log(`전체 메모리: ${(usage.total / 1024 / 1024).toFixed(2)}MB`);
// 캐시 제거하여 메모리 절약
cube.clearCache();
const usageAfter = cube.getMemoryUsage();
console.log(`캐시 제거 후: ${(usageAfter.total / 1024 / 1024).toFixed(2)}MB`);2. 데이터 소스 크기 최소화
// 방법 1: DataViewSource 사용 (복사 없음)
const viewSource = new DataViewSource(columns, dataView);
cube1.setSource(viewSource); // 메모리 효율적
// 방법 2: ColumnStore 사용 (복사 + 최적화)
const columnStore = new ColumnStore(columns, dataFrame);
cube2.setSource(columnStore); // 더 많은 메모리 사용3. 필터링으로 데이터 크기 줄이기
const cube = new DataCube();
cube.setSource(dataViewSource);
// 전체 메모리
const fullUsage = cube.getMemoryUsage();
console.log(`전체: ${fullUsage.total}바이트`);
// 필터링된 큐브 (더 적은 메모리)
const filtered = cube.slice('region', 'Seoul');
const filteredUsage = filtered.getMemoryUsage();
console.log(`필터링: ${filteredUsage.total}바이트`);메모리 모니터링 예시
const cube = new DataCube();
cube.setSource(dataViewSource);
// 초기 메모리
console.log('=== 메모리 사용량 ===');
let usage = cube.getMemoryUsage();
console.log(`소스: ${(usage.source / 1024).toFixed(2)}KB`);
console.log(`집계: ${(usage.aggTables / 1024).toFixed(2)}KB`);
console.log(`피벗: ${(usage.pivotMatrices / 1024).toFixed(2)}KB`);
console.log(`총계: ${(usage.total / 1024).toFixed(2)}KB`);
// 집계 추가
cube.aggregate(['region'], ['sales']);
usage = cube.getMemoryUsage();
console.log(`
n집계 후: ${(usage.total / 1024).toFixed(2)}KB`);
// 피벗 추가
cube.pivot(['region'], ['quarter'], 'sales');
usage = cube.getMemoryUsage();
console.log(`피벗 후: ${(usage.total / 1024).toFixed(2)}KB`);주의사항
- 메모리 사용량은 추정값이며 JavaScript 엔진의 최적화에 따라 실제 값과 다를 수 있다.
- 문자열 길이는 평균값(50자, 30자)으로 추정되므로 실제 데이터와 차이가 날 수 있다.
- 동적으로 캐시가 추가될 때마다 메모리 사용량이 증가한다.
- 대용량 데이터의 경우 정기적으로
clearCache()를 호출하여 메모리를 관리하는 것을 권장한다.
// 기본 사용
const cube = new DataCube();
cube.setSource(dataViewSource);
const usage = cube.getMemoryUsage();
console.log(`메모리 사용량: ${usage.total} 바이트`);
console.log(`소스: ${usage.source} 바이트`);
console.log(`집계: ${usage.aggTables} 바이트`);
console.log(`피벗: ${usage.pivotMatrices} 바이트`);// 메모리 단위 변환
const cube = new DataCube();
cube.setSource(dataViewSource);
const usage = cube.getMemoryUsage();
// 바이트 → KB
const totalKB = usage.total / 1024;
console.log(`총 메모리: ${totalKB.toFixed(2)}KB`);
// 바이트 → MB
const totalMB = usage.total / 1024 / 1024;
console.log(`총 메모리: ${totalMB.toFixed(2)}MB`);// 메모리 성장 추적
const cube = new DataCube();
cube.setSource(dataViewSource);
const memoryLog: { step: string; memory: number }[] = [];
// 초기 상태
memoryLog.push({
step: '초기',
memory: cube.getMemoryUsage().total
});
// 첫 번째 집계
cube.aggregate(['region'], ['sales']);
memoryLog.push({
step: '집계1(region)',
memory: cube.getMemoryUsage().total
});
// 두 번째 집계
cube.aggregate(['product'], ['sales']);
memoryLog.push({
step: '집계2(product)',
memory: cube.getMemoryUsage().total
});
// 피벗
cube.pivot(['region'], ['product'], 'sales');
memoryLog.push({
step: '피벗(region×product)',
memory: cube.getMemoryUsage().total
});
// 메모리 성장 확인
for (const log of memoryLog) {
console.log(`${log.step}: ${(log.memory / 1024).toFixed(2)}KB`);
}// 메모리 상한선 모니터링
const cube = new DataCube();
cube.setSource(dataViewSource);
const MAX_MEMORY_MB = 100; // 100MB 상한선
// 여러 집계 작업
const dimensions = [
['region'],
['product'],
['quarter'],
['region', 'product'],
['product', 'quarter'],
['region', 'quarter']
];
for (const dims of dimensions) {
cube.aggregate(dims, ['sales', 'quantity']);
const usage = cube.getMemoryUsage();
const usageMB = usage.total / 1024 / 1024;
console.log(`${dims.join('×')}: ${usageMB.toFixed(2)}MB`);
// 상한선 초과 시 캐시 제거
if (usageMB > MAX_MEMORY_MB) {
console.log('경고: 메모리 상한선 초과! 캐시를 제거합니다.');
cube.clearCache();
}
}return 메모리 사용량 정보 객체 (단위: 바이트)
- source: 원본 데이터 소스 메모리
- aggTables: 캐시된 집계 테이블 메모리 (모든 테이블의 합)
- pivotMatrices: 캐시된 피벗 매트릭스 메모리 (모든 매트릭스의 합)
- total: 전체 메모리 (source + aggTables + pivotMatrices)
See Also
clearCache캐시 제거하여 메모리 해제aggregate집계 테이블 생성pivot피벗 매트릭스 생성
getMetrics(): Array (opens in a new tab)<ColumnMeta>
현재 등록된 모든 metric 목록을 반환한다.
const metrics = cube.getMetrics();
console.log(metrics.map(m => m.name)); // ['profit_margin', 'roi']return metric 필드 목록
getParentDimension(dimensionName): string (opens in a new tab)
지정된 차원의 상위 차원을 반환한다.
계층 관계를 탐색할 때 사용한다.
cube.getParentDimension('order_date.month'); // 'order_date.year'
cube.getParentDimension('order_date.year'); // undefined| Parameter | Type | Description |
|---|---|---|
| dimensionName | string (opens in a new tab) | 차원 이름 |
return 상위 차원 이름 또는 undefined
getRow(row, filtered?): Array (opens in a new tab)<any (opens in a new tab)>
지정한 행의 값을 큐브 컬럼(dimensions + measures) 순서로 반환한다.
원본 데이터소스의 전체 필드가 아닌, 큐브 스키마에 정의된 컬럼들만 포함한다.
수식 컬럼(expression source)의 계산된 값도 포함된다.
metric 컬럼은 집계 후 계산되는 파생값이므로 데이터소스에 존재하지 않아 undefined를 반환한다.
| Parameter | Type | Description |
|---|---|---|
| row | number (opens in a new tab) | 행 인덱스 |
| filtered | boolean (opens in a new tab) | true이면 필터 적용된 데이터에서 조회, false이면 원본 전체 데이터에서 조회 (기본값: true) |
return 큐브 컬럼 순서의 값 배열. 범위를 벗어나면 undefined
getRows(start?, count?, filtered?): Array (opens in a new tab)<Array (opens in a new tab)<any (opens in a new tab)>>
지정한 범위의 행들을 큐브 컬럼(dimensions + measures) 순서로 반환한다.
원본 데이터소스의 전체 필드가 아닌, 큐브 스키마에 정의된 컬럼들만 포함한다.
수식 컬럼(expression source)의 계산된 값도 포함된다.
metric 컬럼은 집계 후 계산되는 파생값이므로 데이터소스에 존재하지 않아 undefined를 반환한다.
| Parameter | Type | Description |
|---|---|---|
| start | number (opens in a new tab) | 시작 행 인덱스 (기본값: 0) |
| count | number (opens in a new tab) | 가져올 행 수 (기본값: 전체) |
| filtered | boolean (opens in a new tab) | true이면 필터 적용된 데이터에서 조회, false이면 원본 전체 데이터에서 조회 (기본값: true) |
return 행 배열. 범위를 벗어나면 빈 배열
getSchema(): { dimensions: Array<DimensionMeta>;measures: Array<MeasureMeta>;metrics: Array<ColumnMeta>; }
현재 DataCube의 전체 스키마 정보를 반환한다.
addMeasure/addMetric으로 동적으로 추가된 필드도 포함한다.
cube.addMeasure('custom_cost', { source: 'cost * qty' });
cube.addMetric('custom_roi', 'profit / investment');
const schema = cube.getSchema();
// {
// dimensions: [{ name: 'region', type: 'str' }, ...],
// measures: [
// { name: 'sales', type: 'f64', aggregate: 'sum' },
// { name: 'custom_cost', type: 'f64', aggregate: 'sum' }, // 동적 추가됨
// ...
// ],
// metrics: [
// { name: 'margin', source: 'profit / sales * 100', type: 'f64' },
// { name: 'custom_roi', source: '...' }, // 동적 추가됨
// ...
// ]
// }return 스키마 정보 객체
- dimensions: 모든 차원 필드 목록
- measures: 모든 measure 필드 목록 (동적 추가된 measure 포함)
- metrics: 모든 metric 필드 목록
getSlicer(name): Slicer
Slicer를 가져온다.
| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | Slicer 이름 |
getSlicerFilters(): Map (opens in a new tab)<string (opens in a new tab), any (opens in a new tab)>
Slicer 필터만 반환한다.
getSlicers(): Map (opens in a new tab)<string (opens in a new tab), Slicer>
모든 Slicer를 가져온다.
getSourceRowIndex(row, filtered?): number (opens in a new tab)
행 인덱스에 대응하는 원본 소스의 행 인덱스를 반환한다.
filtered=true이면 필터 적용된 뷰 기준, false이면 원본 소스 기준으로 매핑한다.
필터가 없으면 동일한 인덱스를 반환한다.
| Parameter | Type | Description |
|---|---|---|
| row | number (opens in a new tab) | 행 인덱스 |
| filtered | boolean (opens in a new tab) | true이면 필터 적용된 데이터 기준, false이면 원본 전체 데이터 기준 (기본값: true) |
return 원본 소스의 행 인덱스. 범위를 벗어나면 -1
getSourceRowIndices(start?, count?, filtered?): Array (opens in a new tab)<number (opens in a new tab)>
행 인덱스 범위에 대응하는 원본 소스의 행 인덱스 배열을 반환한다.
filtered=true이면 필터 적용된 뷰 기준, false이면 원본 소스 기준으로 매핑한다.
필터가 없으면 start부터 순차적인 인덱스 배열을 반환한다.
| Parameter | Type | Description |
|---|---|---|
| start | number (opens in a new tab) | 시작 행 인덱스 (기본값: 0) |
| count | number (opens in a new tab) | 가져올 행 수 (기본값: 전체) |
| filtered | boolean (opens in a new tab) | true이면 필터 적용된 데이터 기준, false이면 원본 전체 데이터 기준 (기본값: true) |
return 원본 소스의 행 인덱스 배열. 범위를 벗어나면 빈 배열
getValue(row, col, filtered?): any (opens in a new tab)
지정한 행과 컬럼의 단일 값을 반환한다.
원본 데이터소스의 전체 필드가 아닌, 큐브 스키마에 정의된 컬럼 기준으로 조회한다.
metric 컬럼은 집계 후 계산되는 파생값이므로 데이터소스에 존재하지 않아 undefined를 반환한다.
| Parameter | Type | Description |
|---|---|---|
| row | number (opens in a new tab) | 행 인덱스 |
| col | string (opens in a new tab) | number (opens in a new tab) | 큐브 컬럼 인덱스 또는 컬럼 이름 |
| filtered | boolean (opens in a new tab) | true이면 필터 적용된 데이터에서 조회, false이면 원본 전체 데이터에서 조회 (기본값: true) |
return 셀 값. 범위를 벗어나면 undefined
hasColumn(name): boolean (opens in a new tab)
지정된 이름이 DataCube에 존재하는지 검사한다.
| Parameter | Type |
|---|---|
| name | string (opens in a new tab) |
isMetric(name): boolean (opens in a new tab)
| Parameter | Type |
|---|---|
| name | string (opens in a new tab) |
isUpdating(): boolean (opens in a new tab)
현재 이벤트 발생이 중단된 상태인지 확인한다.
return 이벤트 발생이 중단된 상태이면 true, 그렇지 않으면 false
off(eventName, handler): boolean (opens in a new tab)
특정 이벤트에서 핸들러를 제거한다.
const handler = (sender, row) => console.log(row);
table.on('onRowUpdated', handler);
table.off('onRowUpdated', handler);| Parameter | Type | Description |
|---|---|---|
| eventName | never | 이벤트 이름 |
| handler | any (opens in a new tab) | 제거할 이벤트 핸들러 함수 |
return 제거 성공 여부
on(eventName, handler): void (opens in a new tab)
특정 이벤트에 핸들러를 등록한다.
addEventListener()보다 간편한 방식으로 개별 이벤트 핸들러를 등록할 수 있다.
table.on('onRowUpdated', (sender, row, oldValues) => {
console.log('Row updated:', row);
});| Parameter | Type | Description |
|---|---|---|
| eventName | never | 이벤트 이름 |
| handler | any (opens in a new tab) | 이벤트 핸들러 함수 |
onChange(callback): () => void (opens in a new tab)
필터 변경 콜백을 등록한다.
addFilter(), removeFilter(), filterAll() 등으로 필터가 변경될 때 호출된다.
반환된 함수를 호출하면 콜백이 해제된다.
const unsub = cube.onChange(() => {
console.log('filters changed:', cube.getFilters());
});
// 해제
unsub();| Parameter | Type | Description |
|---|---|---|
| callback | () => void | 필터 변경 시 호출될 콜백 함수 |
return 콜백 해제 함수
refreshSlicers(): void (opens in a new tab)
모든 Slicer를 새로고침한다.
registerListener(listener): void (opens in a new tab)
| Parameter | Type |
|---|---|
| listener | any (opens in a new tab) |
removeFilter(name): void (opens in a new tab)
필터를 제거한다.
API 필터에서 제거한다. Slicer 필터에서도 해당 키를 찾아 제거한다.
| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 제거할 필터명 (dimension 이름 또는 expression 필터 이름) |
removeListener(listener): boolean (opens in a new tab)
이벤트 리스너를 제거한다.
| Parameter | Type | Description |
|---|---|---|
| listener | any (opens in a new tab) | 이벤트 리스너 |
return 제거 성공 여부
removeMeasure(name): this
등록된 measure를 제거한다.
measure 제거 후 관련 캐시는 자동으로 무효화된다.
cube.removeMeasure('old_measure');
cube.removeMeasure('cost')
.removeMeasure('expense');| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 제거할 measure 이름 |
return this (메서드 체이닝 가능)
removeMetric(name): this
등록된 metric을 제거한다.
metric 제거 후 관련 캐시는 자동으로 무효화된다.
cube.removeMetric('profit_margin');
cube.removeMetric('roi')
.removeMetric('margin_pct');| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 제거할 metric 이름 |
return this (메서드 체이닝 가능)
removeSlicer(name): boolean (opens in a new tab)
Slicer를 제거한다.
| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 제거할 Slicer 이름 |
return 제거 성공 여부
resetSlicers(): void (opens in a new tab)
모든 Slicer를 초기화한다.
resetUpdate(): void (opens in a new tab)
이벤트 락 카운터를 강제로 0으로 리셋한다.
경고: 이 메서드는 예외 처리 누락 등으로 인해 이벤트 락이 영구적으로 걸린 경우에만 사용해야 한다. 정상적인 흐름에서는 beginUpdate()/endUpdate()를 올바르게 사용해야 한다.
// 디버깅이나 에러 복구 시
if (table.isUpdating()) {
console.warn('Event lock is stuck, resetting...');
table.resetUpdate();
}restoreSlicerState(state): void (opens in a new tab)
저장된 Slicer 상태를 복원한다.
| Parameter | Type | Description |
|---|---|---|
| state | Record (opens in a new tab)<string (opens in a new tab), any (opens in a new tab)> | saveSlicerState()로 저장한 상태 객체 |
rollUp(fromDimension, toDimension): AggTable
차원 계층을 따라 롤업한다.
하위 차원에서 상위 차원으로 세부 수준을 높여 요약하는 OLAP 연산이다.
상세한 데이터를 더 높은 수준의 집계로 변환하며, 드릴다운의 역 연산이다.
개념
롤업(Roll-up)은 세부 차원에서 상위 차원으로 이동하면서 데이터를 요약하는 OLAP 연산이다.
예를 들어, 일(Day) 단위의 상세 데이터를 월(Month)로 요약하거나,
월(Month)의 데이터를 분기(Quarter)로 요약하는 방식으로 동작한다.
계층 구조 예시
시간 계층: 조직 계층: 지역 계층:
년도 회사 국가
↑ ↑ ↑
분기 부서 대륙
↑ ↑ ↑
월 팀 국가
↑ ↑ ↑
일 개인 지역
롤업 방향: 아래에서 위로 (세부 → 요약)동작 방식
- fromDimension에서 현재 집계된 데이터 조회
- toDimension이 fromDimension의 상위 계층임을 확인
- fromDimension의 각 행을 toDimension으로 매핑
- 동일한 상위 차원 값을 가진 행들을 집계
- 측정값을 해당 집계 함수로 병합 (합계, 평균 등)
- 롤업된 결과를 AggTable로 반환
drillDown()과의 비교
| 기능 | drillDown() | rollUp() |
|---|---|---|
| 방향 | 위 → 아래 | 아래 → 위 |
| 차원 이동 | 상위 → 하위 | 하위 → 상위 |
| 세부도 | 증가 (세부화) | 감소 (요약) |
| 행 개수 | 증가 | 감소 |
| 측정값 | 분해/조회 | 병합/요약 |
| 사용 사례 | 상세 분석 드릴다운 | 상위 수준 보고서 생성 |
계층 검증 규칙
// 유효한 롤업 (fromDimension이 toDimension의 하위 계층)
rollUp('date', 'month') // 일 → 월 ✓
rollUp('month', 'quarter') // 월 → 분기 ✓
rollUp('quarter', 'year') // 분기 → 연도 ✓
// 무효한 롤업
rollUp('month', 'date') // 상위 → 하위 ✗ (드릴다운이어야 함)
rollUp('date', 'date') // 동일 차원 ✗
rollUp('date', 'region') // 관계없는 차원 ✗ (계층 관계 없음)측정값 병합 전략
// 날짜별 판매량 데이터
2024-01-01: 100 판매량
2024-01-02: 150 판매량
2024-01-03: 120 판매량
// 월로 롤업 시 (Sum 집계)
2024-01: 370 판매량 (100+150+120)
// 월로 롤업 시 (Average 집계)
2024-01: 123.33 판매량 (평균값)성능 고려사항
- 비용: 하위 차원의 모든 행을 읽고 상위 차원으로 그룹화
- 최적화: 이미 하위 차원 집계가 있으면 재사용 가능
- 메모리: 결과 행 개수가 현저히 감소하므로 메모리 효율적
- 권장: 대시보드 요약이나 경영진 보고서 생성 시 활용
// 기본 사용: 일 → 월로 롤업
const cube = new DataCube();
cube.setSource(dataViewSource);
// 먼저 일별로 집계
const dayAgg = cube.aggregate(['date'], ['sales', 'quantity']);
// 결과: 2024-01-01, 2024-01-02, 2024-01-03, ... (상세)
// 월로 롤업하여 요약
const monthAgg = cube.rollUp('date', 'month');
// 결과: 2024-01, 2024-02, ... (요약)// 다단계 롤업: 일 → 월 → 분기 → 연도
const cube = new DataCube();
cube.setSource(dataViewSource);
// Step 1: 일별 판매 데이터
const dayData = cube.aggregate(['date'], ['sales']);
// Step 2: 월로 롤업
const monthData = cube.rollUp('date', 'month');
// Step 3: 분기로 롤업
const quarterData = cube.rollUp('month', 'quarter');
// Step 4: 연도로 롤업 (최상위 요약)
const yearData = cube.rollUp('quarter', 'year');
// 결과: 2024년의 총합 1건 (가장 요약된 형태)// 조직 계층 롤업: 개인 → 팀 → 부서 → 회사
const cube = new DataCube();
cube.setSource(dataViewSource);
// 직원별 목표 달성률
const employeePerf = cube.aggregate(['employee'], ['target', 'actual']);
// 결과: 100명의 직원 데이터
// 팀 단위로 롤업 (평균 달성률)
const teamPerf = cube.rollUp('employee', 'team');
// 결과: 10개 팀의 평균 성과
// 부서 단위로 롤업
const deptPerf = cube.rollUp('team', 'department');
// 결과: 3개 부서의 평균 성과
// 회사 전체 성과
const companyPerf = cube.rollUp('department', 'company');
// 결과: 회사 전체 1개 행 (최종 요약)// 지역 계층 롤업: 지점 → 지역 → 대구역 → 전국
const cube = new DataCube();
cube.setSource(dataViewSource);
// 지점별 매출
const storeData = cube.aggregate(['store'], ['sales']);
// 결과: 50개 지점
// 지역 단위로 롤업
const regionData = cube.rollUp('store', 'region');
// 결과: 8개 지역
// 대구역 단위로 롤업
const areaData = cube.rollUp('region', 'largeArea');
// 결과: 3개 대구역 (서부, 중부, 동부)
// 전국 단위 (최종)
const nationalData = cube.rollUp('largeArea', 'country');
// 결과: 전국 1개 행// drillDown()과의 비교
const cube = new DataCube();
cube.setSource(dataViewSource);
// 연도 데이터에서 시작
const yearData = cube.aggregate(['year'], ['sales']);
// 2024년 총 매출: 1,000,000
// 방법 1: drillDown으로 분기 단위로 드릴다운
const quarterData = cube.drillDown('year', 'quarter');
// Q1: 200,000 / Q2: 300,000 / Q3: 250,000 / Q4: 250,000
// 방법 2: 분기에서 다시 month로 드릴다운
const monthData = cube.drillDown('quarter', 'month');
// Jan: 50,000 / Feb: 60,000 / ...
// 역방향: rollUp으로 월에서 분기로 롤업
const backToQuarter = cube.rollUp('month', 'quarter');
// Q1: 200,000 (다시 집계)// 피벗과 함께 사용
const cube = new DataCube();
cube.setSource(dataViewSource);
// 일별 제품별 판매 데이터
const dailyData = cube.aggregate(['date', 'product'], ['sales']);
// 월로 롤업하여 월별 제품별 판매
const monthlyByProduct = cube.rollUp('date', 'month')
.then(agg => {
// 월별 제품별 피벗 생성
return cube.pivot(['month'], ['product'], 'sales');
});
// 결과: 월(행) x 제품(열) 의 크로스탭
// 형태: 12행 (월) x 5열 (제품) 매트릭스| Parameter | Type | Description |
|---|---|---|
| fromDimension | string (opens in a new tab) | 현재(하위) 차원 이름 |
| 세부 수준의 차원 (예: 'date', 'month') | ||
| toDimension | string (opens in a new tab) | 롤업할 상위 차원 이름 |
| 더 높은 수준의 차원 (예: 'month', 'year') | ||
| fromDimension의 상위 계층이어야 함 |
return 롤업된 집계 테이블
toDimension 차원으로 그룹화된 데이터, 행 개수 감소
See Also
drillDown역 연산 - 상위에서 하위로 드릴다운aggregate기본 집계 연산pivot롤업 결과의 피벗 처리
saveSlicerState(): Record (opens in a new tab)<string (opens in a new tab), any (opens in a new tab)>
Slicer 상태를 JSON으로 저장한다.
slice(dimensionName, value): DataCube
| Parameter | Type |
|---|---|
| dimensionName | string (opens in a new tab) |
| value | any (opens in a new tab) |
unregisterListener(listener): void (opens in a new tab)
| Parameter | Type |
|---|---|
| listener | any (opens in a new tab) |
updateFilter(name, value): void (opens in a new tab)
필터 값을 업데이트한다.
| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 필터명 (dimension 이름 또는 expression 필터 이름) |
| value | any (opens in a new tab) | 새로운 값 또는 수식 |
updateMeasure(name, options?): this
등록된 measure를 수정한다.
measure의 source, aggregate, type 등을 변경할 수 있다.
필드명 변경은 불가능하며 (removeMeasure + addMeasure 사용),
source나 aggregate 변경 시에만 캐시가 초기화된다.
// 집계 함수 변경
cube.updateMeasure('sales', { aggregate: 'avg' });
// Source 변경
cube.updateMeasure('cost', { source: 'actual_cost * rate' });
// 타입 변경
cube.updateMeasure('count', { type: 'i32' });
// 여러 속성 동시 변경
cube.updateMeasure('total', {
source: 'amount * quantity',
aggregate: 'sum',
type: 'f64'
});| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 수정할 measure 이름 |
| options | { aggregate: 'sum' | 'avg' | 'min' | 'max' | 'count' | 'distinct' | 'product' | 'stdev' | 'stdevp' | 'var' | 'varp' | 'first' | 'last' | 'p25' | 'p50' | 'p75';source: string | (row: any) => any;type: 'i32' | 'f64'; } | 수정할 옵션 |
- source?: 새로운 source (컬럼명 또는 계산식)
- aggregate?: 새로운 집계 함수
- type?: 새로운 타입 ('f64' | 'i32') |
return this (메서드 체이닝 가능)
updateMetric(name, expression): this
등록된 metric을 수정한다.
metric의 계산식을 변경할 수 있다.
계산식 변경 시 캐시가 초기화된다.
// 계산식 수정 (버그 수정)
cube.updateMetric('margin', 'profit / sales * 100');
// 다른 measure 참조로 변경
cube.updateMetric('ratio', 'revenue / cost');
// 메서드 체이닝
cube.updateMetric('m1', 'a + b')
.updateMetric('m2', 'a * b');| Parameter | Type | Description |
|---|---|---|
| name | string (opens in a new tab) | 수정할 metric 이름 |
| expression | string (opens in a new tab) | 새로운 계산식 (measure/metric 이름 참조) |
return this (메서드 체이닝 가능)
static generateAggregateKey(dimensionNames, measureColumns, userKey?): string (opens in a new tab)
집계 캐시 키를 생성한다.
차원과 측정값 조합을 문자열로 표현하여 캐시 키로 사용한다.
userKey가 주어지면 동일한 차원/측정값 조합이라도 논리적으로 분리된 캐시 키를 생성한다.
| Parameter | Type | Description |
|---|---|---|
| dimensionNames | Array (opens in a new tab)<string (opens in a new tab)> | 차원 이름 배열 |
| measureColumns | Array (opens in a new tab)<ColumnMeta> | 측정값 컬럼 메타데이터 배열 |
| userKey | string (opens in a new tab) | (선택) 캐시를 논리적으로 분리하기 위한 사용자 지정 키 |
return 캐시 키 (형식: agg[@userKey]:dim1|dim2|...:measure1:aggregate1|measure2:aggregate2|...)