Update the usage guide with current Auto/Expanded Chart Only behavior, React and Vanilla public APIs, optional HMDesign Dialog, and Gitea commit pinning/update instructions. Preserve unresolved Guard failures, security warnings, accessibility risks, and pending design, main merge, and deployment approvals.
729 lines
62 KiB
Markdown
729 lines
62 KiB
Markdown
# HMDesign OrgChart 공통 사용 가이드
|
||
|
||
개발자·Codex·Claude가 공개 API로 조직 데이터를 연결하기 위한 가이드다. **2026-10-13 P10.7의 검증 기준 Commit은 `d56d587fd20316f19554105f33c200060394962b`**(`feature/orgchart-package`)이다. 이 SHA는 당시 검증한 버전이며 향후 최신 버전을 뜻하지 않는다. 실제 받은 버전은 `git rev-parse HEAD`로 확인한다. 기능 검증 근거는 [최종 Feature 통합 보고서](final-integration-2026-10-13.md), 승인 대기 항목은 [P10.7 정식 통합 승인 준비](release-readiness-p10-7.md)를 따른다. 과거 P-AI1/P10.2의 제한을 현재 지원 여부로 사용하지 않는다.
|
||
|
||
빠른 시작은 아래 데이터 파일과 React 또는 Vanilla 진입점을 함께 복사한다. 예제는 Vite의 TypeScript 환경을 전제로 한다. 기본 예제는 제목·요약·툴바가 있는 Auto 화면이다. **Auto/Expanded Chart Only는 구현 및 기존 브라우저 검증이 완료됐으며**, 외부 UI 없이 삽입할 때는 [Chart Only 공개 API](#chart-only-공개-api-auto)를 사용한다. **Fixed Full Composition은 지원하지만 Fixed 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다. 공유 방식은 공식 Gitea 소스 연결이며 Git Clone 후 의존성 설치·빌드를 별도로 수행한다. npm Registry Publish와 ZIP 배포는 현재 공유 방식에 포함하지 않는다.
|
||
- 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
|
||
# 검증 기준 고정: 기존 변경이 없는 새 Clone에서 실행한다.
|
||
git checkout --detach d56d587fd20316f19554105f33c200060394962b
|
||
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 소비자의 최소 의존성을 의미하지 않는다.
|
||
|
||
Git으로 받은 소스만으로 의존성 설치나 빌드가 완료되는 것은 아니다. `npm.cmd ci`는 해당 Commit의 lockfile을 사용하며 `npm.cmd run build:packages`가 공식 workspace 패키지 빌드 명령이다. 위 타입·테스트·앱 빌드 명령은 소비 환경 검증용 안내이며 P10.7 문서 작업에서 실행한 결과가 아니다.
|
||
|
||
### 버전 고정과 업데이트
|
||
|
||
- 브랜치 Clone은 이동하는 브랜치의 HEAD를 받는다. 재현 가능한 소비 버전은 전체 Commit SHA로 고정하고 `git rev-parse HEAD` 결과를 소비 프로젝트 기록에 남긴다. 위 detached checkout은 브랜치 포인터를 변경하지 않는다.
|
||
- 업데이트 시 기존 로컬 변경을 먼저 확인·보존하고 `git fetch origin feature/orgchart-package`로 후보를 가져온다. 새 승인/검증 SHA를 확인한 뒤 `git checkout --detach <새로 확인한 전체 SHA>`로 바꾼다. 예시 자리표시자를 그대로 실행하지 않는다.
|
||
- Commit을 바꾼 뒤 `npm.cmd ci`와 `npm.cmd run build:packages`를 수행하고 호스트 타입·빌드·핵심 동작을 확인한다. 같은 `0.1.0` package version만으로 두 소스 버전이 동일하다고 판단하지 않는다.
|
||
- 기존 프로젝트에는 필요에 따라 Git Submodule 또는 Subtree로 소스를 연결할 수 있다. Submodule은 부모 저장소의 gitlink Commit, Subtree는 가져온 upstream Commit을 기록한다. 두 방식도 의존성 설치·빌드·호스트 연결은 별도다. 기존 UI Submodule 사용 사례를 **OrgChart Submodule/Subtree 통합 검증 완료**로 표현하지 않는다. OrgChart의 두 통합 방식은 **NOT VERIFIED**다.
|
||
|
||
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 로컬 검증 자료이며 Git으로 받은 소스에서 소비자가 만드는 설치 artifact다. tarball/ZIP을 별도 배포하거나 Registry에 게시하는 절차가 아니다. A/B/C는 별도 호스트 폴더에서 아래처럼 설치한다.
|
||
|
||
```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 <OrgChart
|
||
dataSource={source} stylesheetUrls={stylesheetUrls}
|
||
layoutMode="auto" displayMode="readOnly" autoLayout={autoLayout}
|
||
dragDropEnabled={false} employeePopup="core"
|
||
onStateChange={state => {
|
||
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(<App />);
|
||
window.addEventListener('pagehide', () => root.unmount(), { once: true });
|
||
```
|
||
|
||
```html
|
||
<!-- React index.html -->
|
||
<!doctype html>
|
||
<html lang="ko">
|
||
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>OrgChart</title></head>
|
||
<body style="margin:0">
|
||
<div id="root"></div>
|
||
<script type="module" src="/main.tsx"></script>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
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 <OrgChart dataSource={source}
|
||
stylesheetUrls={{ engine: engineUrl, core: coreUrl }}
|
||
layoutMode="auto" dragDropEnabled={false} employeePopup="hm-dialog" />;
|
||
}
|
||
```
|
||
|
||
### 외부 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<HTMLDialogElement>(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 ? <dialog ref={dialog} aria-label="직원 정보"
|
||
onCancel={event => { event.preventDefault(); onClose(); }}>
|
||
<p>{selection.employee.name} · {selection.employee.position}</p>
|
||
<button type="button" onClick={onClose}>닫기</button>
|
||
</dialog> : null;
|
||
}
|
||
const source = new JsonOrgChartDataSource(singleCompany);
|
||
export function CustomChart() {
|
||
return <OrgChart dataSource={source}
|
||
stylesheetUrls={{ engine: engineUrl, core: coreUrl }} layoutMode="auto"
|
||
employeePopup="adapter" employeePopupAdapter={EmployeePopup} />;
|
||
}
|
||
```
|
||
|
||
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
|
||
<!-- Vanilla index.html -->
|
||
<!doctype html>
|
||
<html lang="ko">
|
||
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>OrgChart</title></head>
|
||
<body style="margin:0">
|
||
<div id="chart"></div>
|
||
<script type="module" src="/main.ts"></script>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
`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은 아니다. Chart Only는 Auto 전용 `presentationMode: 'chart-only'`이며 Fixed에서는 거부된다([Chart Only 공개 API](#chart-only-공개-api-auto)).
|
||
|
||
### 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는 없다.
|
||
- `presentationMode: 'default'`에서 `initialView`를 생략하면 초기 배율 1·Bounds의 가로 중앙·상단 여유 배치다. `initialView: 'fit'` 또는 Chart Only 기본값은 전체 Bounds를 맞추고 가로·세로 중앙에 배치한다. 사용자가 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/presentationMode/initialView/wheelZoom을 바꾸려면 destroy 후 새 handle을 만든다.
|
||
|
||
React에서는 대응 prop으로 변경한다. dataSource 변경은 reload, autoLayout/interactions/dragDropEnabled/selectedCompanyIds와 Auto 팝업 변경은 기존 handle을 갱신한다. aggregationPolicy·CSS URL·layoutMode·displayMode·companyId·companyTopNodes·employeeConnections·zoom·organizationLinks·presentationMode·initialView·wheelZoom 값 변경은 handle을 재생성한다. 객체 옵션은 값으로 비교하므로 같은 값의 inline 객체 자체가 재마운트를 일으키지는 않는다. `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/⌘ + 휠, Chart Only는 modifier 없이 직접 휠 Zoom을 사용한다(`wheelZoom`으로 선택 가능). 휠은 커서 중심 확대·축소이며 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<OrgChartMoveResult> {
|
||
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 <OrgChart dataSource={source} stylesheetUrls={stylesheetUrls}
|
||
layoutMode="auto" dragDropEnabled={false}
|
||
onStateChange={state => {
|
||
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. [AI 페이지 생성 정책](ai-page-generation-rules.md#11-생성-방식-사전-선택)에 따라 생성 방식이 미지정이면 Chart Only / Full Page 선택 질문에 대한 답변을 받은 뒤 진행한다. 이미 명시된 방식은 다시 묻지 않는다. 현재 사용자 요청·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의 연결을 따른다. **생성 방식이 미지정이면 코드 작성 전에 Chart Only / Full Page를 질문하고 답변 후 구현한다.** Chart Only를 선택하면 조직도 캔버스만 표시하고 외부 제목·통계·설명·조작 패널을 만들지 않는다. Full Page를 선택하면 기존 HM UI와 OrgChart Core·React Wrapper로 요청된 UI와 전체 레이아웃을 구성한다. 이미 명시된 방식은 다시 묻지 않으며 Fixed·Auto는 생성 방식 선택을 대신하지 않는다.
|
||
|
||
#### Chart Only 공개 API (Auto)
|
||
|
||
Auto(`layoutMode: 'auto'`)에서 `presentationMode: 'chart-only'`를 지정하면 Core가 **흰색 캔버스 + 조직 노드 + 연결선**만 렌더링한다. React와 Vanilla는 같은 Core 옵션을 사용한다. 기본값은 기존 화면과 동일하다(`'default'`).
|
||
|
||
| 옵션 | 값 | 기본값 | 동작 |
|
||
|---|---|---|---|
|
||
| `presentationMode` | `'default' \| 'chart-only'` | `'default'` | chart-only는 외부 제목·기준일·회사/인원 요약·Zoom/Fit 버튼 바·배율·조작 안내·Drag & Drop 문구·카드형 외곽(회색 패딩·테두리)을 만들지 않는다. 노드 안 조직명·직원 정보는 유지한다. 이동 결과 live status는 화면에서 숨긴 채 보조기술에만 제공한다. |
|
||
| `initialView` | `'center' \| 'fit'` | chart-only `'fit'`, 그 외 `'center'` | fit은 전체 Forest Bounds(연결선 포함)를 16px 여백 안에 담고 가로·세로 중앙에 둔다. 100%를 넘겨 확대하지 않는다. 측정 전에는 조직도를 숨겨 첫 화면이 튀지 않는다. |
|
||
| `wheelZoom` | `'modifier' \| 'direct'` | chart-only `'direct'`, 그 외 `'modifier'` | direct는 캔버스 안에서 일반 휠로 포인터 기준 확대·축소한다. 캔버스 밖은 페이지 스크롤을 유지한다. modifier는 기존 Ctrl/⌘ + 휠이다. |
|
||
|
||
```tsx
|
||
// ChartOnly.tsx — 기본 Entry, HMDesign UI 불필요. 앞의 data.ts를 사용한다.
|
||
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);
|
||
export function ChartOnly() {
|
||
return <OrgChart dataSource={source}
|
||
stylesheetUrls={{ engine: engineUrl, core: coreUrl }}
|
||
layoutMode="auto" presentationMode="chart-only"
|
||
initialView="fit" wheelZoom="direct" dragDropEnabled={false}
|
||
style={{ height: '100vh' }} />;
|
||
}
|
||
```
|
||
|
||
```ts
|
||
// chart-only.ts — 앞의 data.ts와 호스트가 제공하는 HTMLElement를 사용한다.
|
||
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 mountChartOnly(container: HTMLElement): Promise<() => void> {
|
||
const chart = createOrgChart({
|
||
container, dataSource: new JsonOrgChartDataSource(singleCompany),
|
||
stylesheetUrls: { engine: engineUrl, core: coreUrl },
|
||
layoutMode: 'auto', presentationMode: 'chart-only',
|
||
initialView: 'fit', wheelZoom: 'direct', dragDropEnabled: false,
|
||
});
|
||
try {
|
||
await chart.mount();
|
||
if (chart.getState().status !== 'ready') throw new Error('조직도를 표시할 수 없습니다.');
|
||
return () => chart.destroy();
|
||
} catch (error) {
|
||
chart.destroy();
|
||
throw error;
|
||
}
|
||
}
|
||
```
|
||
|
||
- 두 예제는 기본값과 같은 `initialView`/`wheelZoom`을 명시해 선택 가능한 값을 보여준다. Vanilla 호스트는 높이를 정하고 반환 cleanup을 lifecycle 종료 때 호출한다. `/hm-dialog` Entry의 Chart Only도 같은 Props를 쓰며 팝업은 `employeePopup="hm-dialog"`를 명시한다.
|
||
- 캔버스는 컨테이너 높이를 채운다(`height: 100%`). 컨테이너 높이가 정해지지 않으면 최소 320px이다.
|
||
- 자동 Fit 시점: 최초 렌더링, `update()`/`reload()`/React `dataSource` 교체, 회사 선택·Auto layout 변경. 휠 Zoom·Pan·직원 팝업·그룹 접기/펼치기·Drag & Drop ON/OFF·React 단순 리렌더링에서는 다시 Fit하지 않는다. 사용자가 조작하기 전 캔버스 크기가 바뀌면 다시 Fit하고, 조작한 뒤에는 사용자 뷰를 유지한다.
|
||
- Fit 배율이 `zoom.min`보다 작을 수 있다(대규모 조직). 이때 휠 축소는 Fit 배율에서 멈추고, 확대는 `zoom.max`까지 가능하다. 카드 크기·글자 크기는 바꾸지 않는다.
|
||
- 빈 공간 드래그 Pan, 방향키/`+`/`-`, 직원 팝업(기본 Core, `/hm-dialog` 선택형, `employeePopupAdapter`), 그룹 접기·펼치기, Drag & Drop 기본 OFF는 그대로다. 공개 `fitToView()`/`zoomTo()`/`panBy()` handle도 그대로 동작한다.
|
||
- React Wrapper는 `autoLayout`·`interactions`·`selectedCompanyIds`·`zoom` 등 객체 옵션을 값으로 비교한다. 같은 값의 inline 객체로 리렌더링해도 다시 마운트하거나 Fit하지 않는다.
|
||
- **Fixed는 지원하지 않는다.** `presentationMode: 'chart-only'`와 Fixed(`layoutMode` 생략 포함)를 함께 쓰면 `TypeError`를 던진다. 승인된 Workdesk 구성(제목·요약·아트워크·비례 축소)을 임의로 걷어내지 않기 위해서다. Fixed에 Auto와 같은 휠 Zoom/Pan도 없다.
|
||
- 실행 예제와 브라우저 검증: [`examples/org-chart-chart-only`](../../examples/org-chart-chart-only/README.md).
|
||
|
||
P10.2 당시에는 내장 UI와 바깥 회색 영역이 남아 Chart Only가 PARTIAL이었으며 React 초기 Fit 옵션도 없었다. 이 **과거 검증 원본은 [P10.2 통합 보고서](integration-report-p10-2.md)에 보존**한다. 이후 위 공개 옵션 3개가 구현됐고 [최종 통합 검증](final-integration-2026-10-13.md)은 Auto Chart Only React/Vanilla 51/51 PASS를 기록한다. 과거 PARTIAL을 현재 기능의 미완료 상태로 해석하지 않는다. `chartOnly`/`showToolbar`/`showHeader`라는 prop은 현재도 없으며 정확한 API는 `presentationMode`다. `displayMode: 'readOnly'`나 dataset의 `presentation` 생략은 Chart Only 선택을 대신하지 않는다. `createOrgChartTree`도 Auto Chart Only의 대체 API가 아니다.
|
||
|
||
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는 Auto 전용 `presentationMode`로만 제공하며 Fixed Chart Only는 없다.
|
||
- 제공 데이터의 최대 인원/Depth·모든 브라우저·실제 모바일 터치/스크린리더·외부 fresh install을 이번 소스 분석으로 보장하지 않는다.
|
||
- 기존 P10 PARTIAL/Guard FAIL 10건의 원본 기록은 보존한다. 2026-10-13 Feature 통합 대상은 Chart Only·초기 Fit·직접 Wheel Zoom, GuardFix의 F3–F7 해결 5건, AI 생성 방식 사전 선택 정책이다. Cell 디자인·미리보기는 제외한다. 잔여 Guard FAIL 5건·NOT VERIFIED 9건을 유지하며 원본 `status/staticStatus FAIL`, `runtimeStatus NOT VERIFIED`를 제품 전체 PASS로 표현하지 않는다. 범위와 새 결과는 [최종 Feature 통합 보고서](final-integration-2026-10-13.md)를 따른다.
|
||
- [P10.3 GuardReview](guard-review-p10-3.md)는 별도 Worktree에서 작성한 원본 분석이다. 당시 FAIL 10건을 재현했고 제품·Guard 수정 없이 공유했다. 현재 Auto Chart Only는 위 공개 API로 지원하며 Fixed Chart Only, 외부 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/P10.2의 문서 예제 검증 기록은 위에 보존했다. P10.7에서는 검증 기준 SHA·Gitea Commit 고정 절차·현재 Chart Only 지원 상태와 옵션 설명을 공개 소스와 대조했다. 새 전체 타입 검사·제품 테스트·빌드·브라우저 검사는 수행하지 않았다. 제품 코드·기존 디자인·Core 공개 API 변경 없음.
|