금융·공공 SI를 위한 엔터프라이즈 프론트엔드 데이터 그리드 플랫폼
OneGrid는 단순한 HTML 테이블 래퍼가 아닙니다.
core, dom, react, vue, themes, adapters, testing을 분리한 모노레포 구조 위에서
대용량 데이터, 병합 레이아웃, 서버 row model, 편집 이력, 보안 렌더링, 접근성, 문서화까지 함께 다루는
상용 그리드 1.0 릴리즈를 목표로 개발되고 있습니다.
OneGrid는 현재
0.0.0프리릴리즈입니다. API는 1.0 안정화 전까지 조정될 수 있으며, 모든 public contract 변경은API_CHANGELOG.md에 기록합니다.
| 영역 | 현재 수준 |
|---|---|
| 패키지 | @onegrid/core, @onegrid/dom, @onegrid/react, @onegrid/vue, @onegrid/themes, @onegrid/adapters |
| 렌더러 | Vanilla DOM renderer + React/Vue wrapper bridge |
| 데이터 모델 | Client, Infinite, Server, Viewport, Tree row model |
| 대용량 | 10M/100M rows를 Server/Viewport/Segmented virtual scroll로 탐색 |
| 레이아웃 | pinned/frozen pane, row/column virtualization, group/header/cell merge, variable row height |
| 엔터프라이즈 기능 | sort, filter, edit, selection, clipboard, menus, summary, grouping, tree, pivot, pagination, export/import |
| 보안 | CSP nonce, Trusted Types 검증, sanitizer adapter, spreadsheet formula guard, safe renderer boundary |
| 문서/예제 | Docusaurus 문서, examples catalog, Vanilla/React/Vue 예제, Playwright E2E |
OneGrid는 Phase 1~13 구현 이후 TODOLIST.md에서 1.0 release blocker를 별도 추적합니다.
| 게이트 | 상태 | 핵심 결과 |
|---|---|---|
| P0-API Public Contract Freeze | 완료 | public API inventory, type export drift guard, GridOptions.server drift 제거 |
| P0-ROW Server / Viewport Contract | 완료 | route/page cache limit, cancellation signal, stale result guard, 100M blank-window invariant |
| P0-DOM Renderer Lifecycle | 진행 예정 | renderer incremental refresh, overlay/focus/editor scroll consumer hardening |
| P0-PERF Browser Performance | 진행 예정 | 실제 브라우저 frame budget, long task, detached DOM memory gate |
| Column Virtualization | Pivot Builder | Pagination |
|---|---|---|
![]() |
![]() |
![]() |
금융·공공 프로젝트의 그리드는 "데이터를 보여주는 컴포넌트"가 아니라 업무 화면의 핵심 플랫폼입니다. OneGrid는 다음 요구를 처음부터 같은 설계 안에서 다룹니다.
- 대량 조회, 서버 정렬/필터/그룹핑, viewport 기반 탐색
- group header, header merge, cell merge, pinned/frozen pane 조합
- batch editing, pending edit history, undo/redo, external state integration
- Excel/PDF/CSV/print export와 import adapter 경계
- CSP, XSS, Trusted Types, sanitizer, formula injection 방어
- keyboard focus, ARIA grid semantics, screen reader label, browser matrix 검증
@onegrid/core는 DOM을 모릅니다.
컬럼/행/상태/이벤트/플러그인/보안/가상화 계약은 core가 소유하고, DOM/React/Vue는 같은 계약을 소비합니다.
@onegrid/core
-> pure contracts, state, row/column models, security, virtualization
@onegrid/dom
-> rendering, scroll coordination, overlays, focus, menus, editors
@onegrid/react / @onegrid/vue
-> lifecycle bridge, event bridge, slot/renderer bridge
이 구조 덕분에 wrapper가 core 기능을 재구현하지 않고도 동일한 API 정신 모델을 유지합니다.
pnpm add @onegrid/core @onegrid/dom @onegrid/themes현재 저장소는 프리릴리즈 모노레포입니다. 로컬에서 직접 실행하려면 아래 개발 명령을 사용하세요.
pnpm install
pnpm --filter @onegrid/examples dev --host 127.0.0.1 --port 4174import { OneGrid } from "@onegrid/dom";
import type { ColumnDef } from "@onegrid/core";
import "@onegrid/themes/default.css";
interface OrderRow {
readonly id: string;
readonly agency: string;
readonly program: string;
readonly amount: number;
readonly status: "Draft" | "Review" | "Approved";
}
const columns: readonly ColumnDef<OrderRow>[] = [
{ field: "id", headerName: "ID", pinned: "left", width: 140 },
{ field: "agency", headerName: "Agency", sortable: true, filter: "text" },
{ field: "program", headerName: "Program", resizable: true },
{ field: "amount", headerName: "Amount", type: "number", summary: "sum" },
{ field: "status", headerName: "Status", pinned: "right", filter: "set" }
];
const rows: readonly OrderRow[] = [
{ id: "ORD-1001", agency: "Treasury Office", program: "Budget approval", amount: 1200000, status: "Approved" },
{ id: "ORD-1002", agency: "Audit Bureau", program: "Risk sampling", amount: 430000, status: "Review" },
{ id: "ORD-1003", agency: "Welfare Office", program: "Care center", amount: 620000, status: "Draft" }
];
const grid = new OneGrid<OrderRow>({
el: document.querySelector("#grid") as HTMLElement,
columns,
data: rows,
rowKey: "id",
rowModel: "client",
selection: { mode: "range", multiple: true },
editing: { enabled: true, commitMode: "batch", undoRedo: true },
accessibility: { label: "Order approval grid" }
});
grid.setSortModel([{ field: "amount", direction: "desc" }]);import { useRef } from "react";
import { OneGrid, type OneGridHandle } from "@onegrid/react";
import "@onegrid/themes/default.css";
export function OrdersGrid() {
const gridRef = useRef<OneGridHandle<OrderRow>>(null);
return (
<OneGrid<OrderRow>
ref={gridRef}
columns={columns}
data={rows}
rowKey="id"
rowModel="client"
selection={{ mode: "range", multiple: true }}
editing={{ enabled: true, commitMode: "batch", undoRedo: true }}
accessibility={{ label: "Order approval grid" }}
onSelectionChanged={(event) => console.log(event.rowKeys)}
/>
);
}<script setup lang="ts">
import { ref } from "vue";
import { OneGrid, type OneGridExpose } from "@onegrid/vue";
import "@onegrid/themes/default.css";
const grid = ref<OneGridExpose>();
</script>
<template>
<OneGrid
ref="grid"
:columns="columns"
:data="rows"
row-key="id"
row-model="client"
:selection="{ mode: 'range', multiple: true }"
:editing="{ enabled: true, commitMode: 'batch', undoRedo: true }"
:accessibility="{ label: 'Order approval grid' }"
/>
</template>| 기능 | OneGrid 계약 |
|---|---|
| Column model | field, columnId, defaultColumnDef, columnTypes, column state API |
| Header | group header, header merge, label presentation, validation, resize/reorder/pin/hide |
| Row models | client, infinite, server, viewport, tree |
| Virtualization | row virtualization, column virtualization, segmented virtual scroll, variable row height |
| Layout | pinned columns, frozen rows/columns, footer/summary, scroll coordinator |
| Merge | vertical/horizontal/block cell merge, span index, selection/export/focus integration |
| Sorting/filtering | client/server sort and filter contracts, quick filter, filter model |
| Editing | cell editing, batch edit session, pending edits, undo/redo, keyboard policy |
| Selection | row/cell/range selection, checkbox selection, merged-cell range projection |
| Clipboard | copy/paste, headers, formula injection guard, selected range support |
| Menus | header menu, context menu, column tool panel, overlay scroll subscription |
| Summary/grouping/tree/pivot | aggregation, row grouping, tree expand, pivot model and builder UI |
| Export/import | CSV, XLSX, PDF, print, import adapter, server document adapter |
| i18n | locale state, formatter bridge, runtime locale update |
| Security | CSP nonce, Trusted Types, sanitizer adapter, URL protocol allowlist |
| Theming | CSS variables, density, dark/high-contrast, SI palette validation |
| Row model | 사용 상황 | 핵심 원칙 |
|---|---|---|
client |
브라우저 메모리에 안전하게 들어오는 중소 규모 데이터 | client sort/filter/group/edit |
infinite |
flat list를 block 단위로 lazy loading | block cache, retry, loading row |
server |
서버가 정렬/필터/그룹/집계를 소유 | route cache, group expand request, server status |
viewport |
10M~100M rows, 실시간/초대형 조회 | visible range request, logical row index, segmented scroll |
tree |
계층 데이터와 lazy children | expand/collapse, tree selection, server parity |
100M rows 지원은 전체 배열을 만드는 기능이 아닙니다.
OneGrid는 브라우저 메모리에 전체 데이터를 올리지 않고, 현재 viewport 주변만 요청하고 렌더링합니다.
const grid = new OneGrid<OrderRow>({
el,
columns,
rowModel: "viewport",
dataSource: {
async getRows({ startRow, endRow }) {
return {
rows: await fetchRows(startRow, endRow),
rowCount: 100_000_000
};
}
},
viewport: {
initialRowCount: 100_000_000,
viewportSize: 80,
overscan: 24,
prefetchRows: 120
},
virtualization: {
segmented: true,
rowHeight: 32
}
});서버와 뷰포트 모델은 1.0 기준으로 다음 불변식을 갖습니다.
server.maxCacheEntries는 row 수가 아니라 route/page cache entry 수를 제한합니다.GetRowsRequest.signal은 server/infinite 요청 모두에서 cancellation contract로 전달됩니다.- 취소된 응답은
ServerLoadResult.cancelled: true, 뒤늦게 도착한 응답은stale: true로 표시됩니다. - DOM renderer는
stale또는cancelled결과를 현재 화면에 적용하지 않습니다. rowCount는 서버 route 또는 viewport의 authoritative logical count입니다.visibleRowCount는 서버 그룹 확장 후 실제로 조합된 visible entry 수입니다.- 100M viewport는 전체 배열을 만들지 않고 skeleton row와 bounded DOM으로 blank window를 방지합니다.
const grid = new OneGrid<OrderRow>({
el,
columns,
rowModel: "server",
rowKey: "id",
dataSource: {
async getRows(request) {
request.signal?.throwIfAborted?.();
return fetchServerRows(request);
}
},
server: {
pageSize: 100,
maxCacheEntries: 32
}
});| 패키지 | 역할 |
|---|---|
@onegrid/core |
DOM-free public types, state, row/column/merge/security/virtualization contracts |
@onegrid/dom |
DOM renderer, layout, scroll, focus, menus, editors, export runtime |
@onegrid/react |
React lifecycle bridge, ref API, event bridge |
@onegrid/vue |
Vue lifecycle bridge, expose API, event bridge |
@onegrid/themes |
default/clean/compact/dark/high-contrast CSS, SI theme tokens |
@onegrid/pagination |
pagination contract and helper package |
@onegrid/adapters |
export/import adapter plugin boundary |
@onegrid/adapter-xlsx-exceljs |
ExcelJS 기반 XLSX adapter 경계 |
@onegrid/adapter-pdf-pdfkit |
PDFKit 기반 PDF adapter 경계 |
@onegrid/testing |
fixtures, performance helpers, Playwright utilities |
OneGrid의 기본 렌더링 정책은 안전한 쪽으로 닫혀 있습니다.
- 문자열 renderer 결과는 HTML이 아니라 text로 처리합니다.
- HTML renderer는 명시적 opt-in이며 sanitizer가 필요합니다.
eval,new Function, inline event handler를 사용하지 않습니다.- CSP nonce 기반 runtime style injection을 지원합니다.
- Trusted Types 실제 브라우저 검증 경로를 제공합니다.
- Clipboard/CSV export는 spreadsheet formula injection을 차단합니다.
- URL formatter는 허용 protocol만 통과시킵니다.
const grid = new OneGrid({
el,
columns,
data,
security: {
cspNonce: window.__CSP_NONCE__,
html: {
allowHtmlRenderer: true,
sanitizer: appSecurityTeamApprovedSanitizer
}
}
});관련 문서:
SECURITY.mdapps/docs/docs/security/csp.mdxapps/docs/docs/security/xss.mdxapps/docs/docs/security/wrapper-renderers.mdx
테마는 CSS variable 기반입니다. 기본 테마 외에도 compact, dark, high-contrast, SI tenant palette를 구성할 수 있습니다.
import { createSiTheme, validateSiTheme } from "@onegrid/themes";
const bnkTheme = createSiTheme({
name: "bnk-red",
density: "compact",
tokens: {
colors: {
accent: "rgb(215 25 31)",
accentHover: "rgb(139 3 4)",
border: "rgb(183 169 151)",
header: "rgb(248 250 252)",
selected: "rgb(255 238 238)",
muted: "rgb(101 92 79)"
}
}
});
const result = validateSiTheme(bnkTheme);
if (result.valid) {
grid.applyTheme(bnkTheme);
}테마 검증은 contrast, token name, CSS value, minimum size gate를 확인합니다.
로컬 예제 서버:
pnpm --filter @onegrid/examples dev --host 127.0.0.1 --port 4174주요 예제:
| URL | 확인 포인트 |
|---|---|
http://127.0.0.1:4174/#EX-001-001 |
Basic grid setup |
http://127.0.0.1:4174/#EX-002-004 |
Header merge setup |
http://127.0.0.1:4174/#EX-002-008 |
Variable row height |
http://127.0.0.1:4174/#ROW-003 |
Server row model |
http://127.0.0.1:4174/#LAY-003 |
Column virtualization |
http://127.0.0.1:4174/#LAY-004 |
Cell merge layout |
http://127.0.0.1:4174/#F-EDIT |
Editing, batch commit, undo/redo |
http://127.0.0.1:4174/#F-PIVOT |
Pivot builder |
http://127.0.0.1:4174/#F-EXPORT |
Export/import |
http://127.0.0.1:4174/#EX-005-006 |
100M viewport rows setup |
http://127.0.0.1:4174/#THEME-002 |
SI customization |
상용 그리드 품질을 목표로 아래 명령을 지속적으로 유지합니다.
pnpm lint
pnpm typecheck
pnpm test:unit
pnpm test:e2e
pnpm test:a11y
pnpm test:perf:smoke
pnpm bench:metrics
pnpm build
pnpm docs:build브라우저/보안 검증:
pnpm test:e2e:webkit
pnpm test:e2e:edge
pnpm test:e2e:security:browser-matrix
pnpm test:e2e:package-csp
pnpm test:e2e:visualbench:metrics는 100M viewport, 10M segmented scroll, 50K column virtualization,
variable row height, merge, frozen+virtual 조합의 ms, ops/s, ms/op, DOM bound 지표를 출력합니다.
| 문서 | 내용 |
|---|---|
AGENTS.md |
개발 운영 규칙과 품질 게이트 |
ARCHITECT.md |
아키텍처 원칙과 패키지 경계 |
CHECKLIST.md |
릴리즈 체크리스트, Evidence, Risk/Decision Log |
TODOLIST.md |
1.0 release blocker 제거 체크리스트 |
API_CHANGELOG.md |
public API 변경 이력 |
SECURITY.md |
보안 정책 |
apps/docs/docs/getting-started |
설치와 시작 |
apps/docs/docs/api |
GridOptions, ColumnDef, GridApi, Events, Plugins |
apps/docs/docs/features |
기능별 가이드 |
apps/docs/docs/frameworks |
React, Vue, API parity |
apps/docs/docs/security |
CSP, XSS, sanitizer, wrapper renderer security |
apps/docs/docs/features/row-virtualization.mdx |
대용량/가상화 성능 정책 |
OneGrid는 다음 제약을 릴리즈 전까지 계속 유지합니다.
core -> dom/react/vue의존 금지- wrapper에서 core 기능 재구현 금지
- 10M~100M rows 전체 client array 생성 금지
- merge를 단순
rowspan/colspanDOM 문제로 축소 금지 - CSP/XSS/a11y/performance 검증 없는 기능 완료 금지
- 사용자가 보는 화면 기준의 Playwright E2E 유지
- public API 변경은
API_CHANGELOG.md에 기록
- Core contracts와 row/column model 안정화
- DOM renderer layout, virtualization, merge, scroll coordinator hardening
- Enterprise features: edit, selection, clipboard, menu, summary, grouping, tree, pivot, export/import
- CSP/XSS/Trusted Types/security adapter 검증
- Theme token validation과 SI customization
- React/Vue wrapper API parity
- Examples catalog, docs, E2E/a11y/perf/browser matrix final gates
상세 진행 상황은 CHECKLIST.md와 TODOLIST.md를 기준으로 관리합니다.
현재 패키지는 UNLICENSED 프리릴리즈 상태입니다.
npm/CDN 또는 외부 배포 전 프로젝트 라이선스와 상용 배포 정책을 확정해야 합니다.


