본문으로 건너뛰기

계층형 데이터 조회하기

일반적으로 화면에 필요한 데이터는 행(Row)과 열(Column)로 구성된 표 형태의 2차원 데이터로 정형화 되어있습니다.

이러한 형태의 데이터는 엑셀에서 흔히 볼 수 있으며, 단일 서식 데이터, 테이블, 그리드, AI 학습 데이터로 자주 사용됩니다.

관계형 데이터베이스의 SQL 결과는 기본적으로 이러한 구조를 가지고 있기 때문에 쿼리를 적절하게 만들면 됩니다.

하지만 메뉴, 조직도, 차트와 같이 계층형 (Nested) 형식의 데이터 구조를 필요로 할 때가 있습니다. JSON으로 데이터 표현하면 다음고 같은 구조를 가집니다.

[{
"MenuID": "CFG",
"MenuName": "환경설정",
"ParentMenuID": "000",
"ParentMenuName": "HandStack",
"ShowYN": "Y",
"MenuType": "D",
"_Children": [
{
"MenuName": "프로그램",
"ParentMenuID": "CFG",
"ParentMenuName": "환경설정",
"ShowYN": "Y",
"MenuType": "D",
"_Children": null
},
{
"MenuName": "배포",
"ParentMenuID": "CFG",
"ParentMenuName": "환경설정",
"ShowYN": "Y",
"MenuType": "D",
"_Children": null
}
]
}]

HandStack에서는 이러한 데이터의 구조를 SQL 만으로 구성 가능한 기능을 제공합니다. 다음은 wwwroot, transact, dbclient 모듈내의 주요 코드를 설명합니다.

dbclient SQL 쿼리

데이터베이스는 SqlServer 쿼리를 사용합니다. 다른 데이터베이스의 경우 계층형 쿼리를 조금 수정할 필요가 있습니다.

<statement id="LD10" seq="0" use="Y" timeout="0" desc="계층형 목록 테스트 조회" modified="2023-08-31 00:00:00">
<![CDATA[
SELECT
ME.EntityNo
, ME.ApplicationNo
, ME.EntityID
, ME.EntityName
, ME.Acronyms
, ME.SeedData
FROM
MetaEntity ME;

SELECT
MF.EntityNo
, MF.FieldID
, MF.FieldName
, MF.FieldType
, MF.PK
, MF.IX
, MF.UI
, MF.NN
, MF.AI
, MF.MaxLength
, MF.DefaultValue
, MF.Comment
, MF.SortingNo
FROM
MetaField MF;

WITH MenuHierarchy AS (
SELECT MenuID
, MenuName
, ParentMenuID
, ParentMenuName
, ShowYN
, MenuType
FROM Menu
WHERE ParentMenuID IS NULL
UNION ALL
SELECT M.MenuID
, M.MenuName
, M.ParentMenuID
, M.ParentMenuName
, M.ShowYN
, M.MenuType
FROM Menu M
INNER JOIN MenuHierarchy MH ON M.ParentMenuID = MH.MenuID
)

SELECT * FROM MenuHierarchy;
]]>
</statement>

transact 거래 설정

BaseFieldRelation 항목을 구성하여 쿼리 결과를 계층형으로 구성하는 설정을 추가합니다. 설정 항목의 의미는 다음과 같습니다.

  • RelationFieldID: 계층형으로 구성되는 데이터를 포함하는 컬럼 ID입니다.
  • BaseSequence: 현재 거래의 SQL 결과는 3개의 데이터 셋을 반환하게 됩니다. 이때 0, 1, 2 와 같은 순서로 계층형 데이터를 추가할 데이터 결과 순번입니다.
  • RelationMappings: 현재 결과를 자식 데이터 소스로 보고 기준 결과의 데이터 관계를 연결할 필수 컬럼 ID를 설정합니다.
  • ColumnNames: 현재 결과중에 계층형 데이터로 내보낼 컬럼 ID을 설정합니다. 빈 값일 경우 모든 컬럼을 내보냅니다.
  • DisposeResult: 현재 결과를 기준 결과에 내보낸 후 삭제 할 것인지 설정합니다.
{
"ServiceID": "LD10",
"ReturnType": "Json",
"CommandType": "D",
"TransactionScope": false,
"Inputs": [
{
"ModelID": "Dynamic",
"Fields": [
],
"TestValues": [],
"DefaultValues": [],
"Type": "Row",
"BaseFieldMappings": [],
"ParameterHandling": "Rejected"
}
],
"Outputs": [
{
"ModelID": "Dynamic",
"Fields": [
],
"Type": "Grid"
},
{
"ModelID": "Dynamic",
"Fields": [
],
"Type": "Grid",
"BaseFieldRelation": {
"RelationFieldID": "EntityFields",
"BaseSequence": 0,
"RelationMappings": [
{
"BaseFieldID": "EntityNo",
"ChildrenFieldID": "EntityNo"
}
],
"ColumnNames": [],
"DisposeResult": true
}
},
{
"ModelID": "Dynamic",
"Fields": [
],
"Type": "Grid",
"BaseFieldRelation": {
"RelationFieldID": "_Children",
"BaseSequence": 2,
"RelationMappings": [
{
"BaseFieldID": "ParentMenuID",
"ChildrenFieldID": "MenuID"
}
],
"ColumnNames": [],
"DisposeResult": false
}
}
]
}

wwwroot 화면 개발

계층형 데이터는 비정형 데이터로 구성에 따라 화면에서 다루어야 할 방법이 다르기 때문에 다음과 같이 syn_data 데이터 컨트롤을 선언하여 연계할 수 있습니다.

<button type="button" id="btnRequestNestedData" syn-events="['click']" class="btn btn-secondary px-4">
<i class="ti ti-plus mr-2"></i>
계층형 데이터 조회
</button>

<syn_data id="srcMetaEntity" syn-options="{
dataSourceID: 'MetaEntity',
storeType: 'Grid',
columns: [
{ data: 'EntityNo', dataType: 'string' },
{ data: 'ApplicationNo', dataType: 'string' },
{ data: 'EntityID', dataType: 'string' },
{ data: 'EntityName', dataType: 'string' },
{ data: 'Acronyms', dataType: 'string' },
{ data: 'SeedData', dataType: 'string' },
{ data: 'EntityFields', dataType: 'object' }
]
}">
</syn_data>
<syn_data id="srcMenuHierarchy" syn-options="{
dataSourceID: 'MenuHierarchy',
storeType: 'Grid',
columns: [
{ data: 'MenuID', dataType: 'string' },
{ data: 'MenuName', dataType: 'string' },
{ data: 'ParentMenuID', dataType: 'string' },
{ data: 'ParentMenuName', dataType: 'string' },
{ data: 'ShowYN', dataType: 'string' },
{ data: 'MenuType', dataType: 'string' },
{ data: '_Children', dataType: 'object' }
]
}">
</syn_data>
event: {
btnRequestNestedData_click() {
syn.$w.transactionAction({
functionID: 'LD10',
inputs: [
{ type: 'Row', dataFieldID: 'MainForm' }
],
outputs: [
{ type: 'Grid', dataFieldID: 'MetaEntity' },
{ type: 'Grid', dataFieldID: 'MenuHierarchy' }
],
callback: function (error, responseObject, addtionalData) {
debugger;

var metaEntity = $this.store.MetaEntity;
var menuHierarchy = $this.store.MenuHierarchy;

// 메뉴, 조직도, 차트와 같이 계층형 (Nested) 형식의 데이터 활용
}
});
}
}