← 모든 레시피

거래 리포트 · 월간 명세

한 달치 거래를 요약하는 리포트를 만듭니다. 꺾은선·원·도넛 차트를 한 문서에서 쓰고, 숫자 서식과 자동 높이를 배웁니다.

이 템플릿 가져오기

여기서 배우는 것

  • chart-line
  • chart-pie
  • chart-donut
  • expr-format-number
  • node-autoheight

편집기로 만들기

  1. 차트를 넣고 종류를 꺾은선으로 둡니다. 배열 경로 · 라벨 식 · 값 식 셋만 정하면 됩니다.
  2. 차트를 둘 더 넣어 원과 도넛으로 둡니다. 같은 배열을 다른 그림으로 읽는 것이므로 값 식은 그대로 둡니다.
  3. 원과 도넛은 비율을 보여주는 그림입니다. 항목이 여덟 개를 넘으면 조각이 얇아져 아무것도 못 읽으니, 그때는 막대로 바꾸십시오.
  4. 요약 숫자는 라벨로 씁니다. {{ format(sum(data.daily, "amount"), "number", "0,0", "ko-KR") }} 처럼 두면 표와 어긋나지 않습니다.

스키마 · API 로 만들기

  1. 차트는 chartKind 로 종류를 정합니다 — "bar"(기본) · "line" · "pie" · "donut".
  2. 데이터는 표와 같은 모양입니다 — bind 가 배열, columns[0].cell 이 라벨 식, columns[1].cell 이 값 식입니다. 새 키는 chartKind 하나뿐입니다.
  3. 값 식이 숫자가 아니면 그 조각은 0 입니다. 문자열이 오면 통화 서식이 NaN 을 내는 것과 같은 이유입니다 — 숫자를 보내십시오.
  4. format(값, "number", "0,0", 로케일) 로 자릿수를 넣습니다. 패턴을 비우면 0,0 이 기본입니다.

완성 문서

아래 JSON 이 위 두 순서가 만드는 바로 그 문서입니다. 저희 테스트가 이 문서를 검증하고 경고 없이 렌더되는지 확인하므로, 그대로 붙여 넣어 쓰셔도 됩니다.

예시
{
  "v": 1,
  "unit": "px",
  "page": {
    "size": "a4",
    "orientation": "portrait",
    "margin": {
      "t": 48,
      "r": 48,
      "b": 56,
      "l": 48
    },
    "header": "{{ data.company }} · {{ data.period }}",
    "footer": "[[page]] / [[pages]]"
  },
  "fonts": [],
  "styles": [
    {
      "id": "s_h1",
      "fontFamily": "Liberation Sans",
      "fontSize": 22,
      "fontWeight": 700,
      "italic": false,
      "color": "#111111",
      "bg": "transparent",
      "align": "left",
      "valign": "top",
      "lineHeight": 1.5,
      "border": "none",
      "radius": 0,
      "opacity": 1
    },
    {
      "id": "s_l",
      "fontFamily": "Liberation Sans",
      "fontSize": 12,
      "fontWeight": 400,
      "italic": false,
      "color": "#333333",
      "bg": "transparent",
      "align": "left",
      "valign": "top",
      "lineHeight": 1.5,
      "border": "none",
      "radius": 0,
      "opacity": 1
    },
    {
      "id": "s_big",
      "fontFamily": "Liberation Sans",
      "fontSize": 28,
      "fontWeight": 700,
      "italic": false,
      "color": "#111111",
      "bg": "transparent",
      "align": "left",
      "valign": "top",
      "lineHeight": 1.5,
      "border": "none",
      "radius": 0,
      "opacity": 1
    },
    {
      "id": "s_cap",
      "fontFamily": "Liberation Sans",
      "fontSize": 11,
      "fontWeight": 400,
      "italic": false,
      "color": "#666666",
      "bg": "transparent",
      "align": "left",
      "valign": "top",
      "lineHeight": 1.5,
      "border": "none",
      "radius": 0,
      "opacity": 1
    }
  ],
  "nodes": [
    {
      "id": "n_title",
      "parent": "",
      "order": 0,
      "type": "label",
      "x": 0,
      "y": 0,
      "w": 697,
      "h": 28,
      "show": "",
      "styleId": "s_h1",
      "props": {
        "text": "Monthly statement",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Title"
    },
    {
      "id": "n_sum",
      "parent": "",
      "order": 1,
      "type": "label",
      "x": 0,
      "y": 40,
      "w": 340,
      "h": 36,
      "show": "",
      "styleId": "s_big",
      "props": {
        "text": "{{ format(sum(data.daily, \"amount\"), \"number\", \"0,0\", \"en-US\") }}",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Total"
    },
    {
      "id": "n_sum_l",
      "parent": "",
      "order": 2,
      "type": "label",
      "x": 0,
      "y": 78,
      "w": 340,
      "h": 0,
      "show": "",
      "styleId": "s_cap",
      "props": {
        "text": "Total this month",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Total caption"
    },
    {
      "id": "n_line_l",
      "parent": "",
      "order": 3,
      "type": "label",
      "x": 0,
      "y": 116,
      "w": 697,
      "h": 18,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "Daily amount",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Line heading"
    },
    {
      "id": "n_line",
      "parent": "",
      "order": 4,
      "type": "chart",
      "x": 0,
      "y": 140,
      "w": 697,
      "h": 200,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "data.daily",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [
          {
            "header": "Day",
            "cell": "{{ item.day }}",
            "width": 0,
            "align": "left"
          },
          {
            "header": "Amount",
            "cell": "{{ item.amount }}",
            "width": 0,
            "align": "right"
          }
        ],
        "rows": 0,
        "cells": [],
        "chartKind": "line",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Daily trend"
    },
    {
      "id": "n_pie_l",
      "parent": "",
      "order": 5,
      "type": "label",
      "x": 0,
      "y": 364,
      "w": 340,
      "h": 18,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "Share by product group",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Pie heading"
    },
    {
      "id": "n_pie",
      "parent": "",
      "order": 6,
      "type": "chart",
      "x": 0,
      "y": 388,
      "w": 340,
      "h": 220,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "data.mix",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [
          {
            "header": "Group",
            "cell": "{{ item.name }}",
            "width": 0,
            "align": "left"
          },
          {
            "header": "Amount",
            "cell": "{{ item.amount }}",
            "width": 0,
            "align": "right"
          }
        ],
        "rows": 0,
        "cells": [],
        "chartKind": "pie",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Share pie"
    },
    {
      "id": "n_donut_l",
      "parent": "",
      "order": 7,
      "type": "label",
      "x": 357,
      "y": 364,
      "w": 340,
      "h": 18,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "Share by channel",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Donut heading"
    },
    {
      "id": "n_donut",
      "parent": "",
      "order": 8,
      "type": "chart",
      "x": 357,
      "y": 388,
      "w": 340,
      "h": 220,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "data.channel",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [
          {
            "header": "Channel",
            "cell": "{{ item.name }}",
            "width": 0,
            "align": "left"
          },
          {
            "header": "Amount",
            "cell": "{{ item.amount }}",
            "width": 0,
            "align": "right"
          }
        ],
        "rows": 0,
        "cells": [],
        "chartKind": "donut",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Share donut"
    }
  ]
}

요청 본문입니다. template 자리에 위 문서를, data 자리에 아래 데이터를 넣습니다.

요청
{
  "template": "(the document above)",
  "data": {
    "company": "brewmypdf",
    "period": "2026-08",
    "daily": [
      {
        "day": "1",
        "amount": 1200
      },
      {
        "day": "8",
        "amount": 1850
      },
      {
        "day": "15",
        "amount": 1400
      },
      {
        "day": "22",
        "amount": 2100
      },
      {
        "day": "29",
        "amount": 1650
      }
    ],
    "mix": [
      {
        "name": "Subscription",
        "amount": 4200
      },
      {
        "name": "One-off",
        "amount": 2600
      },
      {
        "name": "Consulting",
        "amount": 1400
      }
    ],
    "channel": [
      {
        "name": "Direct",
        "amount": 5100
      },
      {
        "name": "Partner",
        "amount": 3100
      }
    ]
  }
}