본문으로 건너뛰기

TreeView

이 컨트롤은 무엇인가요?

TreeView는 부모-자식 관계를 가진 계층형(트리) 데이터를 화면에 펼쳐 보여주는 컨트롤입니다. 내부적으로는 FancyTree 라이브러리를 감싸서 syn.uicontrols.$tree 싱글턴 객체로 제공합니다.

조직도, 메뉴 트리, 폴더 구조처럼 "상위 항목 아래 하위 항목이 계속 붙는" 형태의 데이터를 다룰 때 사용합니다. 평평한(flat) 배열 데이터를 넣으면 컨트롤이 알아서 parentID 기준으로 계층 구조를 만들어 그려주고, 반대로 화면에 그려진 트리를 다시 평평한 배열로 꺼내올 수도 있습니다.

언제 사용하나요?

  • 조직도/부서 목록, 메뉴/프로그램 목록처럼 계층 구조를 가진 데이터를 트리 형태로 보여줘야 할 때
  • 트리 노드를 검색어로 필터링하거나(filter 확장), 체크박스로 다중 선택해서 값을 가져와야 할 때
  • 노드를 우클릭했을 때 "추가/이름변경/삭제" 같은 메뉴를 띄워야 할 때 — 이 경우 TreeView 단독으로는 우클릭 메뉴를 그리지 못하므로, ContextMenu 컨트롤과 함께 사용합니다. target에 트리 컨테이너 셀렉터(#트리ID)를, delegate에 노드 타이틀 셀렉터(span.fancytree-title)를 지정하는 방식입니다.

빠른 시작

<syn_tree id="tvlTreeView" syn-options="{
itemID: 'key',
parentItemID: 'parentID',
childrenID: 'children',
reduceMap: { key: 'ProgramID', title: 'ProgramName', parentID: 'ParentID', folder: 'FolderYN', icon: false }
}" syn-events="['click', 'dblclick']"></syn_tree>
'use strict';
let $samplePage = {
event: {
tvlTreeView_dblclick(evt, data) {
// data.node.data 에 원본 행 데이터가 그대로 들어 있습니다.
syn.$l.eventLog('tvlTreeView_dblclick', data.node.data.ProgramName);
}
}
}

값을 채워 넣을 때는 setValue에 평평한(flat) 배열을 그대로 넘기면 됩니다. 각 항목은 reduceMap에 지정한 컬럼명(ProgramID, ProgramName, ParentID, FolderYN)을 가지고 있어야 합니다.

syn.uicontrols.$tree.setValue('tvlTreeView', [
{ ProgramID: 'ROOT', ProgramName: '전체', ParentID: null, FolderYN: 'Y' },
{ ProgramID: 'A01', ProgramName: '인사팀', ParentID: 'ROOT', FolderYN: 'Y' },
{ ProgramID: 'A0101', ProgramName: '홍길동', ParentID: 'A01', FolderYN: 'N' }
]);

핵심 포인트:

  • 마크업은 <syn_tree> 커스텀 태그로 작성하고, syn-optionsitemID/parentItemID/childrenID/reduceMap 등을 JSON으로 넣습니다.
  • 페이지에 <script src="/js/syn.loader.js"></script> 한 줄만 있으면 FancyTree를 포함한 나머지 CSS/JS는 자동으로 로드됩니다. 별도의 pageLoadFiles 설정이 필요 없습니다.
  • getValue/setValue는 항상 "평평한 배열"을 주고받습니다. 트리 안에서만 계층(중첩) 구조로 다뤄지고, 컨트롤 밖으로 나올 때는 평평한 형태로 변환됩니다.
  • checkbox: true로 설정하면 체크박스를 통한 다중 선택이 가능해집니다.

예제 실행하기

example/ 폴더의 각 HTML 파일을 handstack의 wwwroot 정적 서버(예: rdy 프로젝트) 경로 아래에 두고 브라우저로 열면 바로 동작을 확인할 수 있습니다.

  • basic.html : 기본 트리 표시 + getValue/setValue/clear/getControl 기능 확인
  • checkbox.html : checkbox: true로 다중 선택하고 선택된 노드 목록을 가져오는 예제
  • contextmenu.html : ContextMenu와 결합해서 노드 우클릭 메뉴를 띄우는 예제
  • filter.html : filter 확장으로 입력한 텍스트에 맞는 노드만 찾아 보여주는 예제

각 예제는 화면 하단 로그 영역(syn.$l.eventLog 출력)에서 이벤트 발생 순서와 전달값을 확인할 수 있습니다.

더 알아보기

  • API 상세는 같은 폴더의 API.md를 참고하세요.
  • 실제 소스: wwwroot/uicontrols/TreeView/TreeView.js, TreeView.css
  • 기존 샘플: wwwroot/sample/uicontrol/treeview.html
  • 실사용 예: qcn.groupware modules/easywork/wwwroot/easywork/view/HDS/EWP/ATM020.html (tplDepartment 다이얼로그의 소속조직 트리) — 검색창(txtTreeNodeFilter)으로 필터링하고, 더블클릭으로 노드를 선택하는 실제 화면 흐름을 참고할 수 있습니다.
  • ContextMenu와 함께 쓰는 방법: ../ContextMenu/README.md, ../ContextMenu/API.md
  • FancyTree 원본 문서: https://github.com/mar10/fancytree/wiki

실전 예제 페이지

/uicontrols/TreeView/example/ 경로의 예제를 아래 iframe에서 바로 확인할 수 있습니다.

basic.html

checkbox.html

contextmenu.html

filter.html

소스와 로드 파일

항목내용
컨트롤TreeView
전역 별칭syn.uicontrols.$tree
버전v2025.3.1
소스uicontrols/TreeView/TreeView.js
스타일uicontrols/TreeView/TreeView.css

API 참조

싱글턴 객체: syn.uicontrols.$tree 소스 파일: wwwroot/uicontrols/TreeView/TreeView.js, wwwroot/uicontrols/TreeView/TreeView.css 내부 라이브러리: FancyTree (jQuery 플러그인)

마크업

<syn_tree id="tvlTreeView" syn-options="{
checkbox: false,
itemID: 'key',
parentItemID: 'parentID',
childrenID: 'children',
reduceMap: { key: 'ProgramID', title: 'ProgramName', parentID: 'ParentID', folder: 'FolderYN', icon: false },
height: '334px',
extensions: ['filter']
}" syn-events="['dblclick', 'select']"></syn_tree>
  • id는 페이지 내에서 유일해야 하며, syn.uicontrols.$tree의 각종 메서드에서 이 id(elID)를 사용합니다.
  • controlLoad 실행 시 원래 태그는 {elID}_hidden으로 이름이 바뀌며 display:none 처리되고, 같은 위치에 실제 FancyTree가 그려질 <div id="{elID}">를 감싼 래퍼(div.tree-container)가 새로 추가됩니다.
  • width/height는 마크업 요소의 인라인 style.width/style.height가 지정되어 있으면 그 값이 syn-options보다 우선합니다.

Options (defaultSetting)

속성타입기본값설명
widthstring'100%'트리 컨테이너 너비
heightstring'300px'트리 컨테이너 높이(넘치면 스크롤)
itemIDstring'id'평평한(flat) 데이터에서 각 행의 고유 키가 되는 컬럼명. getValue/setValue가 트리 ↔ flat 배열 변환 시 사용합니다.
parentItemIDstring'parentID'flat 데이터에서 부모 키 컬럼명
childrenIDstring'children'중첩(nested) 구조로 변환할 때 자식 배열을 담을 속성명
reduceMapobject아래 "reduceMap" 참고트리 내부 필드(key/title/parentID/folder/icon)와 실제 데이터 컬럼명을 매핑
toggleEffectbooleanfalse노드 확장/축소 시 애니메이션 효과 여부 (FancyTree toggleEffect 옵션)
checkboxbooleanfalse노드마다 체크박스를 표시할지 여부. true로 하면 체크박스 클릭으로 다중 선택이 가능해집니다.
extensionsarray['persist', 'filter']사용할 FancyTree 확장 목록. (확장 전체 목록)
persistobject | false아래 "persist" 참고세션 저장(확장/포커스/선택 상태 유지) 확장 옵션. 주의: controlLoad가 실행될 때 내부에서 무조건 false로 재설정되므로, syn-options에서 직접 persist: {...} 객체를 명시해야만 실제로 동작합니다. 기본값만 믿고 켜두면 동작하지 않습니다.
multiobject | false객체 설정다중 선택 확장 옵션. persist와 동일한 이유로 controlLoad 시점에 무조건 false로 재설정됩니다. 다중 선택을 쓰려면 extensions'multi'를 추가하고 syn-options에서 multi: {...} 를 직접 지정하세요.
filterobject아래 "filter" 참고필터 확장(extensions'filter'가 있을 때) 옵션
sourcearray[]FancyTree 초기 데이터. 보통 직접 지정하지 않고 setValue가 내부적으로 채웁니다.
dataTypestring'string'다른 컨트롤과 형식을 맞추기 위한 공통 속성
belongID / getter / setter / controlText / validators / transactConfig / triggerConfig-null/falsesyn.uicontrols 공통 옵션(값 바인딩·유효성검사·트랜잭션 연동용)

reduceMap

getValue/setValue가 트리 노드(key/title/parentID/folder/icon)와 원본 데이터 컬럼을 서로 바꿔치기할 때 쓰는 매핑 테이블입니다.

속성기본값설명
key'key'노드 고유 키에 대응하는 실제 데이터 컬럼명
title'title'노드에 표시할 텍스트에 대응하는 컬럼명
parentID'parentID'부모 노드 키에 대응하는 컬럼명
folder'folder'폴더(하위 노드를 가질 수 있는 노드) 여부 컬럼명. 값은 $string.toBoolean()으로 boolean 변환됩니다.
iconfalse노드 아이콘. false면 아이콘을 표시하지 않습니다.

persist

속성기본값설명
expandLazyfalse지연 로딩(lazy) 노드까지 확장 상태를 복원할지 여부
expandOpts.noAnimationfalse복원 시 애니메이션 생략 여부
expandOpts.noEventsfalse복원 시 이벤트 발생 생략 여부
overrideSourcetrue저장된 상태로 source를 덮어쓸지 여부
store'session'저장소 종류('session' = sessionStorage, 'local' = localStorage)
types'active expanded focus selected'저장할 상태 종류(공백 구분 문자열)

multi

속성기본값설명
mode'sameParent'다중 선택 시 같은 부모를 가진 노드끼리만 선택 가능하도록 제한

filter

속성기본값설명
counterfalse필터링된 자식 노드 개수를 배지로 표시할지 여부
mode'hide'필터에 걸리지 않는 노드 처리 방식('hide' = 숨김, 'dimm' = 흐리게 표시)

메서드

syn.uicontrols.$tree.<메서드명>(...) 형태로 호출합니다.

메서드설명
getControl(elID)등록된 컨트롤 정보(객체 설정)를 반환. tree가 실제 FancyTree 인스턴스입니다.
getValue(elID, meta)트리 전체를 reduceMap 기준의 평평한(flat) 배열로 변환해서 반환합니다.
setValue(elID, value, meta)reduceMap 컬럼명을 가진 평평한(flat) 배열을 받아 중첩 구조로 변환한 뒤 트리를 다시 그립니다(tree.reload).
clear(elID, isControlLoad)트리 내용을 비웁니다(빈 배열로 reload).
getActiveNode(elID)현재 활성(activate)된 노드를 반환합니다.
toogleEnabled(elID)트리 활성/비활성 상태를 토글하고, 토글 후 활성화 여부(boolean)를 반환합니다. (메서드명 오타이지만 실제 API명이 toogleEnabled입니다)
getRootNodeID(elID)루트 노드의 키를 반환합니다(내부적으로 FancyTree가 붙이는 'root_' 접두어는 제거해서 반환).
activateKey(elID, key)지정한 key를 가진 노드를 활성화(선택 포커스 이동)합니다.
expendLevel(elID, level)지정한 깊이(level, 기본 1)보다 얕은 노드까지 모두 펼칩니다. (메서드명 오타이지만 실제 API명이 expendLevel입니다)
collapseLevel(elID, level)지정한 깊이(level, 기본 1)보다 얕은 노드까지 모두 접습니다.
expandAll(elID)모든 노드를 펼칩니다.
collapseAll(elID)모든 노드를 접습니다.
getSelectedNodes(elID)(체크박스/다중 선택 시) 선택된 노드 배열을 반환합니다.
filterNodes(elID, filter)텍스트(또는 필터 함수)와 일치하는 노드만 남기고 나머지는 filter.mode에 따라 숨기거나 흐리게 표시합니다. 일치 노드까지 자동으로 펼칩니다(autoExpand: true). extensions'filter'가 있어야 동작합니다.
filterBranches(elID, filter)filterNodes와 유사하지만, 일치하는 노드가 속한 가지(branch) 전체를 남깁니다(leavesOnly: true).
clearFilter(elID)적용된 필터를 해제합니다.
setNodeSelectedAll(elID, node)주어진 node의 현재 선택 상태를 그 하위 노드 전체에 그대로 적용합니다.
selectedAll(elID, isSelected)트리의 모든 노드를 한 번에 선택(true)/선택 해제(false)합니다.
setLocale(elID, translations, control, options)다국어 텍스트 적용 훅(TreeView는 별도 구현 없음, 빈 함수)

controlLoad(elID, setting)은 컨트롤이 마크업을 실제 FancyTree로 초기화하는 내부 진입점으로, 프레임워크가 자동으로 호출합니다. 직접 호출할 일은 거의 없습니다.

이벤트 (syn-events)

syn-events 배열에 이름을 넣으면 페이지 스크립트의 event 객체에서 {elID}_{이벤트명} 함수를 자동으로 연결해 줍니다. (핸들러 함수 시그니처는 FancyTree 콜백 그대로 (evt, data)이며, 대부분의 경우 data.node로 대상 노드에 접근합니다.)

중요: 아래 eventHooks 목록에 있는 이름만 실제로 동작합니다. 목록에 없는 이름(예: 'select')을 syn-events에 적어도 에러는 나지 않지만 아무 핸들러도 연결되지 않습니다. 체크박스로 선택 상태가 바뀌는 것을 감지하려면 select가 아니라 click(체크박스 클릭 시) 이벤트에서 data.node.isSelected()를 직접 확인하는 방식을 사용하세요.

이벤트명발생 시점
blurTree트리 전체가 포커스를 잃을 때
focusTree트리 전체가 포커스를 얻을 때
activate노드가 활성화(선택)되었을 때
beforeActivate노드가 활성화되기 직전(취소 가능)
beforeExpand노드가 펼쳐지기 직전(취소 가능)
beforeSelect노드의 체크박스 선택 상태가 바뀌기 직전(취소 가능)
blur개별 노드가 포커스를 잃을 때
click노드를 클릭했을 때
collapse노드가 접힐 때
createNode노드 DOM 요소가 생성되었을 때(노드별 커스텀 렌더링에 활용)
dblclick노드를 더블클릭했을 때
expand노드가 펼쳐질 때
focus개별 노드가 포커스를 얻을 때
keydown트리에 포커스가 있는 상태에서 키를 눌렀을 때
keypress트리에 포커스가 있는 상태에서 키를 입력했을 때

핸들러 등록 예:

let $samplePage = {
event: {
tvlTreeView_click(evt, data) {
syn.$l.eventLog('tvlTreeView_click', data.node.title);
},
tvlTreeView_dblclick(evt, data) {
syn.$l.eventLog('tvlTreeView_dblclick', data.node.data);
},
tvlTreeView_activate(evt, data) {
syn.$l.eventLog('tvlTreeView_activate', data.node.key);
}
}
}

참고

  • 내부적으로 jQuery의 .fancytree(setting) 호출을 그대로 감싼 구조이므로, 옵션/콜백의 더 자세한 원본 설명은 FancyTree 문서를 참고하세요: https://github.com/mar10/fancytree/wiki

  • 노드를 우클릭했을 때 메뉴를 띄우려면 TreeView 자체가 아니라 ContextMenu를 함께 사용합니다. target에 트리 컨테이너 셀렉터(#트리ID)를, delegate'span.fancytree-title'을 지정하면 노드 타이틀을 우클릭했을 때 메뉴가 뜹니다.

    <syn_tree id="tvlTreeView" syn-options="{...}" syn-events="['click']"></syn_tree>
    <syn_contextmenu id="ctxTreeItem" syn-options="{
    target: '#tvlTreeView', delegate: 'span.fancytree-title', menu: [...]
    }" syn-events="['beforeOpen', 'select']"></syn_contextmenu>

    이때 ctxTreeItem_beforeOpen(evt, ui)에서 $.ui.fancytree.getNode(ui.target)(또는 syn.uicontrols.$tree.getControl(elID).tree.getActiveNode())로 우클릭된 노드를 얻어 메뉴 항목을 동적으로 켜고 끌 수 있습니다.

  • getValue/setValue는 항상 flat 배열 기준입니다. 트리 내부 계층 변환은 syn.$l.flat2Nested / syn.$l.nested2Flat 유틸리티가 담당합니다.

  • 체크박스 다중 선택 결과를 가져오려면 getSelectedNodes(elID)를 사용하세요(전체 트리 데이터를 가져오는 getValue와는 다릅니다).