[Nexacro N] Grid 렌더링 최적화
Nexacro N Grid에서 oncellstyle 미리 계산 맵, suppressredraw 일괄 갱신, 컬럼 최소화, 이벤트 핸들러 경량화 등 렌더링 성능을 높이는 실전 기법을 정리합니다.
지난 글에서 virtualrowcount로 DOM 수를 줄이는 가상화 기술을 다뤘습니다. 가상화와 함께 렌더링 자체를 최적화하면 더 큰 효과를 얻을 수 있습니다. 이번 글에서는 Grid 렌더링 성능을 높이는 다양한 기법을 속성·이벤트·스타일 세 가지 축으로 정리합니다.
렌더링 병목의 원인
Grid 렌더링이 느린 이유는 크게 세 가지입니다.
- 과도한 DOM: 비가상화 상태에서 수천 행의 셀 DOM
- 빈번한 재렌더링: 루프에서 셀 속성을 변경할 때마다 Grid가 다시 그림
- 무거운 이벤트 핸들러: 스크롤마다 호출되는
oncellstyle에서 복잡한 계산
oncellstyle 최적화 — 미리 계산 맵
oncellstyle은 화면에 셀이 그려질 때마다 호출됩니다. 가상화 환경에서는 스크롤할 때도 호출되므로 이 핸들러가 무거우면 스크롤이 끊깁니다.
핵심 원칙: oncellstyle에서는 계산하지 않는다. 미리 계산된 결과만 조회한다.
// 데이터 로드 완료 콜백에서 1회 스타일 맵 구성
function fn_searchCb(svcId, errCode, errMsg) {
fn_buildStyleMap();
}
var g_rowStyles = {};
function fn_buildStyleMap() {
g_rowStyles = {};
for (var r = 0; r < ds.rowcount; r++) {
var nAmt = parseInt(ds.getColumn(r, "AMT") || "0");
var sState = ds.getColumn(r, "STATE");
if (sState == "ERR") {
g_rowStyles[r] = "#ff444433";
} else if (nAmt < 0) {
g_rowStyles[r] = "#ffaa0022";
}
}
}
// oncellstyle: O(1) 맵 조회만
function grd_oncellstyle(obj, e) {
var sBg = g_rowStyles[e.row];
if (sBg) {
e.backgroundColor = sBg;
}
}
Dataset 데이터가 바뀔 때마다 fn_buildStyleMap()을 다시 호출합니다.
suppressredraw — 일괄 갱신
루프에서 셀 스타일이나 값을 변경하면 매번 Grid가 부분 재렌더링됩니다. suppressredraw로 일시 정지하고 한 번에 그립니다.
function fn_markErrorRows(aErrRows) {
grd.set_suppressredraw(true);
for (var i = 0; i < aErrRows.length; i++) {
var nRow = aErrRows[i];
grd.setCellBackgroundColor(nRow, -1, "rgba(224,85,85,0.2)");
}
grd.set_suppressredraw(false);
grd.redraw(); // 한 번에 렌더링
}
-1을 컬럼 인덱스로 넘기면 행 전체에 색이 적용됩니다.
컬럼 최소화
렌더링 대상 컬럼이 많을수록 그리는 시간이 길어집니다.
// 사용자가 열 숨기기를 선택했을 때
function fn_toggleColumn(sColId, bVisible) {
var oCol = grd.getColumnById(sColId);
if (oCol) {
oCol.set_visible(bVisible);
}
}
화면에 표시할 필요가 없는 키 컬럼이나 내부 상태 컬럼은 visible="false"로 숨기면 렌더링 부하가 줄어듭니다.
헤더·바디 포맷셀 최소화
Grid Format Editor에서 헤더와 바디에 많은 포맷셀이 겹쳐 있으면 각 셀마다 레이어 계산이 누적됩니다.
- 빈 포맷셀(내용 없는 투명 div)을 제거합니다.
- 사용하지 않는 서브 레이어(head2, body2 등)를 삭제합니다.
- 이미지가 있는 셀은
useimagecache="true"로 캐싱합니다.
고정 컬럼(leftcolcount) 최소화
고정 컬럼은 스크롤 시 별도 DOM 레이어를 유지하므로 렌더링 비용이 추가됩니다. 필수적인 키 컬럼 1~2개만 고정하고 나머지는 해제합니다.
grd.set_leftcolcount(1); // 첫 번째 컬럼만 고정
이미지 셀 최적화
Grid 셀에 이미지를 표시하는 경우 useimagecache="true"를 설정하면 동일한 이미지 URL을 캐싱해 재사용합니다.
<Grid id="grd" useimagecache="true" ... />
아이콘 이미지는 PNG 개별 파일보다 SVG 인라인이 렌더링에 유리합니다. 작은 상태 아이콘(체크·경고·오류 표시)은 유니코드 이모지나 단색 Unicode Symbol로 대체하면 이미지 요청 자체를 없앨 수 있습니다.
스크롤 이벤트 debounce
onscroll 이벤트는 스크롤 중 매우 빈번하게 발생합니다. 이 이벤트에서 무거운 처리를 하면 스크롤이 버벅입니다.
var g_scrollTimer = null;
function grd_onscroll(obj, e) {
if (g_scrollTimer) clearTimeout(g_scrollTimer);
g_scrollTimer = setTimeout(function() {
fn_onScrollEnd();
}, 150);
}
function fn_onScrollEnd() {
// 스크롤이 멈춘 뒤 150ms 후 1번만 실행
fn_updateVisibleRowInfo();
}
setTimeout을 이용한 debounce 패턴으로 연속 호출을 한 번으로 줄입니다.
렌더링 성능 측정
최적화 전후를 비교하려면 브라우저 개발자 도구의 Performance 탭을 활용합니다. Grid 렌더링 중 Layout, Paint, Composite 단계에서 시간이 얼마나 소요되는지 확인합니다.
Nexacro N의 tracemode도 활용할 수 있습니다.
nexacro.traceMode = true; // 성능 로그 활성화
콘솔에 컴포넌트별 렌더링 시간이 출력되어 병목 지점을 파악할 수 있습니다.
실전 최적화 순서
- virtualrowcount 설정 (가장 효과 큼)
- oncellstyle 핸들러 미리 계산 맵으로 교체
- suppressredraw + redraw() 패턴 적용
- 불필요한 컬럼 visible=false
- 포맷셀 정리 (Format Editor에서 빈 셀 제거)
- 고정 컬럼 최소화
- 이미지 캐싱 활성화
이 순서대로 적용하면 대부분의 성능 문제가 해결됩니다.
지난 글: Grid 가상화(Virtual Scrolling)
다음 글: Grid 트러블슈팅
읽어주셔서 감사합니다. 😊