FreeReportv0.1.0English

사용법

시작하기

시작하기

FreeReport는 외부 라이브러리가 없는 TypeScript 패키지 하나입니다. 설치하고 스타일시트를 한 번 불러온 뒤, 높이가 있는 요소에 뷰어를 만들면 됩니다.

npm i @freereportjs/core
import { createViewer } from '@freereportjs/core';
import '@freereportjs/core/styles.css';

const definition = await fetch('/reports/sales.json').then(r => r.json());

const viewer = createViewer(document.getElementById('viewer'), {
  definition,
  data: { rows },     // detail rows; other keys are usable in expressions
  lang: 'ko',
});
// <div id="viewer" style="height: 720px"></div>

빌드 도구 없이 (CDN)

script 태그로 불러오면 전역 FreeReport 객체가 생깁니다. 운영 환경에서는 v0.1.0처럼 버전을 고정하세요. latest는 항상 최신 버전입니다.

<link rel="stylesheet" href="https://freereport.app/cdn/latest/freereport.css">
<script src="https://freereport.app/cdn/latest/freereport.min.js"></script>
<div id="viewer" style="height: 720px"></div>
<script>
  FreeReport.createViewer(document.getElementById('viewer'), { definition, data: { rows } });
</script>

프레임워크와 전자정부

코어는 어떤 프레임워크에서든 씁니다. 컴포넌트가 붙을 때 뷰어나 디자이너를 만들고 떨어질 때 destroy하면 됩니다. 바로 쓰는 컴포넌트가 npm에 배포되어 있습니다: @freereportjs/react, @freereportjs/vue(Vue 3), @freereportjs/svelte(Svelte 5), @freereportjs/angular. 각각 뷰어와 디자이너를 제공하며, npm i @freereportjs/core @freereportjs/react처럼 코어와 함께 설치합니다.

  • React: <FreeReportViewer>, <FreeReportDesigner>. 뷰어 ref는 { viewer, report }이고, 디자이너는 onChange, onSave를 받습니다.
  • Vue: 뷰어는 viewer, report를 노출하고, 디자이너는 v-model:definition을 지원하며 change, update:definition, save 이벤트를 보냅니다.
  • Svelte: viewer.getViewer() / getReport(), designer.getDesigner() 또는 onReady.
  • Angular: <free-report-viewer>, <free-report-designer>(standalone). 디자이너는 (definitionChange)를 보내 [(definition)]이 동작하고, (save)도 보냅니다.

definition, data, params, zoom은 뷰어를 그대로 두고 갱신하고, 나머지 속성은 뷰어를 다시 만듭니다. 두 컴포넌트 모두 기본 높이는 100%, 최소 480px입니다.

// npm i @freereportjs/core @freereportjs/react
import { useRef, useState } from 'react';
import { FreeReportDesigner, FreeReportViewer, type ReportDefinition } from '@freereportjs/react';
import '@freereportjs/core/styles.css';

export function SalesReport({ definition, rows }) {
  const ref = useRef(null);   // { viewer, report }
  return <FreeReportViewer ref={ref} definition={definition} data={{ rows }} lang="ko" style={{ height: 720 }} />;
}

export function SalesDesigner({ initial, rows, save }) {
  const [def, setDef] = useState<ReportDefinition>(initial);
  return <FreeReportDesigner definition={def} data={{ rows }} onChange={setDef} onSave={save} />;
}

전자정부 표준프레임워크 예제 프로젝트

예제는 전자정부 표준프레임워크 4.3(Spring 5.3, MyBatis, JDK 8 이상) 프로젝트로, 보고서 조회·인쇄·PDF·엑셀 출력과 출력 이력 관리까지 담았습니다. 서버는 보고서 정의와 데이터를 JSON으로만 내려 주고, 화면 구성·인쇄·파일 생성은 브라우저에서 처리하므로 별도 리포트 서버, ActiveX, 설치 프로그램이 필요 없습니다. 3.x 프로젝트는 import 문과 groupId를 org.egovframe.rte에서 egovframework.rte로 바꾸면 됩니다.

주소응답설명
GET /report/view.do?name=salesJSP뷰어 화면 (reportView.jsp)
GET /report/designer.do?name=salesJSP디자이너 화면 (reportDesigner.jsp)
GET /report/def/{name}.doJSON보고서 정의: REPORT_DEF 저장본, 없으면 classpath:reports/{name}.json
POST /report/def/{name}.doJSON디자이너에서 저장한 정의 저장
GET /report/data/sales.doJSON보고서 데이터 (Service → MyBatis)
POST /report/log.doJSON뷰어 onOutput 이벤트를 REPORT_OUTPUT_LOG에 기록
GET /report/logs.doJSP출력 이력 최근 100건

예제 프로젝트는 요청하시면 보내 드립니다: [email protected]

양식 만들기

보고서 정의

보고서는 JSON 객체입니다. 용지 설정, 기본 글꼴, 필요하면 파라미터와 스타일, 그리고 요소가 배치된 밴드로 이루어집니다. 레이아웃 엔진이 데이터와 묶어 페이지를 만들고, 뷰어·인쇄·PDF·엑셀은 모두 같은 페이지를 그리므로 결과가 똑같습니다.

{
  "name": "거래처별 매출",
  "page": { "size": "A4", "orientation": "portrait", "margins": 15 },
  "font": { "family": "Nanum Gothic", "size": 9 },
  "sort": ["customer", "date"],
  "bands": {
    "title": { "height": 14, "elements": [
      { "type": "text", "x": 0, "y": 0, "w": 180, "h": 10, "text": "거래처별 매출", "style": { "fontSize": 16, "bold": true } }
    ] },
    "columnHeader": { "height": 7, "elements": [
      { "type": "text", "x": 0, "y": 0, "w": 30, "h": 7, "text": "일자", "style": { "bold": true, "border": true } },
      { "type": "text", "x": 30, "y": 0, "w": 110, "h": 7, "text": "거래처", "style": { "bold": true, "border": true } },
      { "type": "text", "x": 140, "y": 0, "w": 40, "h": 7, "text": "금액", "style": { "bold": true, "border": true } }
    ] },
    "detail": { "height": 6, "elements": [
      { "type": "field", "x": 0, "y": 0, "w": 30, "h": 6, "expr": "date", "format": "yyyy-MM-dd", "style": { "border": true } },
      { "type": "field", "x": 30, "y": 0, "w": 110, "h": 6, "expr": "customer", "suppressRepeated": true, "style": { "border": true } },
      { "type": "field", "x": 140, "y": 0, "w": 40, "h": 6, "expr": "amount", "format": "#,##0", "style": { "border": true, "align": "right" } }
    ] },
    "summary": { "height": 8, "elements": [
      { "type": "text", "x": 0, "y": 1, "w": 180, "h": 7, "text": "합계 {sum(amount):#,##0}원 ({koreanAmount(sum(amount))})", "style": { "align": "right", "bold": true } }
    ] },
    "pageFooter": { "height": 8, "elements": [
      { "type": "text", "x": 0, "y": 2, "w": 180, "h": 5, "text": "{page} / {pages}", "style": { "align": "center" } }
    ] }
  }
}
이름타입설명
namestring보고서 이름. 파일 이름과 출력 이벤트에 쓰입니다.
pagePageSetup용지 크기, 방향, 여백.
font{ family, size, color }보고서 기본 글꼴(기본 나눔고딕 9pt).
stylesRecord<string, Style>이름 붙인 스타일. 요소에서 styleName으로 씁니다.
paramsRecord<string, ParamDefinition>뷰어의 조회 조건 패널에 나오는 파라미터.
dataSourcestring데이터에서 상세 행 배열의 이름(기본 "rows").
sortstring[]상세 행 정렬. 예: ["region", "-amount"] ("-"는 내림차순).
filterExpr수식이 참인 행만 남깁니다.
bandsobjecttitle, pageHeader, columnHeader, groups, detail, summary, pageFooter, noData.
pageHeaderSkipFirstboolean첫 페이지에는 페이지 머리글을 찍지 않습니다.
fillEmptyRowsboolean각 페이지의 남은 공간을 빈 상세 행으로 채웁니다(양식 표).
watermarkstring | Watermark | Watermark[]내용 뒤(또는 위)에 찍는 글자·이미지 표시.
embossEmboss | Emboss[]종이에 눌러 찍은 듯한 돋움 직인·로고·글자.

모든 위치와 크기는 밴드 왼쪽 위에서 잰 mm 단위입니다. 글자 크기는 pt입니다.

용지 설정

이름타입설명
size'A3' | 'A4' | 'A5' | 'B4' | 'B5' | 'Letter' | 'Legal' | [w, h]용지 이름 또는 [가로, 세로] mm. 기본 A4.
orientation'portrait' | 'landscape'기본은 세로(portrait).
marginsnumber | [top, right, bottom, left]여백(mm). 기본 15.

밴드

밴드는 이 순서로 찍힙니다. 첫 페이지의 제목, 페이지마다 페이지 머리글과 컬럼 머리글, 그룹 머리글, 행마다 상세 밴드, 그룹 바닥글, 마지막 행 다음의 요약, 페이지마다 맨 아래의 페이지 바닥글.

이름타입설명
titleBand첫 페이지에 한 번, 맨 앞에 찍힙니다(제목, 머리 양식, 한 장짜리 양식 전체).
pageHeaderBand모든 페이지의 맨 위.
columnHeaderBand상세 행이 있는 페이지마다 페이지 머리글 아래(표 제목 행).
groupsGroupDefinition[]그룹 머리글·바닥글. 바깥 그룹부터.
detailBand상세 행마다 한 번.
summaryBand마지막 행 다음(총합계, 차트).
pageFooterBand모든 페이지의 맨 아래(페이지 번호, 페이지 소계).
noDataBand데이터가 없을 때 상세 행 대신 찍힙니다.

밴드 속성

이름타입설명
heightnumber높이(mm).
elementsReportElement[]배치된 요소. 밴드 안에서 x, y, w, h(mm).
canGrowboolean글자가 넘치면 높이를 늘립니다(상세·그룹 밴드는 기본 true).
visibleExpr수식이 참일 때만 밴드를 찍습니다.
pageBreakBefore / pageBreakAfterboolean밴드 앞/뒤에서 새 페이지를 시작합니다.
backgroundstring밴드 배경색.
altBackgroundstring상세 밴드: 한 줄 걸러 배경색.
columns / columnGapnumber상세 밴드: 행을 여러 열로 나란히 찍습니다(라벨 용지, 카드). 간격은 mm.

그룹과 소계

그룹은 값이 같은 연속한 행을 묶습니다. 그룹 머리글·바닥글의 집계는 그 그룹 범위이고, 페이지 바닥글에서는 페이지 범위로, 어디서든 보고서 전체 범위로 계산할 수 있습니다.

"groups": [{
  "by": "region", "sort": "asc", "repeatHeader": true, "keepWithRows": 2,
  "header": { "height": 7, "elements": [
    { "type": "text", "x": 0, "y": 0, "w": 180, "h": 7, "text": "■ {region} ({count()}건)", "style": { "bold": true } }
  ] },
  "footer": { "height": 7, "elements": [
    { "type": "text", "x": 0, "y": 0, "w": 140, "h": 7, "text": "{region} 소계", "style": { "align": "center" } },
    { "type": "field", "x": 140, "y": 0, "w": 40, "h": 7, "expr": "sum(amount)", "format": "#,##0", "style": { "align": "right" } }
  ] }
}],
"pageFooter": { "height": 7, "elements": [
  { "type": "text", "x": 0, "y": 1, "w": 180, "h": 6, "text": "이 페이지 소계 {sum(amount, 'page'):#,##0}원 · 누계 {sum(amount, 'report'):#,##0}원" }
] }
이름타입설명
byExpr연속한 행을 묶는 수식. 예: "region".
sort'asc' | 'desc'먼저 그룹 수식으로 정렬합니다(없으면 순서 유지).
header / footerBand그룹 앞/뒤에 찍힙니다. 집계는 그 그룹 범위입니다.
newPageboolean그룹마다 새 페이지에서 시작합니다.
repeatHeaderboolean그룹이 다음 페이지로 이어지면 머리글을 다시 찍습니다.
keepWithRowsnumber머리글을 최소 이만큼의 행과 같은 페이지에 둡니다(기본 1).
resetPageNumberboolean그룹마다 page / pages 번호를 새로 셉니다.

요소

이름타입설명
type'text' | 'field' | 'line' | 'rect' | 'image' | 'barcode' | 'chart' | 'table' | 'crosstab'요소 종류.
x, y, w, hnumber밴드 안의 위치와 크기(mm).
idstring선택. 디자이너가 자동으로 붙입니다.
style / styleNameStyle / string인라인 스타일. 이름 붙인 스타일 위에 합쳐집니다.
visibleExpr참일 때만 그립니다.
canGrowboolean글자가 넘치면 아래로 늘어납니다(밴드도 함께).
stretchboolean밴드가 늘어나면 같이 늘어납니다(밴드 바닥에 닿은 요소는 기본).
namestring엑셀 "데이터" 모드의 열 이름.

요소 종류

type속성설명
text{ text }고정 글자 또는 템플릿: "합계 {sum(amount):#,##0}원". {{ }}는 중괄호 자체.
field{ expr, format?, nullText?, suppressRepeated? }수식 하나와 서식. suppressRepeated는 위 행과 같은 값을 숨깁니다.
line{ color?, width?, dash? }(x, y)에서 (x + w, y + h)까지. 가로선은 h = 0. 두께 mm.
rect{ fill?, color?, width?, radius? }사각형. width 0이면 테두리 없음.
image{ src? | expr?, fit? }URL·data: URL 또는 데이터 수식. fit: contain | cover | fill.
barcode{ expr, format?, showText?, ecc?, color? }format: 'code128' | 'ean13' | 'qr' | 'datamatrix' | 'pdf417'. ecc: QR 오류 정정 L/M/Q/H.
chart{ chartType, category, series, source?, title?, legend?, format?, showValues?, stacked? }chartType: 'bar' | 'hbar' | 'line' | 'area' | 'pie' | 'doughnut'.
table{ source, columns, rowHeight?, showHeader?, headerStyle?, footerLabel?, altBackground? }밴드 안의 목록(마스터–디테일). columns: { header, expr, width?, format?, align?, sum? }.
crosstab{ rowKey, columnKey, value, aggregate?, format?, totals?, corner?, source?, cellHeight?, rowHeaderWidth?, headerStyle? }행 × 열 교차표와 합계. aggregate: sum | count | avg | min | max.

스타일과 테두리

인라인 스타일은 이름 붙인 스타일(styleName) 위에, 이름 붙인 스타일은 보고서 기본 글꼴 위에 합쳐집니다. 테두리는 변마다 줄여 쓸 수 있어 표의 칸끼리 선을 맞추기 쉽습니다.

"styles": {
  "th": { "bold": true, "align": "center", "background": "#eef1f6", "border": "0.2 solid #8a94a6" },
  "num": { "align": "right", "border": { "bottom": "0.15 solid #b9c1cd" } }
},
"bands": { "detail": { "height": 6, "elements": [
  { "type": "field", "x": 140, "y": 0, "w": 40, "h": 6, "expr": "amount", "format": "#,##0", "styleName": "num", "style": { "bold": true } }
] } }
이름타입설명
fontFamily, fontSizestring, number글꼴과 크기(pt, 기본 9).
bold, italic, underline, strikeboolean글꼴 모양.
color, backgroundstring글자색, 배경색.
align, valign'left' | 'center' | 'right' | 'justify'; 'top' | 'middle' | 'bottom'가로·세로 정렬.
paddingnumber | [t, r, b, l]안쪽 여백(mm, 기본 좌우 1, 상하 0.5).
bordertrue | "0.3 solid #333" | { top, right, bottom, left }true는 0.2mm 검은 실선, CSS 비슷한 문자열, 또는 변별 지정.
lineHeightnumber글자 크기의 배수(기본 1.25).
wrapboolean긴 글자 줄바꿈(기본 true). false면 상자 끝에서 자릅니다.
shrinkToFitboolean한 줄에 들어갈 때까지 글자를 줄입니다.
letterSpacingnumber자간(mm). "견 적 서" 같은 제목에.

데이터와 수식

수식과 함수

필드, 표시 조건, 필터, 텍스트 템플릿은 작고 안전한 수식 언어를 씁니다. 사칙연산, 비교, && || ! ?? ?:, 멤버 접근(customer.name, items[0])과 아래 함수를 쓸 수 있고, 전역 객체에 접근하거나 값을 바꿀 수 없습니다. 텍스트에서는 중괄호 안에 수식을 쓰고 콜론 뒤에 서식을 붙입니다: "{sum(amount):#,##0}원".

price * qty                         arithmetic
amount >= params.minAmount           comparison with a parameter
customer.name ?? '(미지정)'           member access and fallback
qty > 10 ? '대량' : '일반'             condition
sum(amount)                          aggregate over the band's scope
sum(amount, 'page')                  page subtotal
sum(amount) / sum(amount, 'report')  share of the grand total
format(date, 'yyyy년 M월 d일 (ddd)')  2026년 10월 15일 (목)
koreanAmount(1230000)                일금 일백이십삼만원정
bizNo('7928703276')                  792-87-03276
{page} / {pages}                     page x / y in a text template

변수

이름타입설명
page, pagesnumber페이지 번호와 전체 페이지 수(resetPageNumber면 그룹별).
rowNumnumber그룹(또는 보고서) 안의 행 번호.
rowIndexnumber보고서 전체의 행 번호.
groupKey, groupLevelunknown, number현재 그룹 값과 깊이.
nowDate보고서를 만든 시각.
paramsobject파라미터 값: params.from.
row, rows, allRows, dataobject, array현재 행, 범위 안의 행, 모든 행, 데이터 전체.

집계와 범위

범위는 두 번째 인수입니다. 'group'(현재 그룹), 'page'(현재 페이지의 행 — 페이지 소계), 'report'(모든 행). 생략하면 밴드에 맞는 범위를 씁니다. 그룹 밴드는 그룹, 페이지 밴드는 페이지, 나머지는 보고서 전체입니다.

이름타입설명
sum(expr, scope?)number범위 안의 합계.
avg / min / max(expr, scope?)number평균, 최소, 최대.
count(expr?, scope?)number행 수(또는 비어 있지 않은 값의 수).
distinct(expr, scope?)number서로 다른 값의 수.
first / last(expr, scope?)unknown첫/마지막 행의 값.
runningSum(expr, scope?)number현재 행까지의 누계(기본 범위 report).

함수

이름타입설명
format(v, f)string숫자·날짜 서식: format(date, "yyyy-MM-dd").
koreanAmount(n) / korean(n)string1230000 → 일금 일백이십삼만원정.
bizNo(v), phone(v)string1234567890 → 123-45-67890, 0212345678 → 02-1234-5678.
round / floor / ceil(v, digits?)number소수점 자리 반올림·내림·올림.
abs, number, string—변환.
upper, lower, trim, len, left, right, substr, replace, concat, padStart, joinstring문자열 함수.
iif(c, a, b), coalesce(...), isEmpty(v)—조건과 대체 값(?:, ??도 됩니다).
date, today, now, year, month, day, addDays, dateDiff, ymd—날짜.
groupSum(rows, key, value), groupCount(rows, key)array키별 합계·개수. 차트 source로 쓰기 좋습니다.
min2(a, b), max2(a, b)number두 값 중 작은/큰 값.

서식

필드의 format(또는 템플릿에서 콜론 뒤)은 숫자를 엑셀식 패턴으로, 날짜를 yyyy, MM, dd, HH, mm, ss 패턴으로 표시합니다.

서식값설명
#,##0 #,##0.00 0.0%number엑셀식 숫자 서식.
₩#,##0 #,##0원 #,##0,"천"number앞뒤 글자와 천 단위 나누기.
#,##0;(#,##0);-number구역: 양수;음수;0.
yyyy-MM-dd HH:mm:ssdate날짜와 시간.
yyyy년 M월 d일 (ddd)date요일이 붙은 한국식 날짜.
a h:mmdate오전/오후.
korean, koreanNumbernumber일금 ○○원정 / 십이만삼천.
bizNo, upper, lowerstring사업자번호, 대소문자.

조회 조건

파라미터는 뷰어의 조회 조건 패널에 나오고 수식에서는 params.이름으로 씁니다. fetchData를 주면 사용자가 조회를 누를 때마다 서버에서 행을 받아 옵니다.

// definition.params: { from: { label: '시작일', type: 'date', required: true },
//                      region: { label: '지역', type: 'select', options: [{ value: '' , label: '전체' }, { value: '서울' }] } }
createViewer(el, {
  definition,
  paramsOpen: true,
  fetchData: params => fetch('/api/sales?' + new URLSearchParams(params)).then(r => r.json()),
});
이름타입설명
labelstring조회 조건 패널의 이름.
type'string' | 'number' | 'date' | 'boolean' | 'select'입력 종류.
defaultunknown초깃값.
options{ value, label? }[]select의 선택지.
requiredboolean조회 전에 꼭 입력해야 합니다.

바코드·차트·보안 표시

바코드

바코드는 벡터 도형으로 그려 어떤 배율에서도, 인쇄와 PDF에서도 선명합니다. Code128, EAN-13(12자리를 주면 검사 숫자를 붙임), QR(오류 정정 L/M/Q/H), DataMatrix, PDF417을 지원합니다. 상세 밴드를 여러 열(columns: 3)로 찍으면 한 행이 라벨 용지의 라벨 한 칸이 됩니다. 라벨 데모

"detail": { "height": 38, "columns": 3, "columnGap": 3, "elements": [
  { "type": "field", "x": 2, "y": 2, "w": 36, "h": 6, "expr": "name", "style": { "bold": true } },
  { "type": "barcode", "x": 40, "y": 2, "w": 18, "h": 18, "expr": "'https://example.com/a/' + assetNo", "format": "qr", "ecc": "M" },
  { "type": "barcode", "x": 2, "y": 24, "w": 56, "h": 11, "expr": "assetNo", "format": "code128" }
] }

차트·표·교차표

차트는 범위 안의 행(또는 source로 준 배열)에서 행마다 category와 하나 이상의 series 값을 가져옵니다. groupSum()으로 키별 합계를 만들 수 있습니다. table 요소는 밴드 안에 배열을 목록으로 그리고(송장마다 품목), crosstab은 행 × 열 교차표와 합계를 만듭니다. 차트 데모 · 교차 집계 데모

{ "type": "chart", "x": 0, "y": 16, "w": 110, "h": 60, "chartType": "bar", "title": "지역별 매출",
  "source": "groupSum(rows, 'region', 'amount')", "category": "region",
  "series": [{ "expr": "amount", "label": "매출" }], "format": "#,##0" },
{ "type": "table", "x": 0, "y": 20, "w": 180, "h": 20, "source": "items", "altBackground": "#fafbfd",
  "columns": [{ "header": "품목", "expr": "name" }, { "header": "수량", "expr": "qty", "width": 20, "align": "right" },
              { "header": "금액", "expr": "qty * price", "width": 40, "format": "#,##0", "sum": true }] },
{ "type": "crosstab", "x": 0, "y": 0, "w": 180, "h": 50, "rowKey": "region", "columnKey": "category",
  "value": "amount", "aggregate": "sum", "format": "#,##0", "corner": "지역 / 품목군" }

워터마크와 엠보싱

워터마크는 문자열이나 설정 객체, 또는 그 목록입니다. 텍스트가 템플릿이라 보안 워터마크에 출력자와 출력 일시를 넣고 페이지 전체에 반복할 수 있습니다. 사진을 찍거나 복사해도 남습니다.

"watermark": [
  { "text": "{params.user} · {format(now(), 'yyyy-MM-dd HH:mm')} 출력", "repeat": true, "opacity": 0.07, "fontSize": 10, "gap": 40 },
  { "text": "사 본", "fontSize": 110, "color": "#c8102e", "pages": "all" }
],
"emboss": { "text": "(주)세컨드코리아 대표이사 직인 ·", "centerText": "인증", "x": 140, "y": 240, "w": 34 }
이름타입설명
textstring템플릿. 예: "{params.user} · {format(now(), 'yyyy-MM-dd HH:mm')}".
image, imageWidthstring, number글자 대신(또는 함께) 쓰는 이미지와 너비(mm).
repeat, gapboolean, number페이지 전체에 반복(보안 패턴). 간격 mm(기본 45).
opacity, angle, color, fontSize, bold—기본 투명도 0.12, 각도 -35°.
pages'all' | 'first' | 'last' | 'odd' | 'even'찍을 페이지.
layer'back' | 'front'내용 뒤(기본) 또는 위.

웹 엠보싱

엠보싱은 빛과 그림자로 종이에 눌러 찍은 듯한 돋움 직인·로고·글자를 그립니다. 화면, 인쇄, PDF에 모두 나옵니다.

이름타입설명
text, centerTextstring원형 직인 둘레 글자와 가운데 글자(템플릿 가능).
imagestring돋움으로 바꿀 로고 이미지.
x, y, w, hnumber위치와 크기(mm, 기본 오른쪽 아래 32mm 직인).
shape'circle' | 'rect' | 'none'테두리 모양.
depth, color, opacity, fontSize, pages—돋움 깊이(mm, 기본 0.3), 종이 색, 투명도, 페이지.

화면과 출력

뷰어 옵션

뷰어는 화면에 들어오는 페이지만 SVG로 그립니다. 단축키: PageUp/PageDown, Ctrl+Home/End, Ctrl+P(인쇄), Ctrl+휠(확대·축소).

이름타입설명
definitionReportDefinition보고서 정의(JSON).
dataReportData{ rows: [...] }와 수식에서 쓰는 다른 값.
fetchData(params) => data | Promise파라미터로 데이터를 불러옵니다(처음과 조회할 때).
paramsobject파라미터 초깃값(기본값보다 우선).
lang'ko' | 'en'도구 모음 언어(기본 ko).
toolbar{ pages, zoom, print, pdf, xlsx, xlsxData, csv, params }보여 줄 버튼(csv는 기본 꺼짐).
zoomnumber | 'width' | 'page'처음 배율(기본 폭 맞춤).
paramsOpenboolean처음부터 조회 조건 패널을 엽니다.
fileNamestring저장 파일 이름.
licenseKeystring라이선스 키(또는 setLicenseKey 한 번).
onOutput(e) => boolean | void | Promise인쇄·저장 직전마다 호출. false를 돌려주면 취소.
pdfSecurity{ userPassword?, ownerPassword?, permissions? }PDF 암호와 권한(AES-256).
authorstringPDF 문서 정보의 작성자.

뷰어 메서드

이름타입설명
refresh()Promise<void>보고서를 다시 만듭니다.
setData(data) / setParams(p) / setDefinition(def)Promise<void>데이터·파라미터·정의를 바꾸고 다시 만듭니다.
goTo(page) / setZoom(z)void페이지 이동과 배율.
print()Promise<boolean>인쇄(onOutput이 막으면 false).
reportReport아래의 Report 객체(toPdf, toXlsx…).
destroy()void뷰어를 없앱니다.

디자이너

createDesigner는 웹 디자이너를 띄웁니다. 도구 상자, 데이터 필드 목록, 밴드, 속성 창, 미리 보기와 JSON 보기, 실행 취소·다시 실행이 들어 있습니다. 필드 목록과 미리 보기에 보일 수 있게 예제 데이터를 넘겨 주세요. 디자이너 써 보기

import { createDesigner } from '@freereportjs/core';
import '@freereportjs/core/styles.css';

const designer = createDesigner(document.getElementById('designer'), {
  definition,               // leave out to start with a blank report
  data: { rows },           // sample data for the field list and the preview
  lang: 'ko',
  onChange: def => localStorage.setItem('draft', JSON.stringify(def)),
  onSave: def => fetch('/api/reports/sales', { method: 'PUT', body: JSON.stringify(def) }),
});
designer.setMode('preview');      // 'design' | 'preview' | 'json'
const json = designer.getDefinition();

인쇄·PDF·엑셀·CSV

인쇄는 보고서의 용지 크기 그대로 브라우저 인쇄를 쓰며 줄바꿈도 화면과 같습니다. PDF는 트루타입 글꼴을 부분 내장해 글자를 검색하고 복사할 수 있습니다. 엑셀 "양식 그대로(form)"는 인쇄 모양을 병합 셀과 테두리로 살리고, "데이터(data)"는 한 행에 한 건씩 써서 이어서 작업하기 좋습니다.

import { createReport } from '@freereportjs/core';

const report = createReport(definition, { rows });
await report.ready();                    // wait for web fonts, then lay out

await report.print();                    // browser print at the paper size
const pdf = await report.toPdf();        // Blob, fonts embedded
const form = await report.toXlsx('form'); // merged cells and borders, like the printout
const data = await report.toXlsx('data'); // one row per record
await report.download('pdf', '매출 보고서'); // or save directly
이름타입설명
createReport(def, data, options?)Report뷰어 없이 정의와 데이터를 묶습니다.
ready()Promise<LayoutResult>웹 글꼴을 기다린 뒤 레이아웃. 브라우저에서 처음 출력 전에 호출합니다.
layout()LayoutResult페이지, 행 수, 걸린 시간(캐시됨).
print({ pages? })Promise<boolean>정확한 용지 크기로 브라우저 인쇄.
toPdf()Promise<Blob | null>글꼴을 부분 내장한 PDF.
toXlsx('form' | 'data')Promise<Blob | null>"form"은 병합·테두리 유지, "data"는 한 행에 한 건.
toHwpx()Promise<Blob | null>인쇄한 페이지와 같은 배치의 한/글 문서(.hwpx, KS X 6101).
toCsv()Promise<Blob | null>CSV(엑셀에서 한글이 깨지지 않도록 BOM 포함).
download('pdf' | 'xlsx' | 'xlsx-data' | 'csv' | 'hwpx', name?)Promise<boolean>파일을 만들어 저장합니다.

글꼴

나눔고딕과 나눔명조가 들어 있습니다(SIL OFL). 화면과 인쇄에는 웹 글꼴을, PDF에는 트루타입 파일을 쓰며 처음 필요할 때 jsDelivr CDN에서 받습니다. registerFont로 다른 글꼴을 추가할 수 있고, 사내망에서는 파일을 직접 올려 두고 쓰면 됩니다.

import { registerFont } from '@freereportjs/core';

registerFont('Malgun Gothic', {
  css: '/fonts/malgun.css',          // @font-face rules for screen and print (optional)
  normal: '/fonts/malgun.ttf',       // TrueType for the PDF (URL or Uint8Array)
  bold: '/fonts/malgunbd.ttf',
});
// then: "font": { "family": "Malgun Gothic" } or "style": { "fontFamily": "Malgun Gothic" }

서버·보안·운영

서버에서 생성

renderPdf와 renderXlsx는 브라우저 없이(Node, Deno, Bun, 워커) 레이아웃하고 파일을 씁니다. 글자는 PDF에 내장하는 트루타입 글꼴로 재기 때문에 줄바꿈이 같습니다. 일괄 출력, 예약 보고서, 메일 첨부에 쓰세요.

// Node 20+, Deno or Bun — no browser needed
import { writeFile } from 'node:fs/promises';
import { loadFonts, renderPdf, renderXlsx } from '@freereportjs/core';

const fonts = await loadFonts(['Nanum Gothic']);        // TTF bytes (or read your own files)
const pdf = await renderPdf(definition, { rows }, { fonts, params: { user: 'batch' } });
await writeFile('sales.pdf', pdf);
await writeFile('sales.xlsx', await renderXlsx(definition, { rows }, { fonts, mode: 'form' }));

PDF 보안

pdfSecurity를 주면 PDF를 AES-256으로 암호화합니다(PDF 2.0 표준 보안). 열기 암호(userPassword)를 주면 열 때 암호를 묻고, permissions로 PDF 뷰어에서의 인쇄·복사·수정을 막습니다. 소유자 암호를 주지 않으면 임의의 값을 씁니다.

createViewer(el, {
  definition, data,
  pdfSecurity: {
    userPassword: '1234',                      // asked when the PDF is opened (omit: opens freely)
    permissions: { print: true, copy: false, modify: false },
  },
});

출력 이력

onOutput은 인쇄와 저장 직전마다 종류, 보고서 이름, 쪽수, 건수, 파라미터를 넘겨 줍니다. 출력 이력(개인정보 안전성 확보조치)을 남기거나, 출력 사유를 묻거나, false를 돌려줘 막을 수 있습니다. 출력 보안 데모

createViewer(el, {
  definition, data, params: { user: '홍길동' },
  // e = { kind: 'print' | 'pdf' | 'xlsx' | 'xlsx-data' | 'csv', report, pages, rows, params, fileName }
  onOutput: async e => {
    const res = await fetch('/api/output-log', { method: 'POST', body: JSON.stringify(e) });
    return res.ok;   // false cancels the print or download
  },
});

라이선스 키

개인 사용은 키가 필요 없습니다. 회사는 도메인용 키(FR1.…)를 받아 한 번 설정하거나 licenseKey로 넘깁니다. 공개 도메인에서 유효한 키가 없으면 개발자 콘솔에 안내를 한 번 출력할 뿐, 화면은 바뀌지 않고 기능도 꺼지지 않습니다. 로컬과 사내 네트워크 주소에는 키가 필요 없습니다. 가격

import { setLicenseKey } from '@freereportjs/core';

setLicenseKey('FR1.eyJkb21haW5zIjpb...');   // once, before creating viewers
// or: createViewer(el, { definition, data, licenseKey: 'FR1.eyJkb21haW5zIjpb...' })

도움말

AI에게 요청하기

ChatGPT, Claude, Gemini, Cursor에게 FreeReport 보고서 정의를 부탁할 때는 먼저 https://freereport.app/llms-full.txt를 읽게 하세요. 모든 옵션, 요소, 함수, 서식이 한 파일에 정리되어 있습니다. "문서에 없는 옵션은 만들지 말라"고 덧붙이면 틀린 정의가 크게 줄어듭니다.