Skip to content

Tree ​

@jennifersoft/apm-components의 Tree 컴포넌트 문서입니다. 이 문서는 @jennifersoft/apm-components@1.4.7 기준으로 정리했습니다.

계층 데이터를 가상 스크롤로 렌더하는 저수준 트리입니다. 검색·전체선택 등 부가 기능이 필요하면 TreeSelector를 사용하세요.

특징 ​

  • @vueuse/components의 UseVirtualList 기반 가상 스크롤 (행 높이 24px 고정)
  • 제네릭 <T>로 노드에 임의 데이터를 실을 수 있음
  • 접힘·선택 상태를 소유하지 않음 — 이벤트만 emit하고 갱신은 호출자가 함

인터랙티브 데모 ​

Props ​

Prop타입기본값설명
widthnumber— (필수)폭(px). 0이면 100%
heightnumber— (필수)높이(px). 가상 스크롤 계산에 사용
dataTreeNode<T>[]— (필수)트리 데이터
checkedKeysstring[][]외부에서 지정하는 선택 키 목록
typescript
interface TreeNode<T> {
    key: string;
    label: string;
    children: TreeNode<T>[];
    fold?: boolean;      // true면 접힘
    check?: boolean;     // 노드 자체의 선택 상태
    disable?: boolean;
    icon?: IconTypes;    // @jennifersoft/vue-components-v2의 ICON_TYPE
    data?: T;            // 임의 페이로드
}

children은 필수 필드입니다. 리프 노드도 빈 배열을 넣어야 합니다.

이벤트 ​

typescript
emit('change-node-fold', key: string)
emit('click-node-label', args: [key: string, checked: boolean])

click-node-label의 페이로드가 두 인자가 아니라 길이 2의 튜플 하나임에 주의하세요.

typescript
function onLabelClick([key, checked]: [string, boolean]) { /* ... */ }

선택 상태 결정 규칙 ​

checkedKeys와 노드의 check 필드가 함께 작동하며, 우선순위가 있습니다.

checkedKeys선택 여부
비어 있음 ([])노드의 check 값만 사용
하나 이상checkedKeys에 포함되거나 또는 check가 true

NOTE

checkedKeys가 비어 있지 않을 때는 OR 조건입니다. checkedKeys로 선택을 완전히 통제하려면 데이터의 check를 모두 false로 두세요.

슬롯 ​

슬롯설명
icon노드 아이콘 영역을 대체. 내부 Node의 slot props를 그대로 전달

사용 예제 ​

vue
<script setup lang="ts">
import { ref } from 'vue';
import { Tree } from '@jennifersoft/apm-components';
import type { TreeNode } from '@jennifersoft/apm-components';

interface Payload {
    domainId: number;
}

const data = ref<TreeNode<Payload>[]>([
    {
        key: 'domain-1',
        label: 'OrderService',
        fold: false,
        children: [
            { key: 'inst-1', label: 'order-01', children: [], data: { domainId: 1 } },
            { key: 'inst-2', label: 'order-02', children: [], data: { domainId: 1 } },
        ],
    },
]);

const checkedKeys = ref<string[]>([]);

// 접힘 상태는 호출자가 갱신한다
function onFoldChange(key: string) {
    toggleFold(data.value, key);
}

function onLabelClick([key, checked]: [string, boolean]) {
    checkedKeys.value = checked
        ? [...checkedKeys.value, key]
        : checkedKeys.value.filter((item) => item !== key);
}
</script>

<template>
    <Tree
        :width="0"
        :height="320"
        :data="data"
        :checked-keys="checkedKeys"
        @change-node-fold="onFoldChange"
        @click-node-label="onLabelClick"
    >
        <template #icon="slotProps">
            <CustomIcon v-bind="slotProps" />
        </template>
    </Tree>
</template>

관련 타입 ​

typescript
/** 평탄화 후 렌더링에 사용하는 형태 */
interface TreeRenderingData<T = unknown>
    extends Omit<TreeNode<T>, 'children' | 'data'> {
    depth: number;
    leaf: boolean;
}

interface TreeCacheMap<T = unknown> {
    [key: string]: {
        parent: string | null;
        data: TreeRenderingData<T>;
    };
}

내부적으로 convertRenderingData()가 트리를 평탄화하면서 depth와 leaf를 계산합니다. 접힌 노드의 자식은 목록에서 제외되므로 가상 스크롤 길이가 자동으로 줄어듭니다.

주의사항 ​

  • 행 높이는 24px 고정입니다. CSS로 바꾸면 가상 스크롤 위치 계산이 어긋납니다.
  • height를 주지 않거나 0으로 두면 아무것도 보이지 않습니다. 컨테이너 높이를 계산해 넘기세요.
  • 접힘·선택은 컴포넌트가 관리하지 않습니다. emit을 받아 data 또는 checkedKeys를 갱신해야 화면이 바뀝니다.
  • 검색, 전체 선택, 부분 선택(indeterminate)이 필요하면 TreeSelector를 사용하세요.