수식
엑셀 같은 수식 엔진: 계산 컬럼, 함수 약 60개, COL()로 컬럼 전체 합계, PREV()로 누계, 셀에 직접 입력하는 수식, 직접 만든 함수.
실행 예제수식 엔진
계산 컬럼, 함수 약 60개, COL()로 컬럼 전체 합계, PREV()로 누계, 직접 만든 함수까지.
이 데모의 코드
import { createGrid, registerFormulaFunction, type Row } from '@freegridjs/core';
import { button, separator, text, tr, type DemoContext } from '../ui';
import '../data'; // registers the grid's language packs
export default function mount(ctx: DemoContext) {
const { lang } = ctx;
const h = (en: string, ko: string) => tr(lang, { en, ko });
const items = [
[h('Laptop', '노트북'), 2, 1290], [h('Monitor', '모니터'), 4, 320], [h('Keyboard', '키보드'), 10, 59], [h('Mouse', '마우스'), 12, 25],
[h('Desk', '책상'), 3, 410], [h('Chair', '의자'), 6, 230], [h('Headset', '헤드셋'), 5, 89], [h('Webcam', '웹캠'), 7, 75],
] as const;
const data: Row[] = items.map(([item, qty, price], i) => ({ id: i + 1, item, qty, price, discount: i % 3 === 0 ? 0.1 : 0, note: '' }));
// a function of your own: formulas can call it like any other
registerFormulaFunction('VAT', ([amount]) => Math.round(Number(amount) * 0.1));
const grid = createGrid(ctx.host, {
columns: [
{ field: 'item', header: h('Item', '품목'), width: 120 },
{ field: 'qty', header: h('Qty', '수량'), type: 'number', width: 80, editable: true },
{ field: 'price', header: h('Price', '단가'), type: 'number', format: '#,##0', width: 100, editable: true },
{ field: 'discount', header: h('Discount', '할인'), type: 'number', format: '0%', width: 90, editable: true },
// computed columns: a formula on the column, worked out for every row
{ id: 'amount', header: h('Amount', '금액'), type: 'number', format: '#,##0', width: 110, formula: '=ROUND(qty * price * (1 - discount), 0)' },
{ id: 'vat', header: h('VAT', '부가세'), type: 'number', format: '#,##0', width: 90, formula: '=VAT(amount)' },
{ id: 'share', header: h('Share', '비중'), type: 'number', format: '0.0%', width: 90, formula: '=amount / SUM(COL(amount))' },
{ id: 'running', header: h('Running total', '누계'), type: 'number', format: '#,##0', width: 120, formula: '=amount + PREV(running)' },
{ id: 'rank', header: h('Rank', '순위'), type: 'number', width: 70, formula: '=RANK(amount, COL(amount))' },
{ id: 'grade', header: h('Grade', '등급'), width: 90, formula: h('=IFS(share >= 0.25, "A", share >= 0.1, "B", TRUE, "C")', '=IFS(share >= 0.25, "A", share >= 0.1, "B", TRUE, "C")') },
// a cell formula typed into the data (columns with formulas: true)
{ field: 'note', header: h('Your formula', '직접 입력'), width: 200, editable: true, formulas: true },
],
data, locale: lang, getRowId: r => String(r.id),
summary: { rows: [{ label: h('Total', '합계'), columns: { amount: 'sum', vat: 'sum' } }] },
});
grid.on('cellValueChanged', e => ctx.log(`cellValueChanged → ${e.colId}: ${String(e.oldValue)} → ${String(e.newValue)}`));
text(ctx, h('Amount, VAT, share, running total, rank and grade are formulas. Edit Qty, Price or Discount and they follow; type =qty*2 or =UPPER(item) into the last column.',
'금액·부가세·비중·누계·순위·등급은 수식입니다. 수량·단가·할인을 고치면 따라 바뀌고, 마지막 컬럼에 =qty*2나 =UPPER(item)을 입력해 보세요.'));
separator(ctx);
button(ctx, h('Sort by amount', '금액순 정렬'), () => grid.setSort([{ colId: 'amount', dir: 'desc' }]));
button(ctx, h('Price +10%', '단가 10% 인상'), () => {
grid.applyTransaction({ update: grid.getData().map(r => ({ id: r.id, price: Math.round(Number(r.price) * 1.1) })) });
});
button(ctx, h('Fill example formulas', '수식 예시 채우기'), () => {
const examples = ['=qty * 2', '=UPPER(item)', '=IF(amount > 2000, "big", "small")', '=TEXT(price, "#,##0") & " won"', '=LEN(item)', '=ROUND(amount / qty, 1)', '=YEAR(TODAY())', '=COUNTIF(COL(discount), ">0")'];
grid.applyTransaction({ update: grid.getData().map((r, i) => ({ id: r.id, note: examples[i % examples.length] })) });
});
return grid;
}FreeGrid에는 엑셀과 비슷한 수식 엔진이 들어 있습니다. 수식은 처음 한 번만 해석해 두고 다시 쓰기 때문에 10만 행에서도 빠르고, 값이 바뀌면 그 값을 쓰는 계산 컬럼, 합계, 정렬, 필터, 내보내기가 모두 따라 바뀝니다. eval을 쓰지 않아 데이터에 들어 있는 수식이 코드를 실행할 수 없습니다.
계산 컬럼
컬럼에 formula를 주면 데이터에 없는 값도 행마다 계산해 보여 줍니다. 같은 행의 필드 이름(공백이 있으면 [단가 합계]처럼 대괄호)과 다른 계산 컬럼의 id를 그대로 쓸 수 있습니다. COL(필드)는 그 컬럼 전체 값, PREV(필드)는 바로 위 행의 값입니다. 누계처럼 위 행을 참조하는 수식도 10만 행까지 문제없이 계산됩니다.
createGrid(el, {
columns: [
{ field: 'item' },
{ field: 'qty', type: 'number', editable: true },
{ field: 'price', type: 'number', editable: true },
// 계산 컬럼: 데이터에 없어도 행마다 계산됩니다. id가 필요합니다.
{ id: 'amount', header: '금액', type: 'number', formula: '=qty * price' },
{ id: 'share', header: '비중', type: 'number', format: '0.0%', formula: '=amount / SUM(COL(amount))' },
{ id: 'running', header: '누계', type: 'number', formula: '=amount + PREV(running)' },
],
data,
});셀에 입력하는 수식
컬럼에 formulas: true를 주면 사용자가 셀에 =로 시작하는 수식을 직접 입력할 수 있습니다. 화면, 정렬, 복사, 엑셀 내보내기에는 결과가 쓰이고, 편집기를 열면 수식이 다시 보입니다.
// formulas: true인 컬럼은 셀 값이 '='로 시작하면 수식으로 계산합니다
{ field: 'total', type: 'number', editable: true, formulas: true }
data = [{ qty: 2, price: 10, total: '=qty*price' }]; // 화면에는 20, 편집기에는 =qty*price함수
연산자는 + - * / % ^, 글자 잇기 &, 비교 = <> < > <= >=입니다. 인수 구분은 쉼표나 세미콜론을 씁니다. 오류는 #DIV/0!, #REF!, #NAME?, #VALUE!, #CYCLE!처럼 엑셀식 코드로 표시됩니다.
| 이름 | 타입 | 설명 |
|---|---|---|
Math | SUM AVG AVERAGE MIN MAX MEDIAN COUNT COUNTA COUNTBLANK PRODUCT ROUND ROUNDUP ROUNDDOWN INT TRUNC ABS SIGN SQRT POWER EXP LN LOG10 MOD CEILING FLOOR PI | 숫자 계산. SUM·AVG·MIN·MAX는 COL(필드)를 받고, 그 안의 글자는 건너뜁니다. |
Text | CONCAT TEXTJOIN LEN LEFT RIGHT MID UPPER LOWER PROPER TRIM SUBSTITUTE REPLACE REPT FIND SEARCH VALUE TEXT | 글자 처리. TEXT(n, "#,##0.00")은 숫자, TEXT(d, "yyyy-mm-dd")는 날짜를 형식에 맞춰 바꿉니다. |
Logic | IF IFS IFERROR IFNA SWITCH CHOOSE AND OR XOR NOT ISBLANK ISNUMBER ISTEXT ISERROR | IF·IFS·IFERROR·SWITCH·CHOOSE는 선택된 쪽만 계산합니다. |
Dates | TODAY NOW DATE YEAR MONTH DAY WEEKDAY DAYS EDATE EOMONTH | 날짜는 날짜 컬럼처럼 "YYYY-MM-DD" 글자로 다룹니다. DAYS(끝, 시작)은 일수를 셉니다. |
Across rows | COL(field) PREV(field, n) NEXT(field, n) ROW() SUMIF COUNTIF AVERAGEIF LOOKUP XLOOKUP RANK | COL은 컬럼 전체, PREV/NEXT는 n행 위·아래 값. 조건은 ">10", "<>x", "A*"처럼 씁니다. |
=ROUND(qty * price * (1 - discount), 0) ' 할인 적용 금액
=IF(stock < 10, "부족", "충분") ' 조건
=IFERROR(sales / visits, 0) ' 0으로 나누기 방지
=SUMIF(COL(region), "서울", COL(amount)) ' 조건부 합계
=COUNTIF(COL(status), "완료") ' 조건에 맞는 개수
=LOOKUP(managerId, COL(id), COL(name)) ' 다른 행에서 찾기
=amount + PREV(running) ' 누계
=RANK(score, COL(score)) ' 순위
=DAYS(dueDate, TODAY()) ' 남은 일수
=TEXT(price, "#,##0") & "원" ' 서식 있는 글자직접 만든 함수
registerFormulaFunction으로 회사마다 다른 계산 규칙을 함수로 등록하세요. 등록한 함수는 모든 그리드의 수식에서 바로 쓸 수 있고, 함수 안에서 오류가 나면 #VALUE!로 표시됩니다.
import { registerFormulaFunction } from '@freegridjs/core';
// 우리 회사 규칙도 함수로: =VAT(amount), =GRADE(score)
registerFormulaFunction('VAT', ([amount]) => Math.round(Number(amount) * 0.1));
registerFormulaFunction('GRADE', ([score]) => (Number(score) >= 90 ? 'A' : Number(score) >= 70 ? 'B' : 'C'));수식은 원본 데이터 순서를 기준으로 PREV·NEXT·ROW를 계산하고, COL은 필터와 관계없이 모든 행을 봅니다. 그래서 정렬이나 필터를 바꿔도 값이 흔들리지 않습니다.
이 페이지에 대한 질문
그리드 컬럼을 다른 컬럼으로 계산할 수 있나요?
네. 컬럼에 =qty * price 같은 formula와 id를 주면 행마다 계산되고, 화면·정렬·필터·요약·내보내기에 모두 쓰입니다.
그리드에서 누계는 어떻게 만드나요?
PREV를 쓰세요. =amount + PREV(running) 수식을 가진 계산 컬럼은 바로 위 행의 누계에 이번 행 값을 더합니다. 순서는 원본 데이터 순서를 따릅니다.
어떤 엑셀 함수를 쓸 수 있나요?
SUM, AVG, ROUND, IF, IFS, IFERROR, SUMIF, COUNTIF, LOOKUP, RANK, LEFT, MID, TEXT, YEAR, DAYS, EDATE 등 약 60개이고, registerFormulaFunction으로 직접 만든 함수도 추가할 수 있습니다.