FreeReportv0.1.0English

교차 집계

같은 데이터로 지역 × 품목군, 지역 × 월 교차표를 만들고 행·열 합계를 붙입니다.

보고서 정의 (JSON)

{
  "name": "지역별 교차 집계",
  "page": {
    "size": "A4",
    "orientation": "landscape",
    "margins": 14
  },
  "font": {
    "family": "Nanum Gothic",
    "size": 8.5
  },
  "bands": {
    "title": {
      "height": 150,
      "elements": [
        {
          "type": "text",
          "x": 0,
          "y": 0,
          "w": 269,
          "h": 10,
          "text": "2026년 3분기 지역별 교차 집계",
          "style": {
            "fontSize": 16,
            "bold": true
          }
        },
        {
          "type": "text",
          "x": 0,
          "y": 10,
          "w": 269,
          "h": 6,
          "text": "대상 {count()}건 · 매출 합계 {sum(amount):#,##0}원",
          "style": {
            "color": "#4a5568"
          }
        },
        {
          "type": "text",
          "x": 0,
          "y": 20,
          "w": 200,
          "h": 7,
          "text": "1. 지역 × 품목군 (금액, 원)",
          "style": {
            "bold": true
          }
        },
        {
          "type": "crosstab",
          "x": 0,
          "y": 28,
          "w": 269,
          "h": 50,
          "rowKey": "region",
          "columnKey": "category",
          "value": "amount",
          "aggregate": "sum",
          "format": "#,##0",
          "corner": "지역 / 품목군",
          "rowHeaderWidth": 28,
          "cellHeight": 7,
          "headerStyle": {
            "bold": true,
            "align": "center",
            "background": "#eef1f6",
            "border": "0.2 solid #8a94a6"
          },
          "style": {
            "border": "0.15 solid #b9c1cd",
            "align": "right",
            "fontSize": 8
          }
        },
        {
          "type": "text",
          "x": 0,
          "y": 88,
          "w": 200,
          "h": 7,
          "text": "2. 지역 × 월 (건수)",
          "style": {
            "bold": true
          }
        },
        {
          "type": "crosstab",
          "x": 0,
          "y": 96,
          "w": 190,
          "h": 50,
          "rowKey": "region",
          "columnKey": "month(date) + \"월\"",
          "value": "no",
          "aggregate": "count",
          "format": "#,##0",
          "corner": "지역 / 월",
          "rowHeaderWidth": 28,
          "cellHeight": 7,
          "headerStyle": {
            "bold": true,
            "align": "center",
            "background": "#eef1f6",
            "border": "0.2 solid #8a94a6"
          },
          "style": {
            "border": "0.15 solid #b9c1cd",
            "align": "right",
            "fontSize": 8
          }
        }
      ]
    }
  }
}

데이터

긴 목록은 처음 3건만 보여 줍니다.

{
  "rows": [
    {
      "no": 1,
      "date": "2026-09-28",
      "region": "대구",
      "customer": "다온상사",
      "manager": "정하준",
      "product": "노트북 15형",
      "category": "전자",
      "qty": 1,
      "price": 1290000,
      "amount": 1290000
    },
    {
      "no": 2,
      "date": "2026-07-21",
      "region": "서울",
      "customer": "새봄디자인",
      "manager": "강서윤",
      "product": "무선 키보드",
      "category": "주변기기",
      "qty": 7,
      "price": 59000,
      "amount": 413000
    },
    {
      "no": 3,
      "date": "2026-08-09",
      "region": "경기",
      "customer": "온누리건설",
      "manager": "박도윤",
      "product": "27형 모니터",
      "category": "전자",
      "qty": 5,
      "price": 349000,
      "amount": 1745000
    },
    "… 600 items"
  ]
}

이 데모의 코드

import type { ReportDefinition, Style } from '@freereportjs/core';
import { makeSales } from '../../samples/data';
import { mountViewer, select, tr, type DemoContext } from '../ui';

/** Cross tabulation of the sales rows: region × product group (amount) and region × month (count), with totals. */
const head: Style = { bold: true, align: 'center', background: '#eef1f6', border: '0.2 solid #8a94a6' };
const cell: Style = { border: '0.15 solid #b9c1cd', align: 'right', fontSize: 8 };

export const definition: ReportDefinition = {
  name: '지역별 교차 집계',
  page: { size: 'A4', orientation: 'landscape', margins: 14 },
  font: { family: 'Nanum Gothic', size: 8.5 },
  bands: {
    title: {
      height: 150,
      elements: [
        { type: 'text', x: 0, y: 0, w: 269, h: 10, text: '2026년 3분기 지역별 교차 집계', style: { fontSize: 16, bold: true } },
        { type: 'text', x: 0, y: 10, w: 269, h: 6, text: '대상 {count()}건 · 매출 합계 {sum(amount):#,##0}원', style: { color: '#4a5568' } },
        { type: 'text', x: 0, y: 20, w: 200, h: 7, text: '1. 지역 × 품목군 (금액, 원)', style: { bold: true } },
        { type: 'crosstab', x: 0, y: 28, w: 269, h: 50, rowKey: 'region', columnKey: 'category', value: 'amount', aggregate: 'sum', format: '#,##0', corner: '지역 / 품목군', rowHeaderWidth: 28, cellHeight: 7, headerStyle: head, style: cell },
        { type: 'text', x: 0, y: 88, w: 200, h: 7, text: '2. 지역 × 월 (건수)', style: { bold: true } },
        { type: 'crosstab', x: 0, y: 96, w: 190, h: 50, rowKey: 'region', columnKey: 'month(date) + "월"', value: 'no', aggregate: 'count', format: '#,##0', corner: '지역 / 월', rowHeaderWidth: 28, cellHeight: 7, headerStyle: head, style: cell },
      ],
    },
  },
};

export const sampleData = { rows: makeSales(600) };

export default function mount(ctx: DemoContext) {
  const viewer = mountViewer(ctx, { definition, data: sampleData, zoom: 'page', fileName: '교차 집계' });
  select(ctx, tr(ctx.lang, { en: 'Rows', ko: '건수' }), [['200', '200'], ['600', '600'], ['5000', '5,000']], '600', n => {
    void viewer.setData({ rows: makeSales(Number(n)) });
  });
}