# 디자인팀 External Runtime Color Theme 운영 결과 > HISTORICAL_RECORD: 작성·검증 시점(2026-10-06)의 기록이다. 아래 baseline·실측·당시 Slider/On Accent 설명을 보존한다. 현재 Slider Focus, 독립 On Accent fallback, Default JSON 직접 import/Preview/Reset 흐름은 [디자인팀 Theme 가이드](designer-theme-guide.md), Runtime 계약은 [Runtime Theme](runtime-theme.md), 배포는 [Theme delivery](theme-delivery.md)를 따른다. 기준: `main` / `d1015309ab43fba69c8959a3dda660358e0e8fa1`. 시작 시 HEAD 일치, working tree clean을 실제 확인했다. 이전 감사 문서의 수치는 이번 증거로 재사용하지 않았다. ## 1. 기존 Runtime Theme 구조 `packages/ui/src/theme/runtime.ts`의 framework-independent Apply/Reset은 이미 `@hm-design/ui/theme`에서 공개되어 있었다. `src/theme/preview.ts`는 이 API를 재수출하는 Storybook 호환 adapter였다. Production API와 Preview를 동일시하지 않고 실제 package export를 확인했다. CSS의 Primitive → Default/Semantic → Component Mapping을 유지한다. ## 2. 기존에 가능했던 디자인팀 작업 공식 v1 JSON과 CSS variable override, Default preset 데이터, 부분/빈 Theme, invalid fallback, Global/Scoped, 원래 inline 값/priority cleanup이 가능했다. Comparison/GlobalExternal/Verification/Dashboard는 개발·검증용 숨김 Story였다. 디자인팀의 직접 편집·전달 workflow는 없었다. ## 3. 부족했던 것과 구현 JSON을 컬러 원본으로 연결하고 URL fetch/검증·Apply 분리, Default/partial resolve, CDN 실패 fallback, Theme JSON 독립 산출을 추가했다. 기존 편집기를 JSON URL/파일 Preview 소비자로 재사용하고 Default 표시/같은 세션의 여러 Story 확인/즉시 Reset/Copy·Export·Import를 제공한다. DOM을 변경하지 않는 `isRuntimeTheme`은 기존 validator에서 분리했다. 새 Theme engine/React Production Provider/서버/DB/Portal API는 만들지 않았다. ## 4. Default HM Theme Source of Truth 운영 컬러의 원본은 **`themes/default.json` / 서비스별 `themes/*.json`**이다. Default는 기존 계약의 71개 컬러 값을 포함하며 Component Token은 없다. `contract.ts`의 반복된 Default literal을 제거했다. `scripts/theme-defaults.mjs`가 JSON + 컬러 literal 없는 alias/geometry template에서 package `primitives.css`, `default.css`, `default-values.generated.ts`를 생성한다. `defaultRuntimeTheme`는 이 데이터로 구성된다. 2개 radius/geometry는 package template에 유지한다. 현재 생성 CSS의 실제 선언값/alias는 baseline과 동일하며 diff는 생성 설명 주석뿐이다. `npm run build:themes`는 Theme-only JSON 산출이며 UI/Graphic build·version up을 수행하지 않는다. 새 UI package fallback snapshot을 갱신할 때만 `npm run generate:theme-defaults`; `typecheck/build/build-storybook` pre hook은 stale 생성 데이터를 거부한다. 이미 배포한 package fallback은 해당 버전의 Default snapshot이고 최신 Default JSON은 CDN으로 독립 적용할 수 있다. 자세한 저장/배포/캐시 정책은 [Theme delivery](theme-delivery.md)에 정리했다. ## 5. Public Runtime Theme Contract 전체 공식 계약은 기존 73개 그대로이며 [전체 표](runtime-theme-contract.md)를 유지한다. 편집기는 아래 17개 상위 역할만 노출한다. Component Token은 노출하지 않는다. 각 입력에는 실제 계산된 컬러를 표시한다. | Theme Role | Token | Default | Runtime Editable | 영향 | |---|---|---|---|---| | Primary / Brand | `color.action.primary` | `#123328` | Yes | Badge, Pagination current, Button outline, navigation current ink | | Active / Selected | `color.state.active` | `#266651` | Yes | Checkbox, Radio, Switch, Slider, Progress, Calendar/Date Picker selected | | On Active | `color.on.state.active` | `#ffffff` | Yes | 선택 아이콘/텍스트 | | Focus | `color.stroke.input.focus` | `#266651` | Yes | 일반 입력/Checkbox/Calendar ring | | Accent | `color.accent` | `#bddfd4` | Yes | 45% soft highlight | | On Accent | `color.on.accent` | `#123328` | Yes | 강조 면 글자; JSON Default에서는 독립값 | | Destructive | `color.action.destructive` | `#d32f2f` | Yes | 오류, invalid, destructive | | Surface | `color.bg.surface` | `#ffffff` | Yes | Input, Card, Popup | | Content Surface | `color.bg.content` | `#f5f0e5` | Yes | 콘텐츠 면 | | Subtle Surface | `color.bg.subtle` | `#f4f1ea` | Yes | 은은한 면, Calendar today | | Text Primary | `color.fg.primary` | `#0a221d` | Yes | 주요 텍스트 | | Text Secondary | `color.fg.secondary` | `#6e6759` | Yes | 보조 텍스트 | | Placeholder | `color.fg.placeholder` | `#999999` | Yes | 입력 안내 | | Inverse Text | `color.fg.inverse` | `#ffffff` | Yes | 반전 텍스트 | | Default Border | `color.stroke.default` | `#9a9284` | Yes | 컨트롤/Badge/Scroll Area | | Input Border | `color.stroke.input.default` | `#dedbd2` | Yes | 입력/패널 | | Cancel | `color.action.cancel` | `#6e6759` | Yes | 취소 동작 | ## 6. Live Theme Apply 경로 Designer JSON URL/file/HEX → 공식 v1 `RuntimeTheme` → Storybook 세션 store → `resolveRuntimeTheme(theme, defaultJson)` → Global decorator → Production `applyRuntimeTheme(document.documentElement, theme)` → inline CSS Custom Properties → 기존 Semantic/Derived/Component Mapping → Components/body Portal. Production은 서비스 설정 URL → `fetchRuntimeTheme` → 기존 validation → 선택적 Default/partial resolve → Apply이며, 편의 함수 `loadRuntimeTheme`도 같은 경로를 사용한다. Fetch와 Apply는 각각 따로 사용할 수 있다. 회사 CDN URL은 하드코딩하지 않는다. Storybook 전용 UI/저장과 Production 계약/주입 함수를 분리한다. Component는 Theme 출처를 알지 못한다. ## 7. Default Theme Load 처음에는 `themes/default.json` + 빈 override를 같은 Runtime 경로로 적용하고 실제 계산값으로 입력을 채운다. 같은 탭 저장값은 Default JSON 위 partial로 사용한다. Reset으로 JSON Default를 다시 적용한다. Load Default JSON 버튼은 `/themes/default.json`을 실제 fetch/검증하여 적용한다. CSS minifier의 HEX3 결과는 편집기의 공식 HEX6 표현으로 정규화한다. ## 8. Live Update 유효한 컬러 입력을 받은 순간 Global CSS 변수가 바뀐다. 값 변경에는 페이지 reload, 파일 저장, Vite HMR, build, Storybook 재시작/재빌드가 필요 없다. 같은 세션의 다른 Story에서도 적용한다. 기존 `Theme/Runtime Contract` 검증군은 고정 baseline과 분리하기 위해 editor override를 사용하지 않는다. ## 9. Reset `Reset to HM Default`는 session override를 비우고 JSON Default를 즉시 복원한다. invalid HEX draft와 pending URL 요청도 초기화한다. Production `resetRuntimeTheme`은 원래 host inline 값/priority와 package CSS fallback을 복원하며 pending load를 무효화한다. ## 10. Partial Override Export/Import는 입력 문서/명시한 key만 포함하며 부분 JSON을 지원한다. 새 Theme로 교체하여 이전 key를 남기지 않는다. Storybook과 supplied Default를 사용하는 Production resolve는 JSON Default + partial을 합친다. 별도 Default를 전달하지 않는 URL Loader는 package CSS fallback + partial을 사용한다. Primary/Active 두 역할만 전달할 수 있으며 전체 73개 값은 필요하지 않다. ## 11. Export / Import Show JSON, Copy JSON(클립보드 제한 시 직접 복사), JSON 파일 다운로드, JSON 텍스트/파일 Import를 제공한다. 모두 동일한 `{schemaVersion:1,tokens:{...}}` 계약을 사용한다. 유효하지 않은 Import는 현재 Theme를 유지한다. 컬러 작업 범위의 layout 회귀를 막기 위해 편집기에서는 radius를 거부한다. Production의 기존 radius 계약은 유지한다. 서버/DB 없음; 임시 보관은 sessionStorage, 전달/장기 보관은 Export다. ## 12. Production 적용 방법 `import "@hm-design/ui/theme.css"` 후 `loadRuntimeTheme(document.documentElement, serviceConfig.themeUrl)`을 호출한다. 별도로 받은 Default JSON + 서비스 partial은 `resolveRuntimeTheme(serviceTheme, defaults)` 후 Apply한다. JSON fetch/validation은 `fetchRuntimeTheme`, 데이터 Apply/Reset은 기존 API로 분리한다. HTTP/JSON/validation/abort 실패 시 package HM fallback이 유지된다. 오래된 요청 응답은 새로운 Apply/Load/Reset을 덮지 않는다. 상세 실제 API 예제와 갱신/캐시 정책은 [Theme delivery](theme-delivery.md)에 기록했다. CDN 인프라/credentials/publish는 이번 범위에서 만들지 않았다. ## 13. Active / Focus / Primary / Accent 독립성 Verification 컬러는 Primary `#6837a8`, Active `#007f73`, Focus `#c75b00`, Accent `#ffd36d`, Destructive `#d41466`로 서로 다르다. 다섯 역할을 하나씩 입력하며 다른 네 역할이 유지되는지 확인한다. 기존 일반 Focus 검증은 실제 Checkbox/Calendar shadow의 색을 검사한다. 기존 Slider Primary 20% halo와 GNB/LNB inverse outline 예외는 보존한다. ## 14. Component 검증 공유 Specimens는 Button/Input/Checkbox/Radio/Switch/Slider/Progress/Select/Calendar single·range/DatePicker/Pagination/Badge/Table/GNB/LNB/Command를 기존 API 그대로 렌더링한다. Designer extras에 실제 DataTable, Combobox, DropdownMenu, ContextMenu, Menubar를 추가했다. 기존 Specimens 내용과 고정 `verification-baseline.json`은 변경하지 않고 파일로 추출했다. 새 LiveVerification은 실제 input/Import/Reset 버튼으로 Default 17개 표시, 핵심 역할 독립성, 실제 주요 컴포넌트 색, Accent 45%의 10개 Mapping, Export→Import, invalid 보존, partial 교체, 17개 입력, geometry 및 동일 document를 검사한다. Loader 검증은 실제 정적 `/themes/*.json` HTTP 요청, 71개 Default와 생성 데이터 일치, partial/merge/reset/invalid/HTTP 실패/abort와 delayed response race simulation을 검사한다. 기존 Runtime Verification은 모든 73개 입력/Default visual parity/실제 Focus/cleanup/Global·Scoped 등을 다시 검사한다. 정적 빌드 HEX3 정규화만 검증 코드에 추가했다. baseline snapshot은 변경하지 않았다. ## 15. Scoped Portal 제한 Global document Theme는 body Portal에도 적용한다. 명시적 scope는 독립 HM Default + 자기 override이며 wrapper 밖으로 Portal된 UI는 Global을 사용한다. scope/Portal 자동 전달은 기존 FOLLOW_UP으로 유지한다. ## 16. 수정 파일 - Production: `packages/ui/src/theme/contract.ts`, `default-values.generated.ts`, `runtime.ts`, `requests.ts`, `loader.ts`, `index.ts`, 생성 `default.css`/`primitives.css`(현재 선언은 baseline과 동일). - 원본/산출: `themes/default.json`, `service-example.json`; `scripts/theme-contract.mjs`, `theme-defaults.mjs`, `theme-artifacts.mjs`, `theme-templates/`; 루트 `package.json`/`.gitignore`. - Storybook: `.storybook/main.ts`/`preview.tsx`, `src/theme/designer-preview.tsx`, `designer-theme.stories.tsx`, `designer-theme.css`, `designer-extras.tsx`, `specimens.tsx`, `runtime-loader.stories.tsx`, 기존 `runtime-theme.stories.tsx`(Specimens 추출/검증 색 정규화). - 문서: 이 보고서/사용법/Theme delivery, README/CLAUDE, 기존 contract/report/테마 토큰 가이드의 후속 섹션 및 runtime/개발 적용/운영 배포/프로젝트 지침의 현재 상태. ## 17. Git diff 요약 baseline에서 Theme 운영 관련 변경만 관리했다. Component/Pattern 구현, Component API, Primitive 값/Default CSS 선언, viewport/breakpoint 및 layout 토큰을 변경하지 않았다. 새 editor만 자신의 responsive layout CSS를 갖는다. Git commit/push는 수행하지 않았다. 추적되지 않은 신규 파일도 검토 범위에 포함한다. ## 18. 문서 및 감사 이력 [디자인팀 사용법](designer-theme-guide.md)과 [Theme delivery](theme-delivery.md)에 JSON 원본/Preview/Runtime/CDN 배포/캐시/롤백/개발팀 workflow/제한을 정리했다. 기존 Runtime 계약/보고서/테마 토큰 가이드에는 추가 결정을 기록했다. 기존 hardcode audit CSV와 consistency audit는 당시 이력이며 Component 색상 Mapping을 바꾸지 않아 재작성하지 않았다. ## 19. Build / 최종 브라우저 검증 - `npm run typecheck`: PASS / exit 0, 루트와 UI/CENTER workspaces. - `npm run build`: PASS / exit 0. - `npm run build-storybook`: PASS / exit 0. - `npm run build:themes`: PASS, 독립 JSON 2개를 `theme-dist/themes/`에 산출. - **1280 / 768 / 390px 각각**: 기존 Runtime **136 checks / 63 rendered parts**, Designer workflow **96 checks**, URL Loader **119 checks** 모두 PASS. 정적 빌드를 사용하여 Vite HMR이 없는 환경에서 검증했다. 해상도 간 이동 시 문서를 새로 열었고, 각 검증 안의 Theme 변경은 동일 document에서 reload 0으로 수행했다. - Default baseline 색상/geometry parity, 전체 73개 기존 계약, 실제 Checkbox/Calendar Focus, 독립 핵심 역할, partial/reset/invalid/cleanup, JSON Export→Import, HTTP Default/External load, Abort와 응답 race를 확인했다. - 실제 Editor의 Load Default JSON / Load Service Example JSON 버튼을 클릭해 즉시 반영을 확인했다. 기존 Badge Story로 이동한 뒤에도 Primary `rgb(104,55,168)`과 Destructive `rgb(212,20,102)`가 세션에서 유지되었다. - 실제 body Select Portal의 selected 면은 Accent yellow 45% `color(srgb 1 0.827451 0.427451 / 0.45)`, Date Picker selected는 Active `rgb(0,127,115)`였다. Combobox/Dropdown/Context/MenuBar의 실제 highlight와 Table/DataTable selected도 같은 Accent 45%였다. - 별도 임시 fixture의 **8개 generation/artifact checks** PASS: stale fallback 검사, JSON 역할→CSS 생성, 나머지 geometry/Default 유지, Theme-only 산출 시 UI 파일 불변, 최신 운영 JSON 포함, 삭제된 source의 stale artifact 제거, 실제 workspace 원본 미변경. - JSON 텍스트 Export→Import는 검증했다. 파일 Export는 DOM download 링크와 Blob 수명 처리를 구현했으나 내장 브라우저가 download 완료 이벤트를 반환하지 않아 파일 저장 완료는 확인하지 못했다. UI는 다운로드 요청으로 표시하고 Show JSON 직접 복사 경로를 제공한다. 클립보드 권한도 환경에 따라 제한될 수 있다. - 실제 회사 CDN/CORS/캐시·registry publish는 미검증이다. HTTP 검증은 localhost의 정적 Theme JSON으로 CDN delivery를 모사했다. 브라우저/실기기 전체 호환성을 검증했다는 의미는 아니다. 기존 CSS 예시 최적화 warning 1개, Storybook chunk 크기/성능 안내는 비차단으로 유지한다. 처음 sandbox에서 esbuild spawn EPERM이 발생한 빌드는 승인된 실행 환경에서 다시 수행하여 통과했다. 검증 중 HEX3 직렬화/검사 대기 문제는 검증 코드에 보완했고 최종 PASS 결과만 기록했다. 증거: `_local/designer-typecheck.log`, `designer-build.log`, `designer-storybook-build.log`, `designer-runtime-verification.json`, `theme-generation-verification.json`, `designer-external-preview.jpg`. 이전 감사 CSV/136 checks 결과를 복사한 것이 아니라 현재 정적 빌드 output에서 직접 읽었다. ## 20. 최종 판정 **COMPLETE — 요청한 Runtime/JSON/독립 artifact/Preview 구현 범위.** - External override: PASS. - 디자인팀 실시간 JSON Preview/편집/Reset/공식 JSON 전달: PASS. JSON 텍스트 Export/Import로 전달 가능하다. 파일 다운로드 완료는 위 환경 제한을 구분한다. - A. 한 Git Repository인가: **YES**, baseline main/commit 확인. - B. UI와 Theme 배포 단위가 분리되는가: **YES**, `build:themes`는 JSON만 산출한다. UI/CENTER는 독립 workspaces. - C. 컬러 Source of Truth: **`themes/default.json` / 서비스별 top-level `themes/*.json`**. package CSS/Default 데이터는 생성물이고 template는 alias/geometry만 소유한다. - D. UI package 재배포 없이 Theme 변경 가능한가: **YES**, 변경 JSON을 독립 배포하고 Loader를 다시 호출한다. - E. CDN JSON으로 적용 가능한가: **YES**, 소비 서비스의 설정 URL을 공식 fetch/load API에 전달한다. 실제 회사 CDN은 미정/미배포. - F. CDN 실패 시 Default 유지되는가: **YES**, 기존 host override가 없는 통상 root에서 package의 HM Default snapshot을 복원한다. host 소유 inline 값이 있었다면 기존 정책대로 그 값을 복원한다. - G. Default/External이 같은 계약/Loader를 사용하는가: **YES**, 동일 v1 JSON과 fetch/validation/Apply 경로; supplied Default + partial resolve도 제공한다. 실제 CDN 인프라/업로드, registry release, Git commit/push는 실행하지 않았다. 이러한 운영 배포는 이번 범위와 분리하며 Theme-only artifact와 절차는 준비했다. Scoped Portal 자동 전달은 기존 FOLLOW_UP을 유지한다. ## 작업 시간 (KST) - Started At: 2026-10-06 15:41:55 KST - Completed At: 2026-10-06 16:16:53 KST - Actual Duration: 34분 58초 - Estimated Duration: 45~70분 (범위 수정 후 50~75분) - Variance: 최초 예상보다 10분 2초~35분 2초 단축