# HMDesign OrgChart 공통 사용 가이드 개발자·Codex·Claude가 현재 공개 API로 조직 데이터를 연결하기 위한 가이드다. 구현 기준은 `feature/orgchart-package`의 **`5acaf8a29596317133e54e8ad437ee00beba1cf0`**(2026-10-13)과 P10.2에서 통합한 React Wrapper Entry 개선이다. P10.3의 Git 공유 대상은 이 Feature 브랜치이며 실제 받은 버전은 `git rev-parse HEAD`로 확인한다. 과거 P-AI1의 구버전 제한을 현재 지원 여부로 사용하지 않는다. 빠른 시작은 아래 데이터 파일과 React 또는 Vanilla 진입점을 함께 복사한다. 예제는 Vite의 TypeScript 환경을 전제로 한다. **이 가이드의 기본 예제는 현재 Auto 컴포넌트를 사용하는 예제이며, 외부 UI가 없는 Chart Only 구현 완료를 뜻하지 않는다.** ## 1. 설치·패키지·실행 환경 - `@hm-design/org-chart@0.1.0`: 데이터 타입·Validator·JSON DataSource·집계·이동 함수·DOM 렌더러. React 없이 사용한다. 내부 엔진 의존성은 `orgchart@6.0.0`이며 소비자가 엔진을 직접 조작하지 않는다. - `@hm-design/org-chart-react@0.1.0`: 위 Core에 의존하는 React lifecycle adapter. 조직 배치·카드·데이터 검증을 다시 구현하지 않는다. React peer는 `^19.0.0`, HM Dialog 선택에 사용하는 `@hm-design/ui@0.1.0` peer는 optional이다. - 저장소는 npm workspaces다. Git Clone 후 소스에서 설치·빌드한다. 외부 Registry에 두 패키지가 게시됐다는 증거는 이번 범위에 없다. - Core 렌더링은 DOM·Shadow DOM·ResizeObserver·native dialog를 사용하는 브라우저 작업이다. SSR 중에는 mount하지 않는다. React 호스트도 client 경계에서 렌더링한다. ### Feature 브랜치 Git Clone Git, Node.js와 npm, 공식 Gitea 및 npm 레지스트리 접근이 필요하다. Node.js 조건은 `^20.19.0 || >=22.12.0`이며 Node 21은 제외한다. P10.3 실행 환경은 Node 22.23.2 / npm 10.9.8이다. 기존 폴더를 덮어쓰지 않고 새 경로에서 실행한다. ZIP·별도 설치 파일·다른 작업 폴더의 node_modules/dist 복사는 필요하지 않다. ```powershell git clone --branch feature/orgchart-package --single-branch https://gitea.hmac.kr/b25027/HMDesign.git HMDesign-OrgChart cd HMDesign-OrgChart git branch --show-current git rev-parse HEAD node --version npm.cmd --version npm.cmd ci npm.cmd run build:packages npm.cmd run typecheck --ignore-scripts npm.cmd run test:org-chart npm.cmd run build --ignore-scripts ``` `--ignore-scripts`는 위에서 완료한 package build를 pretypecheck/prebuild에서 반복하지 않기 위한 옵션이다. 이 위치에서 Typecheck/Build 본문은 실제로 실행된다. 루트 설치는 Storybook/HM UI를 포함하는 개발 workspace 설치이며 Core 소비자의 최소 의존성을 의미하지 않는다. Vanilla 예제는 아래 build 명령 후 `dev:org-chart-example`로 실행한다. React 예제는 package build 후 `build:org-chart-react-example`, `dev:org-chart-react-example`을 사용한다. HM Dialog Reference는 `npm.cmd run storybook`의 Expanded OrgChart에서 확인한다. 포트가 사용 중이면 기존 서버를 재사용한 결과를 독립 Clone 성공으로 보고하지 않는다. 저장소의 설치 절차를 완료한 환경에서 사용하는 명령이다. Windows에서는 `npm.cmd`를 사용한다. ```powershell npm.cmd run build:org-chart npm.cmd run build:org-chart-react npm.cmd run build:org-chart-example npm.cmd run dev:org-chart-example # 또는 React 예제 서버 npm.cmd run dev:org-chart-react-example ``` 위 명령을 실행하면 기존 예제/산출물을 갱신할 수 있다. 병렬 문서 작업의 검증 명령으로 실행하지 않는다. 실행 위치·포트는 [Vanilla 예제 서버](../../examples/org-chart/serve.mjs), [React Vite 설정](../../examples/org-chart-react/vite.config.ts)을 확인하고 기존 서버와 충돌시키지 않는다. ### 외부 프로젝트 최소 설치 외부 호스트의 최소 의존성을 검증할 때는 **Clone한 소스에서 직접 생성한** `npm pack` 산출물을 로컬 설치한다. tarball을 별도 전달하거나 Registry publish를 요구하지 않는다. 위 package build가 완료된 Clone 루트에서 다음을 실행한 뒤 아래 설치 경로를 생성된 파일 위치로 바꾼다. ```powershell New-Item -ItemType Directory -Path test-results/local-pack -Force npm.cmd pack -w @hm-design/org-chart --ignore-scripts --pack-destination test-results/local-pack npm.cmd pack -w @hm-design/org-chart-react --ignore-scripts --pack-destination test-results/local-pack npm.cmd pack -w @hm-design/ui --ignore-scripts --pack-destination test-results/local-pack ``` 생성물은 Git ignored 로컬 검증 자료다. A/B/C는 별도 호스트 폴더에서 아래처럼 설치한다. Registry publish 여부는 확인되지 않았다. ```powershell # A: Core + Vanilla. React/HMDesign UI 없이 설치한다. npm.cmd install ./hm-design-org-chart-0.1.0.tgz # B: Core + React Wrapper. HMDesign UI/Theme/Tailwind 없이 설치한다. npm.cmd install ./hm-design-org-chart-0.1.0.tgz ./hm-design-org-chart-react-0.1.0.tgz react@19.3.0 react-dom@19.3.0 # C: HM Dialog를 명시적으로 사용할 때만 HMDesign UI와 해당 peer를 추가한다. npm.cmd install ./hm-design-ui-0.1.0.tgz ``` Core와 Wrapper의 `package.json`에서 실제 dependency/peer를 확인한다. C의 HM Theme/Tailwind 연결은 호스트가 제공하며 `/hm-dialog`가 CSS를 자동 import하지 않는다. 기본 Entry는 HM UI runtime/type 의존 그래프를 포함하지 않는다. 설치 여부로 popup을 자동 전환하지 않는다. ### 공개 Export와 CSS Core package의 exports는 `.`, `./style.css`, `./engine.css`, `./browser`다. - 주 진입점: `createOrgChart`, `JsonOrgChartDataSource`, `OrgChartSourceError`, `validateOrgChartDataset`, `isSafeImageUrl`, `normalizeOrgChartDataset`, `calculateSummary`, `snapshotOrgChartDataset`. - 색상: `companyColorPresets`, `getCompanyColorPreset`; 타입: `CompanyId`, `CompanyColorPreset`. - 가상 예제: `createOrgChartExampleDataset` 및 관련 타입. - 이동: `createOrgChartMoveValidator`, `validateOrgChartMove`, `applyOrgChartMove` 및 관련 타입. - 독립 모듈: `createOrgChartTree`, `createPositionSummary`, `createAffiliationSummary`, `createStatusSummary`. 이들은 Full Composition의 Auto 기능 전체를 제공하는 별도 React 컴포넌트가 아니다. - 데이터·옵션·Handle 타입은 같은 주 진입점에서 가져온다. 내부 `src`/`dist` 경로를 소비 코드에서 import하지 않는다. React 기본 `@hm-design/org-chart-react`의 runtime export는 **`OrgChart` 하나**이며 HMDesign UI가 필요 없다. 선택 Entry `@hm-design/org-chart-react/hm-dialog`는 `OrgChart`와 `HMEmployeeDialog`를 export한다. 두 Entry 모두 `OrgChartProps`, `OrgChartEmployeeSelection`, `OrgChartEmployeePopupProps`와 Core 공개 타입을 type-only로 export한다. `JsonOrgChartDataSource` 같은 runtime helper는 Core에서 import한다. 내부 `src`/`dist` Import는 사용하지 않는다. `stylesheetUrls: { engine, core }`는 필수다. Core가 자기 Shadow DOM에 engine CSS → core CSS 순으로 link를 넣는다. document에 CSS를 import하는 것만으로 내부 카드가 스타일링되지는 않는다. 아래 `?url`은 Vite 전용이며 다른 bundler에서는 공개 CSS를 실제 배포 URL로 해석해 전달한다. React 없는 일반 HTML도 공개 browser bundle을 HTTP 서버로 배포해 사용할 수 있다. `./browser`는 ESM named import용이 아니라 build가 생성하는 IIFE `HMOrgChart` 전역 bundle이다. 이 가이드의 Vanilla TypeScript 예제는 주 ESM 진입점을 사용한다. URL JSON은 HTTP(S)/상대 경로를 사용하며 `file://` fetch는 지원하지 않는다. 근거: [Core manifest](../../packages/org-chart/package.json), [Core exports](../../packages/org-chart/src/index.ts), [Data exports](../../packages/org-chart/src/data/index.ts), [React manifest](../../packages/org-chart-react/package.json), [React exports](../../packages/org-chart-react/src/index.ts), [Core build](../../packages/org-chart/build.mjs). ## 2. 빠른 시작용 데이터 — 예제 A / B / E 다음은 **가상 데이터 예제**다. `data.ts`로 저장한다. 예제의 회사·분기 수를 제품 기본값으로 사용하지 않는다. A는 1회사·2본부·본부별 2팀·총 7명(대표 1명 + 본부장 2명 + 팀 직원 4명)이다. B는 회사별 독립 Root 두 개와 회사별 컬러를 보여준다. E는 동일한 `singleCompany`를 React와 Vanilla에서 사용한다. 업무 명칭인 본부를 이 예제에서는 `office`로 대응한다. Contract에 `headquarters` enum을 새로 추가하지 않는다. ```ts // data.ts import { getCompanyColorPreset, validateOrgChartDataset, type CompanyId, type Employee, type Organization, type OrgChartDataset, } from '@hm-design/org-chart'; export function makeCompanyDataset( companyId: CompanyId, teamCounts: readonly number[], ): OrgChartDataset { if (teamCounts.some(count => !Number.isSafeInteger(count) || count < 0)) { throw new Error('팀 수는 0 이상의 정수여야 합니다.'); } const name = getCompanyColorPreset(companyId).companyName; const rootId = companyId + '-root'; const organizations: Organization[] = []; const employees: Employee[] = []; const addEmployee = (id: string, organizationId: string, position: string) => { employees.push({ id, organizationId, affiliationId: companyId, name: '가상 ' + id, position, jobTitle: null, profileImage: null, workStatus: 'active', }); }; addEmployee(companyId + '-ceo', rootId, '대표이사'); organizations.push({ id: rootId, companyId, parentId: null, name, type: 'root', leaderId: companyId + '-ceo', displayOrder: 0, }); teamCounts.forEach((count, index) => { const officeId = companyId + '-office-' + (index + 1); const leaderId = officeId + '-leader'; addEmployee(leaderId, officeId, '본부장'); organizations.push({ id: officeId, parentId: rootId, name: (index + 1) + '본부', type: 'office', leaderId, displayOrder: index, }); for (let team = 0; team < count; team++) { const teamId = officeId + '-team-' + (team + 1); organizations.push({ id: teamId, parentId: officeId, name: (team + 1) + '팀', type: 'team', leaderId: null, displayOrder: team, }); addEmployee(teamId + '-employee', teamId, '사원'); } }); const data: OrgChartDataset = { schemaVersion: '0.1-draft', organizations, employees, affiliations: [{ id: companyId, name }], metadata: { asOfDate: '2026-10-13', dataVersion: 'example-1' }, }; const checked = validateOrgChartDataset(data); if (!checked.valid) throw new Error(JSON.stringify(checked.issues)); return checked.data; } export const singleCompany = makeCompanyDataset('hanmac', [2, 2]); const secondCompany = makeCompanyDataset('saman', [1]); export const multiCompany: OrgChartDataset = { ...singleCompany, organizations: [...singleCompany.organizations, ...secondCompany.organizations], employees: [...singleCompany.employees, ...secondCompany.employees], affiliations: [...singleCompany.affiliations, ...secondCompany.affiliations], metadata: { ...singleCompany.metadata, dataVersion: 'example-multi-1' }, }; const checkedMulti = validateOrgChartDataset(multiCompany); if (!checkedMulti.valid) throw new Error(JSON.stringify(checkedMulti.issues)); ``` Root의 `companyId`는 하위 조직이 상속한다.복수 회사는 `parentId: null`인 별도 Root를 만든다. 회사들을 잇는 가짜 상위 조직을 추가하지 않는다. B는 위 `multiCompany`를 같은 렌더러에 전달하면 된다. ## 3. React 최소 사용 — 예제 D / E Vite React 프로젝트에서 아래 `App.tsx`, `main.tsx`, `index.html`과 앞의 `data.ts`를 사용한다. 기본 조회용 설정은 `dragDropEnabled={false}`다. Zoom/Pan/팝업은 별도 설정으로 계속 사용한다. ```tsx // App.tsx import { OrgChart } from '@hm-design/org-chart-react'; import { JsonOrgChartDataSource } from '@hm-design/org-chart'; import engineUrl from '@hm-design/org-chart/engine.css?url'; import coreUrl from '@hm-design/org-chart/style.css?url'; import { singleCompany } from './data'; const source = new JsonOrgChartDataSource(singleCompany); const stylesheetUrls = { engine: engineUrl, core: coreUrl }; const autoLayout = { type: 'tb' as const, initialVisibleDepth: 'all' as const }; export default function App() { return { if (state.status === 'error') console.error(state.issues); }} onLifecycleError={error => console.error(error)} />; } ``` ```tsx // main.tsx import { createRoot } from 'react-dom/client'; import App from './App'; const container = document.getElementById('root'); if (!container) throw new Error('root 요소가 없습니다.'); const root = createRoot(container); root.render(); window.addEventListener('pagehide', () => root.unmount(), { once: true }); ``` ```html OrgChart
``` React가 전용 div와 Core handle의 mount·subscribe·destroy를 소유한다. 소비자가 같은 div에 다른 자식이나 별도 Core 인스턴스를 넣지 않는다. StrictMode도 wrapper가 lifecycle을 관리한다. 공개 `ref`/imperative handle prop이나 `data`, `title`, `children` prop은 없다. 기본 Entry의 `core` 모드는 Core CSS로 동작하며 HMDesign UI/Theme/Tailwind를 설치하지 않아도 된다. 실제 tarball을 설치한 React 19.3.0/Vite 8.3.0 환경의 타입·Production Build 검증은 [최소 설치 보고서](minimal-install-report-2026-10-13.md)를 참조한다. 다른 bundler·React major의 지원을 이 결과로 확대하지 않는다. ### HMDesign Dialog 선택형 연동 기존 HM Dialog 소비자는 Import를 `/hm-dialog`로 변경하고 `employeePopup="hm-dialog"`를 유지한다. 기본 Entry에 HM UI가 설치되어 있어도 자동 전환하지 않으며, HM Entry만 Import해도 기본 popup은 Core다. 실제 `@hm-design/ui`와 호스트의 `index.css → hm-theme.css`/Tailwind 연결이 필요하다. ```tsx // HMChart.tsx import { OrgChart } from '@hm-design/org-chart-react/hm-dialog'; import { JsonOrgChartDataSource } from '@hm-design/org-chart'; import engineUrl from '@hm-design/org-chart/engine.css?url'; import coreUrl from '@hm-design/org-chart/style.css?url'; import { singleCompany } from './data'; const source = new JsonOrgChartDataSource(singleCompany); export function HMChart() { return ; } ``` ### 외부 Popup Adapter 기본 Entry에 `employeePopup="adapter"`와 `employeePopupAdapter`를 함께 전달한다. Adapter는 `{ selection, onClose }`를 받고 표시·닫기·접근성·focus 복귀를 소유한다. selection에는 직원 원본과 context가 있다. 아래 native dialog는 호스트가 소유하는 예시이며 조직도 카드·Core·데이터를 수정하지 않는다. ```tsx // CustomChart.tsx import { useEffect, useRef } from 'react'; import { OrgChart, type OrgChartEmployeePopupProps } from '@hm-design/org-chart-react'; import { JsonOrgChartDataSource } from '@hm-design/org-chart'; import engineUrl from '@hm-design/org-chart/engine.css?url'; import coreUrl from '@hm-design/org-chart/style.css?url'; import { singleCompany } from './data'; function EmployeePopup({ selection, onClose }: OrgChartEmployeePopupProps) { const dialog = useRef(null); useEffect(() => { if (!selection) return; const element = dialog.current!; element.showModal(); return () => { element.close(); if (selection.context.trigger.isConnected) selection.context.trigger.focus(); }; }, [selection]); return selection ? { event.preventDefault(); onClose(); }}>

{selection.employee.name} · {selection.employee.position}

: null; } const source = new JsonOrgChartDataSource(singleCompany); export function CustomChart() { return ; } ``` Adapter 전달만으로는 활성화되지 않는다. 기본 Entry에서 `hm-dialog`/`adapter`를 선택하고 Adapter를 제공하지 않으면 Core popup을 유지하고 `onLifecycleError`로 구성 오류를 알린다. `onEmployeeClick`이 지정되면 이 선택보다 우선한다. Fixed는 기존 Core popup을 유지하고 외부 popup은 `onEmployeeClick`으로 연결한다. 근거: [Props](../../packages/org-chart-react/src/types.ts), [Wrapper lifecycle](../../packages/org-chart-react/src/OrgChart.tsx), [HM Dialog](../../packages/org-chart-react/src/HMEmployeeDialog.tsx). ## 4. Vanilla TypeScript 최소 사용 — 예제 D / E React가 없는 Vite TypeScript 프로젝트에서 `data.ts`, 아래 `main.ts`과 `index.html`을 사용한다. 반환 cleanup은 페이지/호스트 lifecycle 종료 시 호출한다. ```ts // main.ts import { createOrgChart, JsonOrgChartDataSource } from '@hm-design/org-chart'; import engineUrl from '@hm-design/org-chart/engine.css?url'; import coreUrl from '@hm-design/org-chart/style.css?url'; import { singleCompany } from './data'; export async function mountChart(container: HTMLElement): Promise<() => void> { const chart = createOrgChart({ container, dataSource: new JsonOrgChartDataSource(singleCompany), stylesheetUrls: { engine: engineUrl, core: coreUrl }, layoutMode: 'auto', displayMode: 'readOnly', dragDropEnabled: false, autoLayout: { type: 'tb', initialVisibleDepth: 'all' }, onStateChange: state => { if (state.status === 'error') console.error(state.issues); }, }); try { await chart.mount(); const state = chart.getState(); if (state.status !== 'ready') throw new Error('조직도를 표시할 수 없습니다.'); // Auto에서 필요할 때 호출: 현재 전체 표시 영역을 맞춘다. chart.fitToView(); return () => chart.destroy(); } catch (error) { chart.destroy(); throw error; } } const container = document.getElementById('chart'); if (!container) throw new Error('chart 요소가 없습니다.'); const dispose = await mountChart(container); window.addEventListener('pagehide', dispose, { once: true }); ``` ```html OrgChart
``` `createOrgChart()` 자체는 DOM/요청 부작용 없이 handle을 만든다. `mount(container?)`는 연결된 HTMLElement 하나를 소유하고 첫 load까지 수행한다. 다른 handle이 소유한 컨테이너, 중복 mount, destroy 후 재사용은 거절한다. `destroy()`는 여러 번 호출해도 안전한 최종 종료이며 다시 사용할 때는 새 handle을 만든다. Core의 subscribe(listener)는 unsubscribe 함수를 반환하며 이후 상태 변경을 구독한다. getState()는 현재 상태를 읽는다. Auto의 getData()는 숨긴 회사와 로컬 이동을 포함한 현재 immutable 데이터, getSelectedCompanies()/getAutoLayout()/getInteractions()/getDragDropEnabled()는 현재 옵션을 조회한다. getData()는 Auto 데이터가 준비되지 않으면 undefined다. mount/update/reload의 Promise 완료만으로 성공을 판정하지 않는다. Contract·소스·CSS 오류는 reject 대신 `getState().status === 'error'`/`onStateChange`로 전달될 수 있다. ## 5. JSON 데이터 계약 현재 구현 타입 이름은 **`OrgChartDataset`**다. 과거 [데이터 계약 문서](data-contract.md)의 초안 이름·선택 필드는 현재 타입보다 우선하지 않는다. JSON 문자열을 직접 `JsonOrgChartDataSource`에 전달하면 URL로 해석된다. 문자열 JSON은 먼저 `JSON.parse(text)`하고 Validator를 통과시킨다. ### 필수·선택 필드 - Dataset 필수: `schemaVersion: '0.1-draft'`, `organizations: Organization[]`, `employees: Employee[]`, `affiliations: Affiliation[]`, `metadata: Metadata`. - Dataset 선택: `relations?: OrganizationRelation[]`, `presentation?: PresentationConfig`. - Organization 필수: `id: string`, `parentId: string | null`, `name: string`, `type: 'root' | 'division' | 'office' | 'div' | 'cell' | 'team'`, `leaderId: string | null`, `displayOrder: number`(0 이상 안전한 정수). 선택: `companyId?: string`. - Employee는 모든 필드 필수: `id`, `organizationId`, `affiliationId`, `name`, `position`은 string; `jobTitle`, `profileImage`는 string 또는 null; `workStatus`는 `active | dispatched | onLeave`. - Affiliation 필수: `id: string`, `name: string`. 별도 `Company[]`는 없다. Auto 회사 식별은 Organization.companyId와 등록된 회사 프리셋을 사용하며 직원의 관계사/법인 소속은 Employee.affiliationId로 구분한다. - Metadata 필수: 실제 달력 날짜인 `asOfDate: 'YYYY-MM-DD'`, 비어 있지 않은 `dataVersion: string`. 선택: `schemaVersion?: '0.1-draft'`(최상위와 같아야 함), `updatedAt?: string | null`(timezone 포함 RFC3339), `updateNote?: string | null`. - Relation은 `id`, `sourceOrganizationId`, `targetOrganizationId`, `type: 'cooperation' | 'support'`가 필수다. 추가 관계는 검증/보존되지만 기본 계층 연결선으로 렌더링되지 않는다. - Presentation을 넣으면 `title: string`, `description: string | null`, `hubText: string | null`, `organizationOrder: string[]`, `groups`의 `showEmployees/showSummary/showDispatched/showOnLeave/showAffiliations: boolean` 모두를 넣는다. Presentation 생략은 현재 heading 제거 옵션이 아니다. nullable 필드는 생략하거나 빈 문자열로 대신하지 않는다. 필수 string은 공백뿐인 값이 거절된다. Validator는 trim으로 원본을 고치지 않는다. Contract에 없는 키도 `UNKNOWN_FIELD`다. 원본에 추가 정보가 있다면 별도 자료에 보존하고 Contract용 변환본을 만든다. ### 최소 JSON: 회사 → 본부 → 팀 → 직원 ```json { "schemaVersion": "0.1-draft", "organizations": [ { "id": "company-root", "companyId": "saman", "parentId": null, "name": "삼안", "type": "root", "leaderId": null, "displayOrder": 0 }, { "id": "hq-1", "parentId": "company-root", "name": "1본부", "type": "office", "leaderId": null, "displayOrder": 0 }, { "id": "team-1", "parentId": "hq-1", "name": "1팀", "type": "team", "leaderId": "employee-1", "displayOrder": 0 } ], "employees": [ { "id": "employee-1", "organizationId": "team-1", "affiliationId": "saman", "name": "가상직원", "position": "차장", "jobTitle": "팀장", "profileImage": null, "workStatus": "active" } ], "affiliations": [{ "id": "saman", "name": "삼안" }], "metadata": { "asOfDate": "2026-10-13", "dataVersion": "minimal-1" } } ``` `leaderId`는 해당 조직에 소속된 Employee.id를 참조한다. 위 팀장도 employees에 한 번만 있다. 조직장 별도 레코드를 추가해 총원을 두 번 세지 않는다. 직원은 한 `organizationId`로 직접 소속되며 이름으로 관계를 연결하지 않는다. ID 중복 검사는 collection 내부 기준이고 서로 다른 collection의 같은 문자열은 현재 Validator가 거절하지 않지만, 호스트 변환 단계에서는 엔티티 prefix로 구분하기를 권장한다. `profileImage`는 HTTP(S) 또는 안전한 상대 URL이다. null 또는 이미지 load 실패 시 기존 사람 SVG Placeholder로 전환한다. credentials·data URL·javascript·protocol-relative URL을 이미지 API로 우회하지 않는다. 직위(position)와 직책(jobTitle)은 별개이며 연락처 같은 미지원 정보를 합성하지 않는다. ### 깊은 JSON: 회사 → 부문 → 본부 → 부서 → 팀 → 직원 현재 Auto의 트리는 enum 순서에 종속되지 않고 실제 parentId 관계를 따른다. 다음은 5개 조직 계층 + 직원 카드 예제다. 본부=`office`, 부서=`div` 대응은 이 예제의 변환 선택이며 일반 기업 의미를 강제하지 않는다. ```json { "schemaVersion": "0.1-draft", "organizations": [ { "id": "deep-root", "companyId": "hanmac", "parentId": null, "name": "한맥기술", "type": "root", "leaderId": null, "displayOrder": 0 }, { "id": "division-1", "parentId": "deep-root", "name": "기술부문", "type": "division", "leaderId": null, "displayOrder": 0 }, { "id": "hq-1", "parentId": "division-1", "name": "기술본부", "type": "office", "leaderId": null, "displayOrder": 0 }, { "id": "department-1", "parentId": "hq-1", "name": "설계부서", "type": "div", "leaderId": null, "displayOrder": 0 }, { "id": "team-1", "parentId": "department-1", "name": "설계팀", "type": "team", "leaderId": null, "displayOrder": 0 } ], "employees": [ { "id": "deep-employee-1", "organizationId": "team-1", "affiliationId": "hanmac", "name": "가상직원", "position": "사원", "jobTitle": null, "profileImage": null, "workStatus": "active" } ], "affiliations": [{ "id": "hanmac", "name": "한맥기술" }], "metadata": { "asOfDate": "2026-10-13", "dataVersion": "deep-1" } } ``` 임의의 최대 Depth를 보장하는 API는 없다. 소스는 가변 계층을 처리하지만 극단적으로 깊거나 큰 입력의 렌더링·성능은 실제 브라우저에서 별도로 확인한다. 근거: [실제 타입](../../packages/org-chart/src/data/types.ts), [Validator](../../packages/org-chart/src/data/validate.ts), [DataSource](../../packages/org-chart/src/data/source.ts), [정규화](../../packages/org-chart/src/data/normalize.ts), [집계](../../packages/org-chart/src/data/summary.ts). ## 6. Fixed / Auto 선택과 레이아웃 ### Fixed — 기존 Workdesk 디자인 구성 `layoutMode: 'fixed'`가 생략 시 기본이다. `displayMode: 'interactive' | 'readOnly'`와 별도 설정이다. - Fixed readOnly는 기존 승인 SVG 카드·대표자·허브·요약과 Workdesk 구성/비례 축소를 사용한다. 기준 캔버스는 1920×914.5, 축 X=935다. - 측정된 배치는 `business-management/business-planning/talent-growth/integrated-system/design-planning/solution-sales` ID의 기존 열 구성에 맞는다. 상위 열이 매핑되지 않으면 일반 중앙 flow로 돌아가며 임의 데이터에 승인된 좌표 배치를 보장하지 않는다. 데이터의 ID/parentId를 디자인 매핑에 맞추려고 바꾸지 않는다. - readOnly는 전체 펼침이며 Fixed interactive는 기존 접기 동작을 제공한다. Auto용 TB/LR/Hybrid·회사 선택·Drag & Drop·Zoom/Pan Handle은 Fixed의 새로운 기능이 아니다. - **표시 데이터 제약:** `employee-card.ts`는 Workdesk 표현 카드의 이름을 `홍 길 동`으로 표시하고 Fixed 대표자 카드는 `대표이사` 직위를 표시하는 경로가 있다. 원본 Employee는 유지되지만 화면 표시가 원본 인명과 일치하지 않을 수 있다. 실제 사용자 데이터 표시에는 이를 보고하고 Auto를 검토한다. 이를 이번 문서 작업에서 수정하지 않는다. - readOnly가 제목·기준일·요약까지 제거하는 Chart Only prop은 아니다. ### Auto / Expanded — 데이터 기반 가변 조직도 `layoutMode: 'auto'`는 회사별 독립 Root와 전체 관계를 사용한다. Auto의 회사별 컬러·접기·팝업·Zoom/Pan·이동은 Core 구현을 공유한다. - `autoLayout.type`: `tb`(기본, 위→아래), `lr`(왼쪽→오른쪽), `hybrid`(지정 Depth부터 세로 목록). 현재 기준 소스는 세 유형을 구현한다. - `hybridStartDepth?: 2 | 3 | 4`: 기본 3, global Hybrid용. Root 조직=Depth 1이며 직원 카드를 조직 Depth로 세지 않는다. - `initialVisibleDepth?: 1 | 2 | 3 | 'all'`: 기본 all. 해당 Depth부터 그룹을 접는 초기 정책이고 조직장은 남는다. 값을 변경하면 접힘 상태를 초기 정책으로 재설정한다. 다른 레이아웃 변경은 유효한 ID의 접힘 상태를 유지한다. - `hybridOrganizationIds?: readonly string[]`: TB에서 지정 조직의 자손만 세로 배치. LR/global Hybrid와 동시 사용은 거절한다. 전환 전에 빈 배열로 해제한다. - `interactions: { collapse?, zoom?, pan? }`: 각각 기본 true, 서로 독립이다. Compact 레이아웃, 임의 `layoutConfig`, 엔진 방향 옵션 전달, 공개 collapse setter는 없다. - 초기 자동 배치는 배율 1을 기본으로 실제 Bounds의 가로 중앙을 맞추며 상단 여유를 둔다. 항상 가로·세로 중앙 Fit하는 동작과 다르다. 사용자가 Pan/Zoom한 뒤에는 viewport를 보존한다. - 명시적 Fit은 표시된 Bounds를 맞추고 가로·세로 중앙에 둔다. 큰 입력은 읽기 어려울 만큼 축소될 수 있어 initial Depth 또는 확대를 함께 검증한다. Fit은 수동 zoom.min보다 작아질 수 있다. - Auto unknown 회사 프리셋은 거절한다. 회사 선택 `undefined`=전체, `[]`=명시적 빈 표시. 회사 숨김/복귀는 원본 데이터나 완료된 이동을 삭제하지 않는다. - `companyTopNodes`는 회사 ID별 `employeeId/title/name/profileImage/organizationName/display` 표시 옵션이다. 기본은 Root.leaderId이며 직원은 해당 회사 Root 소속이어야 한다. `employeeId: null`은 상단 카드 배정을 제거한다. `display`는 `card | titleOnly | hidden`. 표시 override는 원본 직원/팝업 identity를 바꾸지 않는다. Vanilla Auto에서 설정 변경은 기존 handle로 한다. ```ts import type { OrgChartHandle } from '@hm-design/org-chart'; export async function configureLayout(chart: OrgChartHandle) { await chart.setAutoLayout({ hybridOrganizationIds: [] }); await chart.setAutoLayout({ type: 'lr', initialVisibleDepth: 2 }); await chart.setInteractions({ collapse: true, zoom: true, pan: true }); await chart.setSelectedCompanies(['hanmac', 'saman']); // chart.setSelectedCompanies()는 전체, []는 빈 선택. } ``` setter는 mount 후 Auto에서 호출한다. Vanilla의 생성 옵션인 layoutMode/displayMode/companyTopNodes/employeeConnections/zoom/organizationLinks를 바꾸려면 destroy 후 새 handle을 만든다. React에서는 대응 prop으로 변경한다. dataSource 변경은 reload, autoLayout/interactions/dragDropEnabled/selectedCompanyIds와 Auto 팝업 변경은 기존 handle을 갱신한다. aggregationPolicy·CSS URL·layoutMode·displayMode·companyId·companyTopNodes·employeeConnections·zoom·organizationLinks 변경은 handle을 재생성하므로 객체를 안정적으로 유지한다. `setAutoLayout`는 patch merge이며 React에서 새 autoLayout 객체에 넣지 않은 필드가 매번 Core 기본으로 초기화된다고 가정하지 않는다. 근거: [Core Handle/옵션](../../packages/org-chart/src/core/types.ts), [배치 정책](../../packages/org-chart/src/core/auto-layout-policy.ts), [회사 검증](../../packages/org-chart/src/core/expanded-company.ts), [Workdesk 좌표](../../packages/org-chart/src/core/workdesk-layout.ts), [Fixed 구성](../../packages/org-chart/src/core/org-chart-content.ts), [Viewport](../../packages/org-chart/src/core/expanded-viewport.ts). ## 7. 디자인·회사별 컬러·카드 재사용 현재 Auto의 `getCompanyColorPreset(id).primary` 값: - `hanmac` 한맥기술: `#CB2E28` - `saman` 삼안: `#F08300` - `jangheon-industry` 장헌산업: `#4580A6` - `jangheon` 장헌: `#1D418D` - `ptc` 피티씨: `#84338E` - `halla` 한라산업개발: `#0068B7` - `baron` 바론컨설턴트: `#0C367F` 색상은 조회 helper와 Core에 맡기고 새 카드 CSS에 복사하지 않는다. Root.companyId → 자손 상속 → legacy Root leader의 등록 affiliation → 생성 옵션 companyId(기본 hanmac)의 순으로 회사가 결정된다. 새 데이터는 Root.companyId를 명시한다. 조직 회사와 직원 affiliation은 다른 값일 수 있다. 카드 강조는 등록된 affiliation 프리셋을 우선하고 미등록 affiliation은 조직 회사 색을 사용한다. 자손의 companyId가 부모 회사와 다르면 Auto에서 `COMPANY_HIERARCHY`다. Fixed는 별도의 기존 부서 ID별 스타일·SVG를 사용한다. 회사 컬러 옵션을 바꿔 Fixed 전체를 재색칠하는 API는 없다. ### 카드와 글자 - Fixed의 기존 대표자 SVG는 146×74, 1Depth 조직장 SVG는 146×68, 더 깊은 조직장/일반 직원의 small SVG는 108×52다. 등록 부서 자산이 없는 조직은 기존 fallback 카드다. 모든 Fixed 데이터에 SVG 매핑이 있는 것은 아니다. - Fixed SVG 직위/이름 기본 크기는 small 15/18px, 대표자 20/24px, 부서장 SVG 직위 18px·이름 24px다. 이름 weight 600, 직위 400이며 전역 서체는 Noto Sans KR 등 기존 fallback을 따른다. 재설계 규격으로 사용하지 않는다. - Auto는 SVG 배경 대신 기존 중립 카드와 회사 강조 Border를 쓴다. 일반 직원 폭 114px·최소 높이 54px, 조직장/상단 카드 폭 154px·최소 높이 72px, 사진 36×36, padding 8px, Border 1px, inset 강조 3px, 이름/직위 13/10px다. 긴 텍스트는 실측 높이가 늘 수 있다. - Auto의 Root 대표자와 하위 조직장 카드는 같은 leader geometry를 사용한다. 1Depth/2Depth별 새 디자인 prop은 없다. 3글자 한글 이름은 표시 계층에서 띄어 쓰고 직위 공백을 표시상 제거하지만 원본과 callback 데이터는 유지한다. - Auto 일반 카드에서 jobTitle·근무 상태를 별도 행으로 출력하지 않는 경로가 있다. 팝업은 원본 jobTitle/workStatus를 보여준다. Fixed의 파견/휴직 보조 카드·요약과 Auto의 직원 카드를 혼동하지 않는다. - Fixed SVG의 외형 Border는 자산에 포함되고 Auto Border는 CSS/실제 연결면에 따라 강조한다. 회사 타이틀 텍스트는 현재 CSS에서 흰색이며 특정 색 조합의 대비가 승인됐다는 뜻은 아니다. ### 연결선·스타일 적용 Auto 전체 트리 방향은 autoLayout.type, 직원 연결 방향은 조직 ID별 `employeeConnections: { [id]: 'top' | 'left' | 'right' }`다. LR/세로 경로는 실제 endpoint를 측정해 연결면을 정하고 LR 직원 카드의 연결/강조면은 왼쪽이다. TB/Hybrid 조직장은 상단 연결을 사용한다. 직원 방향 옵션을 전체 트리 방향 API로 해석하지 않는다. Auto 관계선/직원선은 기존 `--hm-orgchart-expanded-line-color`(기본 `#738399`)를 공유한다. Fixed 선·회사 Border와 별개다. 기본 선과 기존 SVG/회사 프리셋을 변경하지 않는다. Core는 Shadow DOM을 사용하므로 호스트의 일반 selector로 내부 카드가 바뀌지 않는다. 기존 공개 CSS 두 개와 Core를 재사용한다. 별도 승인된 스타일 변경이 있을 때만 [Expanded 스타일 안내](expanded-usage.md)의 실제 변수·스타일 로딩 경로를 확인한다. 변수 이름을 추측하거나 카드 교체 API를 만들지 않는다. 근거: [회사 프리셋](../../packages/org-chart/src/company-color-presets.ts), [Fixed 부서 자산](../../packages/org-chart/src/core/department-card-assets.ts), [대표자 자산](../../packages/org-chart/src/core/representative-card-asset.ts), [직원 카드](../../packages/org-chart/src/core/employee-card.ts), [공통 카드 CSS](../../packages/org-chart/src/styles/employee-card.css), [Auto 카드 CSS](../../packages/org-chart/src/styles/org-chart-expanded.css), [Auto 연결선](../../packages/org-chart/src/core/expanded-layout.ts). ## 8. 인터랙션·팝업·Drag & Drop ### 탐색 - Auto toolbar의 확대/축소는 현재 배율×step 또는 ÷step, 기본 step=1.2, min=0.05, max=3이다. 공개 handle에는 zoomIn/zoomOut이 없으며 `zoomTo(scale)`를 사용한다. - Ctrl/Command+휠 확대, viewport 초점에서 +/−, 빈 공간 드래그/방향키 Pan을 지원한다. `panBy(x,y)`, `getViewportState(): {scale,x,y} | undefined`를 제공한다. - toolbar의 100% 복원은 배율 1과 위치를 함께 복원한다. `zoomTo(1)`는 배율 변경이므로 동일한 동작이 아니다. 공개 resetViewport 메서드는 없다. - `fitToView()`는 Auto에서 전체 표시 범위를 맞춘다. Fixed의 위 handle viewport 호출은 Auto 동작을 제공하지 않는다. - 그룹 타이틀은 click/Enter/Space로 접기·펼치고 조직장은 남긴다. 부모 재확장 시 유효한 자손 접힘 상태를 유지한다. `interactions.collapse=false`는 토글만 끈다. 하위 내용 없는 제목은 aria-disabled로 접기 불가이나 이동/키보드 대상일 수 있다. - Zoom/Pan을 끄면 해당 조작/API를 막지만 Fit은 독립적으로 가능하다. manual Pan이 OFF여도 키보드로 화면 밖 카드에 도달하면 배율을 유지하고 초점을 화면에 드러낸다. ### 팝업 Core/Vanilla 기본 직원 카드 click/Enter/Space는 native dialog를 연다. X·ESC로 닫고 Tab 초점을 가두며 원래 카드로 복귀한다. 카드가 사라졌거나 인스턴스가 정리되면 shell/연결 상태에 따라 복귀한다. **Core dialog에는 외부 클릭으로 닫는 별도 핸들러가 없다.** React Auto의 `employeePopup`은 `core`(기본), `hm-dialog`, `adapter`, `none`이다. HM 모드는 `/hm-dialog` Entry 또는 명시적으로 주입한 `HMEmployeeDialog`를 사용한다. HM Dialog·Avatar·Button으로 X·닫기 버튼·ESC·외부 클릭으로 닫으며 onCloseAutoFocus에서 원래/재생성된 카드 또는 shell에 복귀한다. `adapter`는 호스트의 `employeePopupAdapter`를 명시적으로 선택한다. none은 기본 팝업/button 의미를 제거하지만 키보드 탐색용 초점은 남긴다. Fixed에서는 employeePopup 선택이 적용되지 않고 기존 Core 팝업을 유지한다. `onEmployeeClick(employee, context)`를 지정하면 기본 팝업보다 우선한다. context는 `organization`, `affiliation`, `trigger: HTMLElement`, `originalEvent: MouseEvent | KeyboardEvent`다. 임의 popup API를 추가하지 않고 호스트가 자신이 만든 UI의 닫기·접근성·focus 복귀를 책임진다. ### 이동 정책 - `dragDropEnabled`는 Core/React 공통으로 **기본 false**다. displayMode readOnly도 이 옵션을 대체하는 편집 잠금이 아니다. - OFF는 포인터 이동·Alt+M·handle.move를 차단하고 `EDITING_DISABLED`를 반환한다. OFF에서도 독립 설정에 따른 Pan/Zoom/Fit·팝업·접기는 유지한다. - ON은 Auto 직원 카드/조직 타이틀 드래그 이동, Alt+M → 방향키 대상 선택 → Enter 확정 / ESC 취소를 지원한다. 빈 공간은 Pan이다. - Root, 자신/자손으로 조직 이동, 같은 소속, leaderId 참조를 손상시키는 조직장 이동 및 지정된 상단 직원 이동을 차단한다. 대상 회사 affiliation이 없으면 회사 간 이동도 거절한다. - 동일 회사 직원 이동은 organizationId만 바꾸고 affiliationId를 보존한다. 회사 간 직원 이동은 affiliationId도 대상 회사로 변경한다. - 그룹 이동은 parentId를 바꾸며 회사 간 이동은 하위 조직 전체 companyId와 소속 직원 affiliationId도 대상 회사로 바꾼다. ID·이름·직위·사진·자손 관계·조직장·정렬은 보존한다. - 결과는 **현재 Core 인스턴스의 새 immutable snapshot**이다. 원본 JSON·DataSource·서버·파일은 수정하지 않고 영구 저장 API도 없다. 새 source/reload는 소스 데이터를 다시 적용한다. UI 회사 선택만으로 이동 snapshot을 초기화하지 않는다. - OFF 전환은 진행 중 드래그/키보드 이동을 취소하고 이미 완료한 이동은 남긴다. 설정만 바꿀 때 reload하지 않는다. ```ts import type { OrgChartHandle, OrgChartMoveResult } from '@hm-design/org-chart'; export async function moveEmployee(chart: OrgChartHandle): Promise { chart.setDragDropEnabled(true); const result = await chart.move({ kind: 'employee', sourceId: 'hanmac-office-1-team-1-employee', targetOrganizationId: 'hanmac-office-2-team-1', }); chart.setDragDropEnabled(false); if (result.moved) console.log(result.data); // 새 snapshot, 저장은 호스트 별도 책임 else console.error(result.code, result.message); return result; } ``` 위 예제는 A를 mount한 handle 기준이다. `onMove(result)`도 성공/실패를 알리며 `result.moved`로 분기한다. 성공은 data/request/previousOrganizationId, 실패는 code/message다. React 성공 이동은 열린 HM Dialog를 닫는다. 데이터용 순수 `applyOrgChartMove`는 UI OFF 잠금과 별개인 변환 함수이며 handle.move 대신 호출해 편집 잠금을 우회하지 않는다. 근거: [Viewport 이벤트](../../packages/org-chart/src/core/expanded-viewport.ts), [편집 이벤트](../../packages/org-chart/src/core/expanded-editing.ts), [이동 정책](../../packages/org-chart/src/data/moves.ts), [Core 팝업](../../packages/org-chart/src/core/employee-info-dialog.ts), [HM 팝업](../../packages/org-chart-react/src/HMEmployeeDialog.tsx), [HM Dialog primitive](../../packages/ui/src/components/ui/dialog.tsx). ## 9. 데이터 업데이트 — 예제 C Vanilla `update(input)`는 전달 snapshot을 검증해 현재 인스턴스에 적용하고 DataSource 자체를 교체하지 않는다. `reload(nextSource?)`는 현재/새 source를 load한다. 이후 reload가 이전 source 값을 다시 적용할 수 있다는 차이를 구분한다. 인라인 JsonOrgChartDataSource는 생성 시 값을 캡처하므로 원본 객체를 나중에 바꾸는 방식은 업데이트가 아니다. ```ts import { JsonOrgChartDataSource, validateOrgChartDataset, type OrgChartHandle, } from '@hm-design/org-chart'; export async function updateChart(chart: OrgChartHandle, input: unknown) { const checked = validateOrgChartDataset(input); if (!checked.valid) throw new Error(JSON.stringify(checked.issues)); await chart.update(checked.data); if (chart.getState().status !== 'ready') throw new Error('업데이트 실패'); } export async function replaceSource(chart: OrgChartHandle, input: unknown) { const checked = validateOrgChartDataset(input); if (!checked.valid) throw new Error(JSON.stringify(checked.issues)); await chart.reload(new JsonOrgChartDataSource(checked.data)); if (chart.getState().status !== 'ready') throw new Error('소스 변경 실패'); } ``` React는 dataSource identity가 바뀌면 같은 handle에 reload한다. `useMemo`/module 상수로 source를 안정적으로 만들고 매 render에서 새 source를 만들지 않는다. 다음은 호스트가 받은 새 데이터 prop으로 갱신하는 별도 React 예제다. ```tsx // UpdatingChart.tsx import { useMemo } from 'react'; import { OrgChart } from '@hm-design/org-chart-react'; import { JsonOrgChartDataSource, type OrgChartDataset } from '@hm-design/org-chart'; import engineUrl from '@hm-design/org-chart/engine.css?url'; import coreUrl from '@hm-design/org-chart/style.css?url'; const stylesheetUrls = { engine: engineUrl, core: coreUrl }; export function UpdatingChart({ dataset }: { dataset: OrgChartDataset }) { const source = useMemo(() => new JsonOrgChartDataSource(dataset), [dataset]); return { if (state.status === 'error') console.error(state.issues); }} onLifecycleError={error => console.error(error)} />; } ``` 새 dataset 객체를 전달한다. 성공한 onMove의 data를 집계용 read model로 저장하는 것과 바로 새 source로 넣어 reload하는 것을 구분한다. Core는 최신 요청 revision을 사용해 오래된 load 결과를 무시/취소한다. 재생성 옵션 변경은 viewport/이동의 보존을 보장하지 않는다. ## 10. AI 코딩 에이전트 적용 절차 1. 현재 사용자 요청·Git 기준·공개 API를 확인하고 회사/조직 유형·부모 관계·분기 수·직원 수를 정리한다. Root를 포함한 조직 Depth와 직원 수 집계 기준을 구분한다. 2. 자연어의 가상 데이터를 생성하거나 제공 JSON·CSV·Excel을 Contract로 변환한다. Core가 CSV/XLSX parser나 자연어 생성기를 제공하는 것은 아니다. 외부 변환 결과는 unknown으로 취급한다. 3. 원본 이름·ID·소속·직위·사진·부모 관계를 보존한다. 조직장과 대표자를 총원에 포함할지 확인하고 employees에 한 번만 생성한다. 미지원 필드와 원본을 별도 보존하며 오류 항목을 삭제해 Validator를 통과시키지 않는다. 4. validateOrgChartDataset와 요청별 회사·조직·Depth·인원 검사를 수행한다. Auto 회사 프리셋/회사 경계/상단 직원 제약도 확인한다. Validator 성공만으로 Auto 표시가 보장되지 않는다. 5. 기존 공개 Core/React·카드·컬러·연결선을 재사용해 실제 client 진입점을 만든다. 새로운 엔진·입력 UI·개발 패널을 만들지 않는다. 6. 구현 완료 후 한 번의 통합 검증으로 핵심 동작·데이터 보존·연결선·가독성·viewport·키보드·필요한 타입/build/Guard를 확인한다. 390/768/1280/1440px 및 요청 크기에서 확인한 실행 URL/캡처와 미검증을 보고한다. 예: 삼안 6본부, 팀 5·2·4·1·3·2=17, 총 500명은 실제 생성 데이터의 organizations와 unique employee 수로 검증한다. 한맥기술 3부문×2본부×4팀과 300명도 같은 절차로 처리한다. 이 가이드의 7명 예제 분배를 재사용하지 않는다. 공개 `createOrgChartExampleDataset`는 기존 template에서 `employeeCount: 20 | 100 | 300`, scope `total | per-company`로 가상 예시를 만드는 helper다. 임의 500명 생성·실제 Excel 변환 API가 아니다. 회사별 모드는 선택 회사마다 N명이며 total 모드는 원래 회사별 분배 후 선택 회사만 필터링한다. 실제 사용자 데이터를 이 helper로 재생성하지 않는다. ### Chart Only 정책과 현재 기능의 관계 [AI 페이지 생성 정책](ai-page-generation-rules.md)을 먼저 읽고 AGENTS.md·CLAUDE.md의 연결을 따른다. **별도 요청이 없다면 AI 생성 화면에는 조직도 캔버스만 표시하고 추가 제목·안내·테스트·JSON 패널을 만들지 않는다.** 실제 적용 기준은 최종 통합된 정책이다. P10.2에서 ChartOnly Worktree의 정책 보완을 통합했다. 기본 목표는 **White Canvas + OrgChart Nodes + Connectors**이며 외부 제목·기준일·요약·Zoom/Fit 버튼 바·설명·회색 배경은 요청 없이 추가하지 않는다. 정책은 생성 지침이며 새 Core prop/기능이 아니다. 현재 Auto는 heading/기준일·회사/인원 요약·Zoom/Fit toolbar·배율/조작 안내·Drag & Drop feedback를 내부 생성한다. Fixed readOnly도 heading·기준일과 기존 구성 요소를 유지한다. 현재 공개 옵션에는 `chartOnly`, `showToolbar`, `showHeader` 또는 모든 내장 부가 UI를 끄는 기능이 없다. displayMode readOnly나 presentation 생략으로 해결된다고 주장하지 않는다. `createOrgChartTree`는 기존 Fixed 계열 독립 Tree이며 Auto 기능 전체의 대체 API가 아니다. 요구한 최소 화면을 공개 API로 구현할 수 없으면 제약과 별도 승인 필요성을 보고하고 Core 복제·Shadow DOM 임의 조작·강제 숨김·병렬 정책 수정으로 해결하지 않는다. P10.2의 독립 실제 tarball 브라우저 검사에서는 한맥기술 2본부×2팀·전체 20명 데이터를 React/Vanilla로 렌더링했다. 390/768/1280/1440px에서 흰색 내부 캔버스·7개 조직·20개 고유 직원 카드·TB 계층 연결선·Zoom/Pan과 콘솔 오류 0을 확인했다. 그러나 위 내장 UI와 `rgb(244, 247, 251)` 바깥 영역이 남아 **Chart Only PARTIAL**이다. Fixed readOnly도 제목·기준일을 유지한다. DOM 숨김·삭제·Core 수정은 사용하지 않았다. Auto 초기 배치는 전체 Bounds의 **가로 중앙·상단 16px 정렬**이며 초기 자동 Fit과 다르다. Vanilla의 공개 `fitToView()`는 실제 전체 Bounds로 가로·세로 중앙 배치/축소를 수행한다. React Wrapper에는 공개 handle/ref나 초기 Fit prop이 없으므로 버튼 바 없이 호스트가 이 동작을 직접 요청하는 경로는 없다. 후속 개선 후보는 기본 동작을 유지하는 선택형 부가 UI 표시 옵션과 초기 Fit 정책이다. 이들은 현재 API가 아니며 이번 작업에서 추가하지 않았다. 자세한 근거와 미검증은 [P10.2 통합 보고서](integration-report-p10-2.md)를 참조한다. React 없이 생성해 달라는 명시적 요청은 이 가이드의 Vanilla Core 경로로 처리하고 [Runtime portability](../guidelines/runtime-portability.md)의 명시적 adapter 계약을 확인한다. HM React 화면을 HTML로 모방해 결과라고 제출하지 않는다. ## 11. 오류·검증·지원 한계 ### 검사와 오류 처리 - validateOrgChartDataset는 valid=true이면 깊게 freeze한 snapshot을 반환한다. false이면 `issues: {code,path,message}[]`를 반환한다. - 기본 오류: SCHEMA_VERSION, UNKNOWN_FIELD, REQUIRED_VALUE, DUPLICATE_ID, EMPTY_ORGANIZATIONS, INVALID_TYPE/STATUS/RELATION, PARENT_REFERENCE, LEADER_REFERENCE/LEADER_APPROVAL_REQUIRED, ORGANIZATION_REFERENCE, AFFILIATION_REFERENCE, HIERARCHY_CYCLE, RELATION_REFERENCE, PRESENTATION_REFERENCE, UNSAFE_INPUT. - organizations=[]는 오류이며 employees=[]는 유효할 수 있다. State 타입의 empty가 실제 empty UI 지원 증거는 아니다. - Auto는 추가로 COMPANY_PRESET/COMPANY_HIERARCHY 및 TOP_NODE_CONFIG/TOP_NODE_REFERENCE를 검사한다. 기본 Validator는 회사 프리셋/회사 간 계층 경계를 검사하지 않으므로 두 층을 구분한다. - source 오류는 SOURCE_URL/LOAD/PARSE/ABORT/SNAPSHOT, 스타일 오류는 CORE_STYLE 등으로 구별한다. Core state.error와 React onLifecycleError의 책임을 구분한다. - calculateSummary(data).raw는 unique 직원·direct/inclusive·position·affiliation·status 집계다. 업무 승인 집계는 aggregationPolicy가 없으면 unavailable(policyRequired)이다. 현재 정책 구현은 includeLeaders=true와 membershipMode=primaryOnly 등 제한을 확인하며 임의 excludeLeaders/uniqueEmployee 지원을 선언하지 않는다. ### 주요 미지원·별도 확인 대상 - 자연어→데이터 자동 생성 UI, CSV/XLSX 직접 import, 서버 저장·외부 API·CSV/PDF/이미지 export는 공개 사용 기능이 아니다. - 임의 회사 프리셋 등록 setter·카드 교체·Compact·공개 collapse setter·React imperative ref·Chart Only 전용 옵션은 없다. - 제공 데이터의 최대 인원/Depth·모든 브라우저·실제 모바일 터치/스크린리더·외부 fresh install을 이번 소스 분석으로 보장하지 않는다. - 기존 P10 PARTIAL/Guard FAIL 10건은 그대로다. 새 가이드의 문서 검사와 제품 전체 PASS를 혼동하지 않는다. - [P10.3 GuardReview](guard-review-p10-3.md)는 별도 Worktree에서 작성한 원본 분석이다. 기존 FAIL 10건을 재현했으며 제품·Guard 수정은 없다. P10.3 Git 공유 단계에서도 이 FAIL을 유지한다. 완전한 Chart Only 공개 API, 외부 CSS 테마 표준화, Fixed SVG/PNG 테마화, 디자인 승인, main Merge와 Docker 배포는 별도 후속 작업이다. ### 이 가이드 예제 검증 범위 아래 검증 상태는 이 문서의 예제를 대상으로 한 별도 결과이며 기존 제품 회귀검사 결과가 아니다. - **TypeScript PASS:** 이 문서에서 추출한 TS/TSX 코드 블록 8개를 설치된 TypeScript 7.0.2의 strict·ES2022·Bundler·react-jsx 설정으로 검사했다. Core/React import는 기준 Worktree의 src 공개 진입점, CSS URL은 기존 Vite client 타입에 연결했다. 새 빌드 산출물을 지원 증거로 사용하지 않았다. - **데이터 실행 PASS:** 기준 Worktree의 데이터 모듈을 임시 경로에 컴파일해 JSON 2개와 data.ts의 A/B 데이터 2개를 실제 Validator로 확인했다. A=7조직/7명, B=독립 Root 2개/10명, 조직장 중복 없는 집계, 직원 이동 후 원본 소속·총원 보존, 자손으로 그룹 이동 거절, 회사 프리셋 7개 값을 확인했다. - **Runtime NOT VERIFIED:** App/UpdatingChart의 React 렌더링, mountChart의 Vanilla mount, configureLayout, handle 기반 moveEmployee/updateChart/replaceSource, HTML 진입점의 실제 실행은 확인하지 않았다. 타입 검사와 순수 applyOrgChartMove 실행을 이들 브라우저 예제의 실행 PASS로 해석하지 않는다. - 실제 React/Vanilla 화면 렌더링·CSS load·인터랙션 시연·시각 QA: **NOT VERIFIED**. - 설치/전체 build/Storybook 회귀·외부 소비 프로젝트 fresh install: **NOT VERIFIED**. 위 항목은 UsageGuide 원본 문서 작업 당시의 범위다. **P10.2 추가 검증:** 통합 후 TS/TSX 예제 10개를 현재 package 공개 `.d.ts`에 연결해 strict·skipLibCheck=false로 확인했다(PASS). 기존 실제 npm pack A/B/C 설치·타입·Build·팝업 검사 PASS는 현재 배포 JS/타입/CSS의 byte 동일성을 확인해 재사용했다. 독립 Chart Only React/Vanilla Build와 위 브라우저 검사는 새로 수행했다. 이 가이드의 HMChart/CustomChart 예제 자체의 브라우저 시연, 전체 회귀·성능·SSR·스크린리더·모바일 touch는 NOT VERIFIED다. ## 12. 관련 문서·실제 소스 필요한 부분만 확인한다. - [AGENTS.md](../../AGENTS.md), [CLAUDE.md](../../CLAUDE.md): 프로젝트 공통 제약. - [AI 생성 정책](ai-page-generation-rules.md): 자연어/제공 데이터 생성·보존·최소 화면 정책. - [Expanded 사용법](expanded-usage.md), [Framework 통합](framework-integration.md), [독립 컴포넌트](standalone-components-p1.md): 상세 소비 경로. - [Core 구현](../../packages/org-chart/src/core/index.ts), [공개 옵션](../../packages/org-chart/src/core/types.ts): 초기화·업데이트·정리·Auto setter·이벤트. - [React 구현](../../packages/org-chart-react/src/OrgChart.tsx), [Props](../../packages/org-chart-react/src/types.ts): lifecycle과 갱신 경계. - [데이터 타입](../../packages/org-chart/src/data/types.ts), [검증](../../packages/org-chart/src/data/validate.ts), [이동](../../packages/org-chart/src/data/moves.ts): 실제 Contract와 정책. - [P10 보고서](implementation-report-p10.md): 기존 PARTIAL/Guard FAIL·별도 미검증 기록. 현재 기능의 근거는 보고서보다 이 가이드 기준 소스다. 원본 UsageGuide Worktree의 문서 예제 검증 기록은 위에 보존했다. P10.2에서는 이 가이드의 Entry/설치/Adapter 설명과 Chart Only 정책 연결을 보완한다. 제품 코드·기존 디자인·Core 공개 API를 추가 변경하지 않는다.