컴포넌트/그리드

PivotGrid (피벗 그리드)

VanillaFront 2026. 9. 29. 23:10

 

Va.PivotGrid — 크로스탭 집계 그리드 컴포넌트

평평한 원본 데이터를 축(row/column/value)으로 교차 집계하여 표 형태로 표시하는 피벗 그리드 컴포넌트입니다. Excel 피벗 테이블과 유사한 구조로, 행 축·열 축·값 축을 컬럼에 지정하기만 하면 프레임워크가 자동으로 컬럼을 확장하고 집계값을 계산합니다. Va.Grid를 상속받아 가상 스크롤, 정렬, 고정 컬럼, 다단 헤더, 다중 선택 등 그리드의 모든 기능을 그대로 계승합니다.


클래스 정보

  • Va.PivotGrid extends Va.Grid
  • tagName: 'pivotGrid'
  • isContainer: false
  • 파일: va_pivotgrid.js

기본 사용법

{
    ref: 'refPivotGrid',
    tagName: 'pivotGrid',
    style: { flex: 1, width: '100%' },
    columns: [
        { key: 'itemId',   title: '품목코드', position: 'left',  width: 200, fixed: true },
        { key: 'itemName', title: '품명',     position: 'left',  width: 100, fixed: true },
        { key: 'year',     title: '연도',     position: 'top',   width: 100, pivotSummaryType: 'sum' },
        { key: 'quater',   title: '분기',     position: 'top',   width: 100 },
        { key: 'amt',      title: '금액',     position: 'value', width: 100, pivotSummaryType: 'sum' }
    ]
}

데이터 세팅 (mounted 후):

this.getRef('refPivotGrid').setData([
    { itemId: 'A001', itemName: '노트북', year: 2023, quater: 1, amt: 1500 },
    { itemId: 'A001', itemName: '노트북', year: 2023, quater: 2, amt: 2200 },
    { itemId: 'A001', itemName: '노트북', year: 2024, quater: 1, amt: 1800 },
    { itemId: 'B001', itemName: '마우스', year: 2023, quater: 1, amt:  300 },
    { itemId: 'B001', itemName: '마우스', year: 2024, quater: 1, amt:  450 }
]);

원본은 평평한 배열을 넘기면, 피벗 그리드가 내부에서 자동으로 교차 집계된 컬럼과 셀 값을 만들어냅니다.


피벗 축의 개념 (position)

PivotGrid의 핵심은 컬럼마다 position을 지정하는 것입니다. 3가지 축 중 하나로 분류됩니다.

position의미배치 위치

'left' 행 축 (row group) — 세로로 그룹화할 기준 좌측 고정 영역
'top' 열 축 (column group) — 가로로 확장할 기준 상단 헤더가 값별로 확장됨
'value' 값 축 (measure) — 집계 대상 교차 셀에 집계값 표시
(미지정) 표시 안 함 원본에는 있지만 피벗에 미사용

예시 흐름 — 위 데이터를 위 설정으로 렌더링하면:

  • 왼쪽: itemId, itemName 열이 세로로 그룹화됨
  • 상단 헤더: year × quater 조합으로 컬럼이 자동 생성 (예: 2023-1분기, 2023-2분기, 2024-1분기 ...)
  • 교차 셀: 각 조합별 amt 합계

핸들러는 반드시 문자열 참조 (JSON 원칙)

VanillaFront는 화면 정의를 JSON으로 유지하는 철학입니다. 모든 이벤트/렌더 핸들러는 문자열로 View 메서드 이름을 지정합니다.

{ onRowClick: 'onRowClick', onRender: 'onValueRender' }

인라인 function 사용 금지 — JSON 직렬화/에디터/저장 불가.


컬럼 정의 (columns)

각 컬럼 객체의 속성:

속성타입설명

key String 데이터 필드명 (필수)
title String 헤더 텍스트 (필수)
position String 피벗 축 지정 — 'left', 'top', 'value' (핵심)
width Number 고정 폭 (px)
align String 'left', 'center', 'right'
type String 'number', 'date' 등 (value 컬럼일 때 셀 포맷팅)
decimalPoint Number 소수점 자릿수 (value 컬럼)
pivotSummaryType String 집계 방식 — 'sum', 'avg', 'min', 'max' (value 및 top 축)
pivotGroupSummary Boolean 행 그룹별 소계 행 표시 (left 축에 지정)
fixed Boolean 좌측 고정 (좌측 축은 대개 fixed 권장)

주요 PivotGrid 속성

속성타입기본값설명

columns Array — 컬럼 정의 (필수, position 지정)
data Array [] 평평한 원본 데이터 (자동 피벗 변환)
fakeData Array — 에디터 미리보기용 샘플 데이터
size String 'medium' xsmall, small, medium, large, xlarge
clickToSelect Boolean true 클릭 시 자동 선택
selectType String 'singleSelect' 'singleSelect', 'multiSelect'
stripe Boolean — 홀짝 행 배경 구분
enableCopy Boolean false Ctrl+C 셀 복사

집계 방식 — pivotSummaryType

position: 'value' 컬럼에 지정하면 교차 셀의 계산 방식이 결정됩니다.

값의미

'sum' 합계
'avg' 평균
'min' 최솟값
'max' 최댓값

position: 'top' 컬럼에도 지정할 수 있으며, 이 경우 해당 축 헤더 옆에 소계 컬럼이 자동 추가됩니다 (예: 2023 합계, 2024 합계).


value 컬럼이 1개일 때 vs 여러 개일 때

이 차이가 PivotGrid를 이해하는 핵심입니다.

value 컬럼이 1개

columns: [
    { key: 'itemId',   position: 'left',  title: '품목코드', width: 200, fixed: true },
    { key: 'itemName', position: 'left',  title: '품명',     width: 100, fixed: true },
    { key: 'year',     position: 'top',   title: '연도',     width: 100, pivotSummaryType: 'sum' },
    { key: 'quater',   position: 'top',   title: '분기',     width: 100 },
    { key: 'amt',      position: 'value', title: '금액',     width: 100, pivotSummaryType: 'sum' }
]

→ 새로운 값 컬럼을 만들지 않고 top 축을 따라 확장됩니다. 헤더는 2023 1분기, 2023 2분기, 2024 1분기 식으로 나뉘고 각 셀엔 amt 합계.

value 컬럼이 여러 개

columns: [
    { key: 'itemId',   position: 'left',  title: '품목코드', width: 200, fixed: true },
    { key: 'quater',   position: 'left',  title: '분기',     width: 100, fixed: true },
    { key: 'year',     position: 'top',   title: '연도',     width: 100, pivotSummaryType: 'sum' },
    { key: 'amt',      position: 'value', title: '금액1',    width: 100, type: 'number', align: 'right', pivotSummaryType: 'avg' },
    { key: 'amt2',     position: 'value', title: '금액2',    width: 100, type: 'number', align: 'right', pivotSummaryType: 'avg' }
]

→ top 축 아래에 value 컬럼들이 추가로 계층됨. 헤더는 2023 > (금액1, 금액2), 2024 > (금액1, 금액2) 식의 다단 구조로 확장.


좌측 그룹 소계 — pivotGroupSummary

position: 'left' 컬럼에 pivotGroupSummary: true를 지정하면 그 그룹별로 소계 행이 자동 생성됩니다.

{
    key: 'itemName',
    title: '품명',
    position: 'left',
    pivotGroupSummary: true,   // itemName별 소계 행 자동 추가
    width: 100
}

각 소계 행의 계산은 대응하는 value 컬럼의 pivotSummaryType을 따릅니다.


표시하지 않는 원본 컬럼

position을 지정하지 않으면 그 컬럼은 원본에는 존재하지만 피벗 결과에 표시되지 않습니다. 나중에 활용 가능성이 있는 필드를 원본에 유지하되 UI에는 감출 때 사용합니다.

{
    key: 'itemGroupId',
    title: '품목그룹',
    width: 200
    // position 미지정 → 피벗에 미표시
}

종합 예시 — 다중 value 축 + 소계

config() {
    return {
        tagName: 'page',
        tags: [{
            tagName: 'panel',
            style: { flex: 1 },
            tags: [{
                ref: 'refPivotGrid',
                tagName: 'pivotGrid',
                style: { flex: 1, width: '100%' },
                selectType: 'singleSelect',
                stripe: true,
                enableCopy: true,
                columns: [
                    // 좌측 그룹 축
                    { key: 'itemId',   title: '품목코드', position: 'left', width: 150, fixed: true, align: 'center' },
                    { key: 'itemName', title: '품명',     position: 'left', width: 120, fixed: true,
                      pivotGroupSummary: true },
                    { key: 'quater',   title: '분기',     position: 'left', width: 80,  fixed: true, align: 'center' },
                    
                    // 상단 열 축
                    { key: 'year', title: '연도', position: 'top', width: 120, pivotSummaryType: 'sum' },
                    
                    // 값 축 (2개 → top 아래에 다단 확장됨)
                    { key: 'amt',  title: '금액',   position: 'value', width: 120, type: 'number',
                      align: 'right', pivotSummaryType: 'sum', decimalPoint: 0 },
                    { key: 'qty',  title: '수량',   position: 'value', width: 100, type: 'number',
                      align: 'right', pivotSummaryType: 'avg', decimalPoint: 2 }
                ]
            }]
        }]
    };
}

mounted() {
    ItemService.getPivotList(this, {}, (view, ok, res) => {
        if (ok) view.getRef('refPivotGrid').setData(res.data.list);
    });
}

주의사항

  • 컬럼마다 position 지정 필수 — 'left', 'top', 'value' 중 하나. 미지정 컬럼은 표시 안 됨
  • 좌측 축('left')은 대부분 fixed: true 권장 — 가로 스크롤 시 기준 축 유지
  • 원본 데이터는 평평한 배열로 전달 — 사전 집계 불필요, PivotGrid가 내부에서 처리
  • value 컬럼 개수에 따라 헤더 구조가 달라짐 — 1개면 top 축만 확장, 2개 이상이면 다단 확장
  • pivotSummaryType은 value 및 top 축에 각각 지정 가능 — top에 지정하면 해당 축 소계 컬럼 추가
  • pivotGroupSummary는 left 축에만 지정 — 좌측 그룹별 소계 행 생성
  • 핸들러는 문자열 참조만 — 인라인 function은 JSON 원칙 위반
  • 최상위에 명시적 크기 필요 — style: { flex: 1, minHeight: '...' } 없으면 안 그려짐
  • 데이터 재로드는 setData(list) 전체 교체 — 부분 갱신은 재계산 원칙상 미권장

대안 비교

상황추천

크로스탭 집계 (매출×연도, 지역×제품 등) Va.PivotGrid
일반 목록/편집 Va.Grid
계층 트리 데이터 Va.TreeGrid
컬럼 자체를 동적 생성 (피벗은 아님) Va.DynamicGrid
정적 격자 레이아웃 Va.GridDiv

참고