안녕하세요! 오늘 OPEN_GRID v0.2.0을 공개합니다.
OPEN_GRID는 AUI Grid 같은 상용 그리드를 대체할 수 있는 무료 오픈소스 데이터 그리드 라이브러리입니다. Vanilla JS 코어 위에 React, Vue 3, jQuery, AngularJS, Vanilla 5개 프레임워크를 모두 지원합니다.
🏗️ 오늘의 핵심 작업 — 대규모 OOP 리팩토링
v0.2.0의 가장 큰 변화는 핵심 엔진 코드의 전면 재설계입니다.
단 하루 만에 3,032줄짜리 거대 파일 하나를 12개의 전문 클래스로 분리했습니다.
📊 Before vs After 요약
| 항목 | Before (리팩토링 전) | After (오늘) |
|---|---|---|
| OpenGrid.ts 줄 수 | 3,032줄 | 917줄 (−70%) |
| 클래스 수 | 1개 (모든 것이 한 파일에) | 13개 (역할별 분리) |
| 단위 테스트 | 354개 PASS | 354개 PASS ✅ |
| TypeScript 오류 | 0개 | 0개 ✅ |
| 코드 가독성 | “어디에 뭐가 있지?” | “이 기능은 이 파일” |
🔴 Before — “신 객체(God Object)” 문제
리팩토링 전 OpenGrid.ts는 이런 상태였습니다:
// OpenGrid.ts — 3,032줄짜리 거대 파일
export class OpenGrid {
// 정렬 상태
private _sortList: SortItem[] = [];
// 필터 상태
private _filters: Record<string, FilterItem[]> = {};
// 그룹핑 상태
private _groupFields: string[] = [];
private _groupExpandedKeys: Set<string> = new Set();
private _groupFlatRows: Array<GroupRow | T> = [];
private _isGroupMode = false;
// 트리 상태
private _treeRoots: TreeNode[] = [];
private _treeFlatRows: TreeNode[] = [];
private _treeExpandedKeys: Set<any> = new Set();
private _isTreeMode = false;
// 찾기 바 상태
private _findBar: HTMLElement | null = null;
private _findInput: HTMLInputElement | null = null;
private _findFilter: string = '';
// 키보드, 클립보드, 내보내기, 푸터, 이벤트, 트리거...
// 모두 여기 한 곳에! 😱
// _handleKeyDown() 168줄
// exportExcel() 111줄
// _renderFooterEl() 88줄
// handleCellClick() 71줄
// _rebuildGroups() ...
// _rebuildTree() ...
// (끝없이 계속)
}
문제점:
- 🔴 파일 하나가 3,032줄 — 어디서 뭘 찾아야 할지 모름
- 🔴 “정렬 기능 수정하러 들어갔다가 트리 코드와 싸움”
- 🔴 새 기능 추가할수록 파일이 무한정 커짐
- 🔴 한 곳을 고치면 다른 곳이 영향받을까 봐 두려움
🟢 After — 각자 맡은 역할만 하는 12개 클래스
리팩토링 후 구조입니다:
// ✅ 지금은 이렇게 깔끔합니다
OpenGrid.ts (917줄) ← 총괄 조정자
├── GridRenderer.ts ← 화면에 그리는 일만
├── RowManager.ts ← 행 선택/체크만
├── CellEditManager.ts ← 셀 편집 라이프사이클만
├── CellTypeRegistry.ts ← 셀 타입 판별만
├── CellEventHandler.ts ← 셀 클릭/마우스 이벤트만
├── KeyboardManager.ts ← 키보드/클립보드만
├── SortFilterManager.ts ← 정렬/필터 상태와 로직만
├── GroupTreeManager.ts ← 그룹핑/트리 상태와 로직만
├── FindBarManager.ts ← 찾기 바 UI와 검색만
├── ExportManager.ts ← Excel/CSV/JSON 내보내기만
├── FooterManager.ts ← 푸터 집계 계산과 렌더만
└── TriggerManager.ts ← 이벤트 트리거 훅만
🔍 Before vs After — 구체적인 예시
예시 1 — 키보드 네비게이션
// ❌ Before: OpenGrid.ts 안 어딘가에 168줄짜리 괴물 메서드
private _handleKeyDown(e: KeyboardEvent): void {
if (this._editMgr.activeEditor) return;
this._handleCellKeyEvt('cellKeyDown', e);
const totalRows = this._data.rowCount;
const totalCols = this._colLayout.visibleLeaves.length;
if ((e.ctrlKey) && e.key === 'c') { this._copyToClipboard(); return; }
if ((e.ctrlKey) && e.key === 'v') { this._pasteFromClipboard(); return; }
// ... 100줄 더 ...
switch (e.key) {
case 'ArrowDown': /* ... */ break;
case 'ArrowUp': /* ... */ break;
// ...
}
}
// ✅ After: KeyboardManager.ts 파일에 깔끔하게 분리
// OpenGrid.ts 에는 단 1줄만 남음
private _handleKeyDown(e: KeyboardEvent): void {
this._kbdMgr.handleKeyDown(e); // 끝!
}
// 실제 로직은 KeyboardManager.ts 에서 관리
export class KeyboardManager {
handleKeyDown(e: KeyboardEvent): void { /* ... */ }
private _copyToClipboard(): void { /* ... */ }
private _pasteFromClipboard(): void { /* ... */ }
}
예시 2 — Excel 내보내기
// ❌ Before: OpenGrid.ts 안에 111줄짜리 exportExcel + 관련 헬퍼들
exportExcel(options?: ExportOptions): void {
// 111줄의 복잡한 로직이 OpenGrid 안에...
import('xlsx-js-style').then(({ utils, writeFile }) => {
const rows: any[][] = [];
// 스타일, 색상, 컬럼 너비, 셀 병합...
});
}
private _readCssVar(name: string): string { ... }
private _hexToXlsxRgb(hex: string): string { ... }
// ✅ After: ExportManager.ts 파일로 완전 분리
// OpenGrid.ts 에는 위임만
exportExcel(options?: ExportOptions): void {
this._exportMgr.exportExcel(options); // 끝!
}
exportCsv(options?: ExportOptions): void {
this._exportMgr.exportCsv(options); // 끝!
}
exportSheetsExcel(filename?: string): void {
this._exportMgr.exportSheetsExcel(filename); // 끝!
}
예시 3 — 그룹/트리 상태 관리
// ❌ Before: OpenGrid.ts 안에 흩어진 그룹+트리 상태와 메서드들
private _groupFields: string[] = [];
private _groupExpandedKeys: Set<string> = new Set();
private _groupFlatRows: Array<GroupRow | T> = [];
private _isGroupMode = false;
private _treeRoots: TreeNode[] = [];
private _treeFlatRows: TreeNode[] = [];
private _treeExpandedKeys: Set<any> = new Set();
private _isTreeMode = false;
// + _rebuildGroups(), _rebuildTree(), _getSummaryDefs()
// + groupBy(), clearGroup(), expandAll(), collapseAll()
// + enableTree(), disableTree(), expandNodes()... 모두 한 파일에!
// ✅ After: GroupTreeManager.ts 가 그룹/트리 모든 것을 책임
export class GroupTreeManager {
// 상태 → 이 클래스 안에만
private _groupFields: string[] = [];
private _isGroupMode = false;
private _treeRoots: TreeNode[] = [];
private _isTreeMode = false;
// ...
// 메서드 → 이 클래스 안에만
groupBy(fields: string[]): void { ... }
enableTree(): void { ... }
rebuildGroups(): void { ... }
}
// OpenGrid.ts 는 그냥 전달만
groupBy(fields: string[]): void {
this._grpMgr.groupBy(fields); // 끝!
}
💡 왜 이게 중요한가요?
1. “찾기 쉬워졌다”
이제 버그가 생기면 어디서 고쳐야 할지 바로 알 수 있습니다.
- Excel 내보내기 버그 →
ExportManager.ts열기 - 키보드 동작 이상 →
KeyboardManager.ts열기 - 그룹핑 오동작 →
GroupTreeManager.ts열기
2. “안전하게 고칠 수 있다”
각 클래스는 의존성 주입(Deps 인터페이스)으로 연결되어 있어서,
한 클래스를 수정해도 다른 클래스에 영향이 가지 않습니다.
3. “테스트가 쉬워진다”
거대한 God Class는 테스트가 어렵습니다. 작게 분리된 클래스는
각각 독립적으로 테스트할 수 있습니다. 현재 354개 단위 테스트 ALL PASS.
4. “새 기능 추가가 쉬워진다”
앞으로 새 기능을 추가할 때 기존 코드를 거의 건드리지 않고
새 Manager 클래스만 추가하면 됩니다.
✨ 주요 기능
- 📊 가상 스크롤 — 수만 행도 부드럽게
- 🔀 정렬 · 필터 — 멀티 정렬, 고급 필터 패널
- ✏️ 인라인 편집 — text / number / date / select / radio / checkbox
- 🌳 트리 · 그룹 — 계층형 데이터, 집계 요약
- 📦 Excel · CSV · JSON 내보내기 — 테마 스타일 포함
- 🖼️ 셀 병합 · 고정 행·열
- ♿ 웹 접근성 — WCAG 2.5 키보드 네비게이션, aria-live
- 🎨 컨텍스트 메뉴 · 마스킹 · 워크시트(탭)
🚀 5개 프레임워크 완전 지원
| 프레임워크 | 데모 |
|---|---|
| React 18 | 바로가기 |
| Vue 3 | 바로가기 |
| jQuery | 바로가기 |
| Angular | 바로가기 |
| Vanilla JS | 바로가기 |
🔗 링크
- 🌐 라이브 데모
- 📖 개발자 가이드 (35챕터)
단위 테스트 354개 ALL PASS · MIT 라이선스