[]
SpreadJS에서 데이터 범위 기능은 시트의 축 시스템 내에서 특정 워크시트 영역을 정의하고 관리할 수 있도록 해줍니다. 이 기능은 특히 복잡한 데이터 구조를 표시하고 상호작용할 때 유용하며, 시트 축과 데이터 범위 축 사이에서 셀 좌표(Row/Col)를 변환하여, 좌표를 수동으로 처리하는 대신 비즈니스 로직에 집중할 수 있도록 합니다.
데이터 범위는 내부 워크시트의 데이터 저장 및 스타일 저장을 추상화하여 사용자 정의 인터페이스를 통해 처리할 수 있도록 해줍니다. 이를 통해 사용자는 일반적인 워크시트 렌더링 및 상호작용 기능을 그대로 활용하면서, 자신의 특화된 데이터 구조를 워크시트에 직접 렌더링할 수 있습니다. 예를 들어, 피벗 테이블은 데이터 분석을 위해 설계된 특수한 데이터 저장 구조이며, 데이터 범위를 사용해 데이터와 스타일을 워크시트에 렌더링합니다. 이를 바탕으로 사용자는 분석 리포트, 재무 리포트, 가상 서버 데이터 뷰 등 피벗 테이블과 유사한 사용자 정의 솔루션을 설계할 수 있습니다.
데이터를 효율적으로 조회하고 집계한 후, 데이터 범위를 통해 렌더링함으로써 워크시트 셀에 데이터를 수동으로 작성하지 않아도 되며, 성능과 사용성 모두를 향상시킬 수 있습니다.
참고: 데이터 범위는 서로 겹쳐지면 안 되며, 고유한 이름을 가져야 합니다.
데이터 관리(Data Management): 대규모 데이터셋 내에서 논리적인 범위를 정의하여 시각화 또는 분석 등 대상 작업을 효율적으로 수행할 수 있습니다.
사용자 정의 데이터 표시(Customizable Data Display): 스타일 지정, 병합, 조건부 서식 등을 지원하여 가독성 향상에 기여합니다.
동적 데이터 상호작용(Dynamic Data Interaction): 이벤트 기반 훅, 고정 행(sticky row), JSON 직렬화를 통해 실시간 업데이트가 가능합니다.
향상된 사용자 경험(Improved User Experience): 고정 헤더, 교차 행 스타일, 키보드 탐색 등의 기능으로 최적의 사용성 제공합니다:
데이터 범위 IO(Data Range IO): 데이터 범위를 JSON 또는 Excel 형식으로 내보내기 가능하며, 일관된 데이터 표현을 유지합니다.
다음 단계를 따라 워크시트에서 데이터 범위를 사용할 수 있습니다:
IDataProvider
인터페이스를 구현하여 사용자 정의 데이터 공급자(Data Provider) 생성
워크시트 내에서 대상 범위 선택
Data Range Manager를 사용하여 데이터 범위를 워크시트에 추가
데이터 공급자(Data Provider)는 사용자 정의 인스턴스로, GC.Spread.Sheets.DataRange.IDataProvider
인터페이스를 구현하면 값 설정, 값 가져오기, 변경 취소(Undo), 셀 값 커스터마이징 등을 처리할 수 있습니다.
셀 값의 읽기 및 쓰기는 데이터 범위 내에서 데이터 공급자에 의해 제어되며, 이 기능을 활용하여 데이터 소스에 편리하게 연결할 수 있습니다.
시트가 데이터 범위 내의 셀을 렌더링할 때는 getValue
메서드를 사용하여 값을 가져옵니다.
마찬가지로, setValue
메서드를 사용하면 데이터 범위 내 셀에 값을 할당할 수 있습니다.다음은 데이터 소스로부터 값을 가져오고 설정하는 고객용 데이터 공급자 예제입니다.
class CustomerDataProvider {
// 값
dataSource = [
["ID", "Name", "Age"],
["1", "Mike", "18"],
["2", "Joe", "19"],
["3", "Nick", "30"],
["4", "Sam", "26"]
]
getValue(row, col) {
return this.dataSource[row][col];
}
setValue(row, col, value) {
this.dataSource[row][col] = value;
return true;
}
}
그러나 변경된 내용을 실행 취소/다시 실행(Undo/Redo)하고 싶다면, 변경 내용을 changes
매개변수에 기록해야 하며, 그런 다음 changes
매개변수를 사용하여 데이터 공급자에서 undo
메서드를 구현해야 합니다.
class CustomerDataProvider {
// 값
dataSource = [
["ID", "Name", "Age"],
["1", "Mike", "18"],
["2", "Joe", "19"],
["3", "Nick", "30"],
["4", "Sam", "26"]
]
getValue(row, col) {
return this.dataSource[row][col];
}
setValue(row, col, value, changes) {
var oldValue = this.getValue(row, col);
if (changes) {
// 실행 취소 시 상태를 복원할 수 있도록 필요한 정보를 포함한 change 객체를 생성합니다.
var change = {
row: row,
col: col,
oldValue: oldValue,
type: "value" // 실행 취소 시 식별하기 쉽도록 change에 키를 지정하여 마킹합니다.
};
changes.push(change);
}
this.dataSource[row][col] = value;
return true;
}
undo(change) {
if (change.type === "value") {// setValue 메서드 내에서 마킹
var row = change.row;
var col = change.col;
var oldValue = change.oldValue;
this.setValue(row, col, oldValue);
}
}
}
getStyle
메서드를 사용하면 테이블 헤더, 본문 스타일, 교차 행 스타일 등 데이터 범위의 외형을 사용자 정의할 수 있습니다.
또한 필요한 경우, GC.Spread.Sheets.Style
클래스를 사용하여 formatter
와 같은 공용 스타일 속성을 데이터 공급자에서 사용할 수 있습니다.
class CustomerDataProvider {
// Styles
headerRowCount = 1;
styles = {
header: () => {
var style = new GC.Spread.Sheets.Style();
style.backColor = "#217346";
style.foreColor = "white";
return style;
},
body: () => {
var style = new GC.Spread.Sheets.Style();
style.foreColor = "#217346";
style.backColor = "lightgray";
return style;
}
}
step = 2;
alternatingRowStyle = () => {
var style = new GC.Spread.Sheets.Style();
style.backColor = "#EEEEEE";
return style;
}
getStyle(row, col) {
// when row index is at the row header area
if (row < this.headerRowCount) {
return this.styles.header();
}
// Alternating Row Styles
if (row % this.step === 0) {
return this.alternatingRowStyle();
} else {
return this.styles.body();
}
}
}
applyColumnIndex
속성을 사용하여 데이터 범위의 열에 조건부 서식을 적용할 수도 있으며, 이를 통해 데이터의 가독성을 향상시킬 수 있습니다.
class CustomerDataProvider {
// Conditional Formatting
headerRowCount = 2;
applyColumnIndex = 2; // the applied conditional format column index
condition = (value) => value > 18;
highlightStyle = () => {
var style = new GC.Spread.Sheets.Style();
style.foreColor = "red";
style.cellPadding = "10 0 0 0";
style.watermark = "ADULT";
style.labelOptions = { visibility: 0, foreColor: "#CCCCCC", font: '10px Arial' };
style.vAlign = GC.Spread.Sheets.VerticalAlign.center;
return style;
}
normalStyle = () => {
var style = new GC.Spread.Sheets.Style();
style.vAlign = GC.Spread.Sheets.VerticalAlign.center;
return style;
}
getStyle(row, col) {
if (row < this.headerRowCount) {
var style = new GC.Spread.Sheets.Style();
style.backColor = "#217346";
style.foreColor = "white";
return style;
}
if (row >= this.headerRowCount) {
// if match the condition
if (col === this.applyColumnIndex && this.condition(this.getValue(row, col))) {
return this.highlightStyle();
}
return this.normalStyle();
}
}
}
데이터 공급자에서 getSpans
메서드를 사용하여 여러 행 또는 열에 걸친 헤더를 생성할 수 있습니다.
class CustomerDataProvider{
headerRowCount = 2;
// Spans
spans = [new GC.Spread.Sheets.Range(0, 0, 2, 1), new GC.Spread.Sheets.Range(0, 1, 1, 2)]
getSpans(row, col, rowCount, colCount) {
const spans = [];
for (let span of this.spans) {
if (span.intersect(row, col, rowCount, colCount)) {
spans.push(span);
}
}
return spans;
}
}
아래 표는 다양한 후크 동작과 해당 반환 파라미터를 보여줍니다. 필요에 따라 데이터 공급자에 구현할 수 있습니다.
Hooks | 입력 파라미터 | 반환값 |
---|---|---|
| row: number; rowCount: number; changeType: "add" | "delete"; changes: any[]; | |
| col: number; colCount: number; changeType: "add" | "delete"; changes: any[]; | |
| name: string; | { name: string, dataProvider: GC.Spread.Sheet.DataRange.IDataProvider | boolean; } 값이 false인 경우, 데이터 범위 복사를 방지합니다. 기본값은 false입니다. |
| row: number; col: number; rowCount: number; colCount: number; changes: any[]; | 값이 false인 경우, 데이터 범위 지우기를 방지합니다. 기본값은 false입니다. 이 훅은 콘텐츠를 지울 때 실행됩니다 |
| 이 훅은 데이터 범위가 제거될 때 실행됩니다. | |
| row: number; col: number; e: MouseEvent; |
|
| deltaX: number; deltaY: number; e: MouseEvent; |
|
| row: number; col: number; e: KeyboardEvent; |
|
| { typeName: string; [prop: string]: any; } 역직렬화를 원할 경우, | |
| { typeName: string; [prop: string]: any; } |
sheet.dataRanges
속성은 워크시트 내에서 여러 데이터 범위를 관리하는 데 사용됩니다. 아래 섹션에 언급된 작업을 수행할 수 있습니다.
새로운 데이터 범위를 이름, 데이터 공급자 인스턴스, 범위와 함께 추가하려면 dataRanges
클래스의 add
메서드를 사용합니다.
sheet.dataRanges.add(
"DataRangeName",
CustomDataProvider,
new GC.Spread.Sheets.Range(2, 2, 4, 4)
);
데이터 범위 이름을 기준으로 데이터 범위를 제거하려면, dataRanges
클래스의 remove
메서드를 사용합니다.
sheet.dataRanges.remove("DataRangeName");
시트의 모든 데이터 범위를 지우려면, dataRanges
클래스의 clear
메서드를 사용합니다.
sheet.dataRanges.clear();
데이터 범위 이름을 기준으로 데이터 범위를 가져오려면, dataRanges
클래스의 get
메서드를 사용하며, 이 메서드는 데이터 범위의 이름을 매개변수로 받습니다.
var dataRange = sheet.dataRanges.get("DataRangeName");
시트의 모든 데이터 범위를 가져오려면, dataRanges
클래스의 all
메서드를 사용합니다.
var dataRanges = sheet.dataRanges.all();
데이터 소스가 변경된 후에는 필요에 따라 데이터 범위의 크기와 위치를 변경하고 다시 렌더링할 수 있습니다.
데이터 범위 영역 자체를 다시 그리려면 repaint
메서드를 사용합니다.
dataRange.repaint();
데이터 범위의 크기를 변경하려면, range
메서드에 새로운 rowCount
와 colCount
값을 전달하면 됩니다.
var oldRange = dataRange.range();
dataRange.range(oldRange.row, oldRange.col, oldRange.rowCount + 1, oldRange.colCount + 1);
데이터 범위의 위치를 변경하려면, range
메서드에 새로운 rowIndex
와 colIndex
값을 전달하면 됩니다.
var oldRange = dataRange.range();
dataRange.range(oldRange.row + 1, oldRange.col + 1, oldRange.rowCount, oldRange.colCount);
데이터 범위의 상단 행을 시트에서 항상 보이도록 고정(stick)할 수 있으며, 고정할 행 수는 sticky
속성을 사용하여 정의할 수 있고, 이후에 변경할 수도 있습니다.
IDataRangeOptions
인터페이스를 사용하여 고정 행(sticky rows) 과 같은 추가 옵션을 구성할 수 있습니다.
sheet.dataRanges.add("DataRangeName", CustomDataProvider, new GC.Spread.Sheets.Range(2, 2, 4, 4), {
sticky: { top: 2 }
});
SpreadJS는 데이터 범위를 JSON, SJS, Excel 형식으로 내보내는 기능을 제공하여, 데이터의 일관된 표현을 보장합니다. 다음은 데이터 범위 내보내기 시 기본적으로 고려해야 할 사항들입니다.
JSON 형식
사용자 정의 데이터 공급자 클래스는 직렬화(serialize)를 직접 수행할 수 없습니다. 따라서, 데이터 공급자 내에 클래스 이름과 동일한 문자열 값을 가진 typeName
을 정의해야 합니다.
또한, 역직렬화(deserialize) 시 해당 사용자 정의 데이터 공급자 클래스를 찾을 수 있도록, 데이터 공급자 클래스가 window
와 같은 전역 인스턴스에 등록되어 있어야 합니다.
class CustomDataProvider {
constructor () {
this.typeName = "CustomDataProvider";
}
}
window.CustomDataProvider = CustomDataProvider;
SJS 형식
SpreadJS 애플리케이션에서 사용하기 위해 데이터 범위를 SpreadJS 형식(SJS) 으로 내보낼 때, 모든 JSON 데이터는 손실 없이 저장되는 형식(lossless
format) 으로 유지됩니다.
Excel 형식
Excel은 데이터 범위 기능을 지원하지 않기 때문에, 이러한 범위는 값과 스타일이 포함된 일반 셀 형식으로 내보내집니다.
SpreadJS에서 데이터 범위를 사용할 때 고려해야 할 중요한 정책은 다음과 같습니다:
행과 열 인덱스는 반드시 0 이상이며, 시트의 전체 행/열 수 이내여야 합니다.
행과 열 수에 대한 제한은 없지만, 워크시트 범위를 벗어나는 데이터 범위 영역은 무시됩니다.
데이터 공급자 후크에 전달되는 모든 입력 파라미터는 시트 축이 아닌 데이터 범위 축을 기준으로 합니다 (예: row, column).
데이터 범위 앞에 행 또는 열이 삽입되거나 삭제되면 데이터 범위가 이동됩니다.
반대로, 데이터 범위 외부에서 삽입 또는 삭제가 발생한 경우, 데이터 범위에는 아무런 영향도 주지 않습니다.
전체 데이터 범위를 포함하는 셀 범위를 Clear(지우기) 할 경우, 해당 데이터 범위는 제거됩니다.
필터 대화상자 및 컨텍스트 메뉴에서의 정렬 옵션(Sort options) 은 현재 컨텍스트가 데이터 범위일 경우 숨겨집니다.
데이터 공급자에 toJSON
후크가 구현되어 있지 않은 경우, SpreadJS는 사용자 정의 데이터 공급자의 typeName
속성을 기반으로 기본 JSON 출력을 생성합니다.
fromJSON
사용 시, 일치하는 데이터 공급자가 존재하지 않으면 해당 데이터 범위는 생성되지 않습니다.