feat(org-chart): add core cards and fixed-layout integration

This commit is contained in:
shkim4 committed 2026-10-12 17:58:30 +09:00
1 parent d18e3c3380
commit 61c6f8d1f1
161 files changed
+13044 -1

No files matched your search

+91
View File
@@ -0,0 +1,91 @@
# HMDesign OrgChart — 개발 전 설계 기준
> 현재 P1 기준(2026-10-12): 공통 HM Theme와 분리된 독립 디자인을 사용한다. [개별 컴포넌트·스타일 API](standalone-components-p1.md)와 [구현·검증 결과](implementation-report-p1.md)를 따른다. 아래 초기 설계와 v0.1 보고서는 과거 기록이며 Theme 필수 연결·reserved hub/status는 최신 사용법이 아니다.
상태: **문서 초안 / 사용자 검토 대기 / 구현 미착수**. 기준일: 2026-10-12(KST).
확정 요구사항은 이번 사용자 요청에서 확정된 범위다. `제안`은 승인 전 설계 후보이며, `TBD`는 정책 또는 기술 검토가 필요한 항목이다. 문서 검증의 PASS는 실행 기능의 PASS를 의미하지 않는다.
## 1. 목적과 문서 읽기 순서
여러 업무 화면의 콘텐츠 컨테이너에서 재사용할 Framework-independent OrgChart를 설계한다. 1차 공식 개발 대상은 **Vanilla HTML/JavaScript와 React 두 환경**이다. 표준 DOM API와 JavaScript/TypeScript 기반 Core를 우선하고 React는 별도 Wrapper로 연결한다. Core는 framework runtime과 HM React Component를 직접 import하지 않는다. 특정 Dashboard, Header 높이 또는 Navigation에 종속시키지 않는다. JSON과 향후 REST API가 같은 계약을 제공하며 각 환경의 공개 사용 방식은 유지해야 한다.
1. 이 문서: 확정 범위와 기존 기준과의 차이.
2. [Architecture](architecture.md): 계층별 책임, 의존성, 공개 사용 방식 초안.
3. [Data Contract](data-contract.md): 정규화 계약, TypeScript·JSON Schema 초안, 검증·집계.
4. [UI Composition](ui-composition.md): 콘텐츠 구성, HM 재사용, 컨테이너·접근성.
5. [Integration](integration.md): 엔진 공식 근거, React·Theme 연동, JSON → API 교체.
6. [Implementation Plan](implementation-plan.md): 단계별 완료 조건, Storybook, TBD·리스크, 이번 문서 검증 기록.
7. [Framework Integration](framework-integration.md): Vanilla·React 사용 계약과 예시, 생명주기, CSS·Theme·배포 검증, 미지원 환경.
## 2. 작업 환경 확인
읽기 전용 확인 결과, 다음 환경이 존재한다.
- 실제 Git Root·작업 경로: `D:\디자인시스템\HMDesign-OrgChart`.
- 현재 브랜치: `feature/orgchart-package`.
- 시작 HEAD: `d18e3c33801e165e17b6d971e2f6198eed483c3c`.
- 공식 origin(fetch/push): `https://gitea.hmac.kr/b25027/HMDesign.git`.
- 운영 Worktree: `D:\디자인시스템\HMDesign`의 `main`. 기존 Worktree를 재사용하며 새 Worktree는 생성하지 않았다.
- 시작 시 Local `main`, Feature, 두 remote-tracking ref와 `git ls-remote`의 원격 두 브랜치 SHA가 모두 위 HEAD와 일치했다. `main...feature/orgchart-package`의 좌/우 독자 Commit 수는 `0 / 0`이다.
- 시작 시 개발·운영 Working Tree는 모두 깨끗하고 staged 변경도 없었다.
- 루트 [package.json](../../package.json)의 npm workspaces는 `packages/*`; 현재 패키지는 `@hm-design/ui`, `@hm-design/center-graphic`이다. OrgChart 패키지는 아직 없다.
- 루트 React/React DOM은 `19.3.0`, Storybook은 `10.6.1`, Tailwind는 `4.3.3`이다. 이는 저장소 manifest 확인이며 설치 상태·호환성 검증 결과가 아니다.
- 기존 문서는 `docs/components`, `docs/patterns`, `docs/guidelines`, `docs/reports`, `docs/center-graphic`으로 구분된다. 독립 도메인인 [Center Graphic](../center-graphic/README.md)의 선례에 맞춰 `docs/org-chart`를 신규 설계 문서 위치로 선택했다.
이번 작업은 이 디렉터리의 Markdown 신규 작성만 허용한다. 구현, 설치, package/lockfile·Theme·Component·Pattern·Storybook 변경, Stage·Commit·Push·Merge는 수행하지 않는다. 운영 프로젝트와 `main`도 변경하지 않는다.
## 3. 확정 요구사항
- 엔진은 `dabeng/OrgChart`를 사용한다. 채택할 정확한 버전·배포 모듈 방식은 기술 검증 후 확정한다. [공식 데모](https://dabeng.github.io/OrgChart/), [공식 저장소](https://github.com/dabeng/OrgChart).
- 포함 콘텐츠: 제목·설명, 최상위 리더, 중앙 허브, 부문·실·Div, 셀·팀, 직원 프로필, 조직 계층·연결선, 조직 간 추가 관계, 팀별 인원, 파견·휴직 그룹, 관계사별 인원, 전체 인원 집계.
- 제외: Header, GNB, LNB, Dashboard Header, 페이지 전체 Layout, Routing, 신규 페이지 등록, 전역 Navigation, 상위 앱 인증 처리.
- 최신 추가 요청을 우선하여 Core 노드는 Framework-independent HTML/CSS로 표현하고 HM Theme를 CSS Custom Properties로 연결한다. 공개 React Component는 React 호스트/Adapter의 적합한 영역에서 재사용을 검토하고 Core에는 직접 import하지 않는다. 기존 Color/Semantic Token·Typography·Spacing·Radius 기준을 참조하되 기존 Theme·Component·Pattern이나 공통 디자인 규칙은 변경하지 않는다.
- 조직·직원·관계사·추가 관계·표현 설정·Metadata를 ID 기반으로 분리한다. 엔진용 중첩 트리는 ViewModel에서 파생하며 원본 계약이 아니다.
- Data Source → Data Validation → Normalized Data Contract → ViewModel / Adapter → OrgChart UI를 유지한다. JSON Mock, 외부 JSON, 향후 REST API 공급 방식은 같은 계약을 제공한다.
- 조직·직원·관계·표현 설정 변경은 가능한 한 데이터만 바꿔 반영한다. 집계는 직원 원본에서 계산한다. 미확정 집계 정책을 코드 기본값으로 숨기지 않는다.
- Validation 실패는 제어된 Error 상태로 전환하고 화면 전체 비정상 종료를 방지한다.
- 검토 너비는 390/768/1280/1440px이다. 컨테이너 기준으로 검토하고 모바일 전략은 기술 검토 후 확정한다.
- Storybook은 OrgChart 콘텐츠 단독·가상 데이터 검증용으로 설계한다. Header/Dashboard 조합 Story는 제외한다.
- 실제 API 명세가 없으므로 Endpoint·인증 방식·권한·캐시·재시도 정책은 TBD다. 실제 인물·개인정보는 재사용하지 않는다.
- 1차 공식 개발 대상은 Vanilla HTML/JavaScript와 React다. Vue·Angular·기타 framework는 이번 개발 범위에서 제외하고 Core의 향후 확장 가능성만 유지한다. 공식 개발 대상 지정과 실제 지원 완료는 구별하며 현재 실행 지원은 모두 NOT VERIFIED다.
- Core는 엔진·데이터·Validation·Data Source Adapter·집계·Mount/Update/Reload/Destroy를 공통 관리한다. npm·ESM·브라우저 번들·별도 CSS·타입 선언·Framework Adapter 배포를 검증한다. Theme 미연결 정책은 TBD다.
- 두 환경은 동일 JSON 계약·Data Source Interface·Validation·집계·노드 UI·HM Theme CSS Variables·차트 인터랙션을 사용한다. React Wrapper에 별도 엔진/노드 디자인/업무 로직을 중복 구현하지 않는다.
## 4. 제안과 자료 한계
- 패키지 경로 `packages/org-chart`, 이름 `@hm-design/org-chart`는 **제안 유지**다. `packages/*`와 양립 가능함을 확인했지만 이름·공개 Export·의존성 승인이나 생성은 하지 않았다.
- 최신 확정 요청의 우선 검토안은 `@hm-design/org-chart` Core + `@hm-design/org-chart-react` Wrapper 두 패키지다. 실제 분리는 기존 build 연결과 의존성 검증 후 최종 결정한다.
- 문서의 함수·Interface·Schema·폴더·Story 이름은 구현 초안이다. 현재 공개 API처럼 import하거나 사용하지 않는다.
- Workdesk 조직도 캡처를 시각 레퍼런스로 사용하는 요구는 확정이다. 그러나 이번 첨부에는 작업 지시 텍스트만 있고 캡처 파일은 없다. 콘텐츠 8영역은 사용자 설명에서 도출했다. 실제 카드 크기·색상·곡선·배치·시각적 일치는 **NOT VERIFIED**이며 추정으로 확정하지 않는다.
- 가상 직원은 `가상직원 가`처럼 예시임을 식별할 수 있는 명칭을 사용한다. 사진은 null/fallback 또는 승인된 가상 asset만 사용한다.
## 5. 기존 지침과 적용 차이
기존 기준은 [AGENTS.md](../../AGENTS.md), [화면 선택·완료 계약](../guidelines/ai-screen-contract.md), [공개 인덱스](../guidelines/component-pattern-reference.md), [현재 추가 수정 기준](../guidelines/publisher-additional-review-2026-10-10.md), [Runtime Theme](../guidelines/runtime-theme.md)를 참조한다. 공통 기준 전체를 복제하지 않는다.
- **경로·브랜치·Git 정책 충돌**: 기존 AGENTS는 `HMDesign-Gitea-Transfer/main` 수정과 검증 후 Commit/Push를 요구한다. 이번 요청은 `HMDesign-OrgChart/feature/orgchart-package`의 문서 전용, 운영 `main` 보존, Commit/Push 금지다. 이번 명시적 요청을 우선 적용하며 기존 AGENTS는 수정하지 않는다. 이 예외를 향후 구현·릴리스 권한으로 일반화하지 않는다.
- **Storybook 범위 해석**: 기존 규칙은 신규 업무 전체 화면 등록과 대표 Story 교체를 금지한다. 이번 요청은 신규 패키지의 콘텐츠 단독 Story 설계다. 이를 컴포넌트/도메인 검증으로 분리하며 전체 업무 화면을 등록하지 않는다. 미래 코드 등록 전에 분류·경로를 승인받는다.
- **Navigation·Dashboard 규칙 적용 범위**: 페이지 소비자는 기존 단일 Navigation 규칙을 따른다. 콘텐츠 패키지가 모드를 선택하거나 Dashboard 48px Header·960px 최소 폭을 내장하지 않는다. 상위 컨테이너 제약은 소비자가 제공한다.
- **Guard 범위**: 이번 산출물은 Markdown만 있고 React target/bootstrap이 없다. 화면 생성 Guard를 억지로 실행하거나 PASS를 만들지 않는다. 미래 UI 검증의 적합한 target과 범위는 [Implementation Plan](implementation-plan.md)에서 별도로 다룬다.
- **CENTER 선례의 차이**: OrgChart Core의 framework 독립성과 HM CSS Token 재사용을 함께 유지한다. CENTER의 전용 Token 정책을 복사하지 않는다.
- **Framework 요구와 기존 React 원칙 충돌**: 기존 AGENTS의 신규 React 화면·HM Component 렌더링 원칙은 최신 추가 요청의 Core 독립성·HTML/CSS 노드 조건에 맞춰 제한한다. Core 표준 DOM 표현을 이번 OrgChart 전용 예외로 기록하며 기존 지침을 자동 수정하지 않는다. React 업무 화면 소비자는 적합한 HM 공개 Component를 계속 사용한다. Vanilla 배포는 기존 Runtime Portability 계약과 함께 검증한다.
## 6. 확정 요구사항 추적
- A 프로젝트 목적·패키지 제안 → 이 문서, Architecture §1.
- B 콘텐츠 범위·페이지 제외 → 이 문서 §3, UI Composition §1.
- C HM 재사용 → UI Composition §2, Integration §3.
- D 공급 계층 → Architecture §2, Integration §4.
- E 데이터 모델 → Data Contract §1–3.
- F 데이터 변경·유지보수 → Architecture §4, Integration §4.
- G Validation → Data Contract §4.
- H 인원 집계 → Data Contract §5.
- I 엔진 기술 검토 → Integration §1–2.
- J 화면 8영역·가상 데이터 → UI Composition §1, Data Contract §3.
- K 반응형·컨테이너 → UI Composition §3.
- L 콘텐츠 Storybook → Implementation Plan §2.
- M API 전환 점검 → Integration §4–5.
- 추가 Framework-independent 요구 → Architecture 전체, Framework Integration 전체, Implementation Plan §1–3.
문서화 다음 단계는 사용자 검토·승인이다. 이 문서 작성만으로 개발, 설치 또는 Commit/Push가 승인되지는 않는다.
+117
View File
@@ -0,0 +1,117 @@
# OrgChart Architecture
상태: 개발 전 설계. 확정 범위·환경·우선순위는 [README](README.md)를 따른다. 최신 추가 요청에 따라 Framework-independent Core를 기준으로 한다.
## 1. 독립 Core와 패키지 제안
확정: Core는 React·Vue runtime에 의존하지 않는다. 표준 DOM API와 JavaScript/TypeScript를 사용하고 dabeng/OrgChart 엔진을 Core에서 관리한다. 데이터 계약·Validation·Data Source Adapter·집계는 공통이다. Routing·로그인·Navigation·상위 Layout은 소유하지 않는다.
`packages/org-chart` / `@hm-design/org-chart`는 제안이다. npm workspaces `packages/*`와 양립할 수 있음을 확인했지만 생성·명칭 승인은 하지 않았다.
우선 검토안은 Core `@hm-design/org-chart`와 React Wrapper `@hm-design/org-chart-react` 두 패키지다. 경로 후보는 `packages/org-chart`와 `packages/org-chart-react`다. workspace·build 연결과 Core 소비에 React가 따라오지 않는 dependency·bundle 경계를 입증하고 최종 분리를 결정한다. 같은 패키지의 `/react` subpath는 우선안이 불가능할 때의 비교 항목만 남긴다. Vue·Angular·기타 framework는 이번 범위에서 제외한다. 실제 분리·경로·Export는 승인 전이다.
향후 책임별 구조 예시이며 이번에는 파일을 만들지 않는다.
```text
packages/org-chart/ 제안: framework-independent Core
src/
index.ts 공개 Core facade와 type
data/ 계약·Validation·집계(DOM도 비의존)
sources/ JSON Mock / 외부 JSON 공급
view-model/ ID index·파생 트리·표시 모델
engine/ dabeng adapter·DOM·생명주기
renderers/ 표준 DOM 노드·콘텐츠·안전한 텍스트
styles/ 컨테이너 scope·Theme mapping·Overlay
브라우저 entry·빌드 설정 승인 후 결정
packages/org-chart-react/ 우선 검토안: React 연결만 담당
src/org-chart/ 콘텐츠 단독 Story·가상 fixture 제안 위치
```
Core는 `@hm-design/ui` React API·`@hm-design/center-graphic`을 import하지 않는다. HM Theme CSS는 호스트의 별도 제공 또는 승인된 standalone 배포 경로로 연결한다. Theme 때문에 React peer를 가진 UI 전체 패키지 설치를 Vanilla의 필수 조건으로 만들지 않는다. 엔진 dependency의 버전·포함 범위는 TBD다. React Wrapper만 React/React DOM peer를 검토한다.
루트 [build-package.mjs](../../scripts/build-package.mjs)는 현재 `ui`, `center-graphic`만 허용한다. workspace 자동 탐색만으로 신규 빌드가 완성되지 않는다. 미래 별도 build 또는 최소 기존 빌드 연결 승인이 필요하다. 배포·의존성 격리는 [Framework Integration](framework-integration.md)에서 검증한다.
## 2. 데이터 흐름과 계층 책임
```text
JSON Mock / 외부 JSON / 미래 REST API
↓ Data Source Adapter: 원본 필드 매핑
동일 normalized 계약 후보(unknown)
↓ Data Validation: 구조·참조·정책 검증
검증 성공한 Normalized Data Contract(OrgChartData)
↓ ViewModel / Engine Adapter
조직별 표시 모델·집계·engine용 파생 트리·추가 relation 모델
↓ Framework-independent Core
DOM 콘텐츠·engine 계층 영역·Relation Overlay 후보
↑ 동일 Core 생명주기 호출
Vanilla 호스트 / React Wrapper (동일 Core 재사용)
```
Source 매핑은 Validation 이전에 공통 계약 후보를 만든다. 검증 후 신뢰된 계약과 구별한다. TypeScript cast만으로 신뢰하지 않고 모든 Source가 같은 runtime validator를 거친다.
- Data Source: 데이터를 읽고 매핑한다. 네트워크·취소·향후 캐시를 담당하며 engine DOM이나 framework를 알지 못한다.
- Validation: Schema Version·필수 값·ID·참조·순환·상태·관계·입력 안전성을 검사한다. 성공 데이터 또는 위치별 issue를 반환한다.
- Normalized Contract: 조직·직원·관계사·관계·표현·Metadata를 분리한다. engine용 `children`·`relationship`·`className`은 저장하지 않는다.
- ViewModel / 집계: index·하위 조직·정렬·leader·직원 그룹·집계를 순수 변환한다. 모든 환경이 같은 함수를 사용한다.
- Engine Adapter: 복사한 파생 트리만 전달하며 DOM mount/update/dispose를 관리한다. 원본 수정·fetch·공통 UI 변경은 금지한다.
- Core facade / DOM Renderer: 요청 경쟁, Validation, 상태, 콘텐츠 DOM, 인스턴스 ID, 생명주기·Overlay를 관리한다. 텍스트는 `textContent` 등 안전한 경로로 처리한다.
- React Wrapper(Framework Adapter): React mount/effect/ref/cleanup과 Props를 Core에 연결한다. Validation·집계·engine·노드 디자인·인터랙션 재구현은 금지한다. 다른 framework Adapter는 이번에 설계 구체화/구현하지 않고 동일 facade의 향후 확장성만 보존한다.
- 상위 앱: 컨테이너·Theme bootstrap·Routing·로그인/권한·API client를 소유한다. React 호스트는 적합한 HM 공개 Component 조합을 사용할 수 있다.
## 3. 공개 생명주기 초안
현재 Export가 아닌 제안이다. 데이터 type은 [Data Contract](data-contract.md)와 연결한다. `HTMLElement`는 DOM 경계에만 사용한다.
```ts
interface OrgChartDataSource {
load(context: { signal: AbortSignal }): Promise<unknown>;
}
type ValidationIssue = { code: string; path: string; message: string };
type OrgChartIssue = ValidationIssue | {
code: "SOURCE_LOAD" | "SOURCE_PARSE" | "SOURCE_MAP";
path: "dataSource";
message: string;
};
type OrgChartState =
| { status: "loading" }
| { status: "empty" }
| { status: "ready"; dataVersion: string; aggregation: AggregationResult }
| { status: "error"; issues: readonly OrgChartIssue[] };
interface OrgChartOptions {
dataSource: OrgChartDataSource;
aggregationPolicy?: ApprovedAggregationPolicy;
interaction?: { pan: boolean; zoom: boolean }; // 공개 옵션 후보, 기본값 TBD
onStateChange?: (state: OrgChartState) => void;
}
interface OrgChartHandle {
mount(container: HTMLElement): Promise<void>;
update(input: unknown): Promise<void>;
reload(source?: OrgChartDataSource): Promise<void>;
destroy(): void;
}
declare function createOrgChart(options: OrgChartOptions): OrgChartHandle;
```
`update`는 normalized 스냅샷 후보를 검증·반영하고 `reload`는 현재 또는 새 Source를 읽는다. 두 동작은 동일 Validation·집계·render 경로를 사용한다. 데이터 실패는 state callback으로 전달해 페이지 종료를 방지한다. 호출 순서·Promise·정리 규칙은 [Framework Integration](framework-integration.md)을 따른다.
구조·참조·cycle·보안의 fatal Validation issue는 차트 Error다. 집계 정책 미확정은 차트 Error와 구별하여 ready의 AggregationResult `unavailable`로 전달하는 제안이다. Source 로딩·JSON 구문·매핑 실패도 제어된 Error지만 code/path로 Validation 오류와 구별하고 서버 원문을 전달하지 않는다. warning·업무 승인 필요 issue의 공개 전달 형식과 치명도는 API 승인 전 확정한다.
API Source로 전환해도 facade·각 framework의 사용 구조는 유지하고 Source만 바꾼다. 인증은 호스트 client가 처리한다. 검색·다운로드·편집·추가 메뉴·상세 모달은 이번 범위가 아니다.
## 4. 데이터 변경과 갱신
조직 추가/삭제·명칭·parent 이동, 직원 추가/삭제·조직 이동·직위/직책·사진·상태·관계사 변경, relation 변경, 기준일·제목 변경은 스냅샷 → Validation → ViewModel 재계산으로 반영한다. 전체 재계산 방식을 우선 검토하고 정렬은 Data Contract의 초안을 따른다.
Source 전환·reload·update마다 이전 작업을 취소하고 revision을 비교해 늦은 응답을 무시한다. Metadata 버전이 같아도 새 스냅샷은 검증한다. engine에는 복사본만 전달한다. 확장 상태·focus·zoom 보존은 PoC 후 결정하며 복원 키는 안정된 도메인 ID다.
검증·집계·render는 Core가 소유한 동일 불변 스냅샷을 사용한다. JSON Source의 캡처 시점은 생성 시점으로 제안하고 update는 호출 시 입력을 확보한다. 최종 DOM/state 반영 직전에도 revision·destroy 상태를 검사한다. consumer가 원본 객체를 나중에 바꿔 이미 반영한 화면·숫자가 암묵적으로 바뀌면 계약 실패다.
데이터만 바꾸는 범위는 승인된 계약의 유형·상태·표현 옵션 안이다. 새 업무 정책·관계 의미·카드 기능은 계약/UI 버전 변경이 필요할 수 있다. 모든 미래 변경을 무조건 무코드로 약속하지 않는다.
## 5. 엔진과 콘텐츠 경계
엔진은 조직 parent-child와 기본 계층 연결선을 담당한다. 직원은 조직 ID로 조회해 노드/그룹에 표시하고 리더를 조직으로 중복 삽입하지 않는다.
중앙 허브가 실제 조직인지 설명 콘텐츠인지 TBD다. 임시 visual root는 내부에서만 생성하고 집계에서 제외한다. 다중 root 처리도 미확정이다. `OrganizationRelation`은 parent-child와 별도이며 engine `relationship` 플래그로 치환하지 않는다. 특수 곡선·교차 관계·고정 배치는 별도 Overlay/Layout 후보로 검토한다. 추가 relation은 집계 분모를 변경하지 않는다.
완료 조건과 미결정 의존성은 [Implementation Plan](implementation-plan.md)을 따른다.
+87
View File
@@ -0,0 +1,87 @@
# HMDesign OrgChart P1 — 카드 디자인 1단계 결과
RESULT: **PASS — 요청한 카드 디자인 범위**. 원본 Guard는 기존 Token 추적 문제로 **FAIL**이며 이를 전체 릴리스 PASS로 치환하지 않았다.
## 작업 정보
- 작업 폴더: `D:\디자인시스템\HMDesign-OrgChart`
- 브랜치: `feature/orgchart-package`
- 작업 전/후 HEAD: `d18e3c33801e165e17b6d971e2f6198eed483c3c`
- Started At: 2026-10-12 12:34:53 KST
- Completed At: 2026-10-12 12:56:20 KST
- Actual Duration: 21분 27초
- Variance: 최초 예상 하한 30분 대비 8분 33초 단축 / 상한 45분 대비 23분 33초 단축.
- Estimated Duration: 최초 30–45분. 대표자·사업관리 추가 요청 후 40–60분으로 조정.
- 기존 package.json/package-lock.json 수정과 OrgChart 미추적 구현을 보존했다. Commit/Push/Merge/브랜치 생성/main 수정은 하지 않았다.
## 구현 결과
1. **SVG 자산 방식**: 전달된 10개 SVG를 원본 그대로 복사했다. Vite `?inline`으로 ESM/IIFE에 포함해 HTML/React/file://에서 외부 경로 의존 없이 사용한다. 배경은 장식용 img(`alt=""`, `aria-hidden`, `object-fit:contain`)이며 SVG의 path·gradient를 유지한다. 기존 박스 테두리·배경·우측 가상 장식은 자산 variant에만 제거한다.
2. **조직별 매핑**: `department-card-assets.ts`에서 사업관리·경영기획·디자인기획·솔루션영업·인재성장·통합시스템 6개 부서를 매핑한다. 하위 셀/팀은 실제 parentId를 따라 해당 부서 자산을 상속한다. 사용자 승인에 따라 솔루션통합팀 SVG를 기존 solution-sales에 연결하며 조직명/JSON은 바꾸지 않았다. 알려지지 않은 조직은 기존 카드 디자인을 유지한다.
3. **대표자**: 본문으로 전달된 146×74 SVG를 `대표자.svg`로 저장했다. `representative-card-asset.ts`로 부서와 별도 관리하고 기존 topLeader 독립 슬롯에만 사용한다. Full Composition의 최상단 가상리더 00 카드 1개에 적용했다.
4. **부서 조직장/직원**: 기존 organization.leaderId 및 kind 판별을 재사용한다. 조직장 146×68, 일반 직원 108×52이며 하위 IS/디자인 팀 리더도 146×68이다. 판별 규칙이나 데이터 계약을 변경하지 않았다. 파견·휴직 보조 영역의 카드·배치는 유지했다.
5. **사업관리 신규 벡터**: PNG(`D:\디자인시스템\카드 이미지\사업관리`, 확장자 없음, 408×191)에서 외곽 1,048개 RGBA 표본과 내측 5,619개 표본을 사용했다. 외곽 색상 `#DEE6F0 / #A8BBD1 / #577BA5`, 마지막 alpha 0.6; 내측 `#FFFFFF / #E0E6F4`. 기존 같은 크기 카드의 path와 곡선을 재사용하고 PNG에서 내측 그라데이션 방향도 복원했다. 두 자산에 텍스트·로고·사진·래스터는 없다. 원본 SHA-256은 `a8353bbf3869e4935c78a6774741d304836deb427d290586b439277022b2695f`다.
6. **데이터 레이어**: 이름·직위·직책·상태·프로필·실제 회사 로고 renderer를 유지했다. 새 작은 카드에만 데이터 폰트·간격을 맞추고 로고 영역을 14px 고정 높이로 두어 숨겨진 fallback의 줄바꿈이 이름 영역과 겹치지 않게 했다. 로고의 투명 배경·정상 이미지 시 fallback 숨김은 유지했다.
7. **Storybook**: 사용자 메뉴 5개와 내부 Story 8개를 유지했다. Department Colors는 대표자 1종과 6개 부서의 2종, 총 13개 카드 specimen·실제 크기·자산 출처를 표시한다. 기존 대비 MODEL/미승인 후보 24개 출력도 유지했다. Full Composition은 같은 Core renderer를 사용한다. 새 메뉴를 만들거나 기존 Story를 대체하지 않았다.
8. **독립 패키지**: 자산·스타일은 @hm-design/org-chart 내부에 한정했다. React Wrapper/공개 exports/JSON/공통 Theme/AI Guard/허브/Summary/관계선은 변경하지 않았다. Navigation은 기존 Header 없는 Core 콘텐츠를 유지했다. 이번 작업에는 목록 검색 요구가 없어 FilterBar를 추가하지 않았다.
## 변경 파일 — 이번 작업만
신규:
- `packages/org-chart/src/assets/cards/` SVG 13개(전달 10개, 대표자 1개, 사업관리 생성 2개) 및 README.md.
- `packages/org-chart/src/core/department-card-assets.ts`
- `packages/org-chart/src/core/representative-card-asset.ts`
- `examples/org-chart/create-business-card-assets.py`
- `examples/org-chart/verify-card-design.mjs`
- `docs/org-chart/card-design-stage1.md`
수정:
- `packages/org-chart/src/core/employee-card.ts` — 장식 SVG 레이어/내부 자산 옵션.
- `packages/org-chart/src/core/render.ts` — 조직장·직원 부서 자산 선택.
- `packages/org-chart/src/core/org-chart-content.ts` — 대표자 전용 자산.
- `packages/org-chart/src/core/department-colors.ts` — 카드 specimen/크기/출처/대표자 표시.
- `packages/org-chart/src/core/vendor.d.ts` — svg?inline 타입 선언.
- `packages/org-chart/src/styles/employee-card.css` — 자산 variant 크기/장식 제거/데이터 레이어 조정.
- `packages/org-chart/src/styles/org-chart-content.css` — specimen caption 및 대표자 미리보기 정렬.
생성 산출물: packages/org-chart/dist, examples/org-chart/dist, examples/org-chart-react/dist, storybook-static. 검증 근거는 기존 기록과 별도 `test-results/org-chart-card-design/`에 보존했다.
## 최종 통합 검증
- Core Typecheck **PASS**: `npm.cmd run typecheck:org-chart`.
- Story/preview consumer Typecheck **PASS**: `npm.cmd exec -- tsc -p tsconfig.app.json --noEmit`.
- Core ESM/IIFE/types/CSS Build **PASS**: `npm.cmd run build:org-chart`.
- HTML prepare / React demo Build **PASS**. React Wrapper 소스 변경이 없어 기존 Wrapper 검증을 반복하지 않았다.
- Storybook production Build **PASS**. 기존 500kB chunk 경고는 남아 있으며 빌드 실패는 없다.
- 최종 Chrome **231/231 PASS**, console/runtime error **0**.
- Department Colors / Full Composition / HTML / React / file:// classic 각각 390/768/1280/1440px에서 로딩·원본 크기·배경 장식 제거·실제 DOM Range 텍스트와 프로필/로고의 카드 경계·겹침·페이지 overflow를 확인했다.
- 6개 부서 전체 매핑, 대표자 단독 top slot, 사업관리 조직장/직원 구분, 기존 status 카드 보존 **PASS**. 대표자/조직장/직원 프로필에 로컬 이미지 fixture를 넣어 경계와 로딩을 확인했으며 원본 JSON은 수정하지 않았다.
- HTML/React/file:// 카드의 computed geometry/장식/이미지 표시/위치 판정 일치 **PASS**. 전체 화면 픽셀 동일성이나 모든 사용자 데이터 조합의 적합성을 뜻하지 않는다.
- 원본 SVG 10개의 SHA-256 동일 **PASS**. Logo/JSON/Wrapper/Story 원본 보존은 preservation.json으로 확인한다.
- 사업관리 PNG와 신규 큰 SVG를 Chrome canvas로 원본 408×191 크기에 렌더링해 흰 배경 합성 RGB를 비교했다. 전체 픽셀 채널 평균 오차 **1.113/255**, 95백분위 **1.094/255**. geometry/안티앨리어싱 차이를 포함하며 임의의 지각적 동일성 인증은 아니다. 색상 유사성 검사 **PASS**, 직접 시각 확인 완료.
- 기존 Core 데이터·Summary·전체 Guard 회귀는 재실행하지 않았다. 중간 실패는 작은 카드 로고 높이와 PNG 내측 그라데이션 방향에 한정해 수정했으며, 수정된 산출물의 최종 표시를 재검증했다.
### Guard 원본 결과
수정된 파일만 PARTIAL/STORYBOOK으로 실행했다. 대표자·사업관리 추가 요청으로 코드 graph가 변경된 후 해당 최종 범위를 한 번 더 실행했고, 기존 결과는 guard-five-departments.json으로 보존했다. 원본 status/staticStatus **FAIL**: TOKEN-001 8건, TOKEN-002 NOT VERIFIED 6건, SCOPE-001 NOT VERIFIED 4건. 기존 품질 보고와 같은 Shadow CSS/setProperty/선택적 입력 추적 문제이며 이 작업에서 공통 검사기를 변경하지 않았다.
`styleEvidence`: STATIC **PASS**, RUNTIME **NOT VERIFIED**, DESIGN_INTENT **PASS**, FINAL **NOT VERIFIED**. 이 값은 Guard 증거 연결 상태다. 별도 브라우저 PASS가 raw FAIL을 덮어쓰지 않는다. 결합 styleVerification은 생성/승격하지 않았다. 카드 전용 하드코드 geometry는 공통 HM Component override가 아니라 승인된 OrgChart SVG의 원본 크기다.
## 남은 이슈
- 검사한 가상 데이터의 텍스트·이미지·로고는 잘림/겹침 없이 표시된다. 매우 긴 소비자 이름·직책·임의 폰트 override는 **NOT VERIFIED**이며 고정 카드 크기에서 추가 정책이 필요할 수 있다.
- 작은 실제 로고의 판독성 한계는 남는다. 원본 로고는 crop/recolor하지 않았다.
- PNG로부터 SVG 그라데이션을 복원하므로 래스터 안티앨리어싱까지 정확히 같다는 보장은 없다. 투명도 포함 채널 비교와 원본 SHA를 보존했다.
- 허브·관계선·부서 헤더 대비의 기존 REVIEW와 Guard 추적 FAIL은 다음 별도 작업 대상이다. 전체 Workdesk 레이아웃 비교/통계 재설계/운영 API/전체 접근성 검증은 이번 범위에서 **NOT VERIFIED**.
미리보기: `http://127.0.0.1:6028/?path=/story/orgchart--department-colors`, `http://127.0.0.1:6028/?path=/story/orgchart--full-composition`. 직접 열기 결과: `examples/org-chart/dist/p1-classic.html`.
## 완료 기록
Started At: **2026-10-12 12:34:53 KST**. Completed At: **2026-10-12 12:56:20 KST**. Actual Duration: **21분 27초**. Estimated Duration: **최초 30–45분**, 추가 요청 후 **40–60분**. Variance: 최초 예상 대비 **하한 −8분 33초 / 상한 −23분 33초**. 시간은 실제 clock 도구로 확인했다.
최종 SHA 보존 확인: baseline 111개 중 승인 범위 수정 7개, 나머지 104개 유지, 삭제 0개. 신규 소스/자산/문서 19개. root manifests/기존 Story/JSON/로고/React Wrapper/Guard/기존 보고서의 시작값은 유지했다. staged file 0개, branch/HEAD 동일. 근거: `test-results/org-chart-card-design/preservation.json`.
Guard snapshot 후 자산 출처를 설명하는 주석과 문서만 정리했다. 실행 동작과 Guard 분석 대상의 Token/CSS 구현은 최종 검사 이후 변경하지 않았다. 정상 Typecheck/Build/browser 검사를 문서 편집 때문에 반복하지 않았다.
+122
View File
@@ -0,0 +1,122 @@
# OrgChart 카드 디자인 2단계 — Typography 및 내부 콘텐츠
> P3.2 후속 확정: 대표자 카드에는 모든 직책·역할·상태 뱃지와 빈 뱃지 행을 생성하지 않는다. `대표이사`·`홍 길 동`, 승인 카드 146×74 및 기존 Typography를 유지한다. `대표이사`는 표시 계층에만 적용하며 직원 데이터·팝업 원문과 다른 1Depth/2Depth 뱃지는 보존한다. [현재 표시 계약](ui-composition.md#p32-후속--대표자-뱃지-미사용-2026-10-12)이 아래 과거 기록보다 우선한다.
> P3.2 최신 계약은 [P3.2 보고서](implementation-report-p3.2.md)를 따른다. 대표자는 그대로, 대형 이름 24px·직위 18px, 소형 이름 18px·직위 15px이다. 기존 가중치·line-height·컬러는 보존한다. readOnly 파견/휴직은 일반 소형의 108×52 카드·24×44 사진·텍스트 레이아웃을 공유하고 전용 회색 색상을 유지한다. 상태 타이틀은 일반 2Depth 기본 20px/700/19.7px, 카드 로고만 기존 실제 표시의 1.5배다. 아래 수치·검증은 역사 기록이다.
> P3.1 최신 표시 계약은 [P3.1 보고서](implementation-report-p3.1.md)를 따른다. Noto Sans KR을 OrgChart scope에 적용하며 직위 공백은 표시에서만 제거한다. 조회 화면의 `경영진` 직책 뱃지만 제거하고 실제 직위·다른 뱃지·데이터는 보존한다. 사진 없음/실패에는 공통 사각 `person-placeholder.svg`를 표시한다. 아래 P3 및 P2의 수치와 검증은 역사 기록이다.
> P3 최신 표시 계약: 카드/SVG 크기와 기존 Typography는 유지한다. 표시 이름만 `홍 길 동`, 직위·직책 뱃지는 원문이다. 대표자를 포함하여 직위와 뱃지는 같은 행(Gap 4px), 이름은 아래 행(Gap 0px)이다. 모든 조직 타이틀↔첫 카드 Gap은 8px이다. Workdesk 태그 Border/색상/폰트와 사각형 사진 슬롯을 적용하며, 사진 없음에는 새 프로필 이미지가 없다. 실제 결과는 [P3 보고서](implementation-report-p3.md)에 기록한다. 아래 P2 및 이전 수치·검증은 역사 기록으로 보존한다.
RESULT: PARTIAL
지정 Typography·Gap·말줄임·전체 이름 제공 구현과 해당 범위 검증은 PASS입니다. 제공한 Workdesk 캡처 경로의 파일과 폴더가 현재 없어 사진·텍스트·로고의 실제 운영 화면 정렬 일치는 NOT VERIFIED입니다. Guard 원본 FAIL도 유지합니다.
## 작업 정보
- 작업 폴더: `D:\디자인시스템\HMDesign-OrgChart`
- 브랜치: `feature/orgchart-package`
- 작업 전/후 HEAD: `d18e3c33801e165e17b6d971e2f6198eed483c3c` (동일)
- Started At: 2026-10-12 12:58:39 KST
- Completed At: 2026-10-12 13:17:42 KST
- Actual Duration: 19분 3초
- Estimated Duration: 30–45분
- Variance: 최소 예상 30분보다 10분 57초 짧음
- 기존 Working Tree 보존, Stage·Commit·Push·Merge·브랜치 생성 없음.
## 구현 결과
1. 대표자: 직위 `#224387 / 20px / 400 / 22px / -0.8px`, 이름 `#071D3A / 24px / 600 / 30px / -0.8px` 적용.
2. 그룹장: 직위 `#797979 / 20px / 400 / 22px / -0.8px`, 이름 `#071D3A / 24px / 600 / 30px / -0.96px` 적용.
3. 일반 직원: 직위 `#797979 / 15px / 400 / 18px / -0.6px`, 이름 `#071D3A / 20px / 600 / 24px / -0.8px` 적용. 위 값의 순서는 color / font-size / font-weight / line-height / letter-spacing입니다.
4. 직위와 이름을 별도 세로 Flex 컨테이너에 두고 gap·margin·padding을 모두 0으로 적용했습니다. 카드 Padding과 사진·텍스트 사이 간격은 보존했습니다.
5. 사진은 왼쪽, 직위·이름은 오른쪽, 실제 회사 로고는 우측 상단에 유지했습니다. 기존 직책 배지는 사진 아래쪽 독립 영역으로 배치했습니다. SVG 카드의 상태 텍스트는 접근성 읽기에 유지하고 별도 세 번째 시각 행을 만들지 않았습니다. 파견·휴직 별도 영역의 카드 스타일은 보존했습니다.
6. 카드 크기는 대표자 146×74, 그룹장 146×68, 일반 직원 108×52입니다. 프로필 영역은 각각 36×36, 32×32, 24×24로 이전 단계와 동일합니다. 폰트·사진·카드 크기를 축소하지 않았습니다.
7. 긴 이름에만 말줄임을 적용합니다. 전체 문자열은 DOM에 유지하고, 실제 Overflow 카드만 Tab 순서에 포함합니다. Hover와 Focus로 같은 Shadow Root의 전체 이름 툴팁을 열고 `aria-describedby`로 연결합니다. 툴팁 위로 포인터를 이동할 수 있고 Escape로 닫을 수 있습니다. ResizeObserver·글꼴 로딩으로 판정을 갱신하고 기존 AbortSignal에서 관찰자·이벤트·툴팁을 정리합니다.
8. 기본 가상 이름 41개를 자연스러운 2–3음절 한국어 이름으로 바꿨습니다. 직원 ID·직위·소속·상태·프로필·인원수·JSON 계약은 그대로입니다. 실제 직원 개인정보를 복사하지 않았습니다.
## Storybook 및 공통 적용
- Department Colors: 대표자와 6개 조직의 그룹장·직원 카드에 같은 Core Typography 적용. 기존 메뉴 내 “긴 이름 검증 시작/종료”로 대표자·그룹장·직원 각 1개의 긴 이름을 확인할 수 있습니다. 기본값은 짧은 이름입니다.
- Full Composition: 최상단 대표자, 6개 조직 그룹장, 일반 직원 카드의 기본 이름·Typography·로고 배치 확인.
- 사용자용 메뉴 Logo / Department Colors / Position Summary / Affiliation Summary / Full Composition 5개 유지.
- 내부 OrgChart/Content Story 8개 보존. 별도 신규 Story 메뉴 없음.
- HTML·React는 같은 Core 렌더러와 전용 CSS를 사용하며, 390/768/1280/1440px의 카드 Computed Style·크기·콘텐츠 측정값이 일치했습니다.
- 로컬 미리보기: [Department Colors](http://127.0.0.1:6030/?path=/story/orgchart--department-colors), [Full Composition](http://127.0.0.1:6030/?path=/story/orgchart--full-composition).
## 변경 파일
신규 파일:
- `packages/org-chart/src/core/person-name-overflow.ts`: Overflow 판정·Hover/Focus 툴팁·접근성·정리.
- `examples/org-chart/verify-card-content.mjs`: 이번 단계의 브라우저 검증.
- `docs/org-chart/card-design-stage2.md`: 이 보고서.
수정 파일:
- `packages/org-chart/src/core/employee-card.ts`: SVG 카드의 직위/이름 전용 컨테이너 및 배지 영역.
- `packages/org-chart/src/styles/employee-card.css`: 종류별 Typography·Gap·말줄임·Focus·툴팁.
- `src/org-chart/org-chart-p1.stories.tsx`: 기존 Department Colors 내 긴 이름 검증 토글.
- `examples/org-chart/create-p1-fixture.mjs`: 자연스러운 가상 이름 생성.
- `examples/org-chart/data-p1.json`: 이름 필드만 갱신.
생성 결과: `packages/org-chart/dist/`, HTML/React demo dist, `storybook-static/`, `test-results/org-chart-card-content/`. 공통 Theme·Guard·공개 API·React Wrapper·차트 엔진·허브·Summary 구현은 변경하지 않았습니다.
## 최종 검증
- 변경 패키지 Typecheck: PASS.
- Story 소비 코드 Typecheck (`tsconfig.app.json`): PASS.
- 변경 패키지 Build (ESM / browser IIFE / types / CSS): PASS.
- HTML 준비 및 React demo Build: PASS.
- Storybook production Build: PASS. 기존 큰 chunk 경고는 남아 있습니다.
- 실제 Chrome 브라우저: **368/368 PASS**, console/runtime 오류 0.
- 대표자/그룹장/직원 Computed Style, weight·line-height·letter-spacing·색상·Gap·margin·padding: PASS.
- Department Colors 및 Full Composition 렌더링: PASS.
- 짧은 이름: 줄바꿈·잘림 없이 표시, 불필요한 Tab 진입 없음.
- 긴 이름: Overflow 카드에만 ellipsis·Tab Focus·전체 이름 툴팁, Hover·툴팁 Hover·실제 키보드 Tab·Escape·Focus 유지·접근성 트리 전체 문자열: PASS.
- 긴 이름을 짧은 이름으로 복구할 때 툴팁 제거: PASS.
- 카드 크기와 프로필 크기 보존, 사진/텍스트/로고/배지 겹침, 가시 텍스트 범위, 페이지 Overflow: PASS. 실제 이미지 로딩 사례도 검증했습니다.
- HTML·React 4개 뷰포트 Computed Style/콘텐츠 일치: PASS.
- 데이터 비교: 이름 필드 외 값 동일, JSON 계약·직원 수·소속·프로필 보존 PASS.
- 시작 파일 130개 중 125개 해시 동일, 누락 없음. 변경은 위 5개 기존 파일뿐입니다.
- SVG 13개와 실제 로고 PNG 7개 해시 모두 동일. 이전 품질 보완 파일 및 1단계 보고서 보존.
- `git diff --check`: PASS. 기존 Stage 파일 없음, HEAD·브랜치 동일.
- 기존 1단계 231개 검사는 재실행하지 않았습니다. 통계·데이터 Core 전체·AI Guard 전체 회귀도 재실행하지 않았습니다.
증거:
- `test-results/org-chart-card-content/browser.json`: 브라우저 버전, 368개 결과, 카드별 측정값.
- `test-results/org-chart-card-content/preservation.json`: 해시 보존, Story 수, Git 상태.
- `test-results/org-chart-card-content/baseline.json`: 시작 파일 해시.
- `test-results/org-chart-card-content/data-p1-before.json`: 이름 변경 전 비교 기준.
- `test-results/org-chart-card-content/*-normal.png`, `composition-1440.png`, `representative-tooltip-visible.png`: 실제 렌더링 화면.
### Guard 원본 상태
요청 파일을 대상으로 PARTIAL / STORYBOOK Guard를 1회 실행했습니다. 원본 `status=FAIL / staticStatus=FAIL`을 보존합니다.
- TOKEN-001 FAIL 8개: 변경하지 않은 `department-colors.ts` 및 `style-presets.ts`의 OrgChart 전용 동적 CSS 변수 추적.
- 이전 1단계 FAIL 항목과 code·file·line·message가 모두 동일합니다.
- TOKEN-002 NOT VERIFIED 6개, 패키지 CSS 추적 SCOPE-001 NOT VERIFIED 1개.
- styleEvidence: STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT NOT VERIFIED / FINAL NOT VERIFIED. 기존 Story 레이아웃 CSS의 디자인 의도 항목을 포함한 원본 결과이며, 이번 브라우저 증거로 원본을 덮어쓰지 않았습니다.
- `test-results/org-chart-card-content/guard-storybook.json`에 전체 원본 저장.
- 이번 카드 동작의 PASS와 Guard 전체 상태를 구분합니다. Guard 수정은 범위에서 제외합니다.
## 남은 차이 및 NOT VERIFIED
- `D:\디자인시스템\HMDesign-OrgChart-References\workdesk-orgchart.png`와 상위 References 폴더가 현재 없습니다. 따라서 실제 Workdesk 사진·텍스트·로고 위치, 글꼴 및 미세 정렬 일치는 NOT VERIFIED입니다. 지정 확정 CSS 수치는 우선 적용했습니다.
- 기존 OrgChart 글꼴 설정을 유지했습니다. 실제 Workdesk의 글꼴 차이에 따른 시각적 차이는 캡처를 확인한 후 판단해야 합니다.
- 브라우저 접근성 트리와 키보드 동작은 PASS이나, 별도의 실제 스크린리더 음성 낭독은 NOT VERIFIED입니다.
- Guard의 기존 추적 FAIL/NOT VERIFIED는 미해소입니다.
- 긴 이름을 완전히 한 줄로 표시하려면 고정 카드 폭을 초과하므로, 사용자 확정대로 크기·폰트를 유지하고 말줄임과 전체 이름 툴팁을 적용했습니다.
- 전체 레이아웃·관계선·허브·통계 재설계는 다음 단계 범위이며 이번에는 변경하지 않았습니다.
## P2 최신 확정 표시 규칙 — 2026-10-12
위 2단계 기록과 검증 결과는 보존한다. 조회 전용 Full Composition의 현재 적용 기준은 [P2 보고서](implementation-report-p2.md)다.
- Depth 1 조직장만 146×68, Depth 2 이하 조직장은 108×52 소형 SVG. 독립 카드와 interactive 모드의 기존 조직장 선택은 유지한다.
- 가상 직원의 이름은 표시 계층에서 `홍 길 동`으로 통일하고 직위·직책에도 연속 한글 사이 공백을 적용한다. 원본 데이터 문자열에는 공백을 삽입하지 않는다.
- 기존 font-size, font-weight, line-height, 컬러와 내부 직위/뱃지 행 ↔ 이름 행 gap 0px는 유지한다. 글자 간격에 관한 이전 표시보다 이번 확정 지시를 우선한다.
- 외부 1Depth 헤더 ↔ 대형 카드 gap은 최종 8px이며, 이전 P2 초안의 10px는 적용하지 않는다. 아래 Depth 헤더 ↔ 소형 카드는 10px다.
- 긴 직책의 공백 표시가 회사 로고와 겹치지 않도록 조회 전용 대형 카드의 뱃지를 같은 행 안에서 아래로 정렬하고 로고 위치를 3px 올렸다. SVG·로고 원본, 글자/로고 크기 및 내부 행 높이는 변경하지 않았다.
+261
View File
@@ -0,0 +1,261 @@
# OrgChart Data Contract
상태: **JSON Schema / TypeScript 초안, 승인 전**. ID 기반 분리·동일 계약 사용·Validation·원본 집계는 확정 요구다. 아래 필드명·enum·null·버전 값은 제안이다. 모든 환경이 이 계약과 같은 검증·집계를 공유한다. [Architecture](architecture.md), [API 전환](integration.md)을 함께 읽는다.
## 1. 정규화 모델과 TypeScript 초안
ID는 엔티티별 배열 안에서 유일한 불투명 문자열이다. 이름·배열 위치로 연결하지 않는다. 조직은 `parentId`, 직원은 `organizationId`·`affiliationId`, 조직장은 `leaderId`, 추가 관계는 두 조직 ID를 참조한다. 서로 다른 엔티티의 같은 ID 문자열은 허용할지 TBD이며, DOM에는 엔티티·인스턴스 prefix를 별도로 붙인다.
```ts
type ID = string;
type OrganizationType = "root" | "division" | "office" | "div" | "cell" | "team";
type WorkStatus = "active" | "dispatched" | "onLeave";
type RelationType = "cooperation" | "support";
interface Organization {
id: ID;
parentId: ID | null;
name: string;
type: OrganizationType;
leaderId: ID | null;
displayOrder: number;
}
interface Employee {
id: ID;
organizationId: ID;
affiliationId: ID;
name: string;
position: string; // 직위. jobTitle(직책)과 구별
jobTitle: string | null;
profileImage: string | null;
workStatus: WorkStatus;
}
interface Affiliation { id: ID; name: string }
interface OrganizationRelation {
id: ID;
sourceOrganizationId: ID;
targetOrganizationId: ID;
type: RelationType;
}
interface PresentationConfig {
title: string;
description: string | null;
hubText: string | null;
groups: {
showEmployees: boolean;
showSummary: boolean;
showDispatched: boolean;
showOnLeave: boolean;
showAffiliations: boolean;
};
organizationOrder: ID[];
}
interface Metadata {
schemaVersion: "0.1-draft";
asOfDate: string; // YYYY-MM-DD, 유효한 실제 날짜
dataVersion: string;
updatedAt: string | null; // timezone을 포함한 RFC 3339 갱신 시각
updateNote: string | null;
}
interface OrgChartData {
organizations: Organization[];
employees: Employee[];
affiliations: Affiliation[];
relations: OrganizationRelation[];
presentation: PresentationConfig;
metadata: Metadata;
}
```
제안된 `root/division/office/div/cell/team`은 최상위/부문/실/Div/셀/팀 표현이다. 실제 조직 유형·업무 상태·관계 유형 vocabulary, 직위 필수 여부, 무소속·관계사 미지정 허용은 **TBD**다. 새로운 enum은 schema/API 버전과 함께 확장하고 알 수 없는 값을 임의 재직 상태로 치환하지 않는다.
`schemaVersion`은 계약 호환성, `dataVersion`은 스냅샷 식별이다. 서로 대신 쓰지 않는다. 부모 null은 root 후보, leader null은 공석 후보, 이미지 null은 가상 fallback을 뜻하는 초안이다. API에서 필드 생략이 가능한지와 null 허용은 승인 후 확정한다.
표현 설정은 표시만 바꾸고 원본 소속·집계 분모를 변경하지 않는다. 순서는 제안상 `organizationOrder`에 있는 ID 우선 → 나머지 형제 `displayOrder` → 동률 ID 순서로 결정한다. 전역 목록이 계층 parent를 바꾸지는 않는다. 실제 정렬 우선순위는 TBD다. PresentationConfig에 CSS·HTML·DOM 좌표·API URL을 받지 않는다.
## 2. JSON Schema 초안
JSON Schema Draft 2020-12 제안이다. `format`을 실제 검사하는 validator 설정이 필요하다. 아래 구조는 JSON 구문 검증 대상이며 runtime validator 구현 또는 Schema 승인 결과가 아니다. 중복 ID·참조·순환·업무 규칙은 §4의 별도 검증이 필요하다. 모든 필드를 required로 하고 nullable을 명시하는 엄격한 초안이며 API 매핑에서 같은 형태로 변환한다.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "HMDesign OrgChart draft",
"type": "object",
"additionalProperties": false,
"required": ["organizations", "employees", "affiliations", "relations", "presentation", "metadata"],
"properties": {
"organizations": {"type": "array", "items": {"$ref": "#/$defs/Organization"}},
"employees": {"type": "array", "items": {"$ref": "#/$defs/Employee"}},
"affiliations": {"type": "array", "items": {"$ref": "#/$defs/Affiliation"}},
"relations": {"type": "array", "items": {"$ref": "#/$defs/OrganizationRelation"}},
"presentation": {"$ref": "#/$defs/PresentationConfig"},
"metadata": {"$ref": "#/$defs/Metadata"}
},
"$defs": {
"ID": {"type": "string", "minLength": 1},
"Text": {"type": "string", "minLength": 1},
"NullableText": {"anyOf": [{"$ref": "#/$defs/Text"}, {"type": "null"}]},
"NullableID": {"anyOf": [{"$ref": "#/$defs/ID"}, {"type": "null"}]},
"Organization": {
"type": "object", "additionalProperties": false,
"required": ["id", "parentId", "name", "type", "leaderId", "displayOrder"],
"properties": {
"id": {"$ref": "#/$defs/ID"},
"parentId": {"$ref": "#/$defs/NullableID"},
"name": {"$ref": "#/$defs/Text"},
"type": {"enum": ["root", "division", "office", "div", "cell", "team"]},
"leaderId": {"$ref": "#/$defs/NullableID"},
"displayOrder": {"type": "integer", "minimum": 0}
}
},
"Employee": {
"type": "object", "additionalProperties": false,
"required": ["id", "organizationId", "affiliationId", "name", "position", "jobTitle", "profileImage", "workStatus"],
"properties": {
"id": {"$ref": "#/$defs/ID"},
"organizationId": {"$ref": "#/$defs/ID"},
"affiliationId": {"$ref": "#/$defs/ID"},
"name": {"$ref": "#/$defs/Text"},
"position": {"$ref": "#/$defs/Text"},
"jobTitle": {"$ref": "#/$defs/NullableText"},
"profileImage": {"anyOf": [{"type": "string", "format": "uri-reference", "minLength": 1}, {"type": "null"}]},
"workStatus": {"enum": ["active", "dispatched", "onLeave"]}
}
},
"Affiliation": {
"type": "object", "additionalProperties": false,
"required": ["id", "name"],
"properties": {"id": {"$ref": "#/$defs/ID"}, "name": {"$ref": "#/$defs/Text"}}
},
"OrganizationRelation": {
"type": "object", "additionalProperties": false,
"required": ["id", "sourceOrganizationId", "targetOrganizationId", "type"],
"properties": {
"id": {"$ref": "#/$defs/ID"},
"sourceOrganizationId": {"$ref": "#/$defs/ID"},
"targetOrganizationId": {"$ref": "#/$defs/ID"},
"type": {"enum": ["cooperation", "support"]}
}
},
"PresentationConfig": {
"type": "object", "additionalProperties": false,
"required": ["title", "description", "hubText", "groups", "organizationOrder"],
"properties": {
"title": {"$ref": "#/$defs/Text"},
"description": {"$ref": "#/$defs/NullableText"},
"hubText": {"$ref": "#/$defs/NullableText"},
"organizationOrder": {"type": "array", "uniqueItems": true, "items": {"$ref": "#/$defs/ID"}},
"groups": {
"type": "object", "additionalProperties": false,
"required": ["showEmployees", "showSummary", "showDispatched", "showOnLeave", "showAffiliations"],
"properties": {
"showEmployees": {"type": "boolean"}, "showSummary": {"type": "boolean"},
"showDispatched": {"type": "boolean"}, "showOnLeave": {"type": "boolean"},
"showAffiliations": {"type": "boolean"}
}
}
}
},
"Metadata": {
"type": "object", "additionalProperties": false,
"required": ["schemaVersion", "asOfDate", "dataVersion", "updatedAt", "updateNote"],
"properties": {
"schemaVersion": {"const": "0.1-draft"},
"asOfDate": {"type": "string", "format": "date"},
"dataVersion": {"$ref": "#/$defs/Text"},
"updatedAt": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}]},
"updateNote": {"$ref": "#/$defs/NullableText"}
}
}
}
}
```
빈 문자열·공백만 있는 필수 값은 runtime Validation에서 거부하도록 제안한다. `minLength`만으로 공백 검사가 끝나지 않는다. 입력 길이·배열 크기·깊이 상한은 실제 데이터 규모 검토 후 TBD로 확정한다.
## 3. 가상 JSON 스냅샷 예시
실제 인사 정보와 사진이 없는 예시다. Schema 값은 초안이며 아래 데이터가 운영 계약으로 승인된 것은 아니다.
```json
{
"organizations": [
{"id": "org-root", "parentId": null, "name": "가상 사업본부", "type": "root", "leaderId": "emp-a", "displayOrder": 0},
{"id": "team-a", "parentId": "org-root", "name": "가상 기획팀", "type": "team", "leaderId": "emp-b", "displayOrder": 1},
{"id": "team-b", "parentId": "org-root", "name": "가상 운영팀", "type": "team", "leaderId": null, "displayOrder": 2}
],
"employees": [
{"id": "emp-a", "organizationId": "org-root", "affiliationId": "aff-a", "name": "가상직원 가", "position": "대표", "jobTitle": "본부장", "profileImage": null, "workStatus": "active"},
{"id": "emp-b", "organizationId": "team-a", "affiliationId": "aff-a", "name": "가상직원 나", "position": "책임", "jobTitle": "팀장", "profileImage": null, "workStatus": "active"},
{"id": "emp-c", "organizationId": "team-a", "affiliationId": "aff-b", "name": "가상직원 다", "position": "선임", "jobTitle": null, "profileImage": null, "workStatus": "dispatched"},
{"id": "emp-d", "organizationId": "team-b", "affiliationId": "aff-b", "name": "가상직원 라", "position": "선임", "jobTitle": null, "profileImage": null, "workStatus": "onLeave"}
],
"affiliations": [{"id": "aff-a", "name": "가상 관계사 A"}, {"id": "aff-b", "name": "가상 관계사 B"}],
"relations": [{"id": "rel-a", "sourceOrganizationId": "team-a", "targetOrganizationId": "team-b", "type": "cooperation"}],
"presentation": {
"title": "가상 조직도", "description": "설계 검토용 가상 데이터", "hubText": "협업 중심",
"groups": {"showEmployees": true, "showSummary": true, "showDispatched": true, "showOnLeave": true, "showAffiliations": true},
"organizationOrder": ["org-root", "team-a", "team-b"]
},
"metadata": {"schemaVersion": "0.1-draft", "asOfDate": "2026-10-12", "dataVersion": "mock-1", "updatedAt": null, "updateNote": "초안 예시"}
}
```
직원 고유 ID 수 4, 상태별 raw 수 2/1/1은 fixture 산술 기준이다. 업무상 재직/전체 인원과 파견·휴직 포함 여부는 §5의 정책 승인이 필요하다. 조직장 카드와 상태 그룹에 같은 직원이 보이더라도 raw ID 수가 늘어나지 않아야 한다.
## 4. Validation과 오류 상태
제안 순서: 구조·버전 → 각 collection ID index → parent/직원/관계사/leader 참조 → cycle/root → 업무 규칙·입력 안전성. invalid 데이터를 engine로 전달하지 않는다.
- `SCHEMA_VERSION`: 승인된 버전과 불일치. 자동 추정·silent migration 금지.
- `DUPLICATE_ID`: 같은 collection 내 ID 중복. 임의 last-write-wins 금지.
- `REQUIRED_VALUE`: 누락/null 위반/공백뿐인 필수 값/잘못된 타입.
- `ORGANIZATION_REFERENCE`: 직원 소속이 존재하지 않음. 삭제 시 parent/직원 이동을 일관된 스냅샷으로 제공해야 한다.
- `PARENT_REFERENCE` / `HIERARCHY_CYCLE`: 없는 parent, 자기 parent, 다단계 순환. 정렬로 cycle을 은폐하지 않는다.
- `LEADER_REFERENCE`: leader 직원이 없음. leader가 동일 조직/하위 조직/타 조직 소속이어도 되는지는 TBD이며 정책 없는 불일치는 승인 요구 issue로 보고한다.
- `AFFILIATION_REFERENCE`: 직원 관계사 없음. 미지정 소속 허용 정책은 TBD.
- `INVALID_STATUS` / `INVALID_TYPE`: 허용 vocabulary 외 값. 제안 enum이 실제 승인 목록을 대신하지 않는다.
- `RELATION_REFERENCE` / `INVALID_RELATION`: 없는 양 끝 조직, 자기 관계, 중복 양 끝+유형, 허용되지 않은 유형을 검사한다. 관계 방향·대칭·역방향 중복 허용은 TBD. 일반 관계의 cycle은 조직 parent cycle과 구별한다.
- `PRESENTATION_REFERENCE`: organizationOrder의 없는 ID·중복·정렬 정책 위반.
- `UNSAFE_INPUT`: 실행 가능한 HTML/스크립트 payload·위험한 이미지 URL·제어문자/식별 교란 입력을 검사한다. 표시 문자열은 plain text, external `className/style/nodeTemplate`는 받지 않는다. image scheme/origin·상대 경로 정책·길이 상한은 승인 후 확정한다. JSON Schema `uri-reference`만으로 URL 안전성을 보장하지 않는다.
입력 검사와 출력 안전성은 함께 적용한다. 정상 문자열을 무조건 삭제하지 않고 거부/경고 기준을 문서화한다. DOM 텍스트는 `textContent`로 출력하고 engine 템플릿에는 외부 문자열을 직접 삽입하지 않는다. engine ID는 내부 안전 ID로 변환한다. HTML escaping만으로 이미지 URL 검사를 대체하지 않는다.
구조·참조·cycle·보안 실패는 Error 상태로 전환하고 engine mount/update를 중단한다. 사용자 메시지는 해당 데이터가 표시 불가능함을 설명하되 원본 개인정보를 노출하지 않는다. partial 데이터 자동 제거는 별도 승인 전 하지 않는다. 직원 0명과 조직 0개의 Empty 정의, 갱신 실패 시 직전 유효 화면 유지 여부는 TBD다. 미확정 집계 정책은 차트 유효성과 분리하여 집계를 `unavailable`로 표시하는 방식을 제안한다.
fatal Validation issue는 차트 Error로 처리한다. 집계 정책 미확정은 fatal data issue가 아니며 유효한 차트의 ready와 별도 AggregationResult `unavailable`로 공존하는 안을 제안한다. Source의 HTTP/JSON/매핑 실패는 dataSource 위치의 별도 code로 구별한다. warning·업무 승인 필요 issue의 치명도와 공개 반환 형식은 승인 전 TBD다. 어떤 경로도 원본 서버 오류·개인정보를 callback에 그대로 전달하지 않는다.
Validation·집계·render는 같은 불변 스냅샷을 사용하며 engine는 별도 복사본만 받는다. Source가 여러 API/페이지를 조합할 때의 시간 일관성은 ID 참조 검사와 별도로 보장해야 한다. Source 캡처·revision 반영 계약은 Architecture/Framework Integration을 따른다.
## 5. 인원 집계와 미확정 정책
원본 `employees`에서 계산하며 DOM 카드·engine 노드 수를 세지 않는다. Validation 통과 직원 ID 집합을 사용하고 parent 트리의 하위 조직을 통해 집계한다. 추가 관계·허브·접힌/숨겨진 카드·표시 설정은 조직 소속을 변경하지 않는다.
- 조직 직접 인원: 해당 organizationId의 직원 집합.
- 하위 포함 인원: 자기 조직 + 승인된 parent 트리 하위 조직의 직원 ID 합집합.
- 직위별 인원: 정해진 대상 집합을 `position`으로 그룹화. 직책과 혼합하지 않는다.
- 재직/파견/휴직: 승인된 상태 매핑과 포함 정책을 적용한다. raw 상태 카운트와 재직 정의는 구별한다.
- 관계사별 인원: `affiliationId`로 그룹화하고 동일한 승인 정책을 적용한다.
- 전체 인원: 승인된 전체 대상의 직원 ID 집합. 하위 포함 조직별 숫자를 더하면 중복되므로 그 합을 총원으로 쓰지 않는다.
집계 정책은 PresentationConfig와 별도로 제공하는 제안이다. 아래 type의 값이나 default는 아직 승인되지 않았다.
```ts
interface ApprovedAggregationPolicy {
policyVersion: string;
includedStatuses: WorkStatus[];
activeStatuses: WorkStatus[];
includeLeaders: boolean;
membershipMode: "primaryOnly" | "uniqueEmployee";
}
type AggregationResult =
| { status: "unavailable"; reason: "policyRequired" }
| { status: "ready"; policyVersion: string; total: number };
```
TBD: 파견·휴직을 재직/전체/팀/관계사에 포함하는지, 조직장 포함·겸직 조직장 배분, 조직 간 중복 소속, 파견 원소속/파견지 기준, 퇴사·예정 상태 필요 여부, 관계사 합과 총원 관계. 초안의 단일 `organizationId`는 복수 소속을 표현하지 못한다. 복수 소속이 필요하면 별도 Membership 계약과 schema revision을 승인하고 집계 mode를 확정한다. `uniqueEmployee` 후보만으로 현재 복수 소속이 지원된다고 주장하지 않는다.
정책 없이 임의 총원을 표시하지 않는다. 향후 테스트에는 leader 중복 표시, 상태 변경, 조직 이동, 하위 포함 중복 제거, 관계사 이동, 표시 숨김, 동일 JSON의 Vanilla/React 집계 일치를 포함한다. 같은 조건에 같은 결과가 나와야 한다.
집계 정책 자체도 policyVersion·지원 membershipMode·허용 status·중복 status·업무상 상호 관계를 검증한다. `activeStatuses`와 `includedStatuses`의 관계, leader 제외가 어느 조직의 어떤 직원에 적용되는지는 승인 전 TBD다. 필드 값만 받아 임의 해석하지 않는다. 환경별 정렬 일치를 위해 ID 비교 기준·locale 의존 정렬 여부도 확정한다.
+179
View File
@@ -0,0 +1,179 @@
# OrgChart Framework Integration
상태: 설계 계약·사용 예시 제안. **현재 package/Wrapper/HTML bundle은 존재하지 않으며 예시 API·경로도 승인 전이다.** 이 문서는 코드를 생성하거나 지원 완료를 주장하지 않는다. [Architecture](architecture.md)의 facade와 [Data Contract](data-contract.md)를 공통 기준으로 한다.
## 1. 지원 범위와 책임
1차 공식 개발 대상은 **Vanilla HTML/JavaScript와 React만**이다. 현재 두 환경 모두 구현 미착수·실행 **NOT VERIFIED**다. Vue·Angular·기타 framework는 이번 개발 범위에서 제외하며 Adapter 구현·배포·지원 검증을 계획에 포함하지 않는다. 최초 추가 요청의 Vue 설계 항목은 최신 지원 범위 확정으로 대체했다. 향후 Core facade를 연결할 수 있도록 framework 독립성만 유지한다.
우선 검토 구성은 `@hm-design/org-chart`(Core), `@hm-design/org-chart-react`(React Wrapper)다. 실제 경로·두 패키지 분리는 workspace/build/dependency 검토 후 최종 승인한다. React Wrapper는 Engine Adapter와 다르다: Wrapper는 React와 Core 사이, Engine Adapter는 Core와 dabeng 사이에 있다. Data Source Adapter는 외부 형식과 normalized 계약 사이에 있다.
- Core: 표준 DOM, 공통 계약·Validation·ViewModel·집계·engine·노드 UI·Theme CSS·인터랙션·요청 취소·생명주기.
- Vanilla 호스트: container 제공, Source·options 전달, Theme/CSS/script 로딩, 페이지 제거 시 Destroy.
- React Wrapper: ref/effect로 Core mount, Props 변경을 update/reload 또는 설정 재생성에 연결, 언마운트 시 Destroy. 공통 업무 로직·렌더링 엔진·노드 디자인을 재구현하지 않는다.
- 상위 앱: Layout·Navigation·Routing·API 인증·권한·Theme 공급. Core에 React context나 인증 hook을 요구하지 않는다.
두 환경은 동일 JSON 입력 → 동일 Validation → 동일 계층·집계 → 동일 Core 노드·Theme·인터랙션을 사용한다. React 외부 HM Component는 페이지 조합이며 Core 내부 노드의 다른 디자인을 만드는 근거가 아니다.
## 2. 생명주기 계약 초안
메서드 이름은 확정 생명주기 요구를 구체화한 API 제안이다. engine 자체의 destroy 지원과 혼동하지 않는다.
1. `createOrgChart(options)`: DOM 생성·요청 없이 Handle을 만든다. framework import와 전역 window 접근 부작용을 ESM import 시 피하는 안을 검토한다.
2. `mount(container)`: 연결된 HTMLElement의 전용 자식 root를 소유하고 첫 Source를 읽는다. 호스트의 sibling/기존 콘텐츠를 지우지 않는다. 한 Handle에 한 container이며 중복 mount는 명시적 호출 오류로 거부한다.
3. `update(input)`: mounted 상태에서 normalized 스냅샷 후보를 검증·집계·렌더한다. Source fetch 없이 스냅샷만 바꾼다. 현재 Source 객체는 유지하므로 이후 reload는 그 Source를 다시 읽는다.
4. `reload(source?)`: mounted 상태에서 현재/교체 Source를 읽고 같은 반영 경로를 사용한다. JSON → Mock API/실제 API 전환의 공개 지점이다.
5. `destroy()`: 동기·반복 안전한 최종 정리. 진행 요청 abort·revision 무효화·콜백 중단, Core/engine 이벤트·Observer·frame/timer·DOM·인스턴스 참조를 정리한다. destroyed Handle은 재사용하지 않고 새 Handle을 만든다.
mount/update/reload는 Promise<void> 제안이다. 데이터·Source 실패는 Error state로 알리고 Promise를 정상 완료하는 방식을 우선 검토한다. 잘못된 호출 순서·사용법은 명시적 오류로 reject한다. abort/새 revision에 의해 폐기된 작업은 unhandled rejection 없이 종료하고 DOM/상태를 바꾸지 않는다. mount 진행 중에도 이미 확보한 container의 update/reload는 이전 작업을 무효화한다. mount 전·destroy 후 update/reload는 오류다. Promise 완료는 해당 revision의 반영 또는 controlled error 전환까지이며 이미지/폰트의 모든 로드 완료를 뜻하지 않는다.
상태 전이는 loading → ready/empty/error다. update는 동기 validation 중에도 같은 state 의미를 유지한다. Empty 정의와 갱신 중/실패 시 직전 화면 유지 정책은 TBD다. 같은 Metadata 버전이라도 새 입력을 무시하지 않는다. reload/update 경쟁은 마지막 시작 revision이 우선하도록 제안한다.
엔진 정리 가능성은 최초 PoC에서 입증해야 한다. document 이벤트·관찰자를 해제할 경로를 찾지 못하면 Destroy 완료를 PASS로 표시하지 않는다. core가 소유하지 않은 engine 자원을 `innerHTML = ""`만으로 정리했다고 판단하지 않는다. 제한/통합 대안을 승인받기 전 기능 개발을 확대하지 않는다.
engine 재생성 전 이전 chart가 살아 있는 동안 기존 pan/zoom·minimap 등의 해제 가능성을 확인한다. update/reload 반복 후에도 이벤트/Observer가 증가하지 않는지 검사한다. Core와 데이터 계층의 DOM 없는 import는 upstream 직접 import와 구별하며 지연 engine 로딩/변환 entry를 검증한다.
## 3. 데이터 Source 사용 예시
아래 `createJSONSource`·`createExternalJSONSource`도 제안 API다. 모든 반환값은 같은 validator를 통과한다. 가상 데이터 `data`는 Data Contract §3 스냅샷을 뜻한다.
```ts
// 제안 API; 아직 설치 가능한 공개 함수가 아니다.
const jsonSource = createJSONSource(data);
const externalSource = createExternalJSONSource({ url: configuredJsonUrl });
// URL은 호스트 설정. Core에 회사 서버 주소를 하드코딩하지 않는다.
```
JSON Mock은 객체 스냅샷을 공급하고 consumer 입력을 수정하지 않는다. External JSON은 AbortSignal·HTTP 오류·JSON 구문 오류를 처리한다. source가 JSON을 반환했다는 이유만으로 Validation을 생략하지 않는다.
JSON Source는 생성 시점 입력을 캡처하는 안, update는 호출 시점 입력을 확보하는 안을 제안한다. 같은 객체의 외부 mutation을 자동 변경 알림으로 취급하지 않는다. 검증·집계·DOM 반영은 Core 소유 불변 스냅샷에서 수행하고 최종 state/DOM commit 직전 revision·destroy 상태를 다시 확인한다.
API가 마련됐을 때의 공통 계약 예시:
```ts
// OrgChartDataSource, OrgChartData는 문서의 초안 type이다.
function createApiSource(
request: (signal: AbortSignal) => Promise<unknown>,
mapResponse: (raw: unknown) => OrgChartData
): OrgChartDataSource {
return {
async load({ signal }) {
return mapResponse(await request(signal));
}
};
}
const apiSource = createApiSource(hostApiRequest, mapApiResponse);
await handle.reload(apiSource); // Core/노드 UI/집계/React Wrapper는 유지
```
실제 request·mapping은 명세 확정 후 호스트에서 작성한다. mapResponse type 선언은 보안 경계가 아니며 Core가 결과를 재검증한다. Endpoint·인증·재시도·캐시를 임의로 예시에 넣지 않는다. Mock API는 같은 fixture를 Promise로 반환하고 지연/오류를 주입하여 실제 서버 없이 전환을 검증한다.
## 4. Vanilla HTML 사용 예시
아래 asset 이름·`window.HMOrgChart` namespace는 미래 browser bundle **제안**이며 파일을 제작한 것이 아니다. React 설치 없이 classic script + 별도 CSS + inline JSON으로 `file://` 직접 실행을 검증할 예시다. 포장된 Theme CSS·font·Core CSS·번들이 모두 상대 경로로 제공돼야 한다.
```html
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>가상 조직도 검증</title>
<link rel="stylesheet" href="./assets/hm-theme.css">
<link rel="stylesheet" href="./assets/orgchart.css">
</head>
<body>
<div id="orgchart-host"></div>
<script id="orgchart-data" type="application/json">
{
"organizations": [{"id":"org-a","parentId":null,"name":"가상 조직","type":"root","leaderId":"emp-a","displayOrder":0}],
"employees": [{"id":"emp-a","organizationId":"org-a","affiliationId":"aff-a","name":"가상직원 가","position":"책임","jobTitle":"조직장","profileImage":null,"workStatus":"active"}],
"affiliations": [{"id":"aff-a","name":"가상 관계사"}],
"relations": [],
"presentation": {"title":"가상 조직도","description":null,"hubText":null,"groups":{"showEmployees":true,"showSummary":false,"showDispatched":false,"showOnLeave":false,"showAffiliations":false},"organizationOrder":[]},
"metadata": {"schemaVersion":"0.1-draft","asOfDate":"2026-10-12","dataVersion":"html-mock-1","updatedAt":null,"updateNote":null}
}
</script>
<script src="./assets/orgchart.browser.js"></script>
<script>
const data = JSON.parse(document.getElementById("orgchart-data").textContent);
const { createOrgChart, createJSONSource } = window.HMOrgChart;
const handle = createOrgChart({ dataSource: createJSONSource(data) });
handle.mount(document.getElementById("orgchart-host"))
.catch(() => console.error("OrgChart lifecycle error"));
window.addEventListener("pagehide", () => handle.destroy(), { once: true });
</script>
</body>
</html>
```
위 예시는 실행 검증 전 설계용이다. inline JSON은 가상 데이터만 사용한다. pagehide 이후 bfcache 복귀가 필요한 앱은 새 instance의 재mount를 호스트에서 계획한다. SPA 콘텐츠 제거에서는 pagehide에만 의존하지 않고 해당 제거 시점에 Destroy한다.
ESM 비교 예시(HTTP(S) static server에서 우선 검증):
```js
import { createOrgChart, createJSONSource } from "./assets/orgchart.esm.js";
const handle = createOrgChart({ dataSource: createJSONSource(data) });
await handle.mount(document.getElementById("orgchart-host"));
```
bare npm specifier는 일반 HTML에서 자동 해석되지 않는다. file://의 module/fetch 제한과 서버 기반 ESM 실행을 구별한다. `file://`에서는 inline JSON + classic bundle 경로를 검증하고 외부 JSON fetch가 가능하다고 보장하지 않는다. External JSON/API는 HTTP(S)·CORS·호스트 권한 조건에서 별도로 검증한다.
## 5. React 사용 예시와 Wrapper 계약
아래 package names·Component/Prop은 우선 분리안의 예시이며 실제 Export가 아니다. 동일 Core CSS·Theme를 사용한다. React 호스트의 기존 CSS entry 순서를 지키고 HM 공개 Component를 사용하는 주변 영역은 해당 HM Tailwind source/Theme 규칙을 유지한다.
```tsx
import { useMemo } from "react";
import { createJSONSource } from "@hm-design/org-chart";
import { OrgChart } from "@hm-design/org-chart-react";
import "@hm-design/org-chart/style.css";
// 기존 앱 Theme entry에서 @hm-design/ui/theme.css를 연결한다.
function OrgChartContent({ data }: { data: OrgChartData }) {
const dataSource = useMemo(() => createJSONSource(data), [data]);
return <OrgChart dataSource={dataSource} />;
}
```
Props 후보는 dataSource·aggregationPolicy·interaction·onStateChange다. title/description/group/order는 공통 데이터의 PresentationConfig를 사용한다. React 전용 표현·집계 옵션을 따로 만들지 않는다. 직접 스냅샷 Prop 추가는 public API 승인 후 검토한다.
Wrapper 동작: 최초 ref container 확보 → Core 생성·mount; dataSource 교체 → 기존 Handle.reload(newSource); 동일 Source의 내부 값 변경은 명시적 reload 또는 새 immutable Source로 전달한다. 새 객체를 매 렌더 생성해 불필요한 reload를 유발하지 않는다. options 중 집계·인터랙션 변경은 Core의 갱신 API 확장 또는 cleanup 후 instance 재생성 중 PoC로 결정한다. 미확정 옵션을 조용히 무시하지 않는다.
언마운트는 destroy; effect cleanup→setup이 반복되는 React StrictMode에서도 새 instance만 mount한다. promise 경쟁·늦은 callback·변경되지 않은 props에 의한 중복 engine 생성·callback identity로 인한 무한 reload를 검사한다. Core가 DOM을 소유하는 전용 container 자식에 React가 동시에 노드를 render하지 않는다. Framework wrapper는 engine/node/Validation/집계 CSS를 복제하지 않는다.
SSR/Server Components의 DOM 렌더링은 이번 지원 완료 범위가 아니다. import 시 브라우저 global 부작용·React client boundary·hydrate 안전성은 검토하고 미확인 기능은 NOT VERIFIED로 남긴다.
## 6. CSS·Theme·배포 방식
같은 Core CSS와 HM Variables mapping을 모든 배포가 사용한다. 호스트는 Theme를 로드하고 Core는 컨테이너 내부 전용 selector만 사용한다. Theme 정책과 실제 선언 근거는 [Integration §3](integration.md#3-hm-theme-연결)을 따른다.
- npm: Core ESM + type + CSS, Wrapper ESM + type 후보. exports/types/files/sideEffects를 설계하고 package tarball 안에서 실제 import·CSS·font/asset 존재를 검증한다. 실제 publish는 별도 승인이다.
- ESM: npm bundler import와 브라우저 상대 URL import를 구별한다. Core에 React/React DOM 또는 unresolved bare dependency가 섞이지 않도록 검증한다.
- browser bundle: IIFE/UMD 중 직접 script 사용 형식을 선택한다. `window.HMOrgChart` 후보, 엔진 포함 여부·외부 dependency·license·크기·CSP를 검토한다. inline page 예시는 CSP 승인 완료를 뜻하지 않는다.
- CSS: Core 스타일을 별도 `style.css` export/상대 asset로 제공하고 Theme를 중복 주입하지 않는다. upstream CSS의 container 밖 누출도 포함해 scope를 검증한다.
- TypeScript: Core 선언에는 React type이 없어야 하고 Wrapper만 React Props를 가진다. JS consumer에 TS transpilation을 요구하지 않는다.
- Theme 미연결: 승인된 scope 기본 스타일 제공 vs Theme 필수 연결 중 **TBD**다. 어느 정책이든 HTML/React가 같아야 한다. approved 기본값 없이 fallback 팔레트를 새로 만들지 않는다.
Root의 기존 build script는 두 패키지만 처리하므로 새 Core·Wrapper build 연결 범위는 승인 후 확정한다. 엔진 CommonJS/UMD를 ESM·browser bundle로 변환할 수 있는지, export 기능 dependencies가 필수로 포함되는지, DOM-only engine와 adapter의 tree-shaking 가능성은 **NOT VERIFIED**다. 단순히 빌드 설정을 제안했다는 이유로 배포 가능 PASS를 내지 않는다.
Vanilla Theme를 위해 Core dependency/peer에 React peer를 가진 `@hm-design/ui` 전체 package를 필수로 추가하지 않는다. 승인된 호스트 asset/별도 Theme 산출 경로에서 React 미설치 consumer 동작을 확인한다. Core/engine CSS 누출, 호스트 Theme의 전역 영향, 전달된 font의 상대 URL은 서로 다른 검증 대상이다.
Theme owner는 호스트 하나이며 Core는 scope를 상속한다. 별도 Scoped Theme는 기존 `[data-hm-theme]` mapping 재계산·Scoped partial Default 의미를 따르고 Core가 자동 주입/reset하지 않는다. Global A/Scoped B의 두 instance가 서로 영향을 주지 않는지 확인한다.
## 7. 최소 통합 검증 계획
동일 가상 fixture·공통 설정·Theme로 두 환경을 비교한다. 테스트가 확인해야 할 차이를 명시하고 같은 검사 반복을 피한다.
1. React 설치 없는 HTML 단독 페이지에서 표시. `file://` classic bundle과 HTTP(S) ESM의 asset/console 결과를 각각 기록.
2. React에서 같은 Core로 동일 계층·노드 UI·인터랙션 표시. 새 렌더링 엔진이 없는지 dependency/code 검토.
3. 같은 JSON의 조직 ID·parent·순서·leader·직원 그룹과 공통 집계 결과 일치.
4. 조직 추가/삭제/이동·직원 이동/상태/관계사/사진·title/기준일 변경 후 재검증과 갱신. consumer 원본 객체를 바꿔도 이미 캡처/반영한 스냅샷과 집계가 암묵적으로 바뀌지 않음.
5. JSON → Mock API Source → JSON 전환, API 매핑 계약·loading/error·응답 경쟁·abort 확인.
6. HM Theme 연결 및 승인된 Theme 미연결 정책. computed Variables·node CSS·font·scope가 두 환경에서 일치. Global A + Scoped B + 두 instance의 독립성·Theme owner와 cleanup도 확인.
7. mount/update/reload/destroy·연속 remount·StrictMode·다중 instance에서 이벤트/Observer/timer/DOM 잔존과 늦은 callback 확인.
8. CSS 전역 충돌과 390/768/1280/1440px Overflow·키보드·focus·읽기 가능성 검증.
9. Core build·npm/ESM/browser bundle·CSS/type/license와 React dependency 미포함 검증.
10. 공식 운영 main·기존 UI/Theme/Storybook과 무관 파일 보존 증거. 코드 중복·공통 Core 재사용 검토.
Vue/Angular 실행 검증은 여기 포함하지 않는다. 기술 제약·승인 게이트는 [Implementation Plan](implementation-plan.md)을 따른다.
+110
View File
@@ -0,0 +1,110 @@
# OrgChart Implementation Plan
상태: 설계 검토 후 수행할 개발 계획. **이번 작업은 Markdown만 작성하며 구현을 시작하지 않는다.** 1차 공식 개발 대상은 Vanilla HTML/JavaScript와 React다. Vue·Angular·기타 framework는 제외한다. [README](README.md)의 요구사항 추적과 [Framework Integration](framework-integration.md)의 통합 검증을 따른다.
## 1. 단계별 계획과 완료 조건
### 1단계 — 설계 승인·핵심 기술 PoC
사용자가 이 문서를 검토한 뒤 후속 개발 권한을 확인한다. Core/React Wrapper 두 패키지 분리안과 실제 workspace/build 연결 범위를 결정한다. engine 배포 버전을 고정하고 native DOM·module import·custom node·완전 cleanup·CSS scope를 소규모 PoC로 확인한다. 캡처가 제공되면 콘텐츠 8영역·허브·곡선·카드 배치를 대조한다.
완료 조건: 정확한 engine version/tag/배포 artifact·license와 dependency 범위가 기록됨; React 없는 HTML 구동 가능성, Destroy·다중 instance·custom node 가능성 근거 확보; node Theme mapping·미연결 정책·모바일 선택의 승인 필요 범위가 구분됨. 완전 정리가 불가능하면 기능 구현을 확대하지 않고 차단 사항·대안을 보고한다. 이 기술 PoC는 뒤 단계의 전제라 필요한 최소 중간 검증에 해당한다.
### 2단계 — 공통 데이터 계약·Validation·집계
Schema 초안의 필드/enum/null/version/root/leader·복수 소속과 집계 정책을 승인하고 순수 JS/TS 데이터 계층을 구현한다. JSON Mock/외부 JSON Source와 공통 validator를 만든다. API 명세 없이 Mock API Source로 교체 계약을 확인한다.
완료 조건: 원본 ID 모델·동일 Source Interface; 중복 ID/참조/cycle/상태/관계/안전 입력 실패가 controlled issue로 변환됨; 승인된 집계 정책으로 단일 ID·하위 포함·상태/관계사 이동이 결정적 결과를 생성함. 미승인 정책은 code default로 숨기지 않음. UI·framework 의존 없이 사용할 수 있음.
### 3단계 — Vanilla Core와 콘텐츠
승인한 패키지/build 구조에서 engine adapter, DOM 노드 UI, 제목·허브·summary·상태/관계사 영역을 구현한다. mount/update/reload/destroy, Source 경쟁·취소·instance scope·Core CSS·Theme 연결을 구현한다. Overlay/Layout은 PoC와 승인한 디자인만 구현한다.
완료 조건: Core에 React import 없음; API endpoint/Navigation/로그인 의존 없음; 데이터만 변경해 조직·직원·relation·제목 갱신; 승인 정책의 집계; 요청된 인터랙션·사용자 상태·정리 보장; HTML 단독 배포 예시의 runtime 목표 명확. 임의 추가 검색/Export/메뉴/drag 편집은 없음.
### 4단계 — React Wrapper와 콘텐츠 Storybook
Wrapper는 Core 생성·ref·props 전달·Source 변경·언마운트 cleanup만 연결한다. 동일 Core UI와 CSS를 사용하고 Storybook은 콘텐츠 단독으로 구성한다. 기존 Component/Pattern/Theme/대표 Story는 보존한다. 현 [Storybook main](../../.storybook/main.ts)의 glob은 `src/**/*.stories.ts(x)`이며 패키지 내부 Story는 자동 포함되지 않는다. `src/org-chart/*.stories.tsx`는 제안 위치다.
완료 조건: engine/Validation/집계/노드 UI 중복 없음; 데이터 변경·StrictMode·unmount 시 동일 Core 계약 준수; 모든 Story는 가상 데이터만 사용; Header/Dashboard/신규 페이지 조합 없음. 지원 Props와 stale response 동작을 명시함.
### 5단계 — 배포·최종 통합 검증
npm/ESM/browser bundle·CSS·`.d.ts`와 license·asset·dependency 포함 범위를 실제 build와 consumer에서 확인한다. §3의 최종 검증을 변경 최종 상태 기준으로 한 번 종합 실행한다. 실패를 수정한 경우 영향받은 검사만 다시 수행한다. 같은 조건으로 이미 성공한 검사를 형식적으로 반복하지 않는다.
완료 조건: HTML과 React의 공통 기능/디자인/데이터/집계/인터랙션 일치; 네 컨테이너 너비의 실행 증거; JSON → Mock API 교체; lifecycle/CSS/접근성·asset 결과; build와 browser 결과 별도 보고; NOT VERIFIED·TBD를 감추지 않음; 운영 main 및 요청 밖 파일 보존. 실제 API 지원 완료와 npm publish는 이 단계에서 자동 주장하거나 수행하지 않는다.
## 2. Storybook 구성 제안
최상위 `OrgChart` 콘텐츠 그룹 후보다. 이름·기본 sidebar 대표 Story와 세부 검증 Story 배치는 승인 후 확정한다. 기존 All Story나 navigation Pattern 대표 Story를 교체하지 않는다.
- Basic OrgChart: 작은 가상 ID 계층, 기본 노드.
- Leader Hierarchy: 최상위/하위 leader, 공석, 허브 처리.
- Organization / Team / Cell: 부문·실·Div·셀·팀, 정렬·다단계 계층.
- Employee Cards: 이름·직위/직책·이미지 실패/fallback·긴 텍스트.
- Summary Table: 직접/하위 포함·직위/상태 집계, 정책 필요 상태.
- Multiple Affiliations: 같은 집계 정책의 관계사별 숫자와 총원.
- Dispatched / On Leave: 상태 변경, 원소속 표시 정책, 중복 카드와 ID 집계 구별.
- Full OrgChart Content: 8개 콘텐츠 영역과 추가 관계. Header·GNB/LNB·Dashboard 전체 조합 제외.
- JSON Data Update: 조직/직원 이동·추가/삭제·상태/사진·명칭·기준일 갱신.
- Mock API Data Source: 같은 fixture의 지연/실패/응답 경쟁, JSON Source 전환.
- Loading / Empty / Error: schema/참조/cycle/정책 오류·제어된 상태.
- Responsive Container: 390/768/1280/1440px와 부모 크기 변경, 다중 instance.
Storybook은 React Wrapper의 실제 Core를 검증한다. Vanilla 단독 consumer 검증을 Storybook 성공으로 대체하지 않는다. 지원하지 않는 API·인증·직원 개인정보나 verification Story의 부가 기능을 신규 요구로 복사하지 않는다.
## 3. 최종 통합 검증
[Framework Integration §7](framework-integration.md#7-최소-통합-검증-계획)의 10항목을 최종 증거의 공통 기준으로 사용한다. 다음을 관련 항목에 연결한다.
- 데이터: 모든 Validation 실패 유형·leader/하위 포함/상태/관계사 집계·immutable 원본·같은 입력의 결정적 결과·JSON/Mock API parity.
- HTML/React: 동일 조직 ID/parent/순서/leader/직원 그룹·계산 숫자·노드 UI·Theme·키보드/zoom/확장 상태. Wrapper의 다른 engine 구현 부재와 Core 코드 재사용을 검토.
- lifecycle: data update/reload 경쟁·abort·StrictMode·반복 mount/unmount·destroy 두 번·destroy 후 callback 없음·두 instance의 DOM ID/이벤트/scroll/focus 격리. 이벤트/Observer/DOM 수와 제거 후 동작 증거를 남김.
- lifecycle 재생성: 이전 chart 참조를 잃기 전 handler/Observer 정리, 반복 init/update/reload의 누적 이벤트 검사. facade import와 engine의 DOM global 부작용을 구별하고 지연 로딩 entry를 확인.
- 컨테이너: 네 너비의 캡처·computed style·document/내부 scrollWidth/clientWidth·scrollHeight/clientHeight, 긴 명칭·ID 한 줄 식별·이미지/폰트 로드·absolute 알림 기준·Overlay 좌표.
- 접근성: 이름/label 연결, Tab/Enter/Space/Escape 등 해당 기능 키보드·focus 복귀, disabled/readOnly, screen reader 상태 설명. 실행하지 않은 범위는 NOT VERIFIED.
- 배포: package tarball imports·browser-ready ESM/classic bundle·별도 CSS/타입/상대 asset·React 미설치 HTML·Theme 미연결 승인 정책·CSP/CORS 범위. typecheck/build/browser를 구분하고 통과한 조건만 PASS.
- Theme 배포·소유권: React peer 없는 Vanilla Theme 공급 경로, Core/engine CSS와 호스트 Theme global 영향 구별, 상대 font URL, Global A/Scoped B 두 instance의 computed·Default/partial·cleanup. Wrapper의 별도 summary/state/node 구현 부재 확인.
- 영향: 최종 Git diff·요구사항·관련 docs/Storybook 정합성·불필요한 변경·운영 main/기존 Component/Pattern/Theme 보존.
관련 TypeScript·build·Storybook 명령은 미래 승인된 package scripts로 결정한다. 현재 package의 `prebuild/pretypecheck`가 생성물을 만들 수 있어 문서 전용 작업에서 실행하지 않는다.
Guard는 React consumer target이 실제 존재할 때 [Guard 정책](../guidelines/ai-generation-guard.md)의 적합한 범위·files/result로 1회 적용한다. PARTIAL 콘텐츠 검사가 전체 페이지 계약을 대체하지 않는다. Vanilla Core는 React bootstrap 전제와 충돌할 수 있으므로 기존 [Runtime Portability](../guidelines/runtime-portability.md)를 바탕으로 검사 적용 범위를 별도 합의하고 자동 PASS를 만들지 않는다. Guard 원본 status/staticStatus와 Runtime 증거를 구별하고 STYLE의 STATIC/RUNTIME/DESIGN_INTENT/FINAL을 함께 보고한다. 현재 지침을 이번 문서 작업에서 고치지 않는다.
## 4. TBD 결정 목록과 리스크
- **패키지/배포**: 두 package 최종 분리·경로·exports·type·dependency/peer·build 연결·Core npm 이름·browser namespace/format·engine 고정 버전. vanilla용 bare-import 없는 bundle과 React dependency 격리가 필요하다.
- **생명주기**: upstream 완전 disposal·Observer·document 이벤트 해제, 갱신 시 상태/focus/zoom 보존, 옵션 갱신 방식·대용량 전체 rebuild 성능.
- **데이터**: schemaVersion·enum/null/필수 값·ID 범위·root 수·무소속·leader 소속/겸직·관계 방향/중복·복수 소속 계약·정렬 우선순위·안전 입력 상한.
- **집계**: 파견/휴직 포함·재직 정의·조직장 포함·원소속/파견지·직원 ID 중복·관계사/총원 분모. 승인 없이는 숫자를 업무 기준으로 확정하지 않는다.
- **디자인/모바일**: 캡처 자료 미제공, 허브 실체·곡선 Overlay·카드 anchor·노드/그룹 배치·읽기 가능한 최소 폭/배율·zoom limit·모바일 방식·Theme 미연결 정책. computed 일치와 디자인 승인도 구별한다.
- **API/보안**: 실제 endpoint/auth/권한·캐시/재시도·갱신·버전·이미지 권한·개인정보·외부 입력/URL 정책. 명세 없는 부분은 상상해 구현하지 않는다.
- **지침 정합성**: 기존 React/HM 재사용 원칙과 framework-independent 노드의 예외, 공식 일반 수정 경로와 Feature 문서 경로, Guard runtime 전제. README의 차이를 보존하고 작업별 사용자 지시를 우선한다.
Vue·Angular·기타 framework Adapter는 TBD 개발 범위가 아니라 **이번 범위 제외**다. 미래 별도 요청이 있어야 개발 대상으로 편입한다.
## 5. 이번 문서 작업의 검증 기록
기준 HEAD: `d18e3c33801e165e17b6d971e2f6198eed483c3c`, 경로 `HMDesign-OrgChart`, 브랜치 `feature/orgchart-package`. 최초/최신 추가 요청의 확정 요구를 README에서 추적한다.
- PASS: 작업 전 Git root/branch/HEAD/origin/운영 Worktree·원격 main/Feature SHA·깨끗한 Working Tree 확인.
- PASS: 현재 workspace·package export·Storybook glob·CSS entry·선택 Component/Pattern·Token 실제 파일 확인. 이는 읽기 전용 구조 확인이며 신규 API/배포 검증은 아니다.
- PASS: 문서 7개에 콘텐츠 독립성·Core/React Wrapper·같은 계약/집계/노드/Theme/인터랙션·Source 교체·페이지 제외·TBD·개발 게이트를 반영.
- PASS: 기존 `node scripts/check-doc-links.mjs` 실행에서 Markdown 118개·내부 링크 620개·problems 0. 신규 OrgChart 문서의 내부 링크는 53개다. 외부 URL 도달성 전체 검사는 포함하지 않는다.
- PASS: JSON code/inline 블록 3개 구문, JavaScript/HTML inline script 2개 구문, 가상 fixture의 required/type/enum/null·ID 참조·parent cycle·raw 수 2/1/1, Schema와 TypeScript 초안의 필드 일치를 읽기 전용으로 확인했다. Schema에 사용한 일부 keyword의 구조 검사이며 완전 Draft 2020-12 validator나 TypeScript typecheck가 아니다.
- PASS: 최종 확인 시 개발 Worktree는 `docs/org-chart/*.md` 7개만 untracked, 기존 tracked/staged diff 0. 운영 Worktree는 깨끗하고 Local/원격 main·Feature SHA는 시작 HEAD 그대로였다. package/lockfile·기존 UI/Theme/Storybook/AGENTS 수정, 설치·새 Worktree·Stage·Commit·Push 없음.
- NOT VERIFIED: 캡처 시각 대조, engine 고정 tag/npm 배포물 동일성, 실행/빌드/HTML bundle/React Wrapper/Theme computed/접근성·반응형·생명주기·실제 API. 구현 전 문서 작업이라 실행 검사 PASS를 내지 않는다.
- NOT VERIFIED: 완전한 JSON Schema validation과 TypeScript compiler/typecheck. 현재 Worktree의 관련 dependency가 없어 설치하지 않았고 필드/구문 확인으로 지원 완료를 주장하지 않는다.
- 적용 대상 없음: 이번 화면 생성 Guard(typecheck/build 포함). React bootstrap·UI 변경 없이 Markdown만 작성하므로 실행하지 않는다.
이 단계의 최종 산출물은 사용자 검토 대상 설계 문서다. 다음 기능 개발·설치·Stage·Commit·Push는 시작하지 않고 사용자 검토 및 승인을 기다린다.
## 6. 병렬 읽기 전용 검토 통합
사용자 요청으로 분석 작업자 3개를 동시에 사용했다. 각 작업자는 읽기 전용으로 검토했고 문서 작성·수정은 메인 작업자만 수행했다. 기존 링크/fixture 검사는 작업자마다 중복 실행하지 않았다.
- **엔진 HTML·React 호환성**: 이전 chart를 잃기 전 update/reload cleanup, 문자열 template + createNode 안전 DOM 우선안, DOM global import 부작용·지연 로딩, 우선 두 패키지 표현 통일을 Integration/Architecture/Framework Integration에 통합했다.
- **HM Theme·Token·UI**: React peer 없는 Vanilla Theme 공급, host Theme와 Core/engine CSS 영향 구별, 상대 font, Scoped mapping/Default/owner, Wrapper의 summary/state/node 중복 금지를 Architecture/Integration/UI Composition/Framework Integration에 통합했다.
- **데이터·Validation·API 확장**: fatal data issue와 집계 unavailable/Source 실패의 경계, immutable 스냅샷·최종 revision 확인, 여러 API/페이지의 완전한 동일 기준 스냅샷 조립, 집계 정책 자체 검사·결정적 정렬 조건을 Data Contract/Architecture/Integration/Framework Integration에 통합했다.
각 검토는 설계·공식 소스의 정적 근거다. 실제 이벤트 누수·빌드·computed·API 일관성이 검증됐다는 뜻이 아니며 해당 NOT VERIFIED를 유지한다. 중복 문서·구현 코드·별도 리뷰 파일은 만들지 않았다.
@@ -0,0 +1,95 @@
# HMDesign OrgChart P1 결과 — 2026-10-12
RESULT: **PARTIAL**. 최종 요청의 독립 디자인·개별 공개 API·5개 Story 메뉴·HTML/React 기능 검증은 완료했다. 원본 Guard의 정적 토큰 추적 FAIL, 실측 흰색 부서명 컬러의 일부 명도 대비 부족, Workdesk 픽셀 일치 미검증은 아래에 보존한다. 전체 디자인 승인 또는 배포 승인으로 해석하지 않는다.
## 개발 환경과 보존
- 실제 수정 경로: `D:\디자인시스템\HMDesign-OrgChart`.
- 기존 브랜치: `feature/orgchart-package`; 새 브랜치를 생성하지 않았다.
- 작업 전/후 HEAD: `d18e3c33801e165e17b6d971e2f6198eed483c3c`. Local `main` 및 기존 `origin/main` ref도 동일 SHA다. Git 쓰기 작업 없이 확인했다. 원격 HEAD는 이번 P1에서 새로 조회하지 않았다.
- 공통 UI/Theme/Pattern, 기존 Wrapper 구현, 원본 JSON v1/v2, 기존 v0.1 구현·React 검증 보고서를 보존했다. 시작 시 기존 package.json/lock 수정 및 untracked 패키지 작업을 유지했다.
- 시작 snapshot 671개 파일과 비교한 요청 범위 밖 변경은 0건이다(`test-results/org-chart-p1/preservation.json`). 기존 main checkout은 접근 제한으로 직접 Working Tree 검증하지 못했다. 그 경로에 수정 명령을 실행하지 않았다.
- 별도 브랜치·Stage·Commit·Push·Merge를 수행하지 않았다. 작업은 미커밋 상태다. `git diff --check` 통과.
## 병렬 작업과 통합
사용자의 최초 P1 병렬 구현 요청에 따라 실제 두 Track을 병렬 실행했다. Track A는 공통 EmployeeCard, 조직별 presentation preset, 정식 로고 mapping, 독립 디자인 변수에 집중했다. Track B는 순수 summary selector, Position/Affiliation/Status DOM 모듈, 독립 Story 및 변경 범위 browser verifier를 담당했다. Main은 공통 controller/Tree/Content 통합, 공개 factory, 실제 Workdesk/Figma 근거 확인, 배포 예제와 canonical Story, 빌드·최종 증거 연결을 담당했다. 책임 파일을 분리했고 공유 파일 충돌은 없었다. 정상 테스트를 두 Track에서 반복하지 않았다.
## 디자인·콘텐츠 구현
1. `OrgChartContent`가 독립 Tree와 Summary를 조합한다. 리더·허브는 조직 트리의 가짜 parent를 만들지 않는다. 기존 organization ID/parentId를 유지한다.
2. 최상위 리더, 조직장, 일반 직원은 같은 Core `createEmployeeCard`를 사용한다. 직위·직책·소속 로고·근무 상태를 표시하며 null/실패 프로필 이미지는 이니셜로 대체한다. React 카드 UI는 추가하지 않았다.
3. 중앙 협업·소통 허브는 `presentation.hubText`로 표시한다. 추가 관계선은 구현하지 않았다.
4. 사업관리·경영기획·인재성장·통합시스템 Div·디자인기획·솔루션영업과 확인된 셀/팀 명칭을 fixture에 사용한다. 6개 branch는 organization ID preset에 연결하고 하위 셀/팀이 variant를 상속한다. fixture membership/leader/직위는 가상 설정이다.
5. 우측 PositionSummary는 organization × position 교차표, 직접/하위 포함 인원, 상태별 Raw Count를 같은 summary/selector에서 계산한다. 좁은 container에서는 표와 트리 각각 내부 스크롤을 제공한다.
6. 하단 AffiliationSummary는 7개 관계사 명칭과 실제 로고, 가상 Raw Count를 표시한다. 0명 관계사 3곳도 표시한다.
7. 파견·휴직은 독립 StatusSummary 안에서 원본 직원 참조로 표시한다. 전체 인원에 다시 더하지 않는다. 각 그룹 flag를 독립 적용한다.
8. 카드·Typography·Spacing·Border·Radius·Gradient는 OrgChart 내부 CSS와 presentation preset으로 관리한다. 공통 HM Token을 필수로 사용하지 않고 Theme가 없는 호스트에서도 표시된다. 전용 public 변수만 외부 override에 사용한다. CSS는 Shadow DOM/내부 shell에 격리된다.
9. Full Composition에 Header·GNB·LNB·페이지 Routing을 추가하지 않았다. Storybook 외부 검증 controls만 기존 공개 HM Button을 사용하며 Core 자체의 Theme 의존성과 구분한다.
10. 같은 fake JSON, validation/normalize/summary/lifecycle, Core DOM/CSS를 HTML과 React가 공유한다. 기존 React Wrapper 구현과 Props API는 변경하지 않았다.
## 컬러와 로고 근거
Workdesk `https://workdesk.hmac.kr/`, 표시 버전 `v1.2610.9c5e`, 2026-10-12 관찰. 6개 visible 조직 heading 부모의 computed background-image와 heading white text를 읽었다.
- 사업관리 `.h33`: `#194d82 → #1d395c`.
- 경영기획 `.h33.org-team-link`: `#2164a8 → #254975`.
- 인재성장 `.h38.org-team-link`: `#0f6ace → #044a94`.
- 통합시스템 Div `.h310.org-team-link`: `#8c67b6 → #5d3494`.
- 디자인기획 `.h312.org-team-link`: `#09a7a2 → #00847f`.
- 솔루션영업 `.h36.org-team-link`: `#5a963c → #3f7629`.
부서명 텍스트 실측값은 모두 `#ffffff`. 이는 헤더 측정이며 직원 카드 전체 원본 스타일을 검증한 값이 아니다. 카드 배경, 기본 Typography/Spacing/Radius 및 헤더 시작색을 재사용한 카드 테두리는 DESIGN_ADAPTATION으로 명시했다. 임의 기본값을 공식 Workdesk 값으로 확정하지 않았다. 기존 공통 Theme 값에 억지로 맞추지 않았다.
정식 로고 7종은 접근 가능한 Figma 파일 `Plr6RRLi5XnLIn60x9aG1s`의 footer `22:179`, logo nodes `22:181/187/193/199/205/211/217`에서 받은 PNG다. `(주)한맥기술`, `(주)삼안`, `(주)장헌`, `장헌산업`, `PTC`, `한라산업개발`, `바론컨설턴트`. affiliation ID로 Core map에 연결하며 같은 자산을 카드·하단 summary·Logo에서 사용한다. 원본 PNG/크기/SHA256/출처는 `packages/org-chart/src/assets/affiliations/README.md`에 기록한다. 임시 Figma URL은 배포에 필요 없고 bundle에 inline된다. 실제 프로필 사진·개인정보는 가져오지 않았다. 로고 미확보 항목은 이 7종 범위에서 없다. 미매핑 ID/파일 오류는 명칭 텍스트 fallback으로 처리한다.
## 공개 API·파일 변경
패키지 root에 `createOrgChartTree`, `createPositionSummary`, `createAffiliationSummary`, `createStatusSummary`, 타입 `OrgChartSummaryComponentOptions`를 additive export했다. 기존 API와 `0.1-draft` schema는 유지한다. Summary는 `stylesheetUrl` 하나만 필요하며 Tree는 기존 engine/core URL pair를 사용한다. 모든 factory는 기존 OrgChartHandle을 반환한다. 다른 업무 페이지의 container에 각각 mount/update/reload/destroy할 수 있으며 전체 OrgChart나 Header를 요구하지 않는다. [사용법과 override 예시](standalone-components-p1.md).
- 주요 Core 변경: `core/index.ts`, `render.ts`, `employee-card.ts`, `affiliation-mark.ts`, `style-presets.ts`, `src/index.ts`, `build.mjs`, `vendor.d.ts`.
- 독립 모듈: `org-chart-content.ts`, `org-chart-tree.ts`, `position-summary.ts`, `affiliation-summary.ts`, `status-summary.ts`, `logo-legend.ts`, `department-colors.ts`, `components.ts`.
- 데이터: `content-selectors.ts`와 신규 7개 selector 테스트. 기존 validator/schema/source/aggregation 계약을 바꾸지 않았다.
- 스타일: `design-tokens.css`, `employee-card.css`, `summary-sections.css`, `org-chart-content.css`, `org-chart.css`. 빌드가 모든 내부 CSS를 `dist/style.css` 하나로 inline한다.
- 예제: 가상 `data-p1.json`과 생성기, Theme 없는 `p1.html/p1-classic.html/p1-app.js/p1.css`, React `p1.html/p1.tsx/p1.css`; 기존 examples entry 유지. React Vite input에 P1 entry만 추가했다.
- Storybook: Core component preview, card/internal summary Story, canonical P1 Story. 기존 8개 Story에는 `internal` tag만 추가하여 메뉴에서 숨기고 테스트/내용은 보존했다. 신규 card/status verification Story도 숨겼다.
- 사용자용 OrgChart 메뉴는 **Logo / Department Colors / Position Summary / Affiliation Summary / Full Composition** 5개뿐이다. Department Colors는 실제 swatch/HEX/CSS variable, 카드 배경·직원 텍스트·부서명 텍스트·border/Gradient, OrgChart 전용 여부와 실측/기본값 근거를 표시한다.
- 추가 npm dependency: 없음. 현재 React 19/Storybook 10.6.1/engine 6.0.0을 유지한다. 별도 Summary npm 패키지를 만들지 않았다.
## 검증 결과와 범위
- Core typecheck: 초기 점검 후 Department Colors의 literal union Set 타입 오류를 수정했다. 최종 Core 빌드에 포함된 TS declaration compiler 통과.
- Story/source graph typecheck: `npm.cmd exec -- tsc -p tsconfig.app.json --noEmit` 최종 PASS.
- Core ESM/IIFE/declaration/CSS build PASS. 공개 root import는 DOM 없는 Node에서 정상이고 기존 createOrgChart와 4개 신규 factory 모두 function으로 확인했다.
- 기존 Wrapper typecheck/build PASS. 그 이후 Wrapper 구현은 변경하지 않았다.
- HTML prepare, 별도 React consumer production build, Storybook production build PASS. 500kB chunk와 Storybook plugin timing 경고는 남았으며 오류는 아니다.
- 새 selector 7개 테스트 **7/7 PASS**. 원본 data 테스트/기존 69·79 browser suite를 재실행하지 않았다.
- 최초 신규 browser 검증 **197 checks: 196 PASS / 1 FAIL**. 유일 실패는 개별 meta의 storySort global-only 콘솔 오류였다. 해당 옵션을 제거하고 Storybook만 재빌드했다.
- 오류 및 실제 메뉴 선택 재검증 **4/4 PASS**: 수정된 canonical 5개 iframe 콘솔 오류 0, 실제 manager/sidebar 5개 항목 정확 노출, 내부 검증 그룹 기본 숨김, manager 오류 0. 정상 196개 검사는 반복하지 않았다. 최초 report의 FAIL은 삭제하지 않고 후속 해소 증거로 연결한다.
- Browser Chrome `155.0.8059.39`. HTML/React 가상41명, 18조직, 상태·소속·이동 JSON, 원본 집계/정책 미확정 메시지, group별 표시, keyboard collapse/expand/focus, 긴 이름/직위/직책, null/broken profile, 실로고 로딩·텍스트 fallback, destroy 검증 PASS.
- `390/768/1280/1440px`: HTML/React/Full Composition 문서 overflow·글자 bounds·Navigation 없음 PASS. 트리/교차표 내부 overflow는 의도한 스크롤이다. 전체 6개 branch가 작은 폭에서 한 화면에 모두 보인다는 의미는 아니다.
- Theme 미연결 HTML/React 실제 CSS PASS; host 공통 HM 변수 override에 영향 없음, public OrgChart override 적용 및 다른 instance/page에 영향 없음 PASS. 6개 조직별 Shadow DOM Gradient와 Department Colors legend/실제 카드색 일치 PASS.
- 독립 Summary 3종 및 Tree의 공개 dist API mount/update/destroy PASS. Summary에 engine stylesheet 없고 Tree root leader가 누락되지 않음 확인.
- 로컬 첫 브라우저 접근은 sandbox `ERR_NETWORK_ACCESS_DENIED`로 검사 전 중단됐다. 승인된 로컬 서버 환경에서 신규 검증을 실행했으며 운영 사이트를 수정하지 않았다.
증거 파일: `test-results/org-chart-p1/browser.json`, `storybook-console-recheck.json`, `preservation.json`, `contrast.json`, `storybook-build.log`; HTML/React/Full Composition 각 4개 폭 PNG와 `storybook-sidebar.png`. 원본 v0.1/React 보고서·검증 artifacts는 유지한다.
## Guard·명도 대비·남은 한계
Storybook PARTIAL Guard 원본: **status/staticStatus FAIL**, TOKEN-001 3개(`resolved-bg-surface`, `current-border`, `current-gradient`) 및 TOKEN-002 동적 변수 미검증. 최신 menu 파일 재검사도 같은 static FAIL을 보존한다. Guard는 `?url`로 가져온 private CSS를 일반 consumer CSS 적용으로 간주하지 않아 Shadow DOM 주입과 `style.setProperty` 선언을 static graph에서 연결하지 못한다. `resolved-bg-surface`는 `design-tokens.css`, current 변수는 `applyOrganizationStyle`에서 선언하며 실제 렌더의 6개 swatch/card/Gradient와 override 검증은 PASS다. 공통 Theme/Guard를 변경해 이 결과를 숨기지 않았다. 이 정적 FAIL을 Runtime PASS로 덮거나 전체 Guard PASS로 보고하지 않는다.
Storybook Style: **STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT NOT VERIFIED / FINAL NOT VERIFIED**(Guard 자체 기준). React consumer Guard: **status NOT VERIFIED / static NOT APPLICABLE**, RENDER-002 실제 bootstrap 브라우저 확인 요구; Style **STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED**. 관련 브라우저 증거는 위의 별도 결과다. 원본 Guard JSON을 유지한다. 일반 HM React Component style override를 추가하지 않았다. OrgChart 독립 디자인 의도는 사용자의 2026-10-12 최종 지시를 근거로 적용했다.
흰색 텍스트와 측정 Gradient stop의 WCAG 대비를 계산했다. 일반 텍스트 4.5:1 기준: 통합시스템 시작색 4.44:1, 디자인기획 시작색 2.97:1, 솔루션영업 시작색 3.58:1은 미달한다. 다른 stop은 4.5:1 이상이다. 디자인기획 시작색은 큰 텍스트 3:1 기준도 약간 미달한다. 실측 디자인을 임의로 공식 보정하지 않고 원래 값을 유지했으며 접근성 개선 시 OrgChart 전용 department-color/gradient를 교체할 수 있다. 대비 검증을 전체 AA PASS로 표시하지 않는다. 기본 직원 foreground/흰 배경은 충분한 대비를 가진다.
Workdesk 최신 화면의 큰 배치와 실제 heading 컬러를 반영했으나 픽셀 일치, 카드 모든 spacing/radius/typography의 Figma 상세 정밀 측정은 NOT VERIFIED다. 실제 조직 parent/leader/membership 및 운영 인원은 Mock의 검증 대상이 아니다. Screen reader speech, 물리 touch, SSR/hydration, heap/GC 완전 회복은 NOT VERIFIED다. 원본이 비활성화한 pan/zoom/drag/export는 추가하지 않았다. 복잡한 협업 관계선·직원 상세·정책 승인 및 실제 HR/API 전환은 P2 범위다.
## 시간 기록 (KST)
- Started At: 2026-10-12 10:49:28 KST
- Completed At: 2026-10-12 11:32:48 KST
- Actual Duration: 0시간 43분 20초
- Estimated Duration: 최초 2–3시간
- Variance: 예상보다 76분 40초–136분 40초 단축
Commit·Push·Merge: **진행하지 않음**.
+109
View File
@@ -0,0 +1,109 @@
# OrgChart P2 — Depth별 카드와 Workdesk Full Composition
RESULT: **PASS — 현재 P1 fixture의 조회 전용 Full Composition 수정 범위**.
실제 조직 계층에 따라 1Depth 조직장만 대형 카드를 사용하고, 2Depth 조직장은 소형 카드로 표시한다. Workdesk 원본 연결선·협업 화살표·장식을 공통 Core에 적용했다. 기존 Guard 미검증과 이전 검사의 FAIL은 이 결과와 별도로 보존한다. Workdesk 전체 픽셀 일치나 모든 데이터 조합의 지원을 뜻하지 않는다.
## 작업 범위와 보존
- 수정 경로: `D:\디자인시스템\HMDesign-OrgChart`, 브랜치 `feature/orgchart-package`.
- Workdesk 원본 `D:\디자인시스템\gpd-workdesk\sample\조직도_v1.3`는 읽기 전용으로 참조했다. 운영 main에는 수정하지 않았다.
- 기존 미커밋 변경은 시작 파일 해시와 비교해 보존했다. 원본 카드 SVG·회사 로고·공통 Theme·Component·Pattern·Storybook 메뉴·독립 컴포넌트 API를 유지했다.
- 직원 41명의 원본 fixture는 변경하지 않았다. 직원 ID·이름·리더 정보·직위·소속·상태·조직 관계·집계 계약도 그대로다.
- Stage·Commit·Push·Merge·Reset·Rebase를 수행하지 않았다.
## 카드·이름·간격
- 변경 전: 하위 조직장도 모두 대형 `leader` 자산을 사용했다. 조회 전용 외부 margin이 후속 직원 margin 규칙에 덮여 실제 헤더↔카드 간격은 0px이었다.
- 변경 후 대표자: 전용 SVG **146×74px** 유지.
- 변경 후 1Depth: 주요 조직장 6명 모두 기존 대형 SVG **146×68px** 사용.
- 변경 후 2Depth: fixture의 기존 조직장 3명 모두 소형 SVG **108×52px** 사용. 다른 일반 직원도 소형 카드를 유지한다.
- 대형 여부는 직책 문자열이나 리더 플래그 단독이 아니라 원본 `parentId` 조상 관계로 계산한다. semantic leader 구분과 직책 뱃지는 보존하며 독립·interactive 화면의 카드 정책은 유지한다.
- 최신 사용자 수정 기준으로 1Depth 외부 간격은 **8px**, 2Depth 이하 헤더↔첫 카드 간격은 **10px**다. 화면의 17개 조직 노드에서 Depth별 간격을 확인했다.
- Storybook 1920px 측정값을 Stage 좌표로 환산하면 1Depth 헤더 Y **254.1875px**, 높이 **36px**, 대형 카드 상단 **298.1875px**다. 6개 외부 간격은 약 **8.000002px**, 하위 조직 간격은 **9.99998–10.00001px**였다.
- Full Composition의 대표자·조직장·직원·파견·휴직을 포함한 렌더 카드 43개의 이름은 모두 **홍 길 동**으로 표시한다. 접근성 이름은 **홍길동**이다. 직위·직책도 요청한 한글 자간 표시를 적용하며 원본 데이터는 변경하지 않는다.
- 확정 Typography·카드 크기·프로필 크기와 직위↔이름 내부 gap **0px**를 유지했다. 외부 헤더 간격과 내부 행 간격을 분리했다.
## Workdesk 원본 디자인 적용
- 활성 compact `screenD` 곡선 3개, 대표자↔허브 중앙선, 실제 헤더 상단 중앙을 따라가는 사업관리 곡선의 **SVG Path 5개**를 적용했다. 사업관리 곡선은 원본 renderer의 계산식을 사용한다.
- Stroke `#45639F`, width `0.893023`, dasharray `1.79 1.79`, fill `none`을 사용한다. 기존 상단 pseudo 연결선은 해당 원본 artwork preset에서 숨겨 중복을 제거했다.
- 원본에서 하위 조직 SVG 블록은 비활성 상태이며 현재 사용 가능한 별도 원본 Path가 없다. 하위 셀·팀은 기존 엔진의 실제 계층 경로를 보존하고 원본 점선 스타일을 적용했다. 원본에 없는 관계를 추가하지 않았다.
- 협업 화살표는 원본 PNG 4종으로 **5개**를 배치했다. 원본 SVG image 방식과 `preserveAspectRatio="none"`을 유지한다.
- `Group.svg`, `bg_sky.png`, `bg_main.png`, `Frame2@3x.png`, `1-8-4x-1@2x.png`와 화살표 4종, 총 **9개 자산**을 복사했다. SHA-256이 Workdesk 원본과 모두 일치한다.
- `?inline` import로 Core ESM/IIFE에 자산을 포함해 외부 Workdesk 서버 의존성을 제거했다. HTML·React·직접 열기 classic에서 동일한 자산을 사용한다.
- 장식 0, SVG 선·화살표 4, 조직 7, 허브 12, 통계 18, 파견·휴직 32, 대표자 35의 레이어를 구성했다. 전체 artwork는 카드와 같은 Stage 안에서 함께 축소된다.
- 우측 조직별 총인원, 좌하단 파견·휴직의 가로 2개 그룹, 하단 관계사 한 줄 현황을 유지한다. 독립 PositionSummary의 상세 교차표 기능은 보존했다.
## HTML·React·Storybook과 반응형
- HTML `p1.html?mode=readOnly`, 직접 열기 `p1-classic.html?mode=readOnly`, React `p1.html?mode=readOnly`, 기존 Full Composition Story가 같은 Core DOM/CSS를 사용한다.
- React 예제에 HTML과 같은 `mode` query 처리를 연결했다. 별도 React 노드 UI를 만들지 않았다.
- 기준 Stage **1920×914.5px**와 기존 폭·높이 기반 단일 비례축소를 유지한다. 작은 화면에서 카드 종류를 바꾸거나 조직을 재배열하지 않는다.
- 1920×1080: scale **0.983333**.
- 1440×900: scale **0.733333**.
- 1280×800: scale **0.650000**.
- 768×1024: scale **0.383333**.
- 390×844: scale **0.186458**.
- 네 진입 경로의 조직·카드·간격·통계·자산·축소 측정값이 일치했다. Storybook의 베이지 호스트 배경과 HTML의 흰 배경 때문에 바깥 배경 톤은 다르다.
- 390px에서도 구조 전체가 함께 축소되지만 글자는 매우 작다. 모바일에서 원본 크기 가독성을 제공한다는 의미는 아니다.
## 검증과 근거
- OrgChart Core·React Wrapper Typecheck: **PASS**.
- Core ESM/IIFE·타입·CSS 및 Wrapper Build: **PASS**.
- React 소비 예제 production Build: **PASS**. 약 2.53MB chunk 경고는 원본 이미지 inline 포함에 따른 것으로 남겨 두었다.
- 현재 Storybook dev **6041**의 Full Composition: **PASS**. 전체 Storybook production Build는 이번에 재실행하지 않았다.
- Chrome `155.0.8059.39`, 네 진입 경로·다섯 뷰포트의 P2 브라우저 검사: **484/484 PASS**, 새 console/runtime 오류 **0**.
- 대표자·Depth별 카드 크기, 실제 조상 Depth, 17개 조직의 외부 간격, 이름·직책 뱃지·원본 fixture 보존, Stage geometry·overflow, 카드·헤더·화살표·통계의 사각형 겹침을 확인했다.
- Tooltip 검증은 실제 표시된 홍 길 동 이름 1개의 폭을 일시적으로 **8px**로 제한해 overflow를 유도했다. 실제 hover·tooltip hover·키보드 Tab·Escape·focus 유지·숨김 scroll extent·복구를 확인하고 폭을 원복했다. fixture나 표시 이름을 검증용 긴 이름으로 바꾸지 않았다.
- 1920/1440/390px HTML·Storybook PNG를 육안 확인했다. 실제 텍스트 glyph 충돌과 화살표의 카드 침범은 발견하지 않았다. 확정 내부 gap 0에서 Noto 글꼴의 DOM Range ascent/descent envelope가 교차할 수 있으므로 해당 원시 측정값은 실제 glyph 충돌과 구분해 보존했다.
- 곡선 Path의 bounding box만으로 실제 곡선 겹침을 판정하지 않았다. 라우팅·레이어는 저장된 전체 화면 캡처로 확인했고 자동 곡선 충돌 검증은 **NOT VERIFIED**다.
증거 파일:
- [P2 browser 결과·측정값](../../test-results/org-chart-p2/browser.json).
- [기존 소스 변경 범위](../../test-results/org-chart-p2/source-changes.json), [시작 해시](../../test-results/org-chart-p2/source-baseline.json), [원본 자산 SHA 비교](../../test-results/org-chart-p2/asset-preservation.json).
- [Storybook 1920](../../test-results/org-chart-p2/storybook-1920x1080.png), [1440](../../test-results/org-chart-p2/storybook-1440x900.png), [390](../../test-results/org-chart-p2/storybook-390x844.png).
- [HTML 1920](../../test-results/org-chart-p2/html-1920x1080.png), [1440](../../test-results/org-chart-p2/html-1440x900.png), [390](../../test-results/org-chart-p2/html-390x844.png).
- [Storybook Full Composition](http://127.0.0.1:6041/?path=/story/orgchart--full-composition), [독립 iframe](http://127.0.0.1:6041/iframe.html?id=orgchart--full-composition&viewMode=story).
## Guard·기존 FAIL·미검증
- React 소비 예제에 PARTIAL Guard를 실행했다. 원본 `staticStatus=NOT APPLICABLE`, `status=NOT VERIFIED`, `RENDER-002`를 보존한다.
- `styleEvidence`: STATIC **PASS**, RUNTIME **NOT VERIFIED**, DESIGN_INTENT **PASS**, FINAL **NOT VERIFIED**. Guard CSS checks는 `[]`이며 Core Shadow CSS를 검사 범위에 포함하지 못했다. 이를 Core 스타일 전체 정적 PASS로 확대하지 않는다.
- [Guard 원본](../../test-results/org-chart-p2/guard-react.json)과 실제 P2 브라우저 증거는 별도 파일로 보존하며 Runtime PASS로 원본 status를 덮어쓰지 않았다.
- 기존 `verify-p1.mjs` [baseline](../../test-results/org-chart-full-composition/verify-p1-baseline.json)·[after](../../test-results/org-chart-full-composition/verify-p1-after.json)의 **197건 중 145 PASS / 동일 52 FAIL**을 보존하고 재실행하지 않았다. 이전 fixture·root·교차표·내부 스크롤·Typography 기대값 관련 실패와 P2 검사를 구분한다.
- 사용자가 언급한 v0 `verify.mjs` FAIL **9건**은 이번에 독립 확인하지 못했다. 현재 저장된 [v0 report](../../test-results/org-chart-browser/report.json)는 **6건 중 2 FAIL**로, 이 기록만으로 9건을 확정하지 않는다. 검사 삭제나 무조건적인 기대값 PASS 변경은 없다.
- **NOT VERIFIED**: 실제 운영 Workdesk 최신 화면과의 완전한 픽셀 일치, 전체 스크린리더, 자동 곡선/글꼴 ink 충돌 판정, 모든 미매핑 조직·혼합 root 데이터 조합, 전체 Storybook production Build.
## 변경 파일
기존 소스 6개:
- [render.ts](../../packages/org-chart/src/core/render.ts): 실제 조상 Depth에 따른 readOnly 카드 자산·헤더 역할.
- [employee-card.ts](../../packages/org-chart/src/core/employee-card.ts): readOnly 표시 이름·한글 간격·접근성 이름.
- [status-summary.ts](../../packages/org-chart/src/core/status-summary.ts): 상태 카드에도 동일 표시 옵션 전달.
- [org-chart-content.ts](../../packages/org-chart/src/core/org-chart-content.ts): 대표자 표시와 공통 artwork 조합.
- [org-chart-readonly.css](../../packages/org-chart/src/styles/org-chart-readonly.css): 헤더·카드 간격, 원본 장식·레이어·계층선, 파견/휴직 가로 그룹.
- [React p1.tsx](../../examples/org-chart-react/p1.tsx): HTML과 같은 readOnly query 진입.
추가 소스·자산·문서:
- [workdesk-artwork.ts](../../packages/org-chart/src/core/workdesk-artwork.ts): 원본 Path·5개 image 화살표·장식·동적 사업관리 연결선.
- [Workdesk 자산 README](../../packages/org-chart/src/assets/workdesk/README.md) 및 해당 디렉터리의 원본 자산 9개.
- [verify-p2.mjs](../../examples/org-chart/verify-p2.mjs): 기존 검사와 분리된 P2 브라우저 검증.
- 이 보고서와 현재 계약을 반영한 [UI Composition](ui-composition.md), [카드 디자인 2단계](card-design-stage2.md).
- [React 예제 README](../../examples/org-chart-react/README.md): 조회 전용 query와 P2 검증 실행 안내.
패키지·HTML/React build 산출물과 `test-results/org-chart-p2/` 증거를 생성했다. 이전 보고서·검증 기록을 보존했으며 Commit·Push·Merge는 미진행이다.
별도 [런타임 증거](../../test-results/org-chart-p2/runtime-evidence.json)에 원본 Guard 해시와 브라우저·시각 검토 상태를 연결했다. 현재 source/contract fingerprint가 원본 Guard와 일치함을 확인했다. 원본 status는 변경하지 않았다. 카드 41개의 직위/뱃지 행 하단과 이름 행 상단 간격도 실측 0px(부동소수점 오차 내)이다.
## 시간 기록
- Started At: 2026-10-12 14:52:51 KST
- Completed At: 2026-10-12 15:15:24 KST
- Actual Duration: 22분 33초
- Estimated Duration: 약 45~60분
- Variance: 예상보다 22분 27초~37분 27초 단축
@@ -0,0 +1,43 @@
# HMDesign OrgChart P3.1 — 잔여 디자인 정밀 보완
RESULT: **PASS — 요청한 P3.1 변경 범위**.
집중 브라우저 검사 **486/486 PASS**, 신규 콘솔·런타임 오류 **0건**. 기존 [P3의 732/732 기록](implementation-report-p3.md)은 역사 근거로 보존했고 전체 suite를 재실행하지 않았다. Guard 원본 및 결합 전체 상태는 **NOT VERIFIED**로 유지한다.
1. **Noto Sans KR**: OrgChart 전용 기본 token에 적용했다. 기존 `public/fonts/NotoSansKR-variable.ttf`를 HTML의 기존 복사 경로와 React bundler 경로에서 재사용하고 Storybook의 기존 HM font-face를 사용한다. HM Theme는 변경하지 않았다. 카드·직위·타이틀·허브·표·관계사·파견/휴직·팝업의 computed family를 확인했으며 네 실행 경로의 대표 9개 텍스트에서 Chrome CDP로 실제 custom-font 글리프 사용을 확인했다. 파일 내부 family 이름은 `Noto Sans KR Thin`, PostScript 이름은 `NotoSansKR`와 가중치별 suffix다. 이는 fallback 글꼴이 아니다. 한국어 Core shell의 `lang="ko"`로 `en` Storybook host와 `ko` HTML의 가운데점 글리프 폭 차이도 제거했다.
2. **1Depth·2Depth**: 1Depth를 20px/700/36px·실제 헤더 36px로 맞췄다. 경영기획 2Depth는 원본 `.hw`의 18px/600/16.9px를 적용했다. 인재성장은 같은 기존 값, 나머지는 20px/700/19.7px를 유지한다. 2Depth 114×30.4px·기존 Gradient·Radius 7.1px·Shadow·정렬을 유지했다. 모든 타이틀↔첫 카드 Gap 8px를 실제 측정했다.
3. **경영진 뱃지**: Full Composition의 `workdeskPresentation`에서 `jobTitle.trim() === '경영진'`인 뱃지 DOM만 생성하지 않는다. 빈 attribute container도 생성하지 않는다. 실제 직위·직책 데이터·팝업·통계는 그대로다. 현재 fixture의 대표자 `position="경영진"`, `jobTitle="실장"`은 유지한다. 따라서 대표자 직위 글자와 표의 `경영진` 열은 정상적으로 남는다.
4. **기타 뱃지·이름**: 실장·팀장·디비전장 등의 원본 뱃지는 유지한다. 이름은 기존 `홍 길 동`, 직위는 표시 계층에서만 공백을 제거한다. JSON을 수정하지 않는다. 기존 fixture에 경영진 직책 뱃지가 없어 대표자/1Depth/2Depth/일반/파견/휴직의 메모리 전용 변형을 별도로 검사했다. 여섯 역할에서 뱃지 미표시·직위 유지, 팝업의 원본 직책 보존, interactive 공통 렌더러의 별도 표시 정책을 확인했다.
5. **허브**: 원본 21px/750/24px·아이콘 20px·Gap 8px·좌우 Padding 22/24px·높이 42px를 유지했다. 기존 상속 `max-width:100%`가 198px 축 폭으로 텍스트를 제한하던 부분을 해제했다. 중심을 X 935에 명시하여 실제 폭 약 219.016px에서도 연결선 축을 유지한다. 기존 말풍선 SVG·배경·곡선은 변경하지 않았다.
6. **사진 없음**: 신규 공통 `person-placeholder.svg` 한 종을 모든 카드에서 같은 inline 자산으로 사용한다. 연한 사각 배경·중립 사람 실루엣이며 실사/원형 아바타/장식 없음. 실제 이미지 우선, 이미지 decode 실패 시 공통 SVG fallback을 확인했다. 승인 카드의 예약 슬롯 36×62/32×56/24×44 및 P3 파견/휴직 28×28을 유지한다. 원본 슬롯 55×76/54×70/38×52는 승인 카드보다 커서 복사하지 않았다.
7. **인원 현황 표**: 기존 표 폭 330px·위치·14px/400/1.15·좌우 조직명 padding 7px·행 19px/합계 20px·정렬·Border를 유지했다. 조직명 열 112px, 경영진 열 46px, 나머지는 잔여 폭을 배분한다. `nowrap`을 적용했으며 모든 셀의 실제 텍스트 Range가 셀 내부 한 줄로 들어가는지 확인했다. 숨김·말줄임·글자 축소 없음. 표 높이는 약 200.375px에서 172px로 줄었다. 가상 41명·조직별/직위별 계산·관계사 바는 보존했다.
8. **변경 파일**: 구현·예제·문서 14개. 재생성된 dist와 전체 해시 차이는 [파일 기록](../../test-results/org-chart-p3.1/source-changes.json)에 있다.
- `packages/org-chart/src/core/employee-card.ts`, `src/core/index.ts`
- `packages/org-chart/src/styles/design-tokens.css`, `employee-card.css`, `org-chart-readonly.css`, `org-chart-p3-summary.css`
- 신규 `packages/org-chart/src/assets/person-placeholder.svg`
- `examples/org-chart/p1.css`, `examples/org-chart-react/p1.css`, 신규 `examples/org-chart/verify-p3.1.mjs`
- `packages/org-chart/README.md`, `docs/org-chart/ui-composition.md`, `card-design-stage2.md`, 이 보고서
9. **화면별 결과**: HTML HTTP·직접 `file://` classic·React production HTTP·실제 Storybook 6041 모두 1920×1080, 1440×900, 1280×800, 768×1024, 390×844에서 PASS. 문서 scroll/client 폭·높이가 같고 모든 카드가 Stage 내부에 들어간다. 모바일은 기존 전체 비례 축소라 글자가 작지만 팝업은 축소하지 않는다. [1920 HTML](../../test-results/org-chart-p3.1/html-1920x1080.png), [1920 Storybook](../../test-results/org-chart-p3.1/storybook-1920x1080.png), [390 Storybook](../../test-results/org-chart-p3.1/storybook-390x844.png)을 육안 검토했다.
10. **HTML·React 동일성**: 네 경로의 카드 내용/좌표/크기·허브 위치/폭·표 내용/폭을 화면별로 비교하여 일치했다. 같은 Core와 SVG를 사용하며 HTML 모방 화면을 만들지 않았다. React Wrapper와 Storybook 소스·사용자 메뉴 세 개는 변경하지 않았다.
11. **회귀·검증**: Core/Wrapper Typecheck, Core ESM/IIFE/타입/CSS build, Wrapper build, HTML prepare, React 소비 production build PASS. 여섯 카드 역할의 데스크톱/모바일 팝업·focus trap·ESC 복귀, 대표자 Enter/Space/닫기, HTML/React의 실제 키보드 그룹 링크 이동을 확인했다. 146×74/146×68/108×52 카드 및 P3 상태 카드 114×54 보존. 상태 카드 114×54와 슬롯 28×28은 이번 도입값이 아니라 기존 P3 측정값이다. 기존 큰 이미지 inline chunk 약 2.54MB 경고는 유지한다. 마지막 들여쓰기 정리 뒤 재빌드한 runtime 파일 73개는 검증 시점과 byte 동일하여 브라우저를 반복하지 않았다.
12. **남은 Workdesk 차이**: 원본 Pretendard 대신 이번 확정 Noto Sans KR을 적용한다. 승인 카드 때문에 원본보다 작은 사진 슬롯을 유지한다. 표 열/행과 인원은 현재 가상 fixture를 따른다. 원본 운영 화면과 완전한 픽셀 일치 및 곡선/글리프 ink 자동 교차 검사는 NOT VERIFIED다. 확장형 Auto Layout/layoutConfig API 없음.
13. **Guard·NOT VERIFIED**: 최종 유효 PARTIAL Guard는 React bootstrap `p1.html`에 실행했다. 원본 staticStatus `NOT APPLICABLE`, status `NOT VERIFIED`, `RENDER-002`를 보존한다. 원본 및 결합 CSS 상태는 STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED다. 별도 Runtime은 PASS이며 이름 있는 computed 측정은 PASS이나 Core Shadow CSS 전체의 정적/의도 인증으로 확대하지 않는다. 최초 Guard는 target 기준 result 경로를 중복 지정해 ENTRY/SCOPE 미검증이 발생했다. 해당 실행은 [잘못된 인자 진단](../../test-results/org-chart-p3.1/guard-initial-invalid-result-path.json)으로 보존하고 인자만 바로잡았다. 실제 운영 Workdesk 픽셀 일치, 모든 임의 데이터/테마/브라우저, 스크린리더 발화, 실물 touch, 전체 Storybook production build는 NOT VERIFIED다.
14. **보존·Git**: 작업 경로 `D:\디자인시스템\HMDesign-OrgChart`, 브랜치 `feature/orgchart-package`, HEAD `d18e3c33801e165e17b6d971e2f6198eed483c3c` 유지. Workdesk 원본·운영 main 미수정. JSON·승인 SVG·로고·Workdesk 장식·HM Theme·Guard·과거 검사/보고서·package manifests 등 보존 대상 216개 해시 동일. 시작 시 기존 미커밋 package.json/package-lock.json 및 OrgChart 작업을 보존했다. Stage·Commit·Push·Merge·Reset·Rebase·Force Push 미진행.
초기 집중 검사 102 FAIL도 [초기 기록](../../test-results/org-chart-p3.1/browser-initial.json)에 남긴다. 다섯 화면의 실행 경로 동일성 실패는 host 언어에 따른 허브 폭 차이였고 `lang="ko"`로 수정했다. 나머지 97건은 기존 상태 카드 크기/slot, `.hw` ID, variable font 내부 이름, fixture에 없는 뱃지에 대한 검사 기준을 정확히 잡지 못한 진단 오류였다. 원본 P3의 기대값이나 FAIL 기록은 수정하지 않았다.
검증 근거: [최종 browser JSON](../../test-results/org-chart-p3.1/browser.json), [원본 Guard](../../test-results/org-chart-p3.1/guard-react.json), [Runtime](../../test-results/org-chart-p3.1/runtime-evidence.json), [결합 증거](../../test-results/org-chart-p3.1/linked-evidence.json), [runtime byte 동등성](../../test-results/org-chart-p3.1/artifact-format-equivalence.json).
Workdesk 읽기 전용 근거: `index.css`의 `.hw` 2858–2865, 그룹 헤더 3703–3751, 2Depth 표면 3776–3786, 폭 3884–3899, 허브 2472–2542, 표 1432–1673; `index.html` 255–282 및 `org-dashboard-renderer.js` 1638–1663. P3가 남긴 원본 수치 근거도 보존한다.
미리보기: [Storybook Full Composition](http://127.0.0.1:6041/?path=/story/orgchart--full-composition).
작업 시간:
- Started At: 2026-10-12 16:01:00 KST
- Completed At: 2026-10-12 16:30:44 KST
- Actual Duration: 29분 44초
- Estimated Duration: 35–50분
- Variance: 예상보다 5분 16초–20분 16초 단축
@@ -0,0 +1,73 @@
# HMDesign OrgChart P3.2 — 카드 Typography·파견/휴직·로고 크기 보정
RESULT: **PASS — 요청한 P3.2 변경 범위**. 집중 브라우저 **431/431 PASS**, 정책 측정 보완 **13/13 PASS**, 신규 콘솔·런타임 오류 **0건**. Typecheck·관련 Build PASS. P3.1의 486건 전체 suite를 반복하지 않았으며 기존 보고서·검증 증거 115개 파일을 보존했다. Guard 원본과 결합 전체 상태는 **NOT VERIFIED**로 유지한다.
## 변경 전후 px
아래는 실제 Chrome computed style과 Stage 축소 배율을 제거한 크기다. 측정 원본의 소수점 오차는 약 0.0001px 이내이며 아래 정수는 반올림한 값이다. [43개 카드 전체 측정](../../test-results/org-chart-p3.2/before-after-px.json)에 원시 값·컬러·로고 alpha 영역을 남겼다.
- **1Depth 대형 카드**: 이름 **24→24px**, weight 600·line-height 30px 유지. 직위 **20→18px**, weight 400·line-height 22px 유지. 기존 이름 #071D3A·직위 #797979·letter-spacing 유지. 카드 **146×68**, 사진 **32×56**, 사진↔텍스트 **10px** 유지.
- **2Depth/일반 소형 카드**: 이름 **20→18px**, weight 600·line-height 24px 유지. 직위 **15→15px**, weight 400·line-height 18px 유지. 기존 이름 #323232·직위 #797979·letter-spacing 유지. 카드 **108×52**, 사진 **24×44**, 사진↔텍스트 **4px** 유지. 2Depth의 semantic leader도 동일한 소형 규격이다.
- **대표자**: 이름 **24px/600/30px**, 직위 **20px/400/22px**, 기존 #071D3A/#224387·letter-spacing 유지. 카드 **146×74**, 사진 **36×62** 유지.
- **파견·휴직 타이틀**: **14px/700/20px → 20px/700/19.7px**. 일반 2Depth의 기본 Typography와 같은 CSS 변수 세 개를 공유한다. 전용 회색 Gradient, Border·Shadow·108×24 타이틀 표면은 유지한다. 경영기획·인재성장의 기존 **18px/600/16.9px** 예외는 변경하지 않았다.
- **파견·휴직 직원**: 이름 **13px/700/18px → 18px/600/24px**, 직위 **10px/400/13px → 15px/400/18px**. 일반 소형 카드와 같은 콘텐츠 DOM·Typography 변수·사진 영역·텍스트 좌표·간격을 재사용한다. 이름 #3E4650·직위 #555F6A, 기존 회색 Gradient·Border·Radius·로고 grayscale(1)/opacity 0.72 유지. 이름 아래 별도 상태 문구 없음.
- **파견·휴직 크기 차이**: P3.1 실제값은 카드 **114×54**, 사진 **28×28**이었다. 이번 명시된 최신 요구값인 카드 **108×52**, 사진 **24×44**로 보정했다. 이를 기존 108×52를 그대로 보존한 것으로 표현하지 않는다. 1px Border를 유지하면서 padding을 3px로 조정하여 일반 소형의 4px 사진 원점·32px 텍스트 원점과 일치한다.
- **타이틀↔첫 카드 Gap**: 일반 조직 및 파견·휴직 모두 **8→8px**. 직위↔이름 행 Gap **0px**.
- **Font/name/badge**: Noto Sans KR·기존 호스트 font-face/폰트 파일 유지. 이름은 **홍 길 동**, 직위·뱃지의 글자 사이 공백은 표시에서만 제거한다. 직원 JSON·팝업의 원문 데이터는 변경하지 않는다.
## 카드 로고 적용 범위
렌더링된 43개 직원 카드(대표자 1, 대형 6, 소형 34, 파견·휴직 2) 모두 기존 회사/관계사 PNG를 실제로 표시한다. 원본 SVG·PNG를 수정하지 않고 직원 카드 selector에만 확대를 적용했다.
- **소형**: 기존 실제 contain 영역 **12×12→18×18px**, 정확히 **1.5배**. 컨테이너는 투명한 1px inset을 포함해 14×14→20×20이다. 카드 기준 image origin **(88, 3)px**.
- **대형·대표자**: 실제 contain 영역 **14×14→21×21px**, 정확히 **1.5배**. 컨테이너 16×16→23×23, image origin **(124, 1)px**. top/right 0으로 옮겨 뱃지와의 여유를 확보했다.
- **파견·휴직**: 실제 contain 영역 **14×14→21×21px**, 정확히 **1.5배**. image origin **(85, 1)px**. 파견의 이전 CSS image box는 14×24였지만 object-fit:contain으로 원본 정사각 로고가 실제 표시된 영역은 14×14였다. 원시 box 높이를 단순 1.5배하는 방식으로 확대하지 않았다.
- 모든 원본은 transparent padding을 포함하므로 이미지 box와 실제 유색 로고 영역은 다르다. 예를 들어 한맥 21px box의 유색 범위는 약 **15.5835×9.5011px**다. **contain 영역과 PNG alpha 영역의 폭·높이 모두 1.5배**를 비교했다.
- 원본 비율 유지, 추가 배경·테두리·그림자 없음. 승인 카드 전체 크기 변경 없음. 로고 alpha 영역과 이름·직위·뱃지·프로필 영역 충돌 **0건**, 카드 밖 잘림 **0건**. 이번 fixture에서 1.5배를 모두 수용하므로 별도 최대 크기 제약은 없다.
- 하단 관계사별 인원 현황의 로고 box·바 전체 좌표·집계 문자열은 이전 측정과 동일하다. 확대 selector는 footer에 적용되지 않는다.
## 수정 파일
기존 파일 **6개**를 변경하고 **2개**를 추가했다. 기존 미커밋 package.json/package-lock.json 및 OrgChart 작업은 보존했다.
1. [employee-card.ts](../../packages/org-chart/src/core/employee-card.ts): readOnly 상태 카드의 공통 compact 콘텐츠 DOM, 표시 뱃지 공백 제거. 공개 API 변경 없음.
2. [employee-card.css](../../packages/org-chart/src/styles/employee-card.css): 요청 Typography, 일반/상태 공통 스타일, 카드 한정 로고 확대·위치.
3. [org-chart-readonly.css](../../packages/org-chart/src/styles/org-chart-readonly.css): 일반 2Depth/상태 타이틀 Typography 변수 공유, 대형 로고 top 조정.
4. [ui-composition.md](ui-composition.md): P3.2 최신 계약 추가. 과거 절 보존.
5. [card-design-stage2.md](card-design-stage2.md): P3.2 우선 안내 추가. 과거 절 보존.
6. [Core README](../../packages/org-chart/README.md): P3.2 현재 규격·적용 범위 추가.
7. 신규 [verify-p3.2.mjs](../../examples/org-chart/verify-p3.2.mjs): 변경 전 capture, 변경 항목 집중 검사, 정책 측정 보완 모드.
8. 신규 이 보고서.
관련 Core ESM/IIFE/타입/CSS, Wrapper, HTML example dist, React production dist만 재생성했다. 기존 package manifests/lock·JSON·승인 카드 SVG·로고 PNG·공통 사람 아이콘·Storybook 소스·Theme는 보존한다. 기존 파일 해시와 6개 변경 파일 diff는 [source-changes](../../test-results/org-chart-p3.2/source-changes.json) 및 같은 디렉터리의 diff/에 기록했다.
## 검증 결과
- **PASS**: [집중 브라우저 431/431](../../test-results/org-chart-p3.2/browser.json). HTML HTTP, 직접 file:// classic, React production HTTP, 실제 Storybook의 **1920×1080 / 1440×900 / 1280×800 / 768×1024 / 390×844**, 총 20 captures. 요청 Typography·상태 규격·로고 1.5·카드 크기·Gap·사진 아이콘·집계·하단 로고·경영진 뱃지 미표시를 확인했다.
- **PASS**: 모든 화면의 document scrollWidth=clientWidth, scrollHeight=clientHeight. 모든 카드와 콘텐츠는 Stage 안에 들어간다. 기존 전체 자동 축소 정책을 유지한다. 390px는 기존처럼 전체 비례 축소되어 글자가 작다.
- **PASS**: 대표자·1Depth·소형·파견·휴직 팝업을 네 실행 경로의 데스크톱/390에서 확인했다. 원문 정보·축소하지 않는 팝업·Tab focus trap·ESC 카드 focus 복귀 유지. HTML/React의 1Depth 링크 설정과 실제 Enter navigation 확인.
- **PASS**: 같은 Core의 카드 Typography·위치·크기가 네 실행 경로에서 동일하다. Storybook 사용자 메뉴는 기존 세 개다. Core/Wrapper/Storybook 공개 계약·메뉴를 추가하지 않았다.
- **PASS**: [정책 보완 13/13](../../test-results/org-chart-p3.2/policy-browser.json). 초기 검사 도구의 배경/화살표 selector는 빈 shell/잘못된 attribute를 측정했다. 구현은 그대로 두고 selector만 바로잡아 readOnly·collapse 0·#F5F8FB·연결 path 5·협업 arrow 5를 네 경로의 1920에서 별도 확인했다. 통과한 431건은 다시 실행하지 않았다.
- **PASS**: Core/Wrapper Typecheck, Core ESM/IIFE/타입/CSS Build, Wrapper Build, HTML prepare, React 소비 production Build. 로그는 test-results/org-chart-p3.2/의 typecheck-*.log/build-*.log/prepare-html.log. 기존 inline image chunk 약 2.54MB의 Vite 경고는 유지된다. 신규 브라우저 console/runtime 오류 **0건**.
- **PASS**: 구현 세 파일의 before 대비 코드 검토에서 추가 수정 필요 사항 없음. 실제 [1920 Storybook](../../test-results/org-chart-p3.2/storybook-1920x1080.png), [1920 React](../../test-results/org-chart-p3.2/react-1920x1080.png), [390 Storybook](../../test-results/org-chart-p3.2/storybook-390x844.png) 육안 확인.
- **PASS**: 보존 스냅샷의 기존 **266개 중 260개 동일**, 예정한 **6개만 변경**. Workdesk **45개 모두 동일**, 기존 P3.1 증거 **115개 모두 동일**. JSON·직원 41명·원본 자산 보존. [변경 전 스냅샷](../../test-results/org-chart-p3.2/baseline-preservation.json), [최종 보존 결과](../../test-results/org-chart-p3.2/source-changes.json).
- **PARTIAL / NOT VERIFIED**: [PARTIAL Guard 원본](../../test-results/org-chart-p3.2/guard-react.json)은 staticStatus **NOT APPLICABLE**, status **NOT VERIFIED**, RENDER-002를 유지한다. p1 React bootstrap 소비 graph 검사이며 내부 Core Shadow CSS의 전체 정적 인증을 의미하지 않는다. 원본/결합 CSS는 **STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED**. [별도 Runtime](../../test-results/org-chart-p3.2/runtime-evidence.json) 및 [결합 증거](../../test-results/org-chart-p3.2/linked-evidence.json)의 runtimeStatus는 **PASS**, 이름 있는 computed CSS 측정은 PASS다. 원본 미검증을 덮어쓰지 않았다.
- **FAIL**: 최종 요청 범위에서 **0건**. 최초 baseline의 Storybook 연결 실패는 sandbox/server 실행 환경 차이였다. 서버 실행 환경을 맞춘 뒤 baseline 20개 capture를 완료했다. [초기 진단](../../test-results/org-chart-p3.2/baseline-browser-initial-server-isolation.json)은 보존했다.
## 미해결·검증 한계
요청한 fixture의 1.5배 크기 제한이나 구현 미해결 사항은 없다. 다만 임의의 새 데이터/외부 Theme/다른 브라우저, 운영 Workdesk 전체 픽셀 일치, 전체 Storybook production Build, 스크린리더 발화·실물 touch는 **NOT VERIFIED**다. 기존 P3.1 전체 486건을 현재 결과로 재인증하지 않았다.
PNG alpha bounding box와 텍스트 Range/프로필의 교차는 검사했다. Noto Sans KR의 인접 행 Range 외곽은 line-height보다 커 겹칠 수 있으므로 이를 곧바로 글자 충돌로 판정하지 않았다. 실제 글리프 ink 간 교차를 자동 인증한 것은 아니며 이 항목은 **NOT VERIFIED**다. 대표 화면은 육안으로 읽힘·로고/글자 분리를 확인했다.
## 작업 시간·Git
- Started At: 2026-10-12 16:34:08 KST
- Completed At: 2026-10-12 16:51:16 KST
- Actual Duration: 17분 8초
- Estimated Duration: 25분
- Variance: 7분 52초 단축
- 작업 폴더: D:\디자인시스템\HMDesign-OrgChart
- 브랜치: feature/orgchart-package
- 시작/완료 HEAD: d18e3c33801e165e17b6d971e2f6198eed483c3c (동일)
- 원본 Workdesk·운영 HMDesign main 미수정. Stage·Commit·Push·Merge·Reset·Rebase 없음.
@@ -0,0 +1,54 @@
# HMDesign OrgChart P3 — 디자인 및 조회 기능
RESULT: **PASS — 보존된 41명 fixture와 P3 변경 범위**.
HTML·직접 열기 classic·React·실제 Storybook에서 P3 검사 **732/732 PASS**, 신규 브라우저 오류 **0**. 기존 P2·P1 검사를 재실행하거나 기대값을 바꾸지 않았다. Guard의 원본 미검증은 별도로 보존한다.
1. **P2 대비 수정**: 조회 배경, 하위 연결선, 이름/직위 표시, 직책 뱃지, 타이틀 간격, 하위 조직 제목, 허브, 사진 슬롯, 상태 카드, 조직별 교차표, 관계사 바, 링크, 정보 팝업, 컨테이너 높이 맞춤 및 Storybook 메뉴를 수정했다. 조직 관계·순서·41명 데이터와 승인 자산은 유지했다.
2. **Workdesk 반영**: 읽기 전용 원본 `D:\디자인시스템\gpd-workdesk\sample\조직도_v1.3`의 실제 HTML/CSS/SVG를 확인했다. 배경 `#F5F8FB`, 하위 타이틀의 크기·그라데이션·그림자·글꼴 수치, 직책 뱃지의 팀별 색, 허브 SVG/텍스트, 통계 표 및 관계사 바를 반영했다. [원본 수치·경로 증거](../../test-results/org-chart-p3/workdesk-summary-source-evidence.json)를 보존했다.
3. **카드·타이틀·뱃지·허브**: 대표자 146×74, 1Depth 조직장 146×68, 2Depth 조직장/일반 직원 108×52px 유지. 조회 이름은 `홍 길 동`, 직위와 직책은 원본 문자열이다. 직위와 직책 뱃지를 한 줄에 gap 4px로 놓고 이름과의 gap 0px 유지. 모든 타이틀↔첫 카드 gap 8px. 하위 제목 표면은 114×30.4px이며 부모 팀 색을 상속한다. 허브는 원본 말풍선 SVG, 아이콘 20px/gap 8px, 텍스트 21px/750/24px. 대표자 및 긴 디비전장 뱃지는 동일한 22px 행 내부 하단 정렬로 승인 SVG 우상단 로고와의 충돌을 피했다.
4. **연결선**: 조회 전용에서 엔진의 하위 pseudo 연결선을 숨겼다. 기존 대표자↔허브↔주요 조직 연결 SVG Path 5개, 협업 화살표 5개 및 장식은 유지했다. interactive 연결선·접기 동작은 유지했다. 선을 지우거나 조직 관계를 바꾸지 않았다.
5. **사진/상태**: 사진 없음은 비원형의 빈 직사각형 슬롯으로 표시한다. 실제 직원 사진이나 임의 인물 이미지를 사용하지 않았다. 기존 승인 카드의 예약 너비 36/32/24px를 유지하고 높이를 62/56/44px로 조정했다. 조회 compact 파견·휴직 카드의 이름 하단 상태 문구만 숨기고 그룹 제목과 원본 상태 데이터는 유지한다.
6. **조직별 표 데이터**: 원본 employees에서 `조직 × 직위`를 계산한다. 최상위 직접 소속 1명과 주요 6개 조직의 하위 포함 집계를 분리해 중복 없이 41명. 행 소계는 총괄기획실 1, 사업관리 2, 경영기획 7, 인재성장 7, 통합시스템 10, 디자인기획 10, 솔루션영업 4. 직위 합계는 경영진 1/수석 6/책임 12/선임 11/연구 11. 독립 PositionSummary 공개 API는 유지한다.
7. **관계사 집계**: 기존 회사 로고를 사용한 제목 없는 한 줄 바. 한맥 11, 삼안 11, 장헌 0, 장헌산업 0, 피티씨 9, 한라 0, 바론 10, 총 41명. 바 높이 약 55.4px, 로고 39.3px, 글꼴 19.6px 및 원본 간격·그림자 반영. DOM의 43개 카드는 상태 요약 2개 중복 표시를 포함하며 직원 수는 41명이다.
8. **1Depth 링크**: 공개 `organizationLinks?: Readonly<Record<string, string | undefined>>` 옵션을 Core와 React에 추가했다. 원본 조상 관계가 1Depth이며 URL이 지정된 제목만 링크다. 미지정 제목은 일반 제목. HTTP(S)/상대 URL을 지원하며 다른 scheme·credentials는 거부한다. 기본 실제 URL은 넣지 않았다. 검증에서만 로컬 증명 경로를 사용해 실제 이동·키보드·focus 및 카드 이벤트 분리를 확인했다.
9. **직원 팝업**: 대표자/1Depth/2Depth/일반/파견/휴직 카드 click·Enter·Space로 조회한다. 기존 데이터의 이름·직위·소속·직책·관계사·상태만 표시한다. 닫기/ESC/focus trap/원래 카드 focus 복귀 확인. 팝업은 Stage 밖 native dialog top layer로 표시되며 transform 없음, 데스크톱 폭 420px/390px 화면 폭 358px. 공개 `onEmployeeClick(employee, context)` 지정 시 기본 팝업 대신 호출한다. React callback의 최신 값과 동시 이벤트를 검증했다. 기존 tooltip은 팝업 직전 정리한다.
10. **Scale/Overflow**: 기준 디자인 1920×914.5px. 실제 host·조상 높이·padding·max-height와 viewport를 사용하며 임의 Header 48px 차감 없음. 네 경로 모두 같은 측정값이며 문서 client/scroll 폭·높이 동일, 카드/주요 영역 사각형 충돌 및 Stage 밖 콘텐츠 0건.
- 1920×1080: scale 0.9833333333, Stage 1888×899.2584px, 문서 1920×1080.
- 1440×900: scale 0.7333333333, Stage 1408×670.6334px, 문서 1440×900.
- 1280×800: scale 0.6500000000, Stage 1248×594.4250px, 문서 1280×800.
- 768×1024: scale 0.3833333333, Stage 736×350.5583px, 문서 768×1024.
- 390×844: scale 0.1864583333, Stage 358×170.5161px, 문서 390×844.
- 추가 높이 조건: 1920×650 scale 0.6754098256, Stage 높이 617.6623px. 고정/max-height 400px host scale 0.4371584574, Stage 높이 399.7814px. 재부착된 400px host의 외부 20px/내부 12px padding에서 가용 높이 336px, scale 0.3672130903, Stage 높이 335.8164px.
Stage의 layout scroll extent는 transform 전 1920×915px로 남는다. 실제 변환된 모든 콘텐츠 경계가 가용 영역 안에 들어오는 것을 별도로 확인했으므로 `overflow:hidden`만으로 잘림을 숨긴 결과가 아니다. offsetHeight 915는 CSS 높이 914.5의 정수 반올림이며 높이 계산은 소폭 안전 여유를 둔다.
11. **Storybook**: 사용자 메뉴는 `Logo · Personnel Summary` / `Department Colors` / `Full Composition` 3개. 통합 페이지에서 기존 Logo·PositionSummary·AffiliationSummary Core를 각각 렌더링한다. 기존 독립 검증 Story는 internal로 보존했다. 실제 6041 index·manager·페이지를 확인했으며 메뉴 통합과 Full Composition 배치는 분리했다.
12. **수정 파일**: 기존 소스/문서 27개와 신규 구현·검사·보고 파일은 아래 목록. 재생성된 dist 및 정확한 시작 해시 차이는 [전체 파일 기록](../../test-results/org-chart-p3/source-changes.json)에 있다. 기존 package.json/package-lock.json의 미커밋 변경은 추가로 수정하지 않았다.
- Core: `packages/org-chart/src/core/{employee-card,status-summary,render,types,index,org-chart-content,organization-totals,affiliation-bar}.ts`, `src/index.ts`, `src/styles/{employee-card,org-chart-readonly,org-chart}.css`, `build.mjs`, `README.md`.
- 신규 Core: `packages/org-chart/src/core/employee-info-dialog.ts`, `src/styles/employee-info-dialog.css`, `src/styles/org-chart-p3-summary.css`.
- React: `packages/org-chart-react/src/{OrgChart.tsx,types.ts}`, `README.md`.
- 예제: `examples/org-chart/{p1-app.js,p1.css}`, 신규 `verify-p3.mjs`; `examples/org-chart-react/{p1.tsx,p1.css,README.md}`.
- Story: `src/org-chart/{org-chart-p1.stories.tsx,org-chart-stories.css}`.
- 문서: `docs/org-chart/{integration,ui-composition,card-design-stage2}.md` 및 이 P3 보고서. P1/P2 보고서는 수정하지 않았다.
- 산출물: Core/Wrapper/HTML/React dist, `test-results/org-chart-p3`의 브라우저 JSON·원본 Guard·별도 runtime/linked evidence·진단 이력·캡처·시작/변경 해시. `assemble-evidence.mjs`는 저장된 증거만 연결하며 테스트를 재실행하지 않는다.
13. **HTML·React 검증**: Core/Wrapper typecheck, Core ESM/IIFE/타입/CSS 및 Wrapper build, HTML prepare, React 소비 예제 production build PASS. 공통 Core의 geometry·표·카드가 HTML HTTP/file classic/React HTTP/Storybook에서 일치했다. Chrome 155.0.8059.39의 5개 화면, 링크/callback/키보드, 6종 카드×2화면×4경로 팝업 48개 측정, 높이 제한 조건을 포함해 **732/732 PASS**. 원본 이미지 inline 때문에 React 약 2.54MB chunk 경고는 유지한다. 실제 테스트는 live Storybook 6041이며 오래된 `storybook-static` 결과를 최종 근거로 사용하지 않는다.
14. **기존 PASS/신규 FAIL**: 승인 SVG·로고·장식·41명 fixture·P1/P2 보고서·기존 검사 등 보존 대상 38개 해시 동일. 관련 interactive/독립 API/공통 Core 회귀는 P3 검사에서 PASS. P2 484건과 오래된 verify/verify-p1 suite 전체는 재실행하지 않았으므로 전체 기존 PASS를 재인증하지 않는다. 알려진 과거 FAIL 기록·기대값을 그대로 보존했다. 초기 P3 522건/63 FAIL과 중간 554건/10 FAIL JSON도 보존했으며 최종 신규 FAIL 0. 제품의 focus 복귀/뱃지 겹침/재부착 host padding 오류 및 검사 환경의 오래된 Storybook 경로·선택자·Chrome 0px letter-spacing 직렬화 근거를 수정했다. 실패를 필터링하지 않았다.
15. **남은 시각적 차이**: 원본 Pretendard Variable 대신 기존 승인 글꼴을 유지한다. 원본 사진 55×76/54×70/38×52는 승인 카드보다 커서 그대로 복사하지 않고 슬롯 크기를 맞췄다. 긴 뱃지 행 정렬은 로고 충돌 방지를 위한 조정이다. 표의 실제 열/행은 현재 fixture를 기반으로 하며 원본 API의 직위 목록은 제공되지 않았다. 긴 `경영진`/`통합시스템 Div` 줄바꿈으로 표 높이는 약 200.375px. 바 폭은 기준 좌표에 맞춰 1806px(원본 약 1805.7px). 최종 Storybook 1920/390 및 HTML 1920 캡처를 육안 확인했다. 모바일은 전체 화면 비례축소로 글자가 작지만 정보 팝업은 축소하지 않는다.
16. **Guard/NOT VERIFIED**: PARTIAL Guard를 최종 React bootstrap에 1회 실행. 원본 staticStatus `NOT APPLICABLE`, status `NOT VERIFIED`, `RENDER-002` 보존. 원본 CSS STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED, checks `[]`. [별도 runtime](../../test-results/org-chart-p3/runtime-evidence.json)은 동일 소스·계약 fingerprint와 브라우저 증거 SHA로 연결했다. [결합 증거](../../test-results/org-chart-p3/linked-evidence.json)는 Runtime PASS·명시한 computed CSS PASS이나 Guard의 정적 적용 범위/스타일 미검증 때문에 전체 status NOT VERIFIED를 유지한다. Core Shadow CSS 전체 Guard 인증, 실제 운영 Workdesk 픽셀 일치, 자동 glyph/곡선 교차 검사, 모든 임의 데이터/테마/브라우저, screen reader 발화·실물 touch, 전체 Storybook production build는 NOT VERIFIED.
17. **Git/원본 보존**: `D:\디자인시스템\HMDesign-OrgChart`, `feature/orgchart-package`, HEAD `d18e3c33801e165e17b6d971e2f6198eed483c3c`. Workdesk 원본과 HMDesign main을 수정하지 않았다. 기존 미커밋 작업을 보존하고 Stage·Commit·Push·Merge·Reset·Rebase를 수행하지 않았다.
검증 근거: [최종 browser JSON](../../test-results/org-chart-p3/browser.json), [1920 캡처](../../test-results/org-chart-p3/storybook-1920x1080.png), [390 캡처](../../test-results/org-chart-p3/storybook-390x844.png), [원본 Guard](../../test-results/org-chart-p3/guard-react.json), [초기 진단](../../test-results/org-chart-p3/browser-initial.json), [중간 진단](../../test-results/org-chart-p3/browser-after-card-fixes.json).
미리보기: [Storybook Full Composition](http://127.0.0.1:6041/?path=/story/orgchart--full-composition).
작업 시간:
- Started At: 2026-10-12 15:19:27 KST
- Completed At: 2026-10-12 15:56:54 KST
- Actual Duration: 37분 27초
- Estimated Duration: 45~60분
- Variance: 예상보다 7분 33초~22분 33초 단축
@@ -0,0 +1,72 @@
# HMDesign OrgChart v0.1 구현·최종 검증 보고
RESULT: **PASS** — 이번 요청의 Core/JSON/Vanilla 최소 완료 조건 12개 충족. React Wrapper·Storybook 및 미검증 후속 항목을 포함한 전체 MVP 완료 판정은 아닙니다.
## 개발 환경과 보존
- 작업 경로: `D:\디자인시스템\HMDesign-OrgChart`
- 브랜치: `feature/orgchart-package`
- 작업 전/후 HEAD: `d18e3c33801e165e17b6d971e2f6198eed483c3c` (동일)
- origin(fetch/push): `https://gitea.hmac.kr/b25027/HMDesign.git`
- Local main, 공식 원격 main/Feature도 위 SHA 그대로. 운영 `D:\디자인시스템\HMDesign` Working Tree 깨끗함을 작업 전/후 읽기 전용으로 확인.
- 시작 시 기존 미추적 파일: `docs/org-chart/*.md` 7개. 모두 SHA-256 일치 확인(7/7); 삭제·초기화·전면 수정 없음.
- 기존 `packages/ui`, `packages/center-graphic`, Theme/Token/Storybook/AGENTS 및 Spike 수정 없음. 최종 tracked diff는 루트 package.json·package-lock.json뿐.
- Stage/Commit/Push/Merge 없음. Staged diff 비어 있음. main/운영 프로젝트 수정 없음.
- 수행 방식: Track A(Data), Track B(Core/CSS) 병렬 구현 + 읽기 전용 검토 작업자. Main만 package/export/build/examples와 최종 통합을 수정·관리함.
## 구현 결과
1. 신규 패키지: `packages/org-chart`, `@hm-design/org-chart@0.1.0`. React/React DOM peer·런타임 의존성 없음.
2. 직접 의존성: `orgchart@6.0.0` 고정. upstream 전이 의존성 `html2canvas@1.4.1`, `json-digger@2.0.2`, `jspdf@4.2.1` 및 그 의존성이 lockfile에 추가됨. OrgChart 외 무관한 직접 의존성 추가 없음. export UI는 비활성화하며 export 라이브러리는 브라우저 번들에 포함되지 않음. upstream MIT 고지를 배포 산출물에 포함.
3. Data Contract: 조직/직원/관계사를 ID로 분리하고 문서의 `displayOrder/jobTitle/profileImage/workStatus` 유지. 버전은 문서 초안의 `0.1-draft`. 최신 요청에 따른 최상위 schemaVersion 필수 추가; metadata.schemaVersion은 선택 호환 mirror로 일치 검사. relations/presentation 및 일부 metadata 확장 필드는 선택. 조직·직원 ID namespace는 독립.
4. JSON Source: 객체 또는 주입 URL, 선택 fetch를 지원. 객체는 생성 시 캡처하고 load마다 독립 불변 snapshot 반환. HTTP/JSON/취소 오류를 구별하고 서버 원문을 노출하지 않음. 기본 native fetch를 globalThis에 바인딩. 외부 JSON은 HTTP에서 검증; file:// 예제는 inline JSON 사용.
5. Validation: 필수 값·타입·버전·날짜·중복 ID·참조·parent cycle·leader/관계사·상태·관계·presentation·위험한 HTML/이미지 URL 검증. 구조화된 code/path/message 반환. 외부 문자열은 textContent로 렌더링하고 engine ID는 내부 prefix+숫자로 생성.
6. Summary: 고유 직원 ID 기준 raw total, 조직 direct/inclusive, 직위·관계사·상태 집계. 조직 이동/추가/삭제와 표시 옵션 독립성 테스트. 업무상 총원 포함 정책을 기본값으로 숨기지 않으며 미주입은 policyRequired, 미지원 정책은 unsupportedPolicy. 지원 정책 범위는 명시적 primaryOnly·leader 포함 및 유효 상태 목록. raw 숫자는 승인된 재직 총원이 아님.
7. Core: createOrgChart, mount(container?), update, reload(source?), destroy, subscribe, getState. 마지막 시작 revision 우선·AbortSignal·immutable 입력·최종 commit 검사·재진입 callback 방어. mount 중 CSS load/error를 확인. 요청 및 data 실패는 error state, 잘못된 호출 순서는 사용 오류. destroy는 동기·반복 안전·terminal.
8. Node UI: 조직·부서, 조직장·직원, 이름·직위·직책·상태, 사진/fallback, 원본 조직별 인원. 계층 연결선과 native 버튼 키보드 확장/축소. 여러 root는 독립 엔진으로 실제 조직 관계를 보존. 갱신 시 확장 상태는 초기화; 제거된 focus는 viewport로 복귀.
9. Theme: 호스트의 기존 HM Theme 변수/폰트를 상속. node와 연결선은 기존 Semantic 변수, radius/font 사용. Shadow DOM 안에 upstream engine.css와 전용 style.css를 로드하여 전역 selector 누출 방지. 예제 Theme 산출물은 실제 공개 CSS Entry에서 생성하고 기존 폰트/OFL을 포함; 독립 유지되는 Theme 복제본 없음.
10. Export: ESM index.js + index.d.ts, style.css, engine.css, classic browser subpath. IIFE는 window.HMOrgChart. 브라우저 번들에 unresolved bare import 없음. stylesheets의 실제 URL을 호스트가 stylesheetUrls로 주입해야 함.
11. 예제: `examples/org-chart/dist/index.html`(HTTP ESM/외부 JSON), `classic.html`(직접 열기/Classic inline JSON). build outputs/public Entry만 소비하며 Spike 코드 참조 없음. reload·이동/추가 update·destroy·새 instance 생성 controls. 가상 조직·직원만 사용.
## 최종 검증
- **PASS** TypeScript: `npm run typecheck --workspaces --if-present` — OrgChart/UI/Center Graphic 모두 통과. root 앱 production build를 실행한 결과로 해석하지 않음.
- **PASS** Package Build: ESM 123.50 kB(gzip 29.36), IIFE 126.34 kB(gzip 29.55), 선언/CSS/license 생성.
- **PASS** 데이터 테스트 22/22: 계약/참조/순환/URL/정책/추가·삭제·이동/중복 방지/불변성/취소·늦은 응답/native fetch receiver 회귀.
- **PASS** Chrome 155.0.8059.39 실제 브라우저 검사 69/69: HTTP ESM + file:// Classic 각각 mount/custom node/summary/reload/update; 네 너비 document overflow·내부 scroll geometry·HM color/font/radius·1줄 ID·Theme 연결선 정렬; keyboard Enter/Space·focus 복귀; depth-3 확장 state; 두 instance/Scoped Theme; source/validation error·latest revision·reentrant destroy·late callback 차단; 5회 mount/reload/destroy 자원 안정성. Console/runtime errors 0.
- **PASS** Destroy 관찰 범위: 전용 DOM 제거·호스트 sibling과 다른 instance 보존·chart observer 0·전역 listener 수 안정·요청 abort·destroy 이후 callback 0. Playwright 자체 observer/listener를 chart 자원과 구분함. 전체 heap 회수를 판정한 결과가 아님.
- **PASS** npm tarball consumer: 실제 tarball 추출, DOM 없는 public ESM import, public CSS/browser export, 엄격한 NodeNext 타입 소비(React types 없음), fixture validation/summary. React가 미설치된 사용자 npm 환경에서 새로 dependency install하는 테스트까지 수행한 것은 아님; HTML은 React를 로드하지 않음.
- **PASS** 기존 main/설계 문서/패키지 소스 보존, Git diff --check, 문서 링크 검사(119 files/620 links/problems 0).
- **NOT VERIFIED** AI Guard 원본 status/staticStatus/runtimeStatus. 1회 PARTIAL 실행에서 ENTRY-001/SCOPE-001, checkedFiles=[]: Vanilla package가 React consumer graph 전제에 포함되지 않음. Guard/Theme/Component 규칙 수정 없음. 원본 styleEvidence는 STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED, checks=[]이며 비어 있는 검사 PASS를 디자인 승인으로 사용하지 않음.
- 별도 CSS 증거: 실제 선언 존재 확인 PASS, 위 제한된 browser computed·overflow/interaction PASS. **DESIGN_INTENT와 FINAL은 NOT VERIFIED**: reference 디자인 승인·전체 시각 parity를 주장하지 않음.
검증 증거: `test-results/org-chart-browser/report.json` 및 HTTP/file:// 네 너비 PNG, `test-results/org-chart-consumer/report.json`, `test-results/org-chart-preservation.json`, `test-results/org-chart-pack.json`. 이 생성물은 Git ignored이며 source test harness는 보존함.
## 변경 내역
- 수정: `package.json`(6개 OrgChart 실행 scripts), `package-lock.json`(workspace link와 upstream dependency graph). 이미 등록된 packages/* workspace는 변경하지 않음. 기존 @babel/runtime의 dev 표시 제거는 새 production 전이 dependency 영향이며 버전 교체가 아님.
- 신규 패키지: package.json, README.md, tsconfig.json, tsconfig.build.json, build.mjs, test.mjs, src/index.ts; data/{types,index,validate,normalize,summary,snapshot,source,data.test}.ts; core/{types,index,engine,render}.ts, core/vendor.d.ts; styles/org-chart.css.
- 신규 예제: index.html, classic.html, app.js, demo.css, data-v1.json, data-v2.json, prepare.mjs, serve.mjs, verify.mjs, verify-package.mjs, create-fixtures.mjs.
- 신규 보고서: 이 파일. 기존 7개 설계 문서는 수정하지 않음.
- dependency install은 초기 sandbox DNS 실패 후 승인된 실행으로 완료. npm cleanup에서 ignored node_modules 일부 EPERM 경고가 있었으나 설치 exit 0이며 후속 typecheck/build/runtime 모두 통과.
- 공식 tag 웹 조회는 실패했으므로 npm 6.0.0 배포물과 특정 upstream git tag의 byte 동일성은 **NOT VERIFIED**. 설치 manifest와 lock integrity를 기준으로 버전을 고정함.
## 설계 정합성과 후속 작업
- Architecture/Framework Integration의 공통 데이터→검증→ViewModel→engine→DOM 및 terminal lifecycle 유지. 공개 target snippet에 필요한 CSS URL 계약 추가와 schemaVersion 위치 변화는 위에 명시. 기존 문서의 구현 전/미착수 상태는 과거 기록으로 보존; 신규 package README/이 보고서가 현재 실행 계약.
- 다중 root는 임의 parent 없이 forest 렌더링. leader cross-organization은 정책 미정으로 LEADER_APPROVAL_REQUIRED. 조직 0개는 오류, 직원 0명은 valid ready.
- hubText/showDispatched/showOnLeave는 reserved 필드이며 별도 허브/상태 영역 UI는 이번 최소 범위에서 구현하지 않음. showEmployees/showSummary/showAffiliations는 실제 지원. 추가 관계는 검증하고 계층선 미포함 안내만 표시; 복잡한 relation overlay 제외.
- pan/zoom/minimap/drag/export 비활성화. 390px은 읽을 수 있는 원래 크기와 내부 가로 스크롤로 표시; 고급 반응형 처리·확장/scroll state 보존 제외.
- React Wrapper는 ref/effect·Props/Source→동일 Core·unmount cleanup만 연결. Validation/집계/engine/node/style 중복 구현 금지. Storybook은 이후 가상 fixture·error/loading·policy·Theme scope·lifecycle 콘텐츠 Story로 구성.
- 기술적 리스크: 6.0.0 내부 init observer hook에 의존하는 managed subclass. 버전 upgrade 전 재검토 필요. upstream anonymous node/transition 이벤트는 제거된 DOM과 함께 GC 가능한 참조가 되며 완전 GC·heap 회수는 **NOT VERIFIED**. 매우 큰 데이터의 성능/깊이 상한·보안 CSP/origin 정책·실제 API/auth·screen reader announcements·reference 시각 일치·Theme 미연결 동작·React runtime은 **NOT VERIFIED**.
- Commit/Push/Merge는 요청대로 보류. 이후 사용자 승인 없이 수행하지 않음.
## 시간 기록 (Asia/Seoul)
- Started At: 2026-10-12 10:00:28 KST
- Completed At: 2026-10-12 10:19:19 KST
- Actual Duration: 18분 51초
- Estimated Duration: 최초 약 2~3시간
- Variance: 예상 하한 2시간 대비 101분 9초 단축; 예상 상한 3시간 대비 161분 9초 단축
예상은 참고값입니다. 기존 PoC/설치 환경 재사용과 병렬 구현으로 실제 시간이 줄었으며 미검증 항목을 PASS로 대체하지 않았습니다.
+91
View File
@@ -0,0 +1,91 @@
# OrgChart Integration
상태: 공식 문서·소스 읽기 기반 기술 검토. 설치·실행·번들·브라우저 PoC는 수행하지 않았다. 모든 지원 완료 판정은 **NOT VERIFIED**다. 최신 범위는 Vanilla HTML/JavaScript + React이며 [Framework Integration](framework-integration.md)에서 같은 Core를 연결한다.
## 1. 엔진 버전·라이선스·근거
확정 엔진은 [dabeng/OrgChart](https://github.com/dabeng/OrgChart)다. 별도 프로젝트 `react-orgchart`를 채택한 것으로 해석하지 않는다. 확인일은 2026-10-12(KST)이며 웹 자료는 이후 변경될 수 있다.
- [공식 릴리스 목록](https://github.com/dabeng/OrgChart/releases)은 6.0.0을 최신으로 표시하고 v6부터 native JavaScript로 전환하여 jQuery 의존성을 제거했다고 설명한다. **6.0.0을 우선 검증 후보로 제안**하되 지원/고정 버전 확정은 아니다. 구버전 jQuery 연동 예제를 그대로 사용하지 않는다.
- 확인한 [master package.json](https://raw.githubusercontent.com/dabeng/OrgChart/master/package.json)의 version은 6.0.0, main은 `dist/js/orgchart.min.js`, dependencies는 html2canvas/json-digger/jspdf다. 이 의존성을 Core의 필수 기능 또는 배포물에서 자동 제외 가능하다고 단정하지 않는다. Export/download는 요청되지 않았으므로 UI에는 추가하지 않으며 transitive bundle 영향은 조사한다.
- [공식 LICENSE](https://raw.githubusercontent.com/dabeng/OrgChart/master/LICENSE)와 manifest는 MIT를 표시한다. 엔진 재배포 시 고지·라이선스 포함을 계획하고 실제 배포 artifact의 라이선스와 transitive 의존성은 별도로 확인한다.
- [README](https://github.com/dabeng/OrgChart#readme), [JS 소스](https://raw.githubusercontent.com/dabeng/OrgChart/master/src/js/orgchart.js), [CSS 소스](https://raw.githubusercontent.com/dabeng/OrgChart/master/src/css/orgchart.css), [데모 index](https://dabeng.github.io/OrgChart/)를 읽었다. 데모를 브라우저에서 실행하거나 화면을 대조하지 않았다.
- v6.0.0 tag의 raw 파일 조회와 release API 조회는 도구에서 실패했다. 따라서 **master 소스와 고정 tag/npm 배포물의 동일성은 NOT VERIFIED**다. 구현 전에 배포된 버전·tag·소스 hash·라이선스를 다시 대조하고 설치 후보를 고정한다. npm registry 배포/ESM entry/type 선언 존재도 아직 확인하지 않았다.
## 2. 기능·구조 검토와 책임 분리
아래는 upstream 문서/소스에 기능 경로가 있음을 확인한 결과이며 HMDesign에서의 동작 PASS가 아니다.
- **데이터·계층**: README는 JSON/UL 데이터와 `children` 기반 계층을 설명한다. Core의 flat ID 계약을 Engine Adapter가 파생 트리로 변환한다. engine에 UI 데이터를 원본 그대로 넘기지 않는다.
- **커스텀 노드**: `nodeTemplate(data)`와 `createNode(node, data)` 경로가 있다. 읽은 master는 template 반환값을 문자열 결합하므로 Element 반환 지원을 가정하지 않는다. **정적 skeleton 문자열 + createNode callback의 DOM/textContent 구성**을 우선 검토한다. engine edge/계층을 지우지 않고 콘텐츠 전용 자식만 관리한다. 고정 버전의 callback 순서·크기 측정·확장/축소는 PoC로 확인하며 React root를 노드 안에 만들지 않는다.
- **확장·축소**: show/hide parent/children/siblings 계열 API가 문서화돼 있다. 깊은 조직·custom 노드·focus·키보드와 함께 검증한다.
- **pan·zoom**: pan/zoom과 한계 옵션이 있다. CSS transform·touch·wheel·호스트 스크롤·Overlay 좌표 결합은 미검증이다. 기능 노출과 설정 기본값은 PoC 후 확정한다.
- **가로·세로 배치**: direction/verticalLevel/hybrid/compact 관련 문서가 있다. 일부 조합 제약이 안내되므로 모든 조합 지원으로 쓰지 않는다. 가로/세로 스크롤은 콘텐츠 컨테이너 책임과 함께 검사한다.
- **데이터 갱신**: `init`/`setOptions`의 데이터 교체 경로가 있다. 일관성·성능·확장/zoom 상태 유지까지 보장된 것은 아니다.
JS 소스의 init은 기존 chart DOM을 제거·재생성하고 node 생성은 입력에 parentId를 추가하는 경로가 있다. 템플릿 문자열은 `insertAdjacentHTML` 경로로 삽입되므로 외부 텍스트 직접 주입은 금지한다. Core가 복사한 engine 데이터와 안전한 텍스트/ID를 제공해야 한다. [소스 근거](https://raw.githubusercontent.com/dabeng/OrgChart/master/src/js/orgchart.js).
같은 소스에 이벤트 해제 함수와 Observer 사용은 있으나 공개 완결 `destroy()`는 읽은 소스/README에서 확인하지 못했다. Core의 Destroy는 **engine API가 아니라 우리가 보장해야 할 facade 계약**이다. 인스턴스 제거만으로 document 이벤트·Observer가 정리됐다고 주장하지 않는다. 소스 검토에서 관찰된 정리 한계는 최초 PoC의 차단 조건이며 필요하면 engine 버전/기능 제한/명시적 disposal 지원 또는 승인된 별도 통합 대안을 검토한다. 이번에는 upstream/기존 코드를 수정하지 않는다. [JS 검토 근거](https://raw.githubusercontent.com/dabeng/OrgChart/master/src/js/orgchart.js).
여러 인스턴스는 chartClass 관련 문서만으로 격리를 보장할 수 없다. 컨테이너 Element 전달·인스턴스별 DOM ID·container 내부 query·이벤트·focus·zoom을 실제 검증한다. `.orgchart .node .title/.content` 등 upstream CSS의 고정 크기·색상·generic descendant selector가 custom 콘텐츠와 충돌할 수 있다. engine CSS와 Core CSS를 컨테이너 내부로 제한하고 다른 HM 요소를 비교한다. [CSS 근거](https://raw.githubusercontent.com/dabeng/OrgChart/master/src/css/orgchart.css).
특수 곡선 연결선·중앙 허브·그룹별 자유 카드 배치·교차 조직 relation을 기본 기능만으로 표현할 수 있다는 근거는 확보하지 못했다. **NOT VERIFIED / 별도 PoC**다. 계층 연결선은 engine, 추가 관계는 Relation Overlay, 허브/summary/상태 그룹은 콘텐츠 Layout의 책임으로 나눠 검토한다. fixed 좌표·engine hierarchy 왜곡으로 reference를 맞추지 않는다.
update/reload도 cleanup 대상이다. master의 pan/zoom 해제 경로는 현재 chart에 저장된 handler를 조회하므로 init 후 이전 handler를 회수하지 못할 위험이 있다. **이전 chart 참조를 잃기 전에** pan/zoom·minimap·관련 observer와 이벤트를 정리할 수 있는지 PoC로 확인한다. 반복 재생성 후 container/document 이벤트 수·응답 횟수 증가를 검사한다. 소스상 위험이며 실제 누수 재현은 NOT VERIFIED다.
master는 모듈 평가 시 window/document를 참조하는 경로도 있어 DOM 없는 import 안전성을 주장할 수 없다. Core facade·데이터 계층 import를 engine 로딩에서 분리하고 지연 로딩 또는 승인된 변환 entry를 검증한다. native ESM export가 이미 존재한다고 가정하지 않는다.
## 3. HM Theme 연결
Core 노드는 framework와 무관한 동일 HTML/CSS다. React Wrapper가 다른 노드 디자인을 만들지 않는다. Token 정책은 [Runtime Theme](../guidelines/runtime-theme.md), [Token 가이드](../guidelines/테마-토큰-가이드.md), [Runtime Portability](../guidelines/runtime-portability.md)를 따른다.
확인한 선언은 [default.css](../../packages/ui/src/theme/default.css)의 `--hm-sem-bg-content`, `--hm-sem-fg-primary`, `--hm-sem-fg-secondary`, `--hm-sem-stroke-default`, `--hm-radius-container` 등이다. 노드 surface/상태/연결선의 **최종 매핑은 디자인 승인 전**이며 content 배경 변수를 Card White 면으로 자동 적용하지 않는다. Typography와 spacing은 모든 것이 공개 runtime 입력은 아니다. [card.css](../../packages/ui/src/theme/card.css), [avatar.css](../../packages/ui/src/theme/avatar.css), [table.css](../../packages/ui/src/theme/table.css), [tailwind.css](../../packages/ui/src/tailwind.css)의 실제 규칙을 참고하되 없는 `--hm-spacing-*` 같은 Token을 추측하지 않는다.
React 호스트는 기존 CSS entry `index.css → hm-theme.css`와 공개 HM CSS 순서를 유지한다. Core CSS는 별도로 연결한다. Vanilla는 browser-ready Theme CSS와 상대 fonts/assets가 필요하며 Tailwind 원본 CSS/bare npm import를 그대로 `<link>`에 넣지 않는다. [Theme-only 배포](../guidelines/theme-delivery.md)의 현재 `theme-dist/themes`는 JSON 산출물이므로 standalone CSS가 이미 제공된다고 오인하지 않는다.
Core scope는 전용 컨테이너 class 내부로 제한하고 HM 전역 변수 재선언·global reset·HM Component selector override를 금지한다. OrgChart 전용 alias가 필요하면 기존 의미의 변수를 참조하고 scope에서만 선언한다. Theme 없는 환경의 기본 스타일 제공/필수 연결/오류 정책은 TBD다. 임의 fallback 색으로 미정의 Token을 감추지 않는다. 정확한 standalone Theme artifact 경로·포함 범위·CSS import flatten·font 경로는 배포 PoC에서 확정한다.
Vanilla의 dependency/peer에 `@hm-design/ui` 전체 패키지를 추가해 Theme를 해결하면 React/React DOM/Tailwind peer가 설치 조건에 영향을 줄 수 있다. 승인된 호스트 CSS asset 또는 React peer 없는 Theme 산출 경로를 비교하고 React 미설치 consumer에서 실제 설치·동작을 검증한다. 현재 설치 동작은 NOT VERIFIED다.
현재 `packages/ui/src/theme.css` 원본은 전체 Component/Pattern CSS import와 절대 `/fonts/` 경로를 포함한다. Core prefix만으로 호스트 Theme까지 scope된다고 주장하지 않는다. Vanilla Theme는 전체 CSS와 필요한 변수·font 제공 범위를 구별하고 상대 font 경로를 확인한다. Core/engine CSS 누출과 호스트 Theme 자체의 전역 영향을 별도로 기록한다.
Theme 적용/해제의 owner는 호스트다. Core는 제공된 scope를 상속하고 Theme를 자동 주입/reset하지 않는다. 별도 Scoped Theme는 기존 `[data-hm-theme]`에서 mapping을 재계산하는 계약과 Scoped partial의 미지정 값이 HM Default를 쓰는 의미를 따른다. 단순 후손 override로 Global 값을 재계산한 것처럼 간주하지 않는다. Global A + Scoped B + 두 instance의 computed 값·독립성·cleanup을 검증한다.
## 4. JSON에서 REST API로 전환
공통 `OrgChartDataSource.load({signal}) → Promise<unknown>`은 normalized 계약 후보를 반환한다. JSON Mock·외부 JSON·API adapter는 모두 같은 validator·ViewModel·집계·Core renderer를 사용한다. [사용 예시](framework-integration.md#3-데이터-source-사용-예시)를 참고한다.
바뀌는 영역: 호스트의 Source 생성/선택, 실제 API client 설정, 원본 응답 매핑, 취소·권한·이미지 접근·향후 캐시/재시도 정책, Source 계약 테스트. 고정 계약과 다른 응답은 API adapter에서 변환한다.
유지되는 영역: normalized 필드/ID 계약(승인 버전 기준), runtime Validation, 집계 정책과 공통 함수, engine 변환, Core DOM·노드 디자인·Theme CSS, Core facade, React Wrapper의 Props 구조, 콘텐츠 단독 Story. Core나 Wrapper가 서버 URL/원본 필드에 직접 의존하면 완료 조건 실패다.
실제 API가 생겨 schema를 확장해야 하면 Source 전환만으로 끝나는 것처럼 표현하지 않고 contract migration을 별도 승인한다. Mock API Story는 지연/오류/응답 경쟁을 흉내내는 가상 Source이며 실제 서버·인증 검증으로 보고하지 않는다.
여러 endpoint/pagination으로 수집하면 Source Adapter가 완전한 collection과 같은 기준의 스냅샷을 조립해 한 번에 반환한다. 부분 페이지·서로 다른 버전의 조직/직원/관계를 Core에 순차 반영하지 않는다. ID 참조 검사를 통과해도 시간 일관성을 입증한 것은 아니다. 서버 일관성·버전 확인·재시도 방법은 실제 API 명세 전 TBD다.
## 5. API 전환 전 결정 항목
실제 명세가 없어 다음은 모두 TBD다: Endpoint·메서드·응답 schema·인증/인가·데이터 권한, 갱신 주기·캐시/만료·재시도/오류 처리, 프로필 이미지 권한/origin/만료, 개인정보 노출/로그/보존 정책, dataVersion·기준일·updatedAt의 의미와 충돌 처리. 임의 회사 주소·토큰·인증 규칙은 예시에 넣지 않는다.
HTML 환경도 상위 앱 API client가 인증을 담당한다. React 전용 hook/context를 API adapter의 필수 전제로 삼지 않는다. 공유 스냅샷이 같으면 두 환경의 조직 구조·집계·Theme·인터랙션 결과가 같아야 한다.
## 6. P3 조회 API 추가 — 2026-10-12
위 초기 기술 검토 기록은 보존한다. 현재 패키지의 추가 API는 [Core README](../../packages/org-chart/README.md#organization-links-and-employee-information-p3)와 [React README](../../packages/org-chart-react/README.md#public-api)에 기술한다. 런타임 검증 결과는 해당 구현 보고서에서 별도로 확인한다.
- `organizationLinks`는 기존 조직 ID별 외부 설정 URL map이다. 원본 계층의 1Depth 제목에만 적용하며 누락·빈 URL은 일반 제목으로 남는다. 실제 URL을 패키지에서 만들지 않는다. HTTP(S)·상대 URL을 허용하고 다른 scheme·잘못된 URL·credentials는 생성 시 `TypeError`로 거부한다. 링크는 native 키보드 이동·Focus 표시를 지원하며 카드 클릭과 분리된다.
- 대표자·조직장·직원·파견·휴직 카드의 클릭·Enter·Space는 기본 조회 dialog를 연다. `readOnly`에서도 조회하며 수정 UI는 없다. 이름·직위·조직·직책·관계사·근무 상태 중 기존 값만 표시하고 없는 선택 필드는 생략한다. readOnly 가상 표시 이름은 카드의 `홍 길 동`과 일치하며 데이터는 변경하지 않는다.
- 기본 native modal은 축소 Stage 밖의 같은 Core shell에 위치한다. 닫기 버튼·ESC·Focus trap·카드로 Focus 복귀를 처리하고 정보 팝업을 열면 이름 tooltip을 닫는다. HTML과 React는 같은 구현을 사용한다.
- 선택적 `onEmployeeClick(employee, context)`를 제공하면 기본 팝업 대신 호스트 callback을 실행한다. context는 기존 `organization`·`affiliation`, Focus 가능한 `trigger` 카드, `originalEvent`를 담는다. 이 경우 호스트가 팝업 접근성과 정리를 담당한다. 기존 API를 제거하지 않는 추가 옵션이다.
- Vanilla는 생성 옵션으로 전달하고 설정을 변경할 때 새 handle을 만든다. React는 같은 이름의 Props로 전달한다. 새 URL map 또는 callback 유무 변경은 재생성하며, callback 함수 identity만 바뀌면 최신 committed callback을 사용한다. 공개 Core `setOptions`는 추가하지 않았다.
```js
const chart = createOrgChart({
...configuredBaseOptions,
organizationLinks: configuredOrganizationLinks,
// 생략하면 Core 기본 조회 팝업 사용
onEmployeeClick(employee, context) {
openEmployeeInformation(employee, context);
},
});
```
+163
View File
@@ -0,0 +1,163 @@
# HMDesign OrgChart P1 — 품질 보완 및 디자인 검수
RESULT: **PARTIAL**
이번 변경은 기존 P1의 Gradient 대비 검사, Department Colors, 실제 로고 표시, Guard 원인 분석으로 제한했다. 사용자가 최종 확인한 대로 Workdesk 전체 디자인 재현 첨부는 참고 자료로만 사용했다. 전체 배치·카드 구조를 다시 개발하지 않았다. 기존 기능·데이터 계약·공개 API·React Wrapper·공통 Theme/Token·운영 main은 보존했다.
## 작업 정보
- 작업 폴더: `D:\디자인시스템\HMDesign-OrgChart`
- 브랜치: `feature/orgchart-package`
- 전/후 HEAD: `d18e3c33801e165e17b6d971e2f6198eed483c3c`
- 실제 병렬 수행: Track A/B/C 읽기 전용 분석을 병렬 수행했다. 이후 B는 대비 모듈과 Department Colors, C는 두 Summary Story의 가상 데이터 안내, 메인은 로고 렌더링·CSS·검증·보고서를 담당했다. 같은 파일의 동시 수정은 없었다.
- Started At: 2026-10-12 11:55:44 KST
- Completed At: 2026-10-12 12:25:20 KST
- Actual Duration: 29분 36초
- Variance: 최초 예상 하한 45분 대비 15분 24초 단축, 상한 90분 대비 60분 24초 단축. 병렬 분석 및 기존 PASS 재사용으로 재검증 범위를 줄였다.
- Estimated Duration: 45–90분. 필요한 범위만 검사하며 시간을 채우기 위한 재검증은 하지 않았다.
## Track A — Guard: 원인 분석 PASS, 전체 해결 PARTIAL
공통 Guard를 변경하거나 검사 규칙을 비활성화하지 않았다. 실제 `themeAnalysis`에 인메모리 fixture를 전달한 최소 재현 8/8이 통과했다. 이는 **오탐 재현 PASS이며 실제 Guard PASS가 아니다**.
최종 PARTIAL/STORYBOOK Guard 결과는 원본 `status=FAIL`, `staticStatus=FAIL`로 보존했다. TOKEN-001 FAIL 8건, TOKEN-002 NOT VERIFIED 6건, SCOPE-001 NOT VERIFIED 3건이다.
- Shadow CSS 연결 누락 1건: `--hm-orgchart-resolved-bg-surface`는 `design-tokens.css`에 선언되고 공개 CSS로 번들되어 Shadow DOM에 연결된다. Guard graph는 `?url` import와 Shadow link의 연결을 증명하지 못한다.
- `setProperty` 정의 미수집 4건: `current-gradient`, `current-card-background`, `current-border`는 `applyOrganizationStyle`이 정의한다. Guard가 이 DOM 선언을 수집하지 않아 참조를 미선언으로 판정한다.
- 선택적 입력 오분류 3건: 대비 후보의 `--hm-orgchart-contrast-candidate-color` 및 `--hm-orgchart-contrast-candidate-chip-background`는 로컬 fallback을 가진 외부 override 입력이다. 기본값은 각각 `#10243b`, `#ffffff`이며 실제로 정상 적용된다. 공식 Theme 변수의 누락과 동일하게 처리하는 현재 검사 정책은 이 계약을 표현하지 못한다.
- 동적 TOKEN-002 6건: 유한한 조직 preset/legend 문자열 조합을 정적으로 해석하지 못한다.
- CSS SCOPE-001 3건: 카드·콘텐츠·통계 CSS는 실제 Core CSS 번들과 Shadow link에 포함되지만 Storybook graph 범위 밖으로 판정된다.
브라우저에서 Shadow CSS 로딩, 6개 preset의 computed 값, fallback과 HTML/React 동일 렌더링을 확인했다. 이 증거로 raw FAIL을 덮어쓰지 않았다. 검사한 범위에서 실제 Production 미선언 변수 결함은 발견하지 못했다.
`styleEvidence`: STATIC **PASS**, RUNTIME **NOT VERIFIED**, DESIGN_INTENT **NOT VERIFIED**, FINAL **NOT VERIFIED**. 이 값은 Guard의 증거 연결 상태이며 별도 브라우저 PASS와 구분한다. 이번에는 결합 `styleVerification`으로 승격하지 않았다.
승인 필요한 최소 공통 Guard 변경안: 증명 가능한 Shadow CSS 연결, `setProperty` 정의 수집, 유한 후보 해석, 명시적 선택적 입력 계약 지원. Guard 분석기·style inventory/증거 연결·관련 회귀 fixture와 문서에 영향이 있으므로 별도 승인 전에 구현하지 않는다. 전역 CSS 강제 연결, dummy 선언, 변수 namespace 변경, fallback 일괄 면제는 적용하지 않았다.
근거: `test-results/org-chart-quality/guard-reproduction.json`, `guard-storybook.json`. 분석기 SHA 전/후 동일하며 기존 P1 Guard 원본도 유지했다.
## Track B — Color Contrast: PARTIAL
기준은 [WCAG 2.2 Contrast Minimum](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html)의 일반 텍스트 4.5:1, 큰 텍스트 3:1 및 [Non-text Contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html)의 의미 있는 UI/그래픽 3:1이다. 판정에는 반올림 전 값을 사용했다. 로고를 변색하지 않았다.
### 측정 방법과 범위
Core의 제한된 opaque sRGB/세로 Gradient 모델과 실제 Chrome DPR1 PNG를 분리해 기록했다. 모델은 Gradient stop, 전체 구간, DOM Range의 텍스트 line 구간을 구분한다. PNG에서는 텍스트 옆 빈 padding 열의 실제 배경 픽셀과 computed foreground를 사용한다. 안티앨리어싱된 글자 픽셀로 전경색을 추측하지 않았다. 모델과 픽셀 차이가 2 channel 값을 초과하거나 스크롤에 가려지면 NOT VERIFIED다.
지원하지 않는 alpha·복수 배경·다른 각도·다른 색 공간·합성 효과 등은 임의로 PASS하지 않는다. 이번 PNG 검사의 84개 가려진/일치하지 않은 probe는 NOT VERIFIED로 남겼다. 그중 솔루션영업 두 부서명은 마지막 가지로 스크롤한 별도 캡처에서 추가 검증했다. 숫자는 화면별 반복 측정이며 서로 다른 접근성 요구사항의 개수가 아니다. 전체 WCAG 인증으로 확대하지 않는다.
### 기존 HEX와 실제 텍스트 대비
6개 헤더 Gradient와 흰 부서명 `#ffffff`는 Workdesk `v1.2610.9c5e`, 2026-10-12의 기존 computed 근거를 유지했다. 직원 카드 배경은 자체 기본 `#ffffff`이고 테두리 기본은 해당 조직 시작색이다. 카드 면·테두리·geometry는 **DESIGN_ADAPTATION**, 공식 Workdesk 실측으로 승격하지 않았다. 변수 prefix에 `-card-background`, `-department-color`, `-border`, `-gradient`를 각각 사용한다.
- 사업관리: `#194d82 → #1d395c`, `--hm-orgchart-business-management-*`. 흰색 stop 최소 8.659:1. Full 실제 부서명 9.357:1 **PASS**.
- 경영기획: `#2164a8 → #254975`, `--hm-orgchart-business-planning-*`. stop 최소 6.082:1. Full 실제 부서명 약 6.654–6.676:1 **PASS**.
- 인재성장: `#0f6ace → #044a94`, `--hm-orgchart-talent-growth-*`. stop 최소 5.286:1. Full 실제 부서명 약 5.879–5.882:1 **PASS**.
- 통합시스템 Div: `#8c67b6 → #5d3494`, `--hm-orgchart-integrated-system-*`. stop 최소 4.439:1이지만 Full 실제 텍스트 구간은 약 5.008–5.197:1 **PASS**. stop 하나만으로 실제 텍스트를 FAIL 처리하지 않았다.
- 디자인기획: `#09a7a2 → #00847f`, `--hm-orgchart-design-planning-*`. stop 최소 2.974:1. Full의 15px bold 부서명 약 3.188–3.263:1 **REVIEW**. Department Colors의 19.5px bold 제목은 약 3.228:1로 큰 텍스트 기준 **PASS**, 일반 크기 비교는 약 3.263:1 **REVIEW**다. 큰 제목의 PASS를 Full에 대입하지 않았다.
- 솔루션영업: `#5a963c → #3f7629`, `--hm-orgchart-solution-sales-*`. stop 최소 3.582:1. 스크롤 후 Full 15px 부서명 약 3.916–3.917:1 **REVIEW**. Department Colors 큰 제목 약 3.922:1 **PASS**, 일반 크기 비교 약 3.922:1 **REVIEW**다.
위 Full 수치는 1440px 대표 실측이다. Department Colors의 화면 내 MODEL 값은 픽셀 실측과 별도이며 실제 geometry·브라우저 반올림에 따라 차이가 있다. UI에 MODEL임을 명시했다. Default 값만 검사했으며 모든 소비자 override 조합의 대비를 보장하지 않는다.
기타 확인:
- 직원 이름 `#10243b / #ffffff`: 15.701:1 **PASS**.
- 직위·상태의 보조 텍스트 `#596575 / #ffffff`: 5.927:1 **PASS**. 기본 콘텐츠 면 `#f4f7fb` 기준 약 5.516:1.
- 직책 배지 및 통계 헤더/숫자 텍스트: 검사한 solid 조합 **PASS**. 원본 값 변경 없음.
- 협업 허브 `#45639f → #809dd7` 위 흰 일반 텍스트: 실제 약 3.321:1 **REVIEW**; stop 최소 약 2.720:1. 허브 색은 Workdesk 실측으로 확인된 브랜드 값이 아니라 자체 디자인 기본값이다.
- 관계 표현 연결선 `#bac7d7`: 실제 Storybook 배경 `#f5f0e5`와 약 1.510:1, 흰 면과 1.716:1 **REVIEW**. 모든 카드 프레임·테이블 divider를 무조건 의미 있는 UI 경계로 판정하지는 않았다. 부모-자식 관계 연결선은 정보 전달 그래픽으로 검토했다.
- 키보드 focus-visible: 실제 2px solid `#45639f`, Storybook 배경 대비 5.222:1 **PASS**. 전체 키보드/스크린리더 검증으로 확대하지 않았다.
### 적용한 보완과 승인 대기안
**실제 Production 브랜드 HEX 변경: 0건.** Department Colors에 `Aa`, 실제 변수/HEX, 카드 배경과 부서명 전경 구분, 일반/큰 텍스트 대비, stop 보수값, MODEL/NOT VERIFIED 및 미적용 후보 표시를 추가했다. resize 시 실제 텍스트 구간을 다시 계산한다.
중요한 색상·geometry 변경은 승인 없이 적용하지 않았다. 후보는 `UNAPPROVED_NOT_APPLIED`로 표시하며 Full Composition에 반영하지 않았다.
- 디자인기획 흰 부서명 → `#000000` 후보: 전체 Gradient stop 최소 4.607:1. 텍스트 브랜드 표현 변경에 대한 승인 후 실제 화면 재검증 필요.
- 모든 Gradient의 단순 `#10243b` 전경 후보는 어두운 조직에서 실패한다. Department Colors에 실패 수치도 표시해 보편적 해결안으로 제시하지 않았다.
- 디자인기획/솔루션영업: 흰 label backing `#ffffff` + `#10243b` 후보는 15.701:1. 기존 Gradient를 유지하지만 새 시각 요소이므로 승인 대기. 솔루션영업은 검정/흰색 어느 하나도 전체 Gradient 전 구간을 일반 텍스트 기준으로 통과하지 않는다(검정 stop 최소 3.835:1).
- 큰 bold 텍스트 사용은 별도 대안이며 layout 영향을 확인하고 승인한 뒤 실제 geometry에서 다시 검사해야 한다.
- 허브: 흰 backing + 짙은 텍스트 또는 배경/크기 보완 승인 필요. 후보 수치와 actual 허브 적용은 구분했다.
- 의미 있는 연결선 `#bac7d7 → #7b899d` 후보: 실제 Storybook 배경 3.128:1, 흰 면 3.555:1, `#f4f7fb` 3.308:1. `--hm-orgchart-stroke-default` 보완 범위와 시각 의도 승인 후 적용할 수 있다. 적용하지 않았다.
근거: `browser.json`, `contrast-pixels.json`, `contrast-last-branch.json`, `boundaries.json` 및 대응 PNG.
## Track C — Storybook: 변경 표시 범위 PASS
사용자용 메뉴 **Logo / Department Colors / Position Summary / Affiliation Summary / Full Composition** 5개 유지. 기존 내부 Story 8개 및 테스트 로직 유지.
- Logo: Figma 정식 Footer에서 확보한 7개 PNG(한맥·삼안·장헌·장헌산업·피티씨·한라·바론)와 ID 매핑 유지. 473×473 원본 SHA와 RGBA 투명성 유지, 수정·재제작·가상 대체 없음. 누락 Asset **0건**. 원본 이미지 비율은 contain으로 보존했다.
- 로고 최소 수정: 이미지의 흰 CSS 배경을 transparent로 변경하고, 로고 정상일 때 fallback 글자가 투명 영역 뒤에 보이지 않도록 숨겼다. 이미지 실패 시 fallback을 다시 표시하며 관계사명 접근성 이름을 유지했다. 기존 실제 broken-image PASS를 재사용하고 이번에는 변경된 handler 표시만 검사했다.
- 한라처럼 가로 wordmark인 원본은 직원 16px mark에서 실제 ink 높이가 약 4px, Summary 28px에서는 약 7px라 작은 크기의 판독성 한계가 남는다. 원본 비율을 임의로 crop/recolor하지 않았다. 크기/여백 변경은 후속 디자인 검토 대상이다.
- Department Colors: 6개 조직명, 실제 HEX/CSS Variable, Gradient/Border, 원본 출처와 자체 디자인 구분, 실제 computed/model 대비·미승인 후보 표시 정상. 390px 줄바꿈 정상.
- Position Summary / Affiliation Summary: 두 Story에 가상 직원 자동 집계·운영 인원 아님 안내를 추가했다. Core selector/표/정책은 변경하지 않았다. 가상 raw total 41, 관계사 0명 세 항목 표시를 유지한다. 실제 인사 집계 정책 승인으로 간주하지 않는다.
- Full Composition: 리더·허브·6개 그룹과 부서/팀·직원 카드·직위 통계·관계사 통계·파견/휴직을 유지했다. Header·GNB·LNB·Dashboard Layout 없음. 390/768에서는 통계 아래 배치, 1280/1440에서는 오른쪽 통계 배치. 조직도 및 표의 내부 스크롤 유지, 변경으로 인한 새 겹침/페이지 overflow 없음.
- Position Summary 최초 390px PNG의 안내 첫 paint가 불완전해 그 화면만 다시 확인했다. DOM·최종 PNG에서 두 줄 전체 문장 정상. 원본 캡처를 보존하고 `position-note-recheck.json`과 `storybook-position-summary-390-final.png`를 최종 근거로 사용한다.
- HTML/React: 4개 폭의 computed 카드·로고 스타일 일치. 이미 촬영한 PNG를 비교한 결과 390/768/1280/1440 모두 픽셀 동일. 별도 React UI를 추가하지 않았다.
독립 API는 변경하지 않았다. `OrgChartTree`, `PositionSummary`, `AffiliationSummary`, `StatusSummary`의 기존 factory/React Wrapper/P1 독립 사용 PASS를 재사용한다. 두 Summary의 변경 Story 렌더링은 이번 브라우저 검사로 확인했다. 사용법은 기존 [공개 컴포넌트·스타일 API](standalone-components-p1.md)를 유지한다.
확인된 Workdesk 차이는 유지했다: IS/디자인 하위 팀의 세로 배치, 추가 root 카드·ID/raw metadata·collapse control, 더 큰 전체 높이, 18행 상세 통계, 1440px Story host 제한과 마지막 가지 내부 스크롤, 파견/휴직의 after-tree 배치. 이는 이번 변경으로 생긴 회귀가 아니며 사용자가 예고한 별도 전체 디자인 작업에서 다룬다. 픽셀 완전 일치 PASS가 아니다.
근거: `test-results/org-chart-quality/visual-review.json`. Workdesk 최신 화면은 조직명/색/배치 참고로만 사용했고 실제 직원 개인정보·운영 인원수를 fixture에 복제하지 않았다. Figma Dev Mode 미확인 속성을 실측이라고 기재하지 않았다.
## 검증 및 재사용
이번에 실행:
- Core Typecheck: `npm.cmd run typecheck:org-chart` **PASS**.
- 변경 Story/preview source Typecheck: `npm.cmd exec -- tsc -p tsconfig.app.json --noEmit` **PASS**.
- Core Package Build: `npm.cmd run build:org-chart` **PASS**(ESM/IIFE/types/CSS).
- HTML demo prepare 및 React demo Vite Build **PASS**. React Wrapper 구현 변경 없음.
- Storybook production Build **PASS**. 기존 큰 chunk 경고가 있으며 이번 작업에서 배포 구조를 리팩터링하지 않았다.
- 신규 대비 unit **10/10 PASS**: 수학/비반올림 경계/일반·큰 텍스트/Gradient 구간/unsupported NOT VERIFIED. 기존 unit output을 지우지 않는 별도 폴더로 실행.
- 변경 UI browser **62/62 PASS**, console/runtime error 0. 5개 Story 390/1440 및 Full 768/1280, Theme 미연결 HTML/React 4개 폭 확인.
- Position 390 안내만 후속 **PASS**, 가려진 솔루션영업 제목 2곳 후속 대비 **REVIEW**. 정상 데이터/생명주기 검사를 반복하지 않았다.
- Guard 재현 **8/8 PASS**; 최종 관련 Guard 원본 **FAIL**(앞서 원인/범위 기록).
- 실제 텍스트 대비: 검증된 부분 PASS/REVIEW 구분, 가려지거나 모델 불일치한 부분 NOT VERIFIED 유지.
- `git diff --check` **PASS**. 운영 `D:\디자인시스템\HMDesign`은 읽기 전용 확인: `main`, 동일 HEAD, Working Tree clean. Commit/Push/Merge/새 브랜치/공통 코드 수정 없음.
재사용한 근거: 기존 Core 22/22, HTML 69/69, React 79/79, P1 selector 7/7, P1 browser의 성공 196건 및 console/menu 후속 4/4. 원본 기록은 [P1 구현 보고](implementation-report-p1.md)와 `test-results/org-chart-p1/`에 유지한다. Data Source/validation/집계/JSON 갱신/lifecycle/API/Wrapper/원본 Story 로직이 변경되지 않아 전체 테스트를 다시 실행하지 않았다. 현재 검사는 변경된 스타일·표시·신규 대비 모델·관련 Guard 문제에 한정했다.
## 변경 파일 — 이번 품질 작업만
시작 706파일 SHA snapshot과 비교해 기존 파일 삭제 0건, 수정 7건, 신규 소스/검증 6건과 이 보고서 1건이다. 기존 `package.json`, `package-lock.json` 미커밋 변경, 원본 자산·7개 설계 문서·P1 보고/Story·Spike·공통 코드의 시작값은 보존했다.
수정:
1. `packages/org-chart/src/core/affiliation-mark.ts` — fallback span과 정상/오류 표시 연결.
2. `packages/org-chart/src/core/department-colors.ts` — 대비 정보와 미적용 비교 후보.
3. `packages/org-chart/src/styles/employee-card.css` — 로고 투명 배경 및 fallback 숨김.
4. `packages/org-chart/src/styles/org-chart-content.css` — Department Colors 비교 표시만.
5. `packages/org-chart/src/styles/summary-sections.css` — standalone 주석 정정만.
6. `src/org-chart/core-component-preview.tsx` — Department Colors resize 계산/cleanup.
7. `src/org-chart/org-chart-p1.stories.tsx` — 두 독립 Summary의 가상 데이터 안내.
신규:
8. `packages/org-chart/src/core/contrast.ts` — 내부 대비 모델(공개 API 추가 없음).
9. `packages/org-chart/src/core/contrast.test.ts` — 신규 모델 10개 검증.
10. `examples/org-chart/verify-guard-p1.mjs` — Guard 최소 오탐 재현.
11. `examples/org-chart/verify-quality-p1.mjs` — 변경 UI 62개 검사/PNG/값 수집.
12. `examples/org-chart/audit-quality-pixels.py` — PNG 읽기 전용 대비 감사.
13. `examples/org-chart/verify-quality-boundaries.mjs` — 실제 연결선/포커스 CSS 확인.
14. `docs/org-chart/quality-review-p1.md` — 이번 결과 기록.
`test-results/org-chart-quality/`에는 신규 증거, 실행 로그, PNG 및 baseline을 별도 보존했다. Build 산출물은 기존 생성 경로에서 갱신했다.
## 최종 판정 / 승인 / 후속
**PARTIAL**: 로고 표시·Department Colors 설명·가상 안내·변경 렌더링 보완은 PASS다. 실제 대비 부족과 공통 Guard 개선 승인이 남아 전체 P1을 PASS로 처리하지 않는다. 원본 Guard FAIL은 분석상 오탐으로 구분했지만 승인·수정 없이 PASS로 치환하지 않았다.
승인 대기: 디자인기획/솔루션영업/허브의 텍스트·backing·크기 선택, 의미 있는 연결선 대비 보완, 공통 Guard 제한적 추적 개선. 모두 미적용이다. Workdesk 전체 배치 수정은 사용자가 지정한 별도 Codex 작업으로 남긴다.
NOT VERIFIED: 전체 Heap/GC 회수, 전체 스크린리더, 미확인 Figma Dev Mode 속성, Workdesk 픽셀 완전 일치, 실제 HR API, 미승인 집계 정책, 무관한 Guard 항목, 지원하지 않거나 가려진 contrast probe, 모든 소비자 override의 접근성.
후속 P2: 전체 디자인 작업 결과의 확인, 승인된 대비 대안만 적용/부분 재검사, 작은 wordmark 크기 검토, 승인된 집계 정책·실제 API·전체 접근성·메모리 검증. Commit·Push·Merge는 수행하지 않았다.
## 완료 기록
Started At: **2026-10-12 11:55:44 KST**. Completed At: **2026-10-12 12:25:20 KST**(최종 검사 종료 시각). Actual Duration: **29분 36초**. Estimated Duration: **45–90분**. Variance: **하한 대비 −15분 24초 / 상한 대비 −60분 24초**. 보고서 정리와 결과 표시 시간은 별도다.
최종 보존 근거: `test-results/org-chart-quality/preservation.json` — baseline 706개 중 수정 7개, 유지 699개, 삭제 0개, 신규 7개(보고서 포함), HEAD/main 동일, diff check 0. Preview: `http://127.0.0.1:6027/?path=/story/orgchart--full-composition`.
@@ -0,0 +1,83 @@
# HMDesign OrgChart v0.1 React Wrapper · Storybook 통합 검증
RESULT: **PASS** — 이번 요청의 React 통합 MVP 완료 조건 13개를 충족했습니다. 아래 NOT VERIFIED는 별도 한계로 보존하며 모든 기능·디자인·배포 환경의 검증 완료를 뜻하지 않습니다.
## 구현 결과
1. 작업 경로 `D:\디자인시스템\HMDesign-OrgChart`, 브랜치 `feature/orgchart-package`. 사용자 첨부 요청의 별도 작업/Git 지시를 적용했습니다. 운영 `D:\디자인시스템\HMDesign` 및 `main`은 수정하지 않았습니다.
2. 작업 전/후 HEAD, Local main, 공식 원격 main/Feature 모두 `d18e3c33801e165e17b6d971e2f6198eed483c3c`. 공식 origin은 `https://gitea.hmac.kr/b25027/HMDesign.git`입니다. 시작/완료 시 원격을 읽기 전용으로 확인했습니다.
3. 신규 `packages/org-chart-react`, `@hm-design/org-chart-react@0.1.0`. Core `@hm-design/org-chart@0.1.0` dependency, React `^19.0.0` peer. React DOM은 Wrapper가 사용하지 않아 별도 dependency/peer를 추가하지 않았으며 소비 앱이 제공합니다. 설치 package/lock과 React/DOM `19.3.0`, Storybook `10.6.1`, 엔진 `6.0.0` 일치를 확인했습니다.
4. Runtime Export는 `OrgChart`. Type Export는 `OrgChartProps` 및 Core 공개 타입의 type-only re-export입니다. Props는 공개 `OrgChartOptions`에서 container/onStateChange를 제외하여 재사용합니다. 필수 `dataSource`, `stylesheetUrls`; 선택 `aggregationPolicy`, `className`, `style`, `onStateChange`, `onLifecycleError`. 제목·직원·Zoom 등 임의 Props는 추가하지 않았습니다.
5. 생명주기는 Core 생성 → 구독 → mount, Source identity 교체 → reload, cleanup → 구독 해제·destroy입니다. 최초 추가 Reload가 없고 Callback 변경만으로 재생성하지 않습니다. Core가 변경 setter를 제공하지 않는 Policy identity/CSS URL 값 변경은 정리 후 재생성합니다. 같은 URL 문자열의 새 객체는 재생성하지 않습니다. Source/Policy 내부 mutation은 변경 알림으로 취급하지 않습니다.
6. committed Callback, 소유권, Source 갱신을 순서대로 layout effect에 연결했습니다. commit과 passive cleanup 사이의 오래된 요청 Callback 가능 구간을 닫습니다. generation/operation 검사는 이전 인스턴스 Promise의 늦은 rejection을 차단하며 Core revision/AbortSignal이 오래된 데이터 응답을 차단합니다. Wrapper 안에 DOM 엔진·Validation·Summary·노드 UI·엔진 정리 로직을 복제하지 않았습니다.
7. ESM과 선언을 자체 최소 build.mjs로 생성하며 React, JSX runtime, Core를 external로 유지합니다. 산출물의 client boundary를 보존했습니다. 기존 공통 build 스크립트는 수정하지 않았습니다. 최종 Wrapper ESM 약 3.09 kB/gzip 0.92 kB입니다.
8. 기존 HM Theme/폰트를 호스트에서 제공하고 공개 engine.css/style.css의 실제 URL을 Shadow DOM에 주입합니다. Storybook의 기존 preview CSS/decorator를 그대로 사용하며 별도 소비자는 공개 Theme entry를 사용합니다. Production의 해시 Asset URL과 폰트 로드를 실제 확인했습니다. 새 Theme·브랜드 팔레트나 reset을 만들지 않았습니다.
9. `src/org-chart/org-chart.stories.tsx`, 분류 `OrgChart/Content`. 기존 glob에 포함되므로 `.storybook/main.ts`/preview 변경이 없습니다. 기존 Component/Pattern Story를 바꾸지 않았습니다. 모든 Story는 공개 Wrapper와 Strict Mode를 사용합니다.
10. 필수 Story 8개: Basic Organization, Organization Hierarchy, Employee Cards, Organization Summary, JSON Data Update, Loading / Empty / Error, Responsive Container, Multiple Instances. 기존 HTML v1/v2 JSON을 직접 import하며 추가 시나리오는 파생 가상 데이터 및 같은 Source Interface만 사용합니다. 주변 조작은 공개 HM Button을 실제 렌더링합니다.
11. Navigation·Header·Dashboard·CSV·추가 업무 메뉴·API는 없습니다. FilterBar/DataTable은 검색·필터·정렬·선택 요구가 없어 사용하지 않습니다. 노드/상태/집계는 Framework-independent Core의 승인된 책임이며 React HM 위젯으로 복제하지 않습니다.
12. 실제 empty 화면을 구현한 것으로 표시하지 않습니다. 직원 0명은 Ready, 조직 0개는 Error입니다. Zoom/Pan은 비활성 상태로 유지합니다. hubText/showDispatched/showOnLeave 등 기존 reserved 영역, 복잡한 relation overlay도 추가 구현하지 않았습니다.
## 최종 통합 검증
- **PASS** Wrapper Typecheck: `npm run typecheck:org-chart-react`. 최종 생명주기 수정 후 다시 확인했습니다.
- **PASS** 루트 앱·Storybook TypeScript graph: `tsc -p tsconfig.app.json --noEmit`. 별도 React 소비자도 strict Bundler 타입 검사를 통과했습니다.
- **PASS** Wrapper Package Build: ESM/선언/client boundary/external 확인. Storybook prebuild의 기존 workspace 빌드도 Core/UI/Center Graphic을 포함해 성공했습니다.
- **PASS** 실제 npm tarball Public Export 및 선언: 추출된 Core/Wrapper에서 DOM 없는 ESM import, strict NodeNext consumer, 필수 Source와 미지원 Zoom/container 타입 경계 확인. React/compiler는 현재 검증 환경에서 제공합니다. 새 PC의 fresh dependency install 결과로 해석하지 않습니다.
- **PASS** Storybook Production Build: 최종 Wrapper/Story/CSS 상태의 `storybook build` 성공. 큰 기존 chunk 및 plugin timing 경고가 있으나 build 실패는 없습니다.
- **PASS** Storybook 실제 브라우저: Production의 8개 Story와 개발 6025의 실제 Wrapper 노드 실행. JSON v2 갱신, Unmount/Mount, Source 오류·복구, 직원/조직 0개, 취소를 무시하는 늦은 응답, A 갱신/제거 시 B identity·데이터·Callback 유지 확인.
- **PASS** 독립 React Consumer: `examples/org-chart-react`의 공개 package import·Vite Production Build·실제 Chrome 실행, 초기 mount/Source 교체/최신 Callback/Unmount. Storybook 성공을 소비자 성공으로 대신하지 않았습니다.
- **PASS** 개발 React Strict Mode: setup → cleanup → setup에서 두 번의 Source load와 최종 Ready 1회, 활성 Core host 1개. Production은 replay 없이 초기 load/Ready 각 1회임을 별도로 확인했습니다. 두 Source load는 Strict Mode의 의도된 setup 반복이며 Wrapper의 초기 이중 Reload가 아닙니다.
- **PASS** 생명주기: Callback/className 변경 시 같은 Core·추가 load 없음; Source 교체 시 같은 Core reload; Policy/CSS URL 변경 시 새 Core; 같은 CSS URL 값은 유지. 실패한 구성의 lifecycle callback, 정상 오류→Ready 복구, 늦은 응답·Unmount 후 Callback 차단, 5회 반복 mount/unmount의 DOM/관찰자 기준 유지, 최종 Core DOM/관찰자 0 확인.
- **PASS** HTML/React 공통 데이터: v1 조직 3개·직원 4명·직접 Root/A/B 1/2/1, v2 조직 3개·직원 5명·직접 1/1/3. DOM의 organization ID/parentId·employee ID·노드 텍스트·Heading·Summary를 비교하여 일치했습니다.
- **PASS** Theme CSS: HTML/React의 전경·surface·radius·폰트 computed 일치, 기존 Theme 변수 상속, 두 CSS link의 실제 stylesheet 로드, scoped Theme 변경 시 다른 인스턴스 유지, Engine selector의 light DOM 오염 없음.
- **PASS** 반응형: 실제 390/768/1280/1440px에서 소비자 및 Basic/Employee/Responsive Story의 document overflow·노드 겹침·텍스트 폭·null 사진 fallback 확인. scrollWidth/clientWidth 및 scrollHeight/clientHeight 측정을 저장했습니다. 390px은 원래 노드 크기와 내부 가로 스크롤을 유지합니다. 실제 scrollLeft 이동도 확인했습니다. 소비자 Grid item min-width와 Story Grid track을 수정하여 문서 overflow를 제거했습니다.
- **PASS** 키보드: Enter/Space 접기·펼치기 및 데이터 교체로 기존 focus가 제거될 때 안정된 Core viewport로 복귀. 접기 애니메이션이 끝나기 전 재입력은 기존 Core가 거부하므로 검사도 실제 transition 완료를 기다립니다.
- **PASS** 기존 Vanilla: 기존 HTTP HTML의 v1→v2 비교 및 `file://` classic의 Ready 4명→Update 5명 smoke. 변경 없는 기존 22개 데이터 테스트/69개 전체 브라우저 검사는 반복하지 않았습니다.
- **PASS** Chrome `155.0.8059.39`: 통합 검사 **70/70**, 추가 경계 검사 **9/9**, console/pageerror **0**. 대표 390px 긴 텍스트·Responsive 및 1440px 소비자 캡처도 육안 확인했습니다.
- **PASS** 보존: 시작 파일 **614/614 SHA-256 일치**, 기존 Core/HTML/7개 설계 문서/이전 구현 보고/UI/Center Graphic/Theme/Story/공유 build 설정 보존. `git diff --check` 통과. 운영 main Working Tree는 시작·완료 시 깨끗합니다. Staged diff는 비어 있으며 Stage/Commit/Push/Merge가 없습니다.
## Guard와 미검증
- Storybook과 독립 소비자에 범위별 PARTIAL Guard를 실행했습니다. 최종 원본 status/staticStatus는 **NOT VERIFIED**, FAIL 0입니다. Storybook의 동적 width, 소비자의 동적 className 및 실제 브라우저 증명 요구를 그대로 유지합니다.
- 원본 CSS styleEvidence는 **STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT NOT VERIFIED / FINAL NOT VERIFIED**입니다. 별도 브라우저 computed·컨테이너 검사 PASS는 기록한 요소/상태/폭의 증거이며 Guard 원본이나 디자인 의도 승인을 덮어쓰지 않습니다. Guard 결합 판정은 수행하지 않았습니다.
- 최초 소비자 Guard의 target 상대경로 입력이 맞지 않아 checkedFiles=[] 진단이 발생했습니다. 원본 `guard-consumer-initial.json`을 보존하고 상대경로/entry를 교정한 실제 graph 검사를 별도로 저장했습니다. Story CSS 수정 후 CSS-only PARTIAL은 NOT APPLICABLE이어서 최종 Story graph 검사를 별도로 저장했습니다. 빈 검사 결과를 PASS로 계산하지 않았습니다.
- **NOT VERIFIED** 완전한 Heap/GC 회수, 전체 스크린리더·SSR/hydration, 모든 브라우저/모바일 touch, 매우 큰 데이터 성능·깊이, Workdesk 원본과의 시각적 일치, Theme 미연결 스타일, 실제 API/auth, fresh npm registry 설치 및 임의 배포 base/CSP/origin입니다.
- Scope 한계: 공개 Props의 arbitrary className/style 및 모든 외부 데이터·Theme 조합은 전수 검사하지 않았습니다. readOnly/disabled 입력 필드는 없는 콘텐츠 패키지이며 전체 업무 화면 접근성 검증으로 해석하지 않습니다.
- 기술적 리스크: 기존 Core의 엔진 6.0.0 내부 init observer hook 의존은 유지됩니다. 엔진 upgrade 시 정리 계약 재검토가 필요합니다. Policy identity 변경은 재생성되므로 immutable/memoized Props를 권장합니다. Core 갱신 시 expansion/scroll state 보존은 추가하지 않았습니다.
- 도구 이슈: 로컬 npm symlink EPERM 및 sandbox Chrome loopback 차단은 허용된 실행 권한으로 재시도해 해결했습니다. 최초 sandbox Storybook의 사용자 설정/registry 쓰기 경고도 접근 가능한 개발 서버 재실행 후 사라졌습니다. 운영 6024 서버를 중단하지 않았습니다.
## 파일과 증거
이번 변경(기존 작업 위에 추가):
- 수정 `package.json`: 기존 OrgChart scripts 유지, Wrapper build/typecheck·소비자 build/dev/browser scripts, Core/Wrapper root dependency 연결.
- 수정 `package-lock.json`: root Core/Wrapper dependency, Wrapper workspace link·manifest. 해당 추가를 제거하고 원래 CRLF로 재직렬화한 SHA가 시작 lockfile SHA와 일치하므로 기존 dependency graph/버전은 보존했습니다. npm 재직렬화로 line ending은 LF입니다.
- 신규 `packages/org-chart-react/{package.json,build.mjs,tsconfig.json,tsconfig.build.json,README.md,src/OrgChart.tsx,src/types.ts,src/index.ts}`.
- 신규 `src/org-chart/{org-chart.stories.tsx,org-chart-stories.css}`.
- 신규 `examples/org-chart-react/{index.html,index.css,app.tsx,vite.config.ts,README.md,verify.mjs,verify-package.mjs,verify-boundaries.mjs}`.
- 신규 이 보고서. 시작 시 기존 package/lock의 Core 관련 diff와 untracked docs/examples/Core는 이번 신규 변경과 구별하여 보존했습니다.
검증 생성물(Git ignored):
- `test-results/org-chart-react/report.json`: 70개 검사, HTML/React snapshots, four-width 측정과 PNG.
- `test-results/org-chart-react/boundaries.json`: 추가 9개 경계 검사.
- `test-results/org-chart-react/tarball-consumer/report.json`: public archive/declaration 검사.
- `test-results/org-chart-react/preservation.json`, `baseline.json`, `baseline-lock-reconstructed.json`: 시작/완료 보존 근거.
- `test-results/org-chart-react/guard-storybook-final.json`, `guard-consumer.json`: 최종 원본 Guard. 초기/부분 진단도 별도 보존.
- `test-results/org-chart-react/storybook-build.log`: 최종 Production Build 로그.
확인용 개발 URL:
- Storybook `http://127.0.0.1:6025/?path=/story/orgchart-content--basic-organization`.
- 독립 React 소비자 `http://127.0.0.1:6026/`.
- 기존 운영 Storybook `6024`, 원래 PID `19932` 유지.
## 시간 기록 (Asia/Seoul)
- Started At: 2026-10-12 10:24:24 KST
- Completed At: 2026-10-12 10:42:39 KST
- Actual Duration: 18분 15초
- Estimated Duration: 최초 2~3시간
- Variance: 예상 하한 2시간 대비 101분 45초 단축; 예상 상한 3시간 대비 161분 45초 단축
예상 시간은 참고값입니다. 기존 Core/설치 환경과 병렬 구현을 재사용해 실제 시간이 줄었으며 미검증을 PASS로 대체하지 않았습니다. Commit·Push·Merge는 사용자 요청대로 진행하지 않았습니다.
@@ -0,0 +1,60 @@
# OrgChart P1 독립 디자인과 개별 컴포넌트
2026-10-12 최종 요청을 적용한다. 기존 공통 Theme 필수 연결 규칙은 OrgChart에 적용하지 않는다. 과거 v0.1 검증 기록은 당시 구현 기록으로 보존한다.
## 공개 API와 스타일
패키지 root 공개 API에 `createOrgChartTree`, `createPositionSummary`, `createAffiliationSummary`, `createStatusSummary`를 추가한다. 기존 `createOrgChart`, Source, schema, summary 및 React Wrapper API는 유지한다. 모든 factory가 기존 `mount/update/reload/destroy/subscribe/getState` handle을 반환한다.
```js
import { createPositionSummary, createAffiliationSummary, createOrgChartTree,
JsonOrgChartDataSource } from '@hm-design/org-chart';
import coreUrl from '@hm-design/org-chart/style.css?url';
import engineUrl from '@hm-design/org-chart/engine.css?url';
const source = new JsonOrgChartDataSource(dataset);
const position = createPositionSummary({ container: positionElement,
dataSource: source, stylesheetUrl: coreUrl });
const affiliation = createAffiliationSummary({ container: affiliationElement,
dataSource: source, stylesheetUrl: coreUrl });
const tree = createOrgChartTree({ container: treeElement, dataSource: source,
stylesheetUrls: { core: coreUrl, engine: engineUrl } });
await Promise.all([position.mount(), affiliation.mount(), tree.mount()]);
// 상위 페이지가 DOM container를 자유롭게 배치하고 표시·숨김을 관리한다.
await position.update(nextDataset);
position.destroy(); affiliation.destroy(); tree.destroy();
```
`?url`은 Vite 사용 예시다. 일반 HTML은 배포한 CSS 실제 URL을 전달한다. `/browser` classic bundle도 같은 factory를 `window.HMOrgChart`로 제공한다. ESM import/handle 생성은 DOM 없이 가능하며 mount에만 연결된 HTMLElement가 필요하다. Summary는 engine을 로드하지 않고 engine.css도 필요 없다. 각 모듈은 Header·Routing·전체 페이지에 의존하지 않는다.
React에서는 기존 전체 `OrgChart` Wrapper를 그대로 사용한다. 개별 모듈은 위 factory를 ref/effect에서 mount하고 cleanup에서 destroy하면 된다. JSX 카드·테이블·데이터 계산을 복제하지 않는다. Source는 공유할 수 있으며 각 handle은 동일한 validate/normalize/calculateSummary 흐름을 사용한다. 상위 페이지가 여러 handle에 nextDataset을 전달한다. 자동 전역 데이터 저장소는 추가하지 않는다.
## 독립 디자인 입력
OrgChart는 Shadow DOM 안에서 `engine.css → style.css`를 적용한다. `style.css`는 모든 내부 CSS를 포함하며 공통 HM Theme가 없어도 기본 디자인을 가진다. 공통 Theme·Token·Component는 변경하지 않는다.
```css
.other-page-summary {
--hm-orgchart-font-family: Arial, sans-serif;
--hm-orgchart-spacing-lg: 20px;
--hm-orgchart-radius-container: 6px;
--hm-orgchart-bg-surface: #ffffff;
}
.custom-chart {
--hm-orgchart-design-planning-card-background: #f4ffff;
--hm-orgchart-design-planning-card-color: #10243b;
--hm-orgchart-design-planning-department-color: #ffffff;
--hm-orgchart-design-planning-border: #00847f;
--hm-orgchart-design-planning-gradient: linear-gradient(#00847f, #005b57);
}
```
위 예시는 외부 사용자 override 예시이며 공식 Workdesk 값 확정이 아니다. public 변수를 container에 설정한다. `resolved-*`, `current-*`는 내부 구현 변수다. 기본 변수 전체 목록은 `packages/org-chart/src/styles/design-tokens.css`, 조직별 목록·실측 근거는 `core/style-presets.ts`에서 관리한다. 조직별 ID에 연결하고 하위 셀·팀은 상위 variant를 상속한다. JSON 직원 객체에 컬러를 추가하지 않는다. 카드 배경·직원 텍스트·부서명 텍스트·테두리·Gradient를 각각 바꿀 수 있다.
## Composition·데이터·디자인 근거
`core/org-chart-content.ts`에서 독립 Tree와 Summary 모듈을 선택해 배치한다. 기본 placement는 직위=side, 관계사=footer, 파견·휴직=belowTree이며 내부 `ContentComposition.placements`로 바꿀 수 있다. 외부 호출자는 공개 개별 API의 container를 직접 배치한다. 기존 presentation flags로 전체 조합의 표시를 관리하며 계약 필드 추가 없이 처리한다.
Workdesk `v1.2610.9c5e` 운영 화면에서 확인한 조직 명칭과 6개 조직 헤더 Gradient·흰색 부서명 텍스트를 사용한다. 일반 배경·카드 테두리·Typography·간격·Radius는 독립 구현 기본값(DESIGN_ADAPTATION); 원본 카드 세부값은 NOT VERIFIED다. Figma 접근 가능한 정식 회사 로고 7종은 Core에서 affiliation ID로 매핑하고 카드·현황·Logo에서 재사용한다. 출처·원본 SHA256은 `packages/org-chart/src/assets/affiliations/README.md`에 기록한다. 미매핑/파일 오류는 회사 명칭 텍스트 대체를 사용하고 임의 로고를 생성하지 않는다.
P1 fixture는 실제 조직명과 관계사명만 사용한다. 직원 41명과 직위·직책·소속·근무 상태는 가상이며 프로필은 null/이니셜 placeholder다. 관계사 3곳은 0명을 유지한다. Raw Count는 가상 JSON 고유 employee ID로 자동 계산하고 실제 HR 인원수나 승인된 집계 정책으로 표시하지 않는다.
+114
View File
@@ -0,0 +1,114 @@
# OrgChart UI Composition
## P3.2 후속 — 대표자 뱃지 미사용 (2026-10-12)
대표자 카드는 모든 실행 경로와 독립 견본에서 직책·역할·상태 뱃지 및 빈 attributes/뱃지 행을 생성하지 않는다. 표시 직위 `대표이사`, 이름 `홍 길 동`, 승인 카드 146×74 및 기존 Typography·사진·로고를 유지하며 직위↔이름 간격은 0px이다. 원본 fixture의 대표자 직위는 `경영진`이므로 `대표이사`는 카드 표시에만 적용한다. 직원 JSON·정보 팝업 원문·공개 API와 다른 1Depth/2Depth 카드의 뱃지 정책은 그대로 유지한다. 아래 P3.2 및 이전 보고서·검증은 당시 기록으로 보존한다.
## P3.2 현재 계약 (2026-10-12)
[P3.2 보고서](implementation-report-p3.2.md)의 카드 Typography·상태 영역·로고 보정이 아래 P3.1 수치보다 우선한다. 과거 검증 기록은 그대로 보존한다.
- 대표자 Typography는 유지한다. 대형 이름 24px/600/30px, 직위 18px/400/22px; 소형 이름 18px/600/24px, 직위 15px/400/18px이다. 기존 컬러·letter-spacing·Noto Sans KR 및 `홍 길 동`을 유지한다. 직위·뱃지 공백은 표시 계층에서만 제거한다.
- readOnly 파견·휴직은 SVG 없이 일반 소형 카드의 공통 콘텐츠 레이아웃과 Typography를 재사용한다. 카드 108×52, 사진 24×44, 사진↔텍스트 4px, 직위↔이름 0px이다. 기존 회색 표면·문자색·로고 grayscale을 유지하며 별도 상태 행은 없다. P3.1의 실제 114×54/사진 28×28에서 이번 요청값으로 보정한다.
- 상태 타이틀은 일반 2Depth 기본 20px/700/19.7px를 공유하며 회색 Gradient를 유지한다. 경영기획/인재성장 18px/600/16.9px 예외는 보존한다. 타이틀↔첫 카드 Gap 8px를 유지한다.
- 카드 로고의 실제 contain 표시 영역은 소형 12×12→18×18, 대형·대표자·파견·휴직 14×14→21×21이다. 원본 PNG·승인 SVG 및 하단 관계사 현황 로고는 수정하지 않는다. 직원 카드 selector에만 적용하며 비율·투명 배경을 유지한다.
## P3.1 현재 계약 (2026-10-12)
잔여 디자인 보완의 최신 계약은 [P3.1 보고서](implementation-report-p3.1.md)가 우선한다. 아래 P3/P2 및 초기 설계의 검증 기록은 당시 범위 그대로 보존한다.
- OrgChart 전용 기본 폰트는 Noto Sans KR이다. 기존 `public/fonts/NotoSansKR-variable.ttf`와 호스트 font-face를 재사용하며 HM Theme는 수정하지 않는다. 한국어 Core shell은 `lang="ko"`로 HTML/React/Storybook의 글리프 조형을 맞춘다.
- 이름은 `홍 길 동`, 직위는 표시에서만 공백을 제거한다. Full Composition은 `jobTitle`이 `경영진`인 뱃지만 생성하지 않으며 기타 직책 문자열·원본 데이터·팝업·통계는 유지한다. 원본 `position="경영진"` 텍스트는 유지한다.
- 1Depth는 20px/700/36px·헤더 높이 36px이다. 2Depth 경영기획/인재성장은 18px/600/16.9px, 나머지는 기존 20px/700/19.7px다. 114×30.4 표면·Gradient·Radius·Shadow 및 모든 타이틀↔첫 카드 Gap 8px를 보존한다.
- 허브는 원본 21px/750/24px·아이콘 20px·Gap 8px·좌우 Padding 22/24px·높이 42px를 유지한다. 최대 폭 상속 제약을 해제하고 X 935 중심축을 유지한다.
- 사진 없음/로드 실패는 공통 `person-placeholder.svg`를 사용한다. 실제 사진이 있으면 우선한다. 승인 사진 슬롯 36×62/32×56/24×44, P3 상태 카드 슬롯 28×28 및 비원형 처리를 유지한다.
- 우측 표는 전체 330px 안에서 조직명/경영진 열을 112/46px로 재배분해 한 줄로 표시한다. 나머지 열은 잔여 폭을 나눈다. 숨김/말줄임/폰트 축소 없이 원본 14px, 기존 계산 41명·위치·셀 정렬·행 높이를 보존한다.
- 대표자 146×74, 1Depth 146×68, 하위 108×52 및 P3 상태 카드 114×54를 유지한다. 확장형 Auto Layout/layoutConfig·메뉴·Navigation·공개 API 추가 없음.
## P3 현재 계약 (2026-10-12)
아래 초기 설계와 P2 기록은 보존한다. 충돌하는 표시 계약은 [P3 보고서](implementation-report-p3.md)가 우선한다. 범위는 기존 독립 HTML·React 공통 Core와 해당 Storybook 페이지이며 Navigation·Header를 추가하지 않는다. 기존 semantic table/native dialog는 공통 Vanilla Core adapter를 유지하기 위한 선택이다. FilterBar·DataTable의 검색/정렬/페이지 기능은 요구되지 않아 추가하지 않는다.
- Full Composition 기본 배경 `#F5F8FB`, 원본 장식·허브 곡선·협업 화살표 유지. 조회 전용 엔진의 하위 연결선 생성 경로는 모두 숨긴다.
- 대표자 146×74, 1Depth 146×68, 하위 108×52 유지. 모든 조직 타이틀↔첫 카드 간격 8px, 직위/뱃지↔이름 행 0px. 이름만 `홍 길 동`, 직위·뱃지는 원문.
- 하위 헤더는 Workdesk 소스의 114×30.4 표면·상위 조직 Gradient·Radius 7.1·Shadow를 적용한다. HR은 18px/600/16.9, 다른 헤더는 20px/700/19.7이다. 허브는 원본 SVG 아이콘 20px·Gap 8px·텍스트 21px/750/24를 사용한다.
- 사진 없음은 원형 기본 아바타 대신 빈 사각형 예약 영역이다. 원본에 기본 이미지가 없으며 승인 카드의 기존 폭을 유지하는 디자인 조정이다. 실제 직원 사진은 포함하지 않는다.
- 우측은 직위별 표(주요 조직 하위 포함, 루트 직속, 직위별 열, 소계, 계). 가상 직원 41명·원본 조직 관계·독립 PositionSummary API를 보존한다. 상태 그룹의 카드 하단 상태 문구만 제거한다. 관계사 바는 제목 없는 한 줄이다.
- 축소는 단일 비례 배율 `min(1, 가용 폭 / Stage 폭, 실제 컨테이너 가용 높이 / Stage 높이)`다. 연결 후 실제 Shadow/호스트 조상과 viewport를 관찰한다. 임의 Header 높이나 viewport 절반 offset 제한은 없다.
- `organizationLinks`가 지정된 1Depth 제목만 링크다. `onEmployeeClick`이 없으면 Stage 바깥 native modal에서 기존 직원 정보를 조회한다. 닫기·ESC·키보드 Focus를 지원한다.
- 사용자 메뉴는 `Logo · Personnel Summary`, `Department Colors`, `Full Composition`이다. 이전 단독 Story ID는 내부 태그로 보존한다.
실제 브라우저 측정과 미검증은 P3 보고서에 기록한다. 과거 PASS/FAIL을 이번 상태로 덮어쓰지 않는다.
상태: 콘텐츠 설계, 실행·시각 검증 미수행. [README](README.md)의 확정 범위와 최신 Core 독립성을 따른다. Workdesk 캡처 파일은 이번 첨부에 없어 아래 구성은 사용자 텍스트의 8영역을 기준으로 한다. 실제 이미지와의 배치·색상·카드·곡선 비교는 **NOT VERIFIED**다.
위 상태와 초기 설계 절은 과거 기록이다. 현재 조회 전용 구현은 3a절과 [P2 구현·검증 보고서](implementation-report-p2.md)를 따른다. P2에서는 읽기 전용 Workdesk 로컬 소스와 자산을 확인하고 실제 HTML·React·Storybook을 검증했다. 운영 Workdesk와의 전체 픽셀 일치 여부는 별도 미검증이다.
## 1. 콘텐츠 구성과 데이터 연결
1. **제목·설명**: PresentationConfig의 title/description과 Metadata 기준일. Header·PageHeader·브랜드 Navigation을 생성하지 않는다.
2. **최상위 리더 카드**: 승인된 root 조직의 leaderId를 Employee에 연결한다. 공석·여러 root·겸직 leader 표현은 TBD다. 같은 직원을 중복 집계하지 않는다.
3. **중앙 허브**: hubText의 콘텐츠. 조직 entity인지 설명 허브인지, 리더와의 연결 방식은 TBD다. 조직장 ID나 직원 ID를 허브에 임의 부여하지 않는다.
4. **조직별 그룹**: Organization parent/type/order로 부문·실·Div를 구성한다. 조직 사이 기본 계층 연결선은 engine가 담당한다.
5. **셀·팀·구성원**: 같은 조직 계약에서 cell/team을 표현하고 직원은 organizationId로 조회한다. 이름·직위·직책·이미지/fallback·상태를 조합한다. 직원 전체를 조직 트리의 nested 원본으로 저장하지 않는다.
6. **우측 인원 현황**: 조직 직접/하위 포함·직위별 등 승인된 집계. 우측은 넓은 컨테이너에서의 배치 후보이며 작은 화면에서는 문서 흐름으로 이동하는 안을 검토한다.
7. **파견·휴직 영역**: 같은 직원 원본의 상태별 그룹. 본 조직 카드와 동시 노출/이동 표시 정책은 TBD이며 표시와 집계를 분리한다.
8. **관계사별 인원·총원**: Affiliation과 승인 정책을 사용하는 집계. 조직별 하위 포함 수를 단순 합산하지 않는다. 집계 정책 미승인 시 미확정 상태를 설명한다.
추가 조직 관계는 OrganizationRelation에서 별도 표시한다. 관계 의미·방향·범례·곡선·anchor는 승인 전 확정하지 않는다. 관계를 parent hierarchy로 오인하지 않도록 텍스트 설명을 함께 제공하는 안을 검토한다. 고정 좌표로 인원 수나 조직 배치를 하드코딩하지 않는다.
## 2. HM 재사용과 Core DOM 경계
최신 사용자 요구에 따라 Core는 React Component를 import하지 않는다. OrgChart 전용 표준 HTML/CSS가 **공통 Component를 대체하는 신규 HM primitive**가 되지 않도록 범위를 콘텐츠에 제한한다. 기존 Component·Pattern·Theme·대표 Story는 보존한다.
재사용의 두 경로를 구별한다.
- Core: HM CSS Custom Properties와 기존 Typography·Spacing·Radius 기준을 참조한다. 노드에는 전용 class를 쓰고 HM `data-slot` 또는 일반 Card/Avatar selector를 흉내내지 않는다. 이 예외와 기존 React 지침의 충돌은 README에 기록했다.
- React 소비 앱(Core 콘텐츠 밖): 적합한 주변 콘텐츠에서 공개 `@hm-design/ui` API를 실제 렌더링하는 방향을 검토한다. Wrapper는 Core summary/loading/error/node를 다른 HM UI로 대체하지 않는다. Core 노드마다 React root를 만드는 설계도 채택하지 않는다. 공통 Core의 콘텐츠·디자인·인터랙션을 유지한다.
확인한 공개 자료:
- [Card 문서](../components/card.md), [Card All](../../src/components/card.stories.tsx), [구현](../../packages/ui/src/components/ui/card.tsx): surface/content와 Header/Title/Description/Content 조합. Core 외부의 React 콘텐츠 묶음에서 재사용 후보다.
- [Avatar 문서](../components/avatar.md), [Avatar All](../../src/components/avatar.stories.tsx), [구현](../../packages/ui/src/components/ui/avatar.tsx): 이미지/fallback, sm/default/lg. Core는 `<img>`/안전한 텍스트 fallback을 사용하고 React 호스트에서는 공개 Avatar 후보를 검토한다.
- [Table 문서](../components/table.md), [Table All](../../src/components/table.stories.tsx): 정적 표이며 정렬/선택/페이지는 DataTable 책임이다. 요청된 Summary는 동일 Core의 semantic table로 설계한다. React 소비 앱의 별도 주변 표에서 공개 Table을 검토할 수 있지만 Wrapper가 Summary를 중복 구현하지 않는다. 기존 Table 최소 폭 520px은 Core의 확정 최소 폭이 아니며 사용한 주변 표는 390px 내부 스크롤을 확인한다.
- [공개 인덱스](../guidelines/component-pattern-reference.md)의 Badge/Alert/Skeleton/ScrollArea/Button/Label은 해당 기능 확정 시 문서·대표 Story·Props를 더 확인한다. 현재 import나 재사용 PASS로 보고하지 않는다.
Pattern 선택 근거: FilterBar는 검색·조회 조건용이다. 현재 검색/필터 요구가 없어 추가하지 않는다. DataTable의 정렬/선택/페이지도 요구가 없어 사용하지 않는 방향이다. DashboardLayout/GNB/LNB/PageHeader는 페이지 Shell/Navigation 책임이므로 제외한다. LoadingScreen은 전체 화면용이라 콘텐츠 loading에 바로 사용하지 않는다. 상태 콘텐츠는 Core가 관리하고 React 주변 조합은 기존 Alert/Skeleton을 검토한다. 이는 개발 시 선택 근거이며 구현 재사용 검증은 **NOT VERIFIED**다.
## 3. 컨테이너·반응형·Overflow
Core는 부모의 실제 콘텐츠 크기를 사용한다. viewport에서 Header 높이를 빼거나 GNB/LNB/Dashboard 모드를 읽지 않는다. Dashboard 소비자가 960px 내부 콘텐츠를 주는 경우에도 이를 Core 전역 최소 폭으로 일반화하지 않는다.
390/768/1280/1440px 각각에서 콘텐츠 컨테이너를 직접 제한하여 검사한다. 숫자는 검증 기준이며 확정된 breakpoint·카드 폭이 아니다.
- 390px: 가로 조직도 내부 스크롤·읽을 수 있는 텍스트 배율, 요약/상태 영역 흐름 배치, touch와 focus 접근을 검토한다. 전체 화면을 축소해 텍스트를 읽을 수 없게 하는 기본값은 확정하지 않는다.
- 768px: 조직 분기 wrap과 가로 스크롤, summary의 옆/아래 배치, 긴 조직명·직위·ID를 검사한다.
- 1280/1440px: 전체 계층·리더·허브·우측 summary·추가 관계·직원 카드의 겹침과 여백을 검사한다.
- 조직 수·깊이·직원 수가 증가하면 내부 scroll container가 확장되고 페이지 전체 가로 Overflow는 발생하지 않아야 한다. CSS transform zoom은 layout 크기와 다를 수 있으므로 보이는 영역과 scrollWidth/clientWidth를 함께 측정한다.
- `ResizeObserver`·이미지 로드·폰트 로드·노드 확장·줌·스크롤마다 Overlay 좌표가 동일 좌표계에서 갱신돼야 한다. transform 중복 적용과 stale anchor를 검사한다.
- 모바일 방식은 내부 가로 scroll, branch 축소, 세로 대체 표현 중 PoC로 결정한다. 대체 표현은 승인 전 추가 개발 요구로 만들지 않는다. 최소 콘텐츠 폭·zoom 한계·초기 배율·wheel 우선순위·높이 제약·스크롤 책임은 TBD다.
문서와 내부 컨테이너의 scrollWidth/clientWidth 및 scrollHeight/clientHeight를 기록한다. absolute 알림/overlay의 위치 기준은 해당 인스턴스의 relative 컨테이너여야 한다. 긴 label 줄바꿈과 ID 한 줄 식별 가능성, 접힌 영역에 숨은 focus, 두 인스턴스의 독립 scroll/zoom을 검사한다.
### 3a. 조회 전용 Full Composition (`displayMode: 'readOnly'`, 2026-10-12)
- Storybook `OrgChart / Full Composition`, HTML·React `p1.html?mode=readOnly`가 사용한다. 기본값 `interactive`와 내부 검증 Story·독립 컴포넌트는 변경하지 않는다.
- 생성하지 않는 요소: 하위 조직 접기/펼치기 button, 조직 종류·ID·원본 직원 수·조직장 안내 문구, 제목 설명·추가 관계 안내, 상태 그룹 정책 문구. 조직 그룹 외곽 카드(배경·Border·Radius·Padding)는 CSS로 제거하고 조직/셀·팀 헤더·카드·연결선은 유지한다.
- 배치: Workdesk 실측 1920 × 914.5 캔버스, 대표자·허브 축 X 935, 조직 헤더 X 32/268/505/858/1336/1689(`core/workdesk-layout.ts`, organization ID 기준 presentation preset). 통합시스템 Div·디자인기획 하위는 가로, 나머지는 세로. preset이 없는 조직이 있으면 중앙 흐름 배치로 대체한다.
- 요약: 우측 상단 `조직별 인원 현황`(루트 직속 + 주요 조직 하위 포함 인원 + 계), 좌하단 파견·휴직(제목 108px pill, 카드 유지), 하단 관계사 바(제목 없음, 로고·회사명·인원·총계). 모두 같은 Summary/selector에서 계산한다.
- 축소: `scale = min(1, 가용 폭 / 1920, (viewport 높이 − 문서 offset) / 914.5)`(offset은 viewport 절반까지만 반영). transform scale 1회, 내부 가로 스크롤 없음.
- P2 카드: 실제 `parentId` 조상 관계로 Depth를 계산한다. 대표자 146×74, 1Depth 조직장만 146×68, 2Depth 이하 조직장은 해당 부서의 108×52 소형 SVG를 사용한다. semantic leader와 직책 뱃지는 유지하며 interactive·독립 카드 선택은 보존한다. 전달 자산이 없는 조직의 기존 fallback 정책은 유지한다.
- 최신 확정 표시: 조회 전용 직원 이름은 대표자·파견·휴직을 포함해 모두 `홍 길 동`. 직위·직책·이름의 연속 한글 사이에는 표시 계층에서 공백을 넣고 접근성 이름은 원문을 사용한다. 원본 JSON 이름·ID·조직·인원 계산은 변경하지 않는다. 기존 font-size/weight/line-height/color와 내부 0px 행 간격을 유지한다.
- P2 간격: 1Depth 헤더 높이 36px, Y 약 254.19px, 대형 카드 사이 실제 8px. 2Depth 헤더와 첫 카드 사이 실제 10px. 하단 상태/관계사 슬롯은 측정 캔버스 내에서 고정 배치하여 전체 높이 914.5px와 겹침 없는 배치를 유지한다.
- P2 장식: `core/workdesk-artwork.ts`가 원본 compact 허브 곡선 3개·중앙선·사업관리 추가 곡선, PNG 협업 화살표 5개와 배경/허브 이미지 5개를 같은 Stage에 렌더한다. 원본 9종 자산은 byte 그대로 복사하고 ESM/IIFE에 포함한다. 기존 상단 pseudo 연결선은 이 preset에서 제거한다. 하위 팀의 실사용 원본 SVG는 없어 기존 engine 관계 경로를 유지한다.
- P2 검증: HTML HTTP·classic file://·React production·Storybook에서 1920/1440/1280/768/390px 484건 PASS. Guard 원본의 NOT VERIFIED와 런타임 PASS는 별도 증거로 보존한다. 상세 측정·남은 차이·기존 FAIL은 P2 보고서에 기록한다.
## 4. 접근성·상태·개인정보
Core DOM은 제목 계층·조직/직원 그룹·표의 caption/header를 semantic하게 구성한다. engine 기본 DOM이 완전한 ARIA tree라고 가정하지 않는다. tree 역할 채택 시 키보드 계약 전체를 검증하고, 그렇지 않으면 계층 설명을 제공할 구조를 검토한다.
확장·축소/zoom 조작이 구현될 경우 키보드로 접근 가능한 표준 button과 접근성 이름·focus indicator·disabled 상태를 제공한다. readOnly 데이터 조직도에 수정·drag 메뉴를 추가하지 않는다. 상호작용 후 focus 복귀, 제거된 노드 focus 이동, 오류/로딩 안내의 screen reader 노출과 과도한 알림을 검사한다. 입력 컨트롤이 새로 승인되면 React 호스트는 공개 Label, Core는 framework 독립성 때문에 native label/control 연결을 명시한다.
이미지 접근 실패는 가상 fallback을 보여주며 불필요한 이름 중복 읽기를 피한다. 사진의 origin·권한·실패 정책은 API 연동 시 결정한다. 직원 ID·이름·사진 원본을 로그/오류 메시지에 무분별하게 남기지 않는다. reference의 실제 인물을 fixture로 재사용하지 않는다.
Loading/Empty/Error/Ready, 직원 없는 조직, 공석 leader, 상태 그룹 비어 있음, 정책 없는 집계, 갱신 중 상태를 별도 검증한다. 갱신 중 기존 화면 유지와 부분 오류 허용 정책은 TBD다. 사용자 검토 전에 이러한 설계 후보를 기능 PASS로 표시하지 않는다.