# 계층 데이터

## Content

SpreadJS는 데이터 소스가 계층적임을 정의하기 위해 데이터 스키마에 hierarchy 옵션을 제공합니다.
hierarchy 옵션은 **[GC.Data.IDataSourceOption](https://developer.mescius.com/spreadjs/api/modules/GC.Data#idatasourceoption)** 인터페이스로부터 상속되며, 다음과 같은 매개변수를 제공합니다:

* **type**: 계층 유형을 정의합니다. 사용 가능한 값은 Parent, ChildrenPath, Level 또는 Custom입니다.
* **column**: 계층 데이터를 구성하는 데 사용할 키 열을 지정합니다.
* **levelOffset**: 계층 수준을 증가 또는 감소시킬 수 있는 오프셋을 나타냅니다. 일반적으로 계층 수준은 0에서 시작합니다.
* **outlineColumn**: **GC.Data.IHierarchyOutlineColumnOptions**로부터 상속되며, 계층으로 표시할 수 있는 열을 지정합니다.
* **summaryFields**: **GC.Data.IHierarchySummaryFieldCollection**로부터 상속되며, 필드에 대한 수식(공식)을 정의합니다.
* **parse**: **GC.Data.IHierarchyCustomParseOptions**로부터 상속되며, 커스텀 계층 유형의 기본 키를 부모 키로 파싱합니다.
* **unparse**: **GC.Data.IHierarchyCustomUnparseOptions**로부터 상속되며, 커스텀 계층 유형의 기본 키를 생성합니다.

## 계층 유형

계층에는 Parent, Level, ChildrenPath, Custom의 네 가지 유형이 있습니다. 각 유형은 데이터 매니저에 테이블을 추가할 때 구성할 수 있습니다:

#### Parent 계층

Parent 계층에서는 hierarchy type을 ‘Parent’로 설정하며, 기본 키는 계층 ID를 나타냅니다.
![parent-hierarchy.0a0565](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/parent-hierarchy.0a0565.794f2a.png)

다음 코드는 테이블 시트에서 Parent 계층을 설정하는 방법을 보여줍니다:

```JavaScript
<script>
        var spread = new GC.Spread.Sheets.Workbook(document.querySelector('#ss'), { sheetCount: 0 });
        initSpread(spread)
        function initSpread(spread) {
            var dataManager = spread.dataManager();

            initHierarchyParentType(spread, dataManager);

        }
        function initHierarchyParentType(spread, dataManager) {
            var table = dataManager.addTable("hierarchyParentTable", {
                data: initHierarchyParentData(),
                schema: {
                    hierarchy: {
                        type: 'Parent', // 부모 계층 유형 구성
                        column: 'parentId', // 계층적 보기를 구성하는 데 사용할 수 있는 열을 지정
                    },
                    columns: {
                        'id': { dataName: 'id', isPrimaryKey: true }, // 기본 키는 필수
                        'parentId': { dataName: 'parentId', dataType: "reorder" },
                    }
                }
            });
            var sheet = spread.addSheetTab(0, "HierarchyParent", GC.Spread.Sheets.SheetType.tableSheet);
            sheet.setDefaultRowHeight(40, GC.Spread.Sheets.SheetArea.colHeader);
            sheet.options.allowAddNew = true;

            table.fetch().then(function () {
                var myView = table.addView("myView", [
                    {
                        value: "parentId", width: 200,
                        outlineColumn: {
                            showImage: true,
                            images: ['https://en.onboarding.grapecitydev.com/spreadjs/demos/spread/source/images/task-1.png', 'https://en.onboarding.grapecitydev.com/spreadjs/demos/spread/source/images/task-2.png', 'https://en.onboarding.grapecitydev.com/spreadjs/demos/spread/source/images/task-3.png'],
                            expandIndicator: 'https://en.onboarding.grapecitydev.com/spreadjs/demos/spread/source/images/increaseIndicator.png',
                            collapseIndicator: 'https://en.onboarding.grapecitydev.com/spreadjs/demos/spread/source/images/decreaseIndicator.png',
                        },

                    },
                    { value: "id", width: 200 },
                ]);

                sheet.setDataView(myView);
            });
        }
        function initHierarchyParentData() {
            var data = [
                { id: 1, parentId: -1 },
                { id: 2, parentId: -1 },
                { id: 3, parentId: 1 },
                { id: 4, parentId: 1 },
                { id: 5, parentId: 2 },
                { id: 6, parentId: 2 },
                { id: 6, parentId: 2 },
            ];
            return data;
        }
</script>
```

#### ChildrenPath 계층

ChildrenPath 계층에서는 hierarchy type을 ‘ChildrenPath’로 설정합니다. 이 경우 기본 키는 필수가 아니지만, 자식 항목에 대해 지정하는 것이 좋습니다.
![childrenpath-hierarchy.8ff026](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/childrenpath-hierarchy.8ff026.c87a35.png)

다음 코드는 테이블 시트에서 ChildrenPath 계층을 설정하는 방법을 보여줍니다:

```JavaScript
<script>
        var spread = new GC.Spread.Sheets.Workbook(document.querySelector('#ss'), { sheetCount: 0 });
        initSpread(spread)
        function initSpread(spread) {
            var dataManager = spread.dataManager();
            initHierarchyParentType(spread, dataManager);
        }
        function initHierarchyParentType(spread, dataManager) {
            var taskTable = dataManager.addTable("Tasks", {
                data: initHierarchyChildData(),
                schema: {
                    hierarchy: {
                        type: 'ChildrenPath',
                        column: 'TaskChildren',
                    },
                    columns: {
                        TaskName: { dataName: 'name' },
                        TaskChildren: { dataName: 'children' },
                        // other columns in the child
                    }
                }
            });
            var sheet = spread.addSheetTab(0, "HierarchyParent", GC.Spread.Sheets.SheetType.tableSheet);
            sheet.setDefaultRowHeight(40, GC.Spread.Sheets.SheetArea.colHeader);
            sheet.options.allowAddNew = true;
            taskTable.fetch().then(r => {
                var taskView = taskTable.addView('TaskView', [
                    {
                        value: 'TaskName', outlineColumn: true, width: "*" // 이 옵션은 윤곽 열로 표시되는 열을 나타냄
                    }
                ]);
                sheet.setDataView(taskView);
            })
        }
        function initHierarchyChildData() {
            var data = [
                {
                    name: 'USA',
                    children: [
                        {
                            name: 'Texas',
                            children: [
                                {
                                    name: 'Houston',
                                },
                                {
                                    name: 'Dallas',
                                },
                                {
                                    name: 'San Antonio',
                                }
                            ]
                        }
                    ]
                },
                {
                    name: 'India',
                    children: [
                        {
                            name: 'UP',
                            children: [
                                { name: 'Noida' },
                                { name: 'Ghaziabad' },
                                { name: 'Agra' },
                            ]
                        }
                    ]
                }
            ]
            return data;
        }
</script>
```

#### Level 계층

Level 계층에서는 hierarchy type을 ‘Level’로 설정합니다. 이 경우도 기본 키는 필수가 아니지만, 지정해주는 것이 좋습니다.
![level-hierarchy.001cb2](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/level-hierarchy.001cb2.f43073.png)

다음 코드는 테이블 시트에서 Level 계층을 설정하는 방법을 보여줍니다:

```JavaScript
<script>
        var spread = new GC.Spread.Sheets.Workbook(document.querySelector('#ss'), { sheetCount: 0 });
        initSpread(spread)
        function initSpread(spread) {
            var dataManager = spread.dataManager();

            initHierarchyParentType(spread, dataManager);
        }
        function initHierarchyParentType(spread, dataManager) {
            var taskTable = dataManager.addTable("Tasks", {
                data: initHierarchyLevelData(),
                schema: {
                    hierarchy: {
                        type: 'Level',
                        column: 'TaskLevel',
                    },
                    columns: {
                        TaskName: { dataName: 'name' },
                        TaskId: { dataName: 'id', isPrimaryKey: true }, // 기본 키를 사용하여 계층 ID를 표시 (존재하는 경우 선택적으로 사용)
                        TaskLevel: { dataName: 'level' },
                    }
                }
            });
            var sheet = spread.addSheetTab(0, "HierarchyParent", GC.Spread.Sheets.SheetType.tableSheet);
            sheet.setDefaultRowHeight(40, GC.Spread.Sheets.SheetArea.colHeader);
            sheet.options.allowAddNew = true;
            taskTable.fetch().then(r => {
                var taskView = taskTable.addView('TaskView', [
                    {
                        value: 'TaskName', outlineColumn: true // 이 옵션은 윤곽 열로 표시되는 열을 나타냄
                    }
                ]);

                sheet.setDataView(taskView);
            })
        }
        function initHierarchyLevelData() {
            var data = [
                { name: 'USA', level: -1, id: 1 },
                { name: 'Texas', level: 0, id: 2 },
                { name: 'Houston', level: 1, id: 3 },
                { name: 'California', level: 0, id: 4 },
                { name: 'San Francisco', level: 1, id: 5 },
                { name: 'Los Angeles', level: 1, id: 6 },
            ];
            return data;
        }
 </script>
```

#### Custom 계층

Custom 계층에서는 hierarchy type을 ‘Custom’으로 설정합니다. 이 경우도 기본 키는 선택 사항입니다.
![custom-hierarchy.7ef0f0](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/custom-hierarchy.7ef0f0.bb0797.png)

다음 코드는 테이블 시트에서 Custom 계층을 설정하는 방법을 보여줍니다:

```javascript
<script>
        var spread = new GC.Spread.Sheets.Workbook(document.querySelector('#ss'), { sheetCount: 0 });
        initSpread(spread)
        function initSpread(spread) {
            var dataManager = spread.dataManager();

            initHierarchyParentType(spread, dataManager);

        }
        function initHierarchyParentType(spread, dataManager) {
            var taskTable = dataManager.addTable("Tasks", {
                data: initHierarchyChildData(),
                schema: {
                    hierarchy: {
                        type: 'Custom',
                        column: 'id',
                        parse: function (options) {
                           // 기본 키 "1.1.1"을 "1.1"로 파싱
                           // 반환된 값은 parentId로 처리
                            let seg = options.data.TaskId.split('.');
                            return seg.slice(0, seg.length - 1).join('.')
                        },
                        unparse: function (options) {
                            let parentIds, currentIndex = options.index, parentData = options.parentData, parentKey = parentData && parentData.TaskId;
                            if (parentKey) {
                                let sp = parentKey.split('.');
                                parentIds = sp;
                            } else {
                                parentIds = [];
                            }
                            parentIds.push(currentIndex + 1);
                            return parentIds.join('.');
                        }
                    },
                    columns: {
                        TaskName: { dataName: 'name' },
                        TaskId: { dataName: 'id', isPrimaryKey: true }, // 기본 키를 사용하여 선택적으로 계층 ID를 표시
                    }
                }
            });
            var sheet = spread.addSheetTab(0, "HierarchyParent", GC.Spread.Sheets.SheetType.tableSheet);
            sheet.setDefaultRowHeight(40, GC.Spread.Sheets.SheetArea.colHeader);
            sheet.options.allowAddNew = true;

            taskTable.fetch().then(r => {
                var taskView = taskTable.addView('TaskView', [
                    {
                        value: 'TaskName', outlineColumn: true, width: "*" // 이 옵션은 윤곽 열로 표시되는 열을 표시
                    }
                ]);
                sheet.setDataView(taskView);
            })
        }
        function initHierarchyChildData() {
            var data = [
                {
                    id: '1', name: "1"
                },
                { id: '2', name: '2' },
                { id: '1.1', name: "1.1" },
                { id: '1.1.1', name: '1.1.1' },
                { id: '2.1', name: '2.1' }
            ]
            return data;
        }
</script>
```

## 계층 작업

테이블 시트는 다음과 같은 계층 작업을 지원합니다:

* **지정한 행의 계층 수준을 승격**

테이블 시트는 **promoteHierarchyLevel(row: number)** 메서드를 사용하여 지정한 인덱스의 계층 데이터를 승격할 수 있습니다.

```JavaScript
tableSheet.promoteHierarchyLevel(8);
```

* **지정한 행의 계층 수준을 강등**

테이블 시트는 **demoteHierarchyLevel(row: number, withChildren?: boolean)** 메서드를 사용하여 지정한 행의 계층 수준을 강등할 수 있습니다.

```JavaScript
tableSheet.demoteHierarchyLevel(8);
```

* **지정한 행의 계층 데이터를 위로 이동**

테이블 시트는 **moveUp(row: number)** 메서드를 사용하여 지정한 인덱스의 계층 데이터를 위로 이동할 수 있습니다.

```JavaScript
tableSheet.moveUp(8);
```

* **지정한 행의 계층 데이터를 아래로 이동**

테이블 시트는 **moveDown(row: number)** 메서드를 사용하여 지정한 인덱스의 계층 데이터를 아래로 이동할 수 있습니다.

```JavaScript
tableSheet.moveDown(8);
```

* **선택한 행 다음에 레코드 추가**

테이블 시트는 **addHierarchyItemAfter(row: number, rowData: any)** 메서드를 사용하여 지정한 행 다음에 새 행을 추가할 수 있습니다.

```JavaScript
tableSheet.addHierarchyItemAfter(8, {id: 8, name: "grapecity"});
```

* **선택한 행 위에 레코드 추가**

테이블 시트는 **addHierarchyItemAbove(row: number, rowData: any)** 메서드를 사용하여 선택한 행 위에 레코드를 추가할 수 있습니다. 추가된 레코드는 선택한 레코드의 위치를 대체하고, 선택한 레코드는 추가된 레코드의 자식이 됩니다.

```JavaScript
tableSheet.addHierarchyItemAbove(8, {id: 8, name: "grapecity"});
```

* **선택한 행 아래에 레코드 추가**

테이블 시트는 **addHierarchyItemBelow(row: number, rowData: any)** 메서드를 사용하여 지정한 행의 자식으로 새 행 데이터를 추가할 수 있습니다. 추가된 레코드는 선택한 레코드의 마지막 자식이 됩니다.

```JavaScript
tableSheet.addHierarchyItemBelow(8, {id: 8, name: "grapecity"});
```

* **레코드 삭제**

테이블 시트 해당 레코드와 그 하위 레코드를 삭제할 수 있습니다.

* **레코드 확장**

이 메뉴 항목은 열 머리글을 클릭할 때 나타납니다. **expandAllHierarchyLevels()** 메서드를 사용하여 모든 계층 수준을 확장합니다.

```JavaScript
tableSheet.expandAllHierarchyLevels();
```

* **레코드 축소**

이 메뉴 항목은 열 머리글을 클릭할 때 나타나며, **collapseAllHierarchyLevels()** 메서드를 사용하여 모든 계층 수준을 축소합니다.

```JavaScript
tableSheet.collapseAllHierarchyLevels();
```

## **메뉴 항목 가시성 전환**

Promote, Demote, 위/아래로 이동, 전/후/상/하 위치에 추가, 계층 확장/축소 레벨 등의 메뉴 항목은 런타임 시 표시하거나 숨길 수 있습니다.
아래 스냅샷은 행 머리글을 클릭했을 때 표시되는 메뉴 항목을 보여줍니다.
![row-rightclick.d8db7e](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/row-rightclick.d8db7e.83b204.png)

아래 스냅샷은 열 머리글을 마우스 오른쪽 버튼으로 클릭했을 때 표시되는 메뉴 항목을 보여줍니다.

![column-rightclick.02b3eb](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/column-rightclick.02b3eb.70e04a.png)

다음 코드 샘플은 메뉴 항목의 가시성을 전환하는 방법을 보여줍니다.

```JavaScript
// 계층형 레코드를 위한 메뉴 항목 표시
sheet.options.menuItemVisibility = {
  // 아래 옵션은 행 머리글에 표시
promoteMenuItemVisible: true,
demoteMenuItemVisible: true,
  // 아래 옵션은 열 머리글에 표시
expandAllLevelMenuItemVisible: true,
collapseAllLevelMenuItemVisible: true,
expandToLevelMenuItemVisible: true,
  // 아래 옵션은 행 머리글에 표시되며,
  // 해당 열의 dataType이 rowOrder일 경우 메뉴 항목이 활성화
moveUpMenuItemVisible: true,
moveDownMenuItemVisible: true,
addBeforeMenuItemVisible: true,
addAfterMenuItemVisible: true,
addAboveMenuItemVisible: true,
addBelowMenuItemVisible: true,
};
```

>type=warning
> **참고:** `groupBy`, 행 고정(pinning rows), `resetRow` 등의 일부 기능은 계층형 데이터와 함께 사용할 경우 정상 동작하지 않을 수 있습니다.

## **SpreadJS 디자이너 사용하기**

계층형 데이터는 SpreadJS 디자이너에서도 지원되며, Data Source 및 테이블 시트 리본의 **열(Columns) 탭**을 통해 설정할 수 있습니다. 계층 유형, 요약 수식, 아웃라인 열 이미지 등을 구성할 수 있습니다.

![datasource-hierarchy-designer.0ea922](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/datasource-hierarchy-designer.0ea922.cae737.png?width=941&caption=datasource-hierarchy)
**계층(Hierarchy) 섹션**은 다음 옵션들을 제공합니다:

* <strong>계층 유형(Hierarchy Type)</strong>: Parent, ChildrenPath, Level 중 하나의 유형을 설정할 수 있습니다.

![hierarchy-type-design.469f63](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/hierarchy-type-design.469f63.350fa1.png)

* **요약 수식(Summary Formula)**: 테이블의 특정 열에 대해 계층 요약 수식을 입력할 수 있습니다. 요약 수식에 대한 자세한 내용은 [수식](/spreadjs/docs/v18/features/tablesheet/hierarchy-data/formulas) 항목을 참조하시기 바랍니다.
* **개요 열(Outline Column)**: Outline Column은 아웃라인 열의 표시를 사용자 지정하는 데 사용됩니다.

또한, 테이블 시트(Table Sheet) 디자인 리본의 **메뉴 항목 표시(Menu Item Visibility) 버튼**을 통해 계층 작업 메뉴 항목의 표시 여부도 구성할 수 있습니다.
![menu-item-visibility.90d7e6](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/menu-item-visibility.90d7e6.48d19d.png)
**메뉴 항목 표시(Menu Item Visibility) 버튼**을 클릭하면 **메뉴 항목 표시(Menu Item Visibility) 대화상자**가 열립니다.
![menu-visible-dialog.657796](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/menu-visible-dialog.657796.ebc6b8.png)