@simplysm/sd-claude 14.0.72 → 14.0.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/claude/references/sd-simplysm14/README.md +2 -2
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +50 -47
  3. package/claude/references/sd-simplysm14/apis/angular/buttons.md +13 -3
  4. package/claude/references/sd-simplysm14/apis/angular/forms.md +20 -9
  5. package/claude/references/sd-simplysm14/apis/angular/kanban.md +6 -3
  6. package/claude/references/sd-simplysm14/apis/angular/layout.md +18 -12
  7. package/claude/references/sd-simplysm14/apis/angular/modal.md +2 -2
  8. package/claude/references/sd-simplysm14/apis/angular/routing.md +3 -2
  9. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +2 -1
  10. package/claude/references/sd-simplysm14/apis/angular/sheet.md +1 -1
  11. package/claude/references/sd-simplysm14/apis/angular/visual.md +26 -8
  12. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +1 -1
  13. package/claude/references/sd-simplysm14/apis/core-browser/README.md +39 -8
  14. package/claude/references/sd-simplysm14/apis/core-common/README.md +87 -9
  15. package/claude/references/sd-simplysm14/apis/core-node/README.md +11 -2
  16. package/claude/references/sd-simplysm14/apis/excel/README.md +17 -3
  17. package/claude/references/sd-simplysm14/apis/lint/README.md +9 -0
  18. package/claude/references/sd-simplysm14/apis/orm-common/README.md +35 -10
  19. package/claude/references/sd-simplysm14/apis/orm-node/README.md +15 -5
  20. package/claude/references/sd-simplysm14/apis/sd-claude/README.md +3 -3
  21. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +37 -7
  22. package/claude/references/sd-simplysm14/apis/service-client/README.md +56 -24
  23. package/claude/references/sd-simplysm14/apis/storage/README.md +3 -1
  24. package/package.json +1 -1
@@ -48,8 +48,8 @@ ORM 호출, 파일 변환, 비즈니스 로직 등은 위 두 사유에 해당
48
48
  - **lint** — ESLint flat config 작성 시 `@simplysm/lint/eslint-recommended` 프리셋 또는 `@simplysm/lint/eslint-plugin` 개별 규칙 import. 자세히: [apis/lint/README.md](./apis/lint/README.md)
49
49
  - **orm-common** — Dialect 독립 ORM 코어. `DbContext` 서브클래싱 + Table/View/Procedure 빌더 + `expr` AST + `Queryable` 체이닝으로 SQL 을 만들고 dialect QueryBuilder 로 렌더. 자세히: [apis/orm-common/README.md](./apis/orm-common/README.md)
50
50
  - **orm-node** — Node 환경에서 `DbContext` 를 MSSQL/MySQL/PostgreSQL 실 연결에 붙이는 어댑터(`createOrm`), raw SQL/bulk insert (`createDbConn`). 자세히: [apis/orm-node/README.md](./apis/orm-node/README.md)
51
- - **sd-claude** — `.claude/` 자산 배포 및 `sd-claude` CLI 제공. 라이브러리 코드 API 없음(npm 배포 전용). 자세히: [apis/sd-claude/README.md](./apis/sd-claude/README.md)
52
- - **sd-cli** — `sd.config.ts` 작성 타입, Vitest 의 Angular AOT plugin(`sdAngularPlugin`), TS 패키지 증분 컴파일 엔진(`SdTsCompiler`). 자세히: [apis/sd-cli/README.md](./apis/sd-cli/README.md)
51
+ - **sd-claude** — simplysm 표준 Claude 자산(스킬·룰·훅) 동기화Claude 계정 저장/전환 CLI(`sd-claude auth save`/`switch`). 라이브러리 코드 API 없음. 자세히: [apis/sd-claude/README.md](./apis/sd-claude/README.md)
52
+ - **sd-cli** — simplysm 빌드/배포 오케스트레이션 CLI. 서브커맨드(`check`/`watch`/`dev`/`device`/`build`/`publish`/`replace-deps`/`init`), `sd.config.ts` 작성 타입, Vitest 의 Angular AOT plugin(`sdAngularPlugin`), TS 패키지 증분 컴파일 엔진(`SdTsCompiler`). 자세히: [apis/sd-cli/README.md](./apis/sd-cli/README.md)
53
53
  - **service-client** — `@simplysm/service-server` 와 WebSocket 으로 통신하는 클라이언트. RPC·이벤트 구독·파일 업/다운로드·원격 ORM 실행을 단일 `ServiceClient` 에서 제공(Node/브라우저 공용). 자세히: [apis/service-client/README.md](./apis/service-client/README.md)
54
54
  - **service-common** — 서버/클라이언트가 공유하는 서비스 프로토콜·메시지·서비스 인터페이스·앱 구조·이벤트 정의. 자세히: [apis/service-common/README.md](./apis/service-common/README.md)
55
55
  - **service-server** — Fastify + WebSocket 위에 서비스(`defineService`)·JWT 인증(`auth`)·빌트인 ORM/AutoUpdate/AppStructure·V1 레거시 호환을 부트스트랩(`createServiceServer().listen()`). 자세히: [apis/service-server/README.md](./apis/service-server/README.md)
@@ -4,29 +4,29 @@ Angular 21 기반 클라이언트 UI 라이브러리. 폼/리스트/시트/모
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **앱 부트스트랩** — `provideSdAngular`, `SdAngularConfigProvider`, `setupBgTheme`, `SdSystemLogProvider`. 부트스트랩/루트 컴포넌트에서 1회.
8
- - **테마 (`SdThemeProvider`, `SdThemeSelector`)** — dark/fontSize 토글. `provideSdAngular`가 localStorage 자동 연동.
9
- - **라우팅/페이지 컨텍스트** — 자세히: [routing.md](./routing.md). `SdRouterLink`, `injectViewTypeSignal`, `setupCanDeactivate` 등.
10
- - **앱 구조/메뉴/권한** — 자세히: [app-structure.md](./app-structure.md). `SdAppStructureProvider`, `injectPermsSignal`, `SdMenu`.
11
- - **모달** — 자세히: [modal.md](./modal.md). `SdModalProvider`, `SdModal`, `SdPromptModal`/`SdConfirmModal`, `SdAddressSearchModal`.
12
- - **토스트** — 자세히: [toast.md](./toast.md). `SdToastProvider`(info/success/warning/danger/notify/try).
13
- - **버튼류** — 자세히: [buttons.md](./buttons.md). `SdButton`, `SdAnchor`, `SdAdditionalButton`, `SdModalSelectButton`.
14
- - **입력/폼 컨트롤** — 자세히: [forms.md](./forms.md). `SdForm`, `SdTextfield`/`SdTextarea`/`SdNumpad`/`SdRange`/`SdDateRangePicker`, `SdCheckbox`/`SdSwitch`/`SdCheckboxGroup(Item)`.
15
- - **드롭다운/셀렉트** — 자세히: [select-dropdown.md](./select-dropdown.md). `SdDropdown(Popup)`, `SdSelect(Item|Button)`.
16
- - **레이아웃 (사이드바/탑바)** — 자세히: [layout.md](./layout.md). `SdSidebar*`, `SdTopbar*`.
17
- - **시트(테이블)** — 자세히: [sheet.md](./sheet.md). `SdSheet`, `SdSheetColumn`, `SdSheetColumnCellTemplate`, `SdSheetConfig*`.
18
- - **CRUD 화면 골격** — 자세히: [crud.md](./crud.md). `SdBaseContainer`, `SdCrudList`, `SdCrudDetail`.
19
- - **서버 공유 데이터 (`SharedData*` + 선택 컨트롤)** — 자세히: [shared-data.md](./shared-data.md). `SdSharedDataProvider`, `SdSharedDataSelect(Button|List)`, `matchesSearchText`.
20
- - **선택/확장/정렬 매니저 훅** — 자세히: [selection-managers.md](./selection-managers.md). `useSelectionManager`, `useExpandingManager`, `useSortingManager`.
21
- - **칸반 보드** — 자세히: [kanban.md](./kanban.md). `SdKanbanBoard`, `SdKanban`, `SdKanbanLane`.
22
- - **시각 컴포넌트 (라벨/노트/프로그레스/달력/바코드/차트)** — 자세히: [visual.md](./visual.md).
23
- - **인프라 프로바이더 (서비스 클라이언트/파일 다이얼로그/스토리지/시스템 설정/인쇄)** 자세히: [infrastructure.md](./infrastructure.md).
24
- - **`SdBusyContainer` / `SdBusyProvider`** — busy 오버레이. `provideSdAngular`가 라우터 navigation 동안 globalBusyCount 자동 증감.
25
- - **`SdPermissionTable`** — `SdPermission<TModule>[]` 트리를 `Record<string, boolean>` 모델로 편집하는 권한표.
26
- - **`SdStatePreset` / `SdStatePresetDef`** — 화면 상태(`state`)를 키별 프리셋으로 저장/복원. `SdSystemConfigProvider` 활용.
27
- - **`SdTiptapEditor`** — Tiptap 기반 리치 에디터. `value`, `extensions`, `validatorFn`.
28
- - **유틸 디렉티브/파이프 등** 인라인 항목 참조.
29
- - **유틸 타입/함수**인라인 항목 참조.
7
+ - **앱 부트스트랩** — 루트 컴포넌트에서 `provideSdAngular`로 zoneless·테마·에러 핸들러·`.capture/.passive/.once` 이벤트 변형·라우터 navigation busy 자동 등록. `SdAngularConfigProvider`(clientName), `setupBgTheme`(body 배경 토글), `SdSystemLogProvider`(severity별 로그 후크). 본문 인라인.
8
+ - **테마** `SdThemeProvider`(dark/fontSize 신호 + localStorage 자동 영속화), `SdThemeSelector`(토글 UI). 본문 인라인.
9
+ - **라우팅/페이지 컨텍스트** — 페이지 코드(`a.b.c`)·뷰 타입(`page|modal|control`)·새창 navigation·`SdRouterLink`·canDeactivate 헬퍼. 자세히: [routing.md](./routing.md).
10
+ - **앱 구조/메뉴/권한** — 서버 `AppStructureService` 트리를 받아 메뉴/권한 신호로 노출. `SdAppStructureProvider`·`injectPermsSignal`·`SdMenu`/`SdFlatMenu`/`SdPermission`. 자세히: [app-structure.md](./app-structure.md).
11
+ - **모달** — 프로그래밍 `SdModalProvider.showAsync` 또는 선언형 `<sd-modal>`. 내장 `SdPromptModal`/`SdConfirmModal`/`SdAddressSearchModal`. 자세히: [modal.md](./modal.md).
12
+ - **토스트** — `SdToastProvider`로 info/success/warning/danger 전역 알림 + 커스텀 컴포넌트 `notify` + 에러 자동 처리 `try`. 자세히: [toast.md](./toast.md).
13
+ - **버튼류** — 일반 `SdButton`, 링크형 `SdAnchor`, 본문+부가 `SdAdditionalButton`, 모달 검색 선택 `SdModalSelectButton`. 자세히: [buttons.md](./buttons.md).
14
+ - **입력/폼 컨트롤** — `<sd-form>` 안에 텍스트/숫자/날짜/체크박스 류 컨트롤. `SdTextfield`, `SdTextarea`, `SdNumpad`, `SdRange`, `SdDateRangePicker`, `SdCheckbox`/`SdSwitch`/`SdCheckboxGroup(Item)`. 자세히: [forms.md](./forms.md).
15
+ - **드롭다운/셀렉트** — 트리거+팝업 `SdDropdown(Popup)`, 옵션 선택 `SdSelect(Item|Button)`. 자세히: [select-dropdown.md](./select-dropdown.md).
16
+ - **레이아웃 (사이드바/탑바)** — 풀 화면 `<sd-sidebar-container>` 와 상단바 컨테이너. `SdSidebar*`, `SdTopbar*`. 자세히: [layout.md](./layout.md).
17
+ - **시트(테이블)** — 가상 스크롤 데이터 그리드. 컬럼 정의·정렬·페이징·선택·확장 트리·설정 모달 내장. `SdSheet`, `SdSheetColumn`, `SdSheetColumnCellTemplate`, `SdSheetConfigModal`. 자세히: [sheet.md](./sheet.md).
18
+ - **CRUD 화면 골격** — 페이지/모달 공통 컨테이너 + 리스트/디테일 골격. `SdBaseContainer`, `SdCrudList`, `SdCrudDetail`. 자세히: [crud.md](./crud.md).
19
+ - **서버 공유 데이터 (코드성 마스터)** — 부서·거래처 등 키 기반 등록·구독·자동 부분 갱신. `SdSharedDataProvider`, `SdSharedDataSelect(Button|List)`, `matchesSearchText`. 자세히: [shared-data.md](./shared-data.md).
20
+ - **선택/확장/정렬 매니저 훅** — `<sd-sheet>`/`<sd-select>` 내부 로직을 외부 컴포넌트에서 재사용. `useSelectionManager`, `useExpandingManager`, `useSortingManager`. 자세히: [selection-managers.md](./selection-managers.md).
21
+ - **칸반 보드** — 드래그·드롭 카드 보드. `SdKanbanBoard`, `SdKanban`, `SdKanbanLane`. 자세히: [kanban.md](./kanban.md).
22
+ - **시각 컴포넌트**라벨 `SdLabel`, 알림 박스 `SdNote`, 진행률 `SdProgress`, 월별 달력 `SdCalendar`, 바코드 `SdBarcode`, ECharts 래퍼 `SdEcharts`. 자세히: [visual.md](./visual.md).
23
+ - **인프라 프로바이더** 서비스 클라이언트 팩토리 `SdServiceClientFactoryProvider`, 파일 다이얼로그 `SdFileDialogProvider`, localStorage 래퍼 `SdLocalStorageProvider`, 시스템 설정 `SdSystemConfigProvider` + `injectSdSystemConfigResource`, 인쇄/PDF `SdPrintProvider`, 글로벌 에러 핸들러/이벤트 플러그인. 자세히: [infrastructure.md](./infrastructure.md).
24
+ - **`SdBusyContainer` / `SdBusyProvider`** — busy 오버레이 컴포넌트와 전역 busy 카운트. 본문 인라인.
25
+ - **`SdPermissionTable`** — 권한 트리 편집 표. `SdPermission<TModule>[]` 입력 `Record<string, boolean>` 모델. 본문 인라인.
26
+ - **`SdStatePreset` / `SdStatePresetDef`** — 화면 상태 프리셋 저장/복원. 본문 인라인.
27
+ - **`SdTiptapEditor`** — 리치 텍스트 에디터. 본문 인라인.
28
+ - **유틸 디렉티브/파이프**이벤트(`SdResize`/`SdIntersection`/`SdEvents`)·단축키(`SdCommandDirective`)·표시 효과(`SdRipple`/`SdShowEffect`)·검증(`SdInvalid`)·템플릿(`SdTypedTemplate`/`SdItemOfTemplate`)·포맷(`FormatPipe`)·spacer(`SdGap`)·접힘(`SdCollapse`/`SdCollapseIcon`)·탭(`SdTab`/`SdTabItem`)·리스트(`SdList`/`SdListItem`)·페이저(`SdPagination`)·signal 헬퍼(`mark`/`setSafeStyle`/`setupModelHook`). 본문 인라인.
29
+ - **유틸 타입**`DirectiveInputSignals`/`UndefToOptional`/`WithOptional`/`SelectModalOutputResult`. 본문 인라인.
30
30
 
31
31
  ## 앱 부트스트랩
32
32
 
@@ -36,60 +36,63 @@ bootstrapApplication(AppComponent, {
36
36
  });
37
37
  ```
38
38
 
39
- - `provideSdAngular({ clientName })`: zoneless CD, NgIcons config, 글로벌 에러 핸들러(`SdGlobalErrorHandlerPlugin`), `SdOptionEventPlugin`(이벤트 `.capture`/`.passive`/`.once`), 테마 dark/fontSize ↔ localStorage 동기화, SwUpdate 폴링, Router navigation 중 `SdBusyProvider.globalBusyCount` 자동 ±1.
39
+ - `provideSdAngular({ clientName })`: zoneless CD, NgIcons config, 글로벌 에러 핸들러(`SdGlobalErrorHandlerPlugin`), `SdOptionEventPlugin`(이벤트 `.capture`/`.passive`/`.once`), 테마 dark/fontSize ↔ localStorage 동기화, SwUpdate 폴링(실패 시 백오프 5분→1시간), Router navigation 중 `SdBusyProvider.globalBusyCount` 자동 ±1.
40
40
  - `SdAngularConfigProvider.clientName`: localStorage 키 prefix·service client name 으로 사용. `provideSdAngular` 옵션에서 설정됨.
41
- - `SdSystemLogProvider.writeFn?`: severity별 로그 후크 등록(서버 전송 등). 자동으로 console에도 출력.
42
- - `setupBgTheme({ theme?, lightness? })`: 컴포넌트 constructor 내에서 호출. body `--background-color` CSS 변수 토글, 파괴 시 자동 복원.
41
+ - `SdSystemLogProvider.writeFn?: (severity: "error"|"warn"|"log", ...data) => Promise<void> | void`: severity별 로그 후크 등록(서버 전송 등). 호출 console에도 항상 출력.
42
+ - `setupBgTheme({ theme?, lightness? })`: 컴포넌트 constructor 내에서 호출. body `--background-color` CSS 변수 토글, destroy 시 자동 복원. `theme: "primary"|"secondary"|"info"|"success"|"warning"|"danger"|"gray"|"blue-gray"`, `lightness: "lightest"|"lighter"` (default `"lightest"`).
43
43
 
44
44
  ## 테마
45
45
 
46
- - `SdThemeProvider`: `dark = signal<boolean>`, `fontSize = signal<number>` (presets `[12,14,16,20,24,28]`), `increaseFontSize()`/`decreaseFontSize()`. dark body에 `sd-theme-dark` 클래스, fontSize는 html `font-size` 적용. `provideSdAngular`가 localStorage 영속화.
47
- - `SdThemeSelector`: 토글 UI 컴포넌트.
46
+ - `SdThemeProvider`: `dark = signal<boolean>`, `fontSize = signal<number>` (presets `[12,14,16,20,24,28]`), `increaseFontSize()`/`decreaseFontSize()`. dark=true body에 `sd-theme-dark` 클래스, fontSize는 html `font-size` 적용. `provideSdAngular`가 localStorage 영속화.
47
+ - `SdThemeSelector`: dropdown 형 토글 UI (글자 크기 ± 버튼 + dark 스위치). 별도 입력 없음.
48
48
 
49
49
  ## SdBusyContainer / SdBusyProvider
50
50
 
51
- `<sd-busy-container [busy] [message] [type] [progressPercent]>`. type: `"spinner" | "bar" | "cube"`. `busy=true` 동안 keydown 차단.
52
- `SdBusyProvider.globalBusyCount = signal(0)`. > 0이면 body 전면 오버레이.
51
+ `<sd-busy-container [busy] [message] [type] [progressPercent]>`. type: `"spinner" | "bar" | "cube"` (default = `SdBusyProvider.type()` = `"bar"`). `busy=true` 동안 keydown 차단.
52
+
53
+ `SdBusyProvider`:
54
+ - `globalBusyCount = signal(0)`. > 0이면 body 전면 오버레이.
55
+ - `type = signal<SdBusyType>("bar")`: 전역 busy 컨테이너의 기본 type. 변경 시 즉시 반영.
53
56
 
54
57
  ## SdPermissionTable
55
58
 
56
- `<sd-permission-table [items] [(value)] [disabled]>`. `items: SdPermission<TModule>[]` (`SdAppStructureUtils.getPermissions` 결과), `value: Record<string, boolean>` (코드.use|edit 형식).
59
+ `<sd-permission-table [items] [(value)] [disabled]>`. `items: SdPermission<TModule>[]` (`SdAppStructureUtils.getPermissions` 결과), `value: Record<string, boolean>` (`<fullCode>.use|edit` 형식). 트리 접기/펼치기 + 그룹 일괄 토글.
57
60
 
58
61
  ## SdStatePreset
59
62
 
60
- `<sd-state-preset [key] [(state)] [size]>`. `state` `SdSystemConfigProvider` `key` 아래 프리셋 배열로 저장/로드. 별 아이콘 클릭 → 이름 입력 모달 저장.
63
+ `<sd-state-preset [key] [(state)] [size]>`. `key` 신호 아래 `SdStatePresetDef[]` = `{ name, state }[]` `SdSystemConfigProvider` 저장. 별 아이콘 클릭 → 이름 프롬프트추가. 각 프리셋 클릭 시 `state.set(obj.clone(preset.state))`.
61
64
 
62
65
  ## SdTiptapEditor
63
66
 
64
- `<sd-tiptap-editor [(value)] [disabled] [readonly] [required] [placeholder] [validatorFn] [extensions]>`. value=HTML string. `extensions?: AnyExtension[]` 으로 Tiptap 확장 주입.
67
+ `<sd-tiptap-editor [(value)] [disabled] [readonly] [required] [placeholder] [validatorFn] [extensions]>`. value=HTML string. 기본 extensions: StarterKit + TextStyle + Color + Highlight + TextAlign + Image + Underline. `extensions?: AnyExtension[]` 으로 Tiptap 확장 주입(기본 위에 추가).
65
68
 
66
69
  ## 유틸 디렉티브/파이프
67
70
 
68
71
  이벤트·표시 보조용. constructor injection 또는 셀렉터 attach.
69
72
 
70
73
  - `SdOptionEventPlugin`: `(click.capture)`, `(scroll.passive)`, `(touchmove.capture.passive)`, `(transitionend.once)` 등 `.capture/.passive/.once` 변형 이벤트 바인딩 활성화. `provideSdAngular`로 자동 등록.
71
- - `SdResizeDirective` (`[sdResize]`): RO 기반 size 변화 emit (`{ heightChanged, widthChanged, target, contentRect }`).
72
- - `SdIntersectionDirective` (`[sdIntersection]`): IO entry emit.
73
- - `SdEvents`: 다양한 native event의 `.capture`/`.passive`/`.once` output 디렉티브 (예: `(scroll.passive)`, `(touchstart.passive)`).
74
- - `SdCommandDirective`: `[sdRefreshCommand]`/`[sdSaveCommand]`/`[sdInsertCommand]` Ctrl+Alt+L/Ctrl+S/Insert 단축키 emit. 최상위 열린 모달 또는 모달이 없을 때만 처리.
74
+ - `SdResizeDirective` (`[sdResize]`): ResizeObserver 기반 size 변화 emit (`SdResizeEvent { heightChanged, widthChanged, target, contentRect }`). requestAnimationFrame 디바운스.
75
+ - `SdIntersectionDirective` (`[sdIntersection]`): IntersectionObserver entry emit (`SdIntersectionEvent { entry }`).
76
+ - `SdEvents`: 다양한 native event의 `.capture`/`.passive`/`.once` output 디렉티브 (예: `(scroll.passive)`, `(touchstart.passive)`, `(focus.capture)`, `(transitionend.once)`).
77
+ - `SdCommandDirective` (`[sdRefreshCommand]`/`[sdSaveCommand]`/`[sdInsertCommand]`): Ctrl+Alt+L/Ctrl+S/Insert 단축키 emit. 최상위 열린 모달 내부 또는 모달 없는 페이지일 때만 처리.
75
78
  - `SdRipple` (`[sdRipple]="bool"`) / `setupRipple(enableFn?)`: pointerdown 시 원형 ripple.
76
79
  - `SdShowEffect` (`[sdShowEffect]="bool"` + `[sdShowEffectType]="'l2r'|'t2b'"`) / `setupRevealOnShow`: viewport intersection 시 fade-in.
77
- - `SdInvalid` (`[sdInvalid]="msg"`) / `setupInvalid(getMsg)`: hidden input의 customValidity 로 form 검증 + 표시 인디케이터.
78
- - `SdTypedTemplate` (`<ng-template [typed]>`): template context 타입 추론용 (typeToken).
79
- - `SdItemOfTemplate` (`<ng-template [itemOf]>`, ctx `{ $implicit, item, index, depth }`) — 컬렉션 컴포넌트 항목 템플릿.
80
- - `FormatPipe` (`{{ v | format:fmt }}`): `DateTime`/`DateOnly`는 `toFormatString(fmt)`, string은 `X` 자리표시(예: `'XXX-XXXX-XXXX'`).
81
- - `SdGap`: spacer. `height|width|widthEm` 또는 `heightPx|widthPx`. 단위 키: `xxs|xs|sm|default|lg|xl|xxl`.
82
- - `SdCollapse [open]` / `SdCollapseIcon`: 접힘.
80
+ - `SdInvalid` (`[sdInvalid]="msg"`) / `setupInvalid(getMsg)`: hidden input의 customValidity 로 form 검증 + 좌상단 빨간 점 인디케이터.
81
+ - `SdTypedTemplate` (`<ng-template [typed]="typeToken">`): template context 타입 추론용.
82
+ - `SdItemOfTemplate` (`<ng-template [itemOf]="items">`, ctx `SdItemOfTemplateContext { $implicit, item, index, depth }`) — 컬렉션 컴포넌트(`SdSelect`/`SdCalendar` 등) 항목 템플릿.
83
+ - `FormatPipe` (`{{ v | format:fmt }}`): `DateTime`/`DateOnly`는 `toFormatString(fmt)`, string은 `X` 자리표시(예: `'XXX-XXXX-XXXX'`, `|` 로 다중 길이).
84
+ - `SdGap`: spacer. `height|width` 단위 키(`xxs|xs|sm|default|lg|xl|xxl`), `heightPx|widthPx|widthEm` 픽셀/em. 값 0이면 `display:none`.
85
+ - `SdCollapse [open]` / `SdCollapseIcon [open] [openRotate=90] [icon]`: 접힘 패널/아이콘.
83
86
  - `SdTab [(value)]`/`SdTabItem [value]`: 탭.
84
- - `SdList`/`SdListItem`: 리스트. `SdListItem` `layout: "accordion"|"flat"`, `selectedIcon`, `contentStyle/Class`.
85
- - `SdPagination [(currentPage)] [totalPageCount] [visiblePageCount=10]`.
87
+ - `SdList [inset]`/`SdListItem [layout="accordion"|"flat"] [open] [selected] [selectedIcon] [readonly] [contentStyle/Class]` + `<ng-template #toolTpl>` (옵션 도구 영역).
88
+ - `SdPagination [(currentPage)] [totalPageCount] [visiblePageCount=10]`. 그룹 단위 페이지 이동.
86
89
  - `mark(signal)`: array/object signal의 in-place mutation 후 shallow copy로 trigger.
87
90
  - `setSafeStyle(renderer, el, partial)`: renderer.setStyle 일괄.
88
- - `setupModelHook(model, canFn)`: WritableSignal의 set/update를 `canFn(value) -> boolean | Promise<boolean>`로 가로채기. constructor 내에서 호출.
91
+ - `setupModelHook(model, canFnSignal)`: WritableSignal의 set/update를 `canFn(value) -> boolean | Promise<boolean>`로 가로채기. `canFn` 자체가 `Signal<...>` 형식 (`input<(v)=>...>()` 그대로 전달). constructor 내에서 호출.
89
92
 
90
93
  ## 유틸 타입/기타
91
94
 
92
- - `DirectiveInputSignals<T>`: 컴포넌트의 InputSignal 프로퍼티만 추출(`{ name: T }`). undefined 필드는 optional.
95
+ - `DirectiveInputSignals<T>`: 컴포넌트의 InputSignal 프로퍼티만 추출(`{ name: T }`). undefined 포함 필드는 optional.
93
96
  - `UndefToOptional<T>`: undefined 포함 필드를 optional 로 변환.
94
97
  - `WithOptional<T, K>`: 특정 키만 optional 로.
95
98
  - `SelectModalOutputResult<TKey> = { selectedKeys: TKey[] }`: 모달 선택 결과.
@@ -10,7 +10,7 @@
10
10
 
11
11
  ## `<sd-anchor>`
12
12
 
13
- 링크형 클릭 요소. `disabled`, `theme`(`primary|secondary|...|blue-gray`, default `primary`). tabindex 자동.
13
+ 링크형 클릭 요소. `disabled`, `theme`(`primary|secondary|...|blue-gray`, default `primary`). 비활성 시 tabindex 제거.
14
14
 
15
15
  ```html
16
16
  <sd-anchor [theme]="'danger'" (click)="del()">삭제</sd-anchor>
@@ -18,7 +18,14 @@
18
18
 
19
19
  ## `<sd-additional-button>`
20
20
 
21
- 본문 + 우측 부가 버튼 슬롯. `size`.
21
+ 본문 영역 + 우측 부가 버튼 슬롯(`<sd-anchor>`, `<sd-button>` projection). `size: "sm"|"lg"`, `inset`.
22
+
23
+ ```html
24
+ <sd-additional-button>
25
+ 본문 내용
26
+ <sd-button (click)="...">+</sd-button>
27
+ </sd-additional-button>
28
+ ```
22
29
 
23
30
  ## `<sd-modal-select-button>`
24
31
 
@@ -32,6 +39,8 @@
32
39
  [modalOptions]="{ resizable: true }"
33
40
  [required]="true"
34
41
  [disabled]="false"
42
+ [size]="'sm'"
43
+ [inset]="false"
35
44
  [searchIcon]="customIcon">
36
45
  {{ displayLabel() }}
37
46
  </sd-modal-select-button>
@@ -39,4 +48,5 @@
39
48
 
40
49
  - `SdSelectModal<TKey>` = `SdModalContentDef<SelectModalOutputResult<TKey>>` + `selectMode: InputSignal<"single"|"multi"|undefined>` + `selectedKeys: InputSignal<TKey[]>`.
41
50
  - `SdSelectModalInfo<T>` = `SdModalInfo<T, "selectMode"|"selectedKeys">`.
42
- - erase 아이콘 클릭 시 `value` 초기화(`undefined` 또는 `[]`).
51
+ - value 가 있고 required 가 아니면 좌측에 erase 아이콘 노출 → 클릭 시 초기화(single 은 `undefined`, multi `[]`).
52
+ - 검색 버튼 클릭 → `SdModalProvider.showAsync` 호출, 결과 `{ selectedKeys }` 를 `selectMode` 에 맞춰 `value` 에 반영.
@@ -13,7 +13,7 @@
13
13
 
14
14
  - 내부에 hidden submit 버튼 자동 삽입(Enter 키 submit 지원).
15
15
  - `requestSubmit()` 메서드 노출.
16
- - invalid 시 reportValidity → `formInvalid` emit.
16
+ - invalid 시 `reportValidity` → `formInvalid` emit, valid 시 `formSubmit` emit.
17
17
 
18
18
  ## `<sd-textfield<K>>`
19
19
 
@@ -25,23 +25,28 @@
25
25
 
26
26
  `SdTextfieldTypes`: `number→number`, `text|password|color|email|format→string`, `date|month|year→DateOnly`, `datetime|datetime-sec→DateTime`, `time|time-sec→Time`. `sdTextfieldTypes` 배열로 키 목록 enum.
27
27
 
28
- 주요 input: `value` (model), `type` (required), `placeholder`, `title`, `inputStyle/Class`, `disabled/readonly/required` (booleanAttribute), `min/max/minlength/maxlength`, `pattern`, `validatorFn(value) => string | undefined` (커스텀 메시지), `format`, `step`, `autocomplete`, `minDigits`, `size`, `theme`.
28
+ 주요 input: `value` (model), `type` (required), `placeholder`, `title`, `inputStyle/Class`, `disabled/readonly/required` (booleanAttribute), `min/max/minlength/maxlength`, `pattern`, `validatorFn(value) => string | undefined` (커스텀 메시지), `format`, `step`, `autocomplete`, `useNumberComma` (default `true` — 숫자 표시 시 천단위 콤마), `minDigits` (display 시 최소 자릿수), `size`(`"sm"|"lg"`), `inline`, `inset`, `theme`.
29
+
30
+ readonly/disabled 상태에서는 `<input>` 비표시(`_contents` div만 노출).
29
31
 
30
32
  ## `<sd-textarea>`
31
33
 
32
- `[(value)]: string`, `placeholder`, `title`, `minRows=1`, `size`, `validatorFn`, `theme`, `inputStyle/Class`.
34
+ `[(value)]: string`, `placeholder`, `title`, `minRows=1` (행수는 값 줄바꿈 수와 minRows 중 큰 값), `disabled/readonly/required`, `inline`, `inset`, `size`, `validatorFn`, `theme`, `inputStyle/Class`.
33
35
 
34
36
  ## `<sd-numpad>`
35
37
 
36
- 숫자 키패드. `[(value)]: number`, `placeholder`.
38
+ 숫자 키패드. `[(value)]: number`, `text = signal<string|undefined>` (내부 표시값), `placeholder`, `required`, `inputDisabled` (입력란만 비활성), `useEnterButton` (ENT 버튼 노출), `useMinusButton` (- 버튼 노출), `(enterButtonClick)` 출력.
37
39
 
38
40
  ## `<sd-range<K>>`
39
41
 
40
- `type: K` (TextfieldTypes 키), `[(from)]`, `[(to)]: SdTextfieldTypes[K]`, `inputStyle`.
42
+ `type: K` (TextfieldTypes 키), `[(from)]`, `[(to)]: SdTextfieldTypes[K]`, `required`, `disabled`, `inputStyle`. `to.min`은 `from` 으로 강제.
41
43
 
42
44
  ## `<sd-date-range-picker>`
43
45
 
44
- `[(periodType)]: "일"|"월"|"범위" = "범위"`, `[(from)]: DateOnly`, `[(to)]: DateOnly`.
46
+ `[(periodType)]: "일"|"월"|"범위" = "범위"`, `[(from)]: DateOnly`, `[(to)]: DateOnly`, `required`.
47
+ - `"일"`: 단일 date 입력. `to`는 `from` 따라감.
48
+ - `"월"`: month 입력. `from`은 1일, `to`는 말일 자동 설정.
49
+ - `"범위"`: from~to date 양쪽 입력. from > to 시 to를 from으로 보정.
45
50
 
46
51
  ## `<sd-checkbox>` / `<sd-switch>`
47
52
 
@@ -50,14 +55,20 @@
50
55
  <sd-switch [(value)]="enabled" [size]="'sm'" />
51
56
  ```
52
57
 
53
- `value: boolean` (model), `canChangeFn(value) => boolean | Promise<boolean>` — false 면 변경 차단. `radio` (checkbox만), `disabled`, `size`, `inline`, `inset`, `theme`, `contentStyle` (checkbox).
58
+ `value: boolean` (model), `canChangeFn(value) => boolean | Promise<boolean>` — false 면 변경 차단 (`setupModelHook` 사용). 공통: `disabled`, `size`, `inline`, `inset`, `theme`. checkbox 추가: `radio` (radio 모드 — 다시 클릭해도 false 안 됨), `icon` (체크 아이콘 변경, default `tablerCheck`), `contentStyle`, `theme` 에 `"white"` 옵션 추가(시트 헤더용).
54
59
 
55
60
  ## `<sd-checkbox-group<T>>` / `<sd-checkbox-group-item<T>>`
56
61
 
57
- `<sd-checkbox-group [(value)]="selected"> <sd-checkbox-group-item [value]="opt">{{opt}}</sd-checkbox-group-item> ... </sd-checkbox-group>`. value: `T[]` (다중).
62
+ ```html
63
+ <sd-checkbox-group [(value)]="selected">
64
+ <sd-checkbox-group-item [value]="opt" *ngFor="let opt of options">{{ opt }}</sd-checkbox-group-item>
65
+ </sd-checkbox-group>
66
+ ```
67
+
68
+ `SdCheckboxGroup`: `value: T[]` (model, default `[]`), `disabled`. `SdCheckboxGroupItem`: `value: T` (required), `inline`.
58
69
 
59
70
  ## 검증
60
71
 
61
- - 컨트롤 내부에서 `setupInvalid(getMessage)` 사용 (cf. directives). 빈 문자열이면 valid.
72
+ - 컨트롤 내부에서 `setupInvalid(getMessage)` 사용 빈 문자열이면 valid, 그 외 메시지는 invalid 사유. 좌상단 빨간 점 인디케이터.
62
73
  - `validatorFn` 은 컨트롤 입력값을 검사해 메시지 반환.
63
74
  - `SdForm` submit 시 자동으로 reportValidity 호출 + invalid 인디케이터 표시.
@@ -5,6 +5,7 @@
5
5
  ```html
6
6
  <sd-kanban-board [(selectedValues)]="selected" (drop)="onDrop($event)">
7
7
  <sd-kanban-lane [value]="laneA" [busy]="loading">
8
+ <ng-template #titleTpl>레인 A</ng-template>
8
9
  <sd-kanban [value]="card" [draggable]="true" [selectable]="true">{{ card.title }}</sd-kanban>
9
10
  </sd-kanban-lane>
10
11
  </sd-kanban-board>
@@ -14,15 +15,17 @@
14
15
 
15
16
  - `selectedValues = model<T[]>([])`.
16
17
  - `drop = output<SdKanbanBoardDropInfo<L, T>>` (`{ sourceKanbanValue?, targetLaneValue?, targetKanbanValue? }`).
17
- - `dragKanban = signal<SdKanbanDragRef | undefined>` — 자식이 드래그 시작 시 설정.
18
+ - `dragKanban = signal<SdKanbanDragRef | undefined>` — 자식이 드래그 시작 시 설정. document `dragend` 시 자동 해제.
18
19
 
19
20
  ## `<sd-kanban-lane<L, T>>`
20
21
 
21
- `value: L`, `busy`, `useCollapse`, `collapse` (model). drop target 구현.
22
+ `value: L`, `busy`, `useCollapse`, `collapse` (model, default `false`). drop target 구현. `useCollapse=true` 면 토글 아이콘 노출. 자식 `kanbanControls.filter(selectable)` 가 있으면 전체선택 체크박스 노출.
23
+
24
+ content templates: `<ng-template #titleTpl>` (헤더 영역), `<ng-template #toolTpl>` (전체선택 우측 도구 영역).
22
25
 
23
26
  ## `<sd-kanban<L, T>>`
24
27
 
25
- `value: T`, `draggable`, `selectable`, `contentClass`. drag ref + drop target 둘 다 구현(카드 위에 드롭 가능).
28
+ `value: T`, `draggable`, `selectable`, `contentClass`. drag ref + drop target 둘 다 구현(카드 위에 드롭 가능). Shift+클릭 시 `selectable=true` 인 경우 `selectedValues` 토글.
26
29
 
27
30
  ## 타입
28
31
 
@@ -5,37 +5,43 @@
5
5
  ```html
6
6
  <sd-sidebar-container>
7
7
  <sd-sidebar>
8
- <sd-sidebar-user [userMenu]="userMenu">유저영역</sd-sidebar-user>
8
+ <sd-sidebar-user [userMenu]="userMenu">유저영역 컨텐츠</sd-sidebar-user>
9
9
  <sd-sidebar-menu [menus]="menus" [layout]="'accordion'" [getMenuIsSelectedFn]="isSel" />
10
10
  </sd-sidebar>
11
11
  <ng-content />
12
12
  </sd-sidebar-container>
13
13
  ```
14
14
 
15
- - `SdSidebarContainer`: `toggle = signal(false)`. Router NavigationStart 시 자동 false.
16
- - `SdSidebar`: 부모 container의 toggle 추종.
17
- - `SdSidebarMenu`: `menus: SdMenu[]`, `layout: "accordion"|"flat"`, `getMenuIsSelectedFn?`.
18
- - `SdSidebarUser`: `userMenu?: SdSidebarUserMenu`.
15
+ - `SdSidebarContainer`: `toggle = signal(false)`. 데스크탑은 토글 시 본문 left padding 제거, 모바일(`max-width:520px`)에서는 사이드바가 슬라이드. Router `NavigationStart` 이벤트에 자동 false (페이지 이동 시 자동 닫힘).
16
+ - `SdSidebar`: 부모 container의 toggle 추종. content projection.
17
+ - `SdSidebarMenu`: `menus: SdMenu[]`, `layout: "accordion"|"flat"` (미지정 시 `menus.length <= 3` 이면 `"flat"`, 아니면 `"accordion"` 자동), `getMenuIsSelectedFn?: (menu) => boolean`. 자식 메뉴는 항상 `"accordion"`. `menu.url != null` 이면 클릭 시 새창 open.
18
+ - `SdSidebarUser`: `userMenu?: SdSidebarUserMenu` + 상단 영역 content projection. userMenu.title 클릭 시 메뉴 항목 펼침/접기.
19
19
 
20
20
  ```typescript
21
- interface SdSidebarUserMenu { /* see source */ }
21
+ interface SdSidebarUserMenu {
22
+ title: string;
23
+ menus: { title: string; onClick: () => void }[];
24
+ }
22
25
  ```
23
26
 
24
27
  ## 탑바
25
28
 
26
29
  ```html
27
30
  <sd-topbar-container>
28
- <sd-topbar [sidebarContainer]="sc">
31
+ <sd-topbar>
32
+ <h4>{{ title }}</h4>
29
33
  <sd-topbar-menu [menus]="menus" />
30
- <sd-topbar-user [menus]="userMenus" />
34
+ <sd-topbar-user [menus]="userMenus">유저표시</sd-topbar-user>
31
35
  </sd-topbar>
36
+ <ng-content />
32
37
  </sd-topbar-container>
33
38
  ```
34
39
 
35
- - `SdTopbar`: `sidebarContainer?: SdSidebarContainer` 입력(미지정 시 inject 시도). 햄버거 버튼이 `sc.toggle` 토글.
36
- - `SdTopbarMenu`: `menus: SdMenu[]`, `getMenuIsSelectedFn?`.
37
- - `SdTopbarUser`: `menus: SdTopbarUserMenu[]` (required).
40
+ - `SdTopbarContainer`: flex-column 100% 컨테이너.
41
+ - `SdTopbar`: `sidebarContainer?: SdSidebarContainer` 입력(미지정 시 inject 시도). 사이드바 있으면 햄버거 버튼 노출 → 클릭 시 `sc.toggle` 토글.
42
+ - `SdTopbarMenu`: `menus: SdMenu[]`, `getMenuIsSelectedFn?`. 각 최상위 menu는 dropdown 으로 노출. leaf 클릭 후 dropdown 자동 닫힘.
43
+ - `SdTopbarUser`: `menus: SdTopbarUserMenu[]` (required, `{ title, onClick }[]`) + 트리거 영역 content projection. dropdown 으로 menus 표시, 클릭 후 자동 close.
38
44
 
39
45
  ## 메뉴 데이터
40
46
 
41
- `SdMenu` (`./app-structure.md` 참조)을 그대로 입력. 선택 상태는 `getIsMenuSelected(menu, fullPageCode, customFn?)` 또는 `getMenuIsSelectedFn` 으로.
47
+ `SdMenu` (`./app-structure.md` 참조)을 그대로 입력. 선택 상태는 `getIsMenuSelected(menu, fullPageCode, customFn?)` 또는 `getMenuIsSelectedFn` 으로. leaf 메뉴는 `getMenuRouterLinkOption(menu)` 으로 `[sdRouterLink]` 옵션 자동 생성.
@@ -45,8 +45,8 @@ interface SdModalContentDef<O> {
45
45
 
46
46
  ```typescript
47
47
  const am = inject(SdActivatedModalProvider, { optional: true });
48
- am?.modalComponent(); // SdModal 인스턴스 (title() 등)
49
- am?.contentComponent(); // 컨텐츠 인스턴스
48
+ am?.modalComponent(); // signal<SdModal> (title() 등)
49
+ am?.contentComponent(); // signal<T> 컨텐츠 인스턴스
50
50
  am.canDeactivateFn = () => isClean(); // 닫기 차단 (false 반환 시)
51
51
  ```
52
52
 
@@ -5,11 +5,12 @@ Angular Router 위에 페이지 코드(`a.b.c` 형식)·뷰 타입·새창 네
5
5
  ## `SdRouterLink` 디렉티브
6
6
 
7
7
  ```html
8
- <a [sdRouterLink]="{ link: '/home/order/list', params: { id }, queryParams, outletName, window: { width, height } }">go</a>
8
+ <a [sdRouterLink]="{ link: '/home/order/list', params: { id }, queryParams, outletName, window: { width: 800, height: 600 } }">go</a>
9
9
  ```
10
10
 
11
- - 일반 클릭 → `router.navigate`. Ctrl/Shift 클릭 또는 새창 모드(`isWindow`)일 때 → `SdNavigateWindowProvider.open` 으로 새 창. Alt+click 무시.
11
+ - 일반 클릭 → `router.navigate`. Ctrl/Shift 클릭 또는 현재가 새창 컨텍스트(`SdNavigateWindowProvider.isWindow`)일 때 → `SdNavigateWindowProvider.open` 으로 새 창. Alt+click 무시.
12
12
  - `outletName` 지정 시 named outlet 으로 navigate.
13
+ - `window: { width, height }` 옵션은 isWindow 컨텍스트에서 새 창 features 로만 사용 (Ctrl/Shift 새창에는 미적용).
13
14
 
14
15
  ## `SdNavigateWindowProvider`
15
16
 
@@ -15,12 +15,13 @@ const sm = useSelectionManager({
15
15
  sm.hasSelectable; sm.isAllSelected;
16
16
  sm.getSelectable(item); // true | string(reason) | undefined
17
17
  sm.select(item); sm.deselect(item); sm.toggle(item); sm.toggleAll();
18
- sm.isSelected(item); sm.getCanChangeFn(item);
18
+ sm.isSelected(item); sm.getCanChangeFn(item); // () => boolean (체크박스 canChangeFn 으로 직결)
19
19
  ```
20
20
 
21
21
  - `single`이면 select 시 기존 키 대체. `multi` 면 추가.
22
22
  - 키 비교는 `obj.equal` (`@simplysm/core-common`).
23
23
  - `trackByFn` 반환이 `null`이면 선택 불가.
24
+ - `hasSelectable`은 `selectMode != null` 여부 (선택 가능 모드인지). 선택 가능한 아이템 존재 여부는 `isAllSelected` 로직에서 자동 처리.
24
25
 
25
26
  ## `useExpandingManager<T>(binding)`
26
27
 
@@ -25,7 +25,7 @@
25
25
 
26
26
  ## `<sd-sheet<TItem>>` 입력/출력
27
27
 
28
- - inputs: `key`, `items`, `trackByFn`, `selectMode`, `autoSelect: "click"|"focus"`, `getItemSelectableFn`, `getChildrenFn` (트리), `useAutoSort`, `visiblePageCount=10`, `totalPageCount`, `itemsPerPage`, `focusMode: "row"|"cell" = "cell"`, `inset`, `contentStyle`, `getItemCellClassFn`, `getItemCellStyleFn`, `hideConfigBar`, `columnControlsInput` (외부 정의된 컬럼 추가).
28
+ - inputs: `key`, `items`, `trackByFn`, `selectMode`, `autoSelect: "click"|"focus"`, `getItemSelectableFn`, `getChildrenFn` (트리), `useAutoSort`, `visiblePageCount=10`, `totalPageCount`, `itemsPerPage`, `focusMode: "row"|"cell" = "cell"`, `inset`, `contentStyle`, `getItemCellClassFn`, `getItemCellStyleFn`, `hideConfigBar`, `columnControlsInput: readonly SdSheetColumn[]` (외부 정의된 컬럼 추가, `<sd-sheet-column>` 자식 + 이 입력을 합쳐 처리).
29
29
  - outputs: `itemKeydown: SdSheetItemKeydownEventParam<T> { item, event }`, `cellKeydown: SdSheetCellKeydownEventParam<T> { item, key, event }`.
30
30
  - models: `selectedKeys: unknown[]`, `expandedItems: TItem[]`, `sorts: SortingDef[]`, `currentPage: number`.
31
31
  - `key` 지정 시 설정 모달(`SdSheetConfigModal`)로 너비/숨김/고정/순서를 `SdSystemConfigProvider`에 영속화.
@@ -4,31 +4,46 @@
4
4
 
5
5
  ## `<sd-label>`
6
6
 
7
- 배지 형 라벨. `theme`(테마 컬러), `color` (커스텀), `clickable`. content projection.
7
+ 배지 형 라벨. `theme`(테마 컬러), `color` (커스텀 background), `clickable`. content projection.
8
8
 
9
9
  ## `<sd-note>`
10
10
 
11
- 알림 박스 형. `theme`, `size`, `inset`. content projection.
11
+ 알림 박스 형. `theme`, `size: "sm"|"lg"`, `inset`. content projection.
12
12
 
13
13
  ## `<sd-progress>`
14
14
 
15
15
  ```html
16
- <sd-progress [theme]="'primary'" [value]="60" [size]="'sm'" [inset]="false" />
16
+ <sd-progress [theme]="'primary'" [value]="0.6" [size]="'sm'" [inset]="false" />
17
17
  ```
18
18
 
19
- `theme` (required), `value: number` (0-100, required), `size`, `inset`.
19
+ `theme` (required), `value: number` (required, **0~1 비율**), `size`, `inset`. 표시 텍스트는 `value | percent: "1.0-2"`, 진행바 폭은 `value*100%` (0~100 클램프).
20
20
 
21
21
  ## `<sd-calendar<T>>`
22
22
 
23
- 월별 달력 렌더.
23
+ 월별 달력 렌더. 6행 × 7열 고정. `<ng-template itemOf>` 로 각 셀의 아이템 렌더.
24
24
 
25
- - 필수: `items: T[]`, `getItemDateFn: (item, idx) => DateOnly`.
25
+ ```html
26
+ <sd-calendar [items]="events" [getItemDateFn]="byDate" [yearMonth]="curMonth">
27
+ <ng-template [itemOf]="events" let-item="item">
28
+ <div>{{ item.title }}</div>
29
+ </ng-template>
30
+ </sd-calendar>
31
+ ```
32
+
33
+ - 필수 input: `items: T[]`, `getItemDateFn: (item, idx) => DateOnly`.
26
34
  - `yearMonth = input(new DateOnly().setDay(1))` (해당 월).
27
35
  - `weekStartDay = 0`, `minDaysInFirstWeek = 1`.
36
+ - 필수 content: `<ng-template [itemOf]="items">` (`SdItemOfTemplate`, ctx: `$implicit/item/index/depth`).
28
37
 
29
38
  ## `<sd-barcode>`
30
39
 
31
- `bwip-js` 래퍼. `type: BarcodeType` (required), `value: string`. `BarcodeType` 은 bwip-js 지원 심볼로지 union (`code128`, `qrcode`, `ean13`, ... — `sd-barcode.ts` 참조).
40
+ `bwip-js` 래퍼. `type: BarcodeType` (required), `value: string`. `BarcodeType` 은 bwip-js 지원 심볼로지 union (`code128`, `qrcode`, `ean13`, ... — 전체 목록은 `sd-barcode.ts` 참조).
41
+
42
+ ```html
43
+ <sd-barcode [type]="'qrcode'" [value]="data" />
44
+ ```
45
+
46
+ 내부적으로 `bwipjs.toSVG`로 생성 후 `bypassSecurityTrustHtml`. value 빈 문자열/렌더 실패 시 빈 출력.
32
47
 
33
48
  ## `<sd-echarts>`
34
49
 
@@ -38,4 +53,7 @@ ECharts 5/6 래퍼.
38
53
  <sd-echarts [option]="option" [notMerge]="false" [loading]="false" />
39
54
  ```
40
55
 
41
- `option: echarts.EChartsOption` (required).
56
+ - `option: echarts.EChartsOption` (required) — 변경 시 `chart.setOption(option, { notMerge })`.
57
+ - `notMerge` (default `false`).
58
+ - `loading` (default `false`) — `showLoading`/`hideLoading` 토글.
59
+ - `hostDirectives: SdResizeDirective` 로 호스트 리사이즈 감지 → `chart.resize()`. renderer `"svg"` 고정.
@@ -19,7 +19,7 @@ await FileSystem.requestPermissions(): Promise<void> // Android 11+: 설정
19
19
 
20
20
  // 경로/URI
21
21
  await FileSystem.getStoragePath(type: StorageType): Promise<string>
22
- await FileSystem.getUri(filePath: string): Promise<string> // FileProvider URI
22
+ await FileSystem.getUri(filePath: string): Promise<string> // 네이티브: FileProvider URI / Web: Blob URL (사용 후 `URL.revokeObjectURL` 필수)
23
23
 
24
24
  // 디렉토리
25
25
  await FileSystem.readdir(dirPath: string): Promise<FileInfo[]>
@@ -6,14 +6,45 @@
6
6
 
7
7
  ## 사용 트리거 인덱스
8
8
 
9
- - **DOM 탐색/가시성** (`Element` 확장: `findAll`/`findFirst`/`prependChild`/`getParents`/`isOffsetElement`/`isVisible`) — DOM 트리 탐색·삽입·가시성 판정 필요 시.
10
- - **포커스/탭 이동** (`Element` 확장: `findTabbableParent`/`findFirstTabbableChild`) — 키보드 포커스 흐름 제어 시.
11
- - **레이아웃/스크롤** (`HTMLElement` 확장: `repaint`/`getRelativeOffset`/`scrollIntoViewIfNeeded`) 드롭다운/팝업 위치 계산, 고정 헤더 가림 스크롤 보정 시.
12
- - **클립보드/측정 헬퍼** (`copyElement`/`pasteToElement`/`getBounds`, `ElementBounds`) copy/paste 이벤트 처리, 다수 요소 경계 일괄 측정 시.
13
- - **파일 다이얼로그/다운로드** (`openFileDialog`/`downloadBlob`) 파일 선택 UI, Blob 다운로드 트리거 시.
14
- - **진행률 fetch** (`fetchUrlBytes`, `DownloadProgress`) 바이너리 다운로드 + 진행률 콜백 필요 시.
15
- - **IndexedDB 저장소** (`IndexedDbStore`, `StoreConfig`) 브라우저 영속 키/값 저장소 (트랜잭션 보장).
16
- - **IndexedDB 가상 파일시스템** (`IndexedDbVirtualFs`, `VirtualFsEntry`) `IndexedDbStore` 위에 경로(`/a/b/c`) 기반 파일/디렉토리 모델링.
9
+ DOM 탐색/가시성 `Element` 확장:
10
+
11
+ - **`findAll`** `el` 하위에서 선택자 일치 요소 전부 배열로 받을 때.
12
+ - **`findFirst`** `el` 하위에서 선택자 일치 요소만 받을 (`querySelector` `null` 대신 `undefined`).
13
+ - **`prependChild`**자식 목록의 앞으로 삽입할 때.
14
+ - **`getParents`** 가까운 부모부터 위로 거슬러 올라가는 조상 배열이 필요할 때.
15
+ - **`isOffsetElement`** 어떤 요소가 자식 absolute 기준이 되는지(`position: relative/absolute/fixed/sticky`) 판정할 때.
16
+ - **`isVisible`** 화면에 실제로 보이는지(클라이언트 영역 + `visibility` + `opacity`) 판정할 때.
17
+
18
+ 포커스/탭 이동 — `Element` 확장:
19
+
20
+ - **`findTabbableParent`** — 현재 요소를 감싸는 가장 가까운 tabbable 부모를 찾을 때 (모달·툴팁의 포커스 트랩 호스트 추적).
21
+ - **`findFirstTabbableChild`** — 컨테이너에 진입했을 때 처음 포커스할 자식을 찾을 때.
22
+
23
+ 레이아웃/스크롤 — `HTMLElement` 확장:
24
+
25
+ - **`repaint`** — 스타일 변경 후 즉시 동기 reflow 가 필요할 때.
26
+ - **`getRelativeOffset`** — 드롭다운/팝업을 부모 기준 absolute 좌표로 띄울 때 (스크롤·border·transform 보정 포함).
27
+ - **`scrollIntoViewIfNeeded`** — 고정 헤더/컬럼에 가려진 셀을 위/왼쪽으로만 보정 스크롤할 때.
28
+
29
+ 클립보드/측정 헬퍼:
30
+
31
+ - **`copyElement`** — `<el @copy>` 이벤트 핸들러로 내부 첫 input/textarea 값을 클립보드로 보낼 때.
32
+ - **`pasteToElement`** — `<el @paste>` 이벤트 핸들러로 클립보드 값을 내부 첫 input/textarea 에 전체 교체할 때.
33
+ - **`getBounds`** / **`ElementBounds`** — 다수 요소의 뷰포트 경계를 `IntersectionObserver` 로 한 번에 측정할 때 (입력 순서 유지·타임아웃 보장).
34
+
35
+ 파일 다이얼로그/다운로드:
36
+
37
+ - **`openFileDialog`** — 사용자에게 파일 선택 UI 를 띄워 `File[]` 을 받을 때 (취소·빈 선택은 `undefined`).
38
+ - **`downloadBlob`** — 메모리 상의 `Blob` 을 사용자 다운로드로 흘려보낼 때 (파일명 sanitize).
39
+
40
+ 진행률 fetch:
41
+
42
+ - **`fetchUrlBytes`** / **`DownloadProgress`** — 큰 바이너리를 `Uint8Array` 로 받으며 진행률 콜백을 받을 때.
43
+
44
+ IndexedDB:
45
+
46
+ - **`IndexedDbStore`** / **`StoreConfig`** — 브라우저 영속 키/값 저장소가 필요할 때 (트랜잭션·재진입 안전 `open`).
47
+ - **`IndexedDbVirtualFs`** / **`VirtualFsEntry`** — 위 저장소 위에 경로(`/a/b/c`) 기반 파일/디렉토리 트리를 올릴 때.
17
48
 
18
49
  ## DOM 탐색/가시성 — `Element` 확장
19
50
 
@@ -4,15 +4,52 @@
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **에러 클래스** — throw 시 트리 메시지/원인 체인 필요할 때. `SdError`, `ArgumentError`, `NotImplementedError`, `TimeoutError`.
8
- - **날짜/시간/UUID/캐시 타입** `DateTime`, `DateOnly`, `Time`, `Uuid`, `LazyGcMap` 사용 시. (자세히: [types.md](./types.md))
9
- - **큐·이벤트 features** 디바운스·직렬 큐, 타입 안전 EventEmitter. (자세히: [features.md](./features.md))
10
- - **유틸리티 네임스페이스** — `obj`, `str`, `num`, `bytes`, `path`, `json`, `xml`, `wait`, `transfer`, `err`, `dt`, `primitive`. (자세히: [utils.md](./utils.md))
11
- - **Array/Set/Map 전역 확장 메서드** `.single()`, `.toMap()`, `.toTree()`, `.distinct()`, `Set.adds()`, `Map.getOrCreate()` 등. (자세히: [extensions.md](./extensions.md))
12
- - **환경변수** `env(key)`, `env(key, value)`, `parseBoolEnv(v)`. `process.env` 우선, fallback `import.meta.env`.
13
- - **템플릿 문자열 태그** — IDE 하이라이팅 + indent trim. `js`, `ts`, `html`, `tsql`, `mysql`, `pgsql`. 동작은 모두 동일(문자열 결합 + 들여쓰기 정규화).
14
- - **ZIP 처리** — `ZipArchive(data?)`: `get/exists/write/extractAll/compress/close`. 사용 후 반드시 `await archive.close()`.
15
- - **공통 타입** — `Bytes` (= `Uint8Array`), `PrimitiveTypeMap/Str/Type` (string/number/boolean/DateTime/DateOnly/Time/Uuid/Bytes), `DeepPartial<T>`, `Type<T>` (생성자 타입).
7
+ - **에러 클래스** — throw 시 트리 메시지/원인 체인 필요할 때. (자세히: 아래 [에러 클래스](#에러-클래스))
8
+ - `SdError`일반 에러 (cause 체인, "상위 => 하위" 메시지)
9
+ - `ArgumentError`인자 검증 실패 (인자 객체를 YAML 로 메시지에 첨부)
10
+ - `NotImplementedError` 미구현 분기/추상 메서드
11
+ - `TimeoutError`대기 시간 초과 (`wait.until` 에서 자동 throw)
12
+ - **날짜·시간·UUID·캐시 타입** (자세히: [types.md](./types.md))
13
+ - `DateTime` 불변 날짜+시간 (밀리초 정밀도, 로컬 타임존). 변환·산술·포맷.
14
+ - `DateOnly`불변 날짜만 (`yyyy-MM-dd`). ISO 8601 주차(`getWeekSeqOfYear`/`Month`).
15
+ - `Time` 불변 시간만 (`HH:mm:ss.fff`, 24h 순환).
16
+ - `Uuid` — UUID v4 (`Uuid.generate()`), bytes 변환.
17
+ - `LazyGcMap` — 마지막 접근 후 N ms 만료 LRU Map. **`dispose()` 필수**.
18
+ - **큐·이벤트 features** (자세히: [features.md](./features.md))
19
+ - `EventEmitter<TEvents>` — 타입 안전 이벤트 (브라우저·Node 공용).
20
+ - `DebounceQueue` — 연속 호출 중 마지막만 실행 (입력 자동완성·일괄 상태 변경).
21
+ - `SerialQueue` — 순차 실행 (작업 사이 gap 옵션, 에러 발생 후에도 계속 실행).
22
+ - **유틸리티 네임스페이스** (자세히: [utils.md](./utils.md))
23
+ - `obj` — 객체 깊은 복사/비교/병합(`clone`/`equal`/`merge`), 3-way merge, `omit`/`pick`/`map`, 체인 경로(`"a.b[0].c"`) get/set/delete, `unflatten`, `clearUndefined`.
24
+ - `str` — 한글 조사(`getKoreanSuffix`), 전각→반각(`replaceFullWidth`), case 변환(`toPascalCase`/`toCamelCase`/`toKebabCase`/`toSnakeCase`), `isNullOrEmpty`, `insert`.
25
+ - `num` — 비숫자 섞인 문자열 파싱(`parseInt`/`parseFloat`/`parseRoundedInt`), `isNullOrEmpty`(0 포함 타입 가드), 천 단위 + 소수점 포맷(`format`).
26
+ - `bytes` — `Uint8Array` 결합(`concat`), hex/base64 인코딩·디코딩.
27
+ - `path` — POSIX 경로 join/basename/extname (브라우저용, 슬래시 전용).
28
+ - `json` — 커스텀 타입(Date/DateTime/DateOnly/Time/Uuid/Set/Map/Error/Uint8Array) 마커 직렬화 `stringify`/`parse`, null→undefined 복원.
29
+ - `xml` — fast-xml-parser 래퍼 `parse`/`stringify`, `stripTagPrefix` 옵션.
30
+ - `wait` — `wait.time(ms)`, `wait.until(cond, interval, maxCount)` (초과 시 `TimeoutError`).
31
+ - `transfer` — Worker postMessage 용 `encode`/`decode`, `Uint8Array.buffer` zero-copy transferList.
32
+ - `err` — 미지의 에러를 메시지 문자열로(`err.message`).
33
+ - `dt` — date-format 저수준 `format`/`normalizeMonth`/`convert12To24` (`DateTime`/`DateOnly`/`Time` 내부용, 직접 사용 드묾).
34
+ - `primitive` — 런타임 값 타입 추론(`primitive.typeStr`) → `PrimitiveTypeStr`.
35
+ - **Array/Set/Map 전역 확장 메서드** — `index.ts` import 시 자동 적용. (자세히: [extensions.md](./extensions.md))
36
+ - Array 조회: `.single()` (0/1개 단언), `.first()`/`.last()` (조건부 find), `.filterExists()` (null 제거), `.ofType()`.
37
+ - Array 비동기: `.filterAsync()`/`.mapAsync()`/`.mapManyAsync()` (순차), `.parallelAsync()` (`Promise.all`).
38
+ - Array 변환: `.groupBy()`, `.toMap()`, `.toArrayMap()`, `.toSetMap()`, `.toObject()`, `.toTree("id", "parentId")`.
39
+ - Array 중복·정렬: `.distinct({ keyFn })`, `.orderBy()`/`.orderByDesc()`, `.shuffle()`.
40
+ - Array 비교·병합: `.diffs(target, { keys })` (INSERT/DELETE/UPDATE), `.oneWayDiffs()`, `.merge()`.
41
+ - Array 집계: `.sum()`, `.min()`, `.max()`.
42
+ - Array mutable: `.distinctThis()`, `.orderByThis()`, `.insert()`, `.remove()`, `.toggle()`, `.clear()`.
43
+ - Set: `.adds(...)`, `.toggle(value, "add"|"del"?)`.
44
+ - Map: `.getOrCreate(key, defaultOrFactory)`, `.update(key, (v) => newV)`.
45
+ - **환경변수** — `env(key)`/`env(key, value)`, `parseBoolEnv(v)`. `process.env` 우선, fallback `import.meta.env`. (자세히: 아래 [환경변수](#환경변수))
46
+ - **템플릿 문자열 태그** — IDE 코드 하이라이팅 + indent trim. `js`/`ts`/`html`/`tsql`/`mysql`/`pgsql` (모두 동일 동작, 하이라이팅 차별화 목적). (자세히: 아래 [템플릿 문자열 태그](#템플릿-문자열-태그))
47
+ - **ZIP 처리** — `ZipArchive(data?)`: 읽기·쓰기·압축·해제. `get/exists/write/extractAll/compress/close`. 사용 후 **`await archive.close()`** 필수. (자세히: 아래 [ZIP 처리](#zip-처리))
48
+ - **공통 타입** (자세히: 아래 [공통 타입](#공통-타입))
49
+ - `Bytes` — `Uint8Array` 별칭 (Node `Buffer` 대체).
50
+ - `PrimitiveTypeMap`/`PrimitiveTypeStr`/`PrimitiveType` — string/number/boolean/DateTime/DateOnly/Time/Uuid/Bytes 매핑 (orm-common 공유).
51
+ - `DeepPartial<T>` — 재귀 optional (원시 타입은 그대로).
52
+ - `Type<T>` — 클래스 생성자 타입 (DI·팩토리 패턴).
16
53
 
17
54
  ## 에러 클래스
18
55
 
@@ -51,3 +88,44 @@ const code = ts`
51
88
  ```
52
89
 
53
90
  모두 동일 함수, IDE 하이라이팅 차별화 목적.
91
+
92
+ ## ZIP 처리
93
+
94
+ ```typescript
95
+ import { ZipArchive } from "@simplysm/core-common";
96
+
97
+ // 읽기
98
+ const archive = new ZipArchive(zipBytes); // Blob 또는 Uint8Array
99
+ try {
100
+ const content = await archive.get("file.txt"); // Bytes | undefined
101
+ const exists = await archive.exists("file.txt"); // boolean
102
+ const all = await archive.extractAll(onProgress?); // Map<fileName, Bytes|undefined>
103
+ } finally {
104
+ await archive.close(); // 필수
105
+ }
106
+
107
+ // 쓰기
108
+ const archive = new ZipArchive();
109
+ archive.write("file.txt", bytes);
110
+ const zipBytes = await archive.compress(); // 내부적으로 extractAll → 전체 메모리 로드
111
+ await archive.close();
112
+ ```
113
+
114
+ `extractAll` 진행률 콜백: `{ fileName, totalSize, extractedSize }`. `compress()` 는 전체 파일을 메모리에 로드하므로 대용량 ZIP 주의.
115
+
116
+ ## 공통 타입
117
+
118
+ ```typescript
119
+ type Bytes = Uint8Array; // Buffer 대체
120
+
121
+ type PrimitiveTypeMap = { // orm-common 공유
122
+ string: string; number: number; boolean: boolean;
123
+ DateTime: DateTime; DateOnly: DateOnly; Time: Time;
124
+ Uuid: Uuid; Bytes: Bytes;
125
+ };
126
+ type PrimitiveTypeStr = keyof PrimitiveTypeMap;
127
+ type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
128
+
129
+ type DeepPartial<T>; // 재귀 optional, 원시 타입 유지
130
+ interface Type<T> extends Function { new (...args: unknown[]): T; }
131
+ ```
@@ -8,5 +8,14 @@ Node.js 전용 유틸·기능 묶음. 파일 IO/glob, 경로 변환, 자식 프
8
8
  - **`pathx` 네임스페이스** — POSIX 경로 변환, 하위 경로 판정, 디렉토리 치환, target 필터링. 자세히: [pathx.md](./pathx.md)
9
9
  - **`cpx` 네임스페이스** — 시스템 인코딩 감지 + 자식 프로세스 spawn/spawnSync (인코딩 자동 디코딩, exitCode 기반 reject). 자세히: [cpx.md](./cpx.md)
10
10
  - **`FsWatcher`** — chokidar 기반 디바운스/이벤트 병합 + Windows EPERM 자동 복구 파일 감시. 자세히: [fs-watcher.md](./fs-watcher.md)
11
- - **`setupConsola` / `PrettyReporter` / `createFileReporter` / `withMaxLevel`** — Node 앱 consola 셋업, 컬러 콘솔/JSON 파일 회전 리포터. 자세히: [consola.md](./consola.md)
12
- - **`Worker` / `createWorker` / `WorkerProxy` / `WorkerModule` / `PromisifyMethods`** worker_threads 위 타입 안전 RPC 래퍼 (메서드 호출 + 이벤트 send/on). 자세히: [worker.md](./worker.md)
11
+ - **consola 셋업** — Node 앱 진입점에서 로깅 환경(콘솔/파일) 구성. 자세히: [consola.md](./consola.md)
12
+ - **`setupConsola`** 진입점 1회 호출. dev/prod·`SD_DEBUG`·`cli` 조합으로 리포터 자동 구성.
13
+ - **`PrettyReporter`** — 색상·아이콘·tag·stack/cause 정리 포함 콘솔 출력 리포터.
14
+ - **`createFileReporter`** — `<cwd>/.logs/app.<날짜>.log` JSON 회전 + 보존 기간 정리.
15
+ - **`withMaxLevel`** — 기존 리포터에 level 상한 필터 씌우기 (dev 콘솔에서 debug 가리기 등).
16
+ - **Worker 래퍼** — worker_threads 위 타입 안전 RPC (메서드 호출 + 이벤트 + 워커 stdout 메인 전달). 자세히: [worker.md](./worker.md)
17
+ - **`Worker.create`** — 메인 측. 워커 파일을 띄우고 메서드 직접 호출 가능한 Proxy 반환.
18
+ - **`createWorker`** — 워커 측. `methods` 등록 + `send(event, data)` 로 이벤트 발행. `export default` 필수.
19
+ - **`WorkerProxy<TModule>`** — `Worker.create` 반환 타입. 워커 모듈 타입 추론에 사용.
20
+ - **`WorkerModule`** — 워커 default export 의 구조 인터페이스 (`__methods`/`__events`). 직접 구현 불필요.
21
+ - **`PromisifyMethods<T>`** — 메서드 반환을 `Promise<Awaited<R>>` 로 감싸는 매핑 타입. `WorkerProxy` 내부 사용.
@@ -4,11 +4,25 @@ OOXML(xlsx) 워크북을 lazy-load 로 읽고 쓰는 클래스 묶음. ZIP 내
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
  - **`ExcelWorkbook`** — xlsx 바이트/Blob 을 열거나 새 워크북을 만들 때. 사용 후 `close()` 필수.
7
- - **`ExcelWorksheet`** — 시트 단위로 셀/행/열 접근, 데이터테이블 입출력, 틀 고정/줌, 조건부 서식, 이미지 삽입.
8
- - **`ExcelCell` / `ExcelRow` / `ExcelCol`** — 단일 값·수식·스타일·병합, 행/열 전체 일괄 접근, 너비.
7
+ - **`ExcelWorkbook.setDefaultStyle`** — 워크북 전역(폰트·정렬·numFmt 등) 표준을 한번에 적용.
8
+ - **`ExcelWorksheet`** — 시트 단위접근의 진입점. 이름·범위 조회.
9
+ - **`getDataTable` / `setDataMatrix` / `setRecords`** — 시트와 레코드 배열(또는 2D 매트릭스) 간 입출력.
10
+ - **`copyCell` / `copyRow` / `copyCellStyle` / `copyRowStyle` / `insertCopyRow`** — 셀/행 복제·삽입(템플릿 시트 채울 때).
11
+ - **`setZoom` / `freezeAt`** — 시트 뷰 보기 설정(확대·틀 고정).
12
+ - **`setTabColor`** — 시트 탭 색 ARGB 지정.
13
+ - **`addConditionalFormat`** — 셀/범위에 조건부 서식 규칙 적용.
14
+ - **`addImage`** — 시트에 이미지(png/jpg 등) 삽입.
15
+ - **`ExcelCell`** — 단일 셀의 값·수식·스타일·병합.
16
+ - **`ExcelRow`** — 행 단위 셀 일괄 접근.
17
+ - **`ExcelCol`** — 열 단위 셀 접근 + 열 너비 설정.
9
18
  - **`ExcelWrapper`** — Zod 스키마로 헤더·타입을 정의해 레코드 배열로 read/write.
10
19
  - **`ExcelUtils`** — `"A1"`↔좌표, 범위 주소, Excel 직렬 날짜 ↔ 타임스탬프, numFmt 변환.
11
- - **타입 (`ExcelValueType`, `ExcelStyleOptions`, `ExcelFont`, `ExcelConditionalRule` 등)** 입력 옵션·반환값 정의.
20
+ - **`ExcelValueType`** 셀에 넣고 있는 값의 union (number/string/boolean/DateOnly/DateTime/Time/undefined).
21
+ - **`ExcelStyleOptions`** — `setStyle` / `setDefaultStyle` 입력 옵션 (배경·테두리·정렬·numFmt·폰트).
22
+ - **`ExcelFont`** — `ExcelStyleOptions.font` 의 폰트 속성(크기·family·bold·italic·underline·color·strike).
23
+ - **`ExcelConditionalRule` / `ExcelConditionalRuleStyle`** — `addConditionalFormat` 의 규칙·강조 스타일 타입.
24
+ - **`ExcelAddressPoint` / `ExcelAddressRangePoint`** — 0 기반 좌표 / 범위 좌표.
25
+ - **`ExcelNumberFormat` / `ExcelBorderPosition` / `ExcelHorizontalAlign` / `ExcelVerticalAlign` / `ExcelFontUnderline`** — 스타일 옵션의 enum literal 들.
12
26
 
13
27
  ## ExcelWorkbook
14
28
  ```typescript
@@ -6,6 +6,15 @@ Simplysm 컨벤션용 ESLint 플러그인 패키지. 커스텀 규칙 묶음(`./
6
6
 
7
7
  - **`./eslint-recommended` (default export)** — 프로젝트 `eslint.config.{js,mjs,cjs}` 에서 그대로 spread 해 simplysm 표준 lint 규칙을 적용할 때.
8
8
  - **`./eslint-plugin` (default export)** — recommended 를 사용하지 않고 개별 규칙만 골라 쓰거나, 다른 flat config 에서 `@simplysm/<rule>` 로 참조할 때.
9
+ - **`ng-no-async-effect`** — Angular `effect()` 콜백을 async 로 작성하지 않게 막을 때.
10
+ - **`ng-template-no-strict-null-check`** — Angular 템플릿에서 `=== null|undefined` 대신 `== null` 로 통일시킬 때.
11
+ - **`ng-template-no-todo-comments`** — Angular 템플릿 내 `<!-- TODO: ... -->` 를 빌드 경고로 남길 때.
12
+ - **`ng-template-sd-require-binding-attrs`** — `sd-*` 컴포넌트에 plain attribute 못 쓰게 하고 property binding 으로 강제할 때.
13
+ - **`no-hard-private`** — `#field` 대신 TypeScript `private _field` 스타일을 강제할 때.
14
+ - **`no-subpath-imports-from-simplysm`** — `@simplysm/<pkg>/src/...` 직접 import 를 차단할 때.
15
+ - **`ts-no-throw-not-implemented-error`** — `NotImplementedError` 잔존을 빌드 경고로 표면화할 때.
16
+ - **`ts-no-unused-injects`** — Angular `inject()` 로 받은 미사용 필드를 정리할 때.
17
+ - **`ts-no-unused-protected-readonly`** — Angular 컴포넌트 인라인 템플릿/클래스 어느 쪽에서도 안 쓰는 `protected readonly` 필드를 정리할 때.
9
18
 
10
19
  ## `./eslint-recommended`
11
20
 
@@ -9,16 +9,41 @@ Dialect 독립 ORM 코어. `DbContext` 상속으로 테이블/뷰/프로시저
9
9
  - **`Queryable` / `queryable()`** — `DbContext` 에 등록된 테이블/뷰에서 SELECT/INSERT/UPDATE/DELETE/UPSERT 빌더 체이닝, JOIN/include/recursive CTE/UNION/search 할 때. 자세히: [queryable.md](./queryable.md)
10
10
  - **`Executable` / `executable()`** — 등록된 프로시저 호출 결과 받을 때. 자세히: [executable.md](./executable.md)
11
11
  - **`expr` namespace + `ExprUnit`/`WhereExprUnit`** — `where`/`select`/`groupBy`/`having`/`update` 콜백 안에서 비교·논리·문자열·날짜·집계·윈도우·CASE·subquery 등 SQL 표현식 만들 때. dialect 독립 AST. 자세히: [expr.md](./expr.md)
12
- - **`parseSearchQuery(text)`** — `Queryable.search()` 내부에서 쓰는 OR/`+`AND/`-`NOT/`"…"`/`*` 구문 파서. 직접 LIKE 패턴이 필요할 때만 외부 사용.
13
- - **`QueryBuilder` (`createQueryBuilder(dialect)`, `QueryBuilderBase`, `ExprRendererBase`, `*QueryBuilder`, `*ExprRenderer`)** executor 측에서 `QueryDef` dialect SQL 렌더할 때. 응용 코드는 직접 호출 X.
14
- - **`QueryDef` 타입군 (`./types/query-def`)**executor·테스트에서 빌더가 만든 AST 직접 다룰 때. `QueryDef`, `SelectQueryDef`, `InsertQueryDef`, ..., `QueryDefObjectName`, `DDL_TYPES`, `DdlType`.
15
- - **`Expr` 타입군 (`./types/expr`)**Expr AST 노드 타입(`ExprColumn`/`ExprValue`/`ExprRaw`/`ExprEq`/...`ExprWindow`/`ExprSubquery`). `DateUnit`. ExprRenderer 구현·검사에서 사용.
16
- - **`DataType` / `ColumnPrimitive*` (`./types/column`)** — SQL DataType union(`int`/`bigint`/`varchar`/`datetime`/`uuid`/...) TS 타입 매핑(`ColumnPrimitiveMap`/`ColumnPrimitiveStr`/`ColumnPrimitive`/`ColumnMeta`). `dataTypeStrToColumnPrimitiveStr` 상수, `InferColumnPrimitiveFromDataType<T>` / `inferColumnPrimitiveStr(value)` 런타임 추론.
17
- - **`Dialect` / `dialects` / `DataRecord` / `DbContextExecutor` / `ResultMeta` / `IsolationLevel` / `Migration` / `QueryBuildResult` (`./types/db`)**executor 구현·DbContext 외부 인터페이스. `Migration[]` DbContext 서브클래스의 `migrations` 프로퍼티로 오버라이드해 `initialize()` 에서 적용.
18
- - **`DbContextBase` / `DbContextStatus` / `DbContextDdlMethods` (`./types/db-context-def`)** — 외부에서 DbContext인터페이스로 다룰 때 (Queryable/Executable/ViewBuilder 의존).
19
- - **`DbTransactionError` / `DbErrorCode`**executor트랜잭션 에러를 표준화해 throw, 호출측은 `instanceof` 분기. 코드: `NO_ACTIVE_TRANSACTION`, `TRANSACTION_ALREADY_STARTED`, `DEADLOCK`, `LOCK_TIMEOUT`.
20
- - **`parseResults(rows, meta)` / `pickResultSets(rawResults, buildResult)` (`./utils/*`)** — executor raw 결과 가공. `parseResults` flat row 중첩 객체(JOIN 그룹핑 + 타입 파싱). `pickResultSets(raw, { resultSetIndex, resultSetStride })` 는 다중 결과 셋에서 필요한 셋만 추출 (예: MySQL 배치 INSERT 의 OUTPUT SELECT).
21
- - **`_Migration` / `SD_BUILDER`** 내부용. `_Migration` `_migration(code PK)` 시스템 테이블, `SD_BUILDER` 는 `queryable()`/`executable()` 팩토리 함수에 붙는 메타 심볼 (`initialize()` DbContext 프로퍼티에서 builder 회수할 사용).
12
+ - **`parseSearchQuery(text)`** — `Queryable.search()` 내부에서 쓰는 OR/`+`AND/`-`NOT/`"…"`/`*` 구문 파서. 직접 LIKE 패턴이 필요할 때만 외부 사용. (`ParsedSearchQuery` = `{ or, must, not }`)
13
+ - **`createQueryBuilder(dialect)`** — `Dialect` 문자열로 dialect QueryBuilder 인스턴스 받을 (executor 진입점).
14
+ - **`QueryBuilderBase` / `ExprRendererBase`**커스텀 dialect 구현을 위해 상속 베이스 필요할 때.
15
+ - **`MysqlQueryBuilder` / `MssqlQueryBuilder` / `PostgresqlQueryBuilder`** (와 대응 `*ExprRenderer`) — 특정 dialect 인스턴스를 직접 new 하거나 `instanceof` 분기할 때. 일반은 `createQueryBuilder` 사용.
16
+ - **`QueryDef` (union) + `QueryDefObjectName` (`./types/query-def`)** — executor 받는 query AST 의 union 타입 / 모든 DDL/DML 이 공통으로 쓰는 `{database?, schema?, name}` 형태의 DB 객체 식별자.
17
+ - **`SelectQueryDef`, `InsertQueryDef`, `InsertIfNotExistsQueryDef`, `InsertIntoQueryDef`, `UpdateQueryDef`, `DeleteQueryDef`, `UpsertQueryDef`, `SelectQueryDefJoin`, `CudOutputDef`**DML AST 노드를 직접 만들거나 검사할 (executor·테스트).
18
+ - **DDL QueryDef (`Create*` / `Drop*` / `Rename*` / `Add*` / `Modify*` / `Truncate*` / `Clear*` / `Schema*` / `ExecProc*` 등)** — 스키마 변경/프로시저 호출 AST 직접 다룰 때 (`getXxxQueryDef()` 반환 타입과 일치).
19
+ - **`DDL_TYPES` (배열) / `DdlType` (union)** queryDDL 인지 런타임/타입 레벨에서 검사할 때. 트랜잭션 DDL 차단 가드에서 사용.
20
+ - **`Expr` (union) / `WhereExpr` (union)** — SELECT·ORDER BY·SET 콜백이 모두 받는 일반 표현식 AST 와, WHERE·HAVING 전용 boolean 표현식 AST. ExprRenderer dispatch 하는 대상.
21
+ - **개별 Expr 노드 (`ExprColumn`, `ExprValue`, `ExprRaw`, `ExprEq`, `ExprLike`, `ExprConcat`, `ExprDateDiff`, `ExprSwitch`, `ExprCount`, `ExprWindow`, `ExprSubquery` )** Expr renderer 구현, AST 검사, 디버깅 개별 노드 타입 필요할 때. 응용 코드는 `expr.*` 헬퍼만 사용.
22
+ - **`WinFn` (union) / `WinSpec` / `WinFn*` 개별 노드** — Window 함수 AST 분기 (`rowNumber`, `rank`, `lag`, `sumOver` 등) 를 직접 처리할 때.
23
+ - **`DateUnit`** — `dateDiff`/`dateAdd` 의 단위 union (`"year" | "month" | "day" | "hour" | "minute" | "second"`). 동적으로 단위 선택할 때.
24
+ - **`DataType` (`./types/column`)** — SQL 타입 union (`{type:"int"}` / `{type:"varchar",length}` / `{type:"decimal",precision,scale?}` ...). DDL/cast 의 타입 인자.
25
+ - **`ColumnPrimitive` / `ColumnPrimitiveStr` / `ColumnPrimitiveMap`** — Column 값으로 허용되는 TS 타입 union (`string|number|boolean|DateTime|DateOnly|Time|Uuid|Bytes|undefined`), 그 키 이름 union, 키→타입 매핑. ExprUnit/ResultMeta 가 사용.
26
+ - **`ColumnMeta`** — `ColumnBuilder.meta` 의 형태 (`{type, dataType, autoIncrement?, nullable?, default?, description?}`). 외부에서 column 정의를 읽어 DDL 만들 때.
27
+ - **`dataTypeStrToColumnPrimitiveStr`** — `DataType.type` → `ColumnPrimitiveStr` 상수 매핑. cast 결과 dataType 추론에 사용.
28
+ - **`InferColumnPrimitiveFromDataType<T>`** — `DataType` 타입에서 TS 값 타입 추론 (제네릭 타입 유틸).
29
+ - **`inferColumnPrimitiveStr(value)`** — 런타임 값에서 `ColumnPrimitiveStr` 추론. NULL/unknown 이면 throw.
30
+ - **`Dialect`** — `"mysql" | "mssql" | "postgresql"` union. dialect 분기/`createQueryBuilder` 인자.
31
+ - **`dialects`** — `Dialect[]` 상수 배열. 테스트의 `it.each(dialects)` 또는 모든 dialect 순회용.
32
+ - **`DataRecord`** — query 결과 row 의 재귀 타입 (`{ [key]: ColumnPrimitive | DataRecord | DataRecord[] }`). Queryable/Executable 의 결과 제약.
33
+ - **`DbContextExecutor`** — 외부에서 DbContext 에 주입할 executor 인터페이스 (`connect`/`close`/`beginTransaction`/`commitTransaction`/`rollbackTransaction`/`executeDefs`). 신규 dialect/원격 executor 구현 시.
34
+ - **`ResultMeta`** — `executeDefs` 가 받는 `{columns: Record<string,ColumnPrimitiveStr>, joins: Record<string,{isSingle}>}`. raw row → 타입 변환·JOIN 그룹핑에 필요.
35
+ - **`IsolationLevel`** — `"READ_UNCOMMITTED" | "READ_COMMITTED" | "REPEATABLE_READ" | "SERIALIZABLE"`. `connect`/`transaction` 인자.
36
+ - **`Migration`** — `{name, up(db)}`. DbContext 서브클래스의 `migrations` 프로퍼티 타입. `initialize()` 가 적용.
37
+ - **`QueryBuildResult`** — `QueryBuilder.build()` 반환 (`{sql, resultSetIndex?, resultSetStride?}`). executor 가 `pickResultSets` 로 좁힐 때 필요.
38
+ - **`DbContextBase` (`./types/db-context-def`)** — DbContext 의 코어 인터페이스 (`status`/`database`/`schema`/`executeDefs`/`getNextAlias` 등). Queryable·Executable·ViewBuilder 가 의존하므로 mock/대체 구현 시 사용.
39
+ - **`DbContextStatus`** — `"ready" | "connect" | "transact"`. status 분기 시.
40
+ - **`DbContextDdlMethods`** — `createTable`/`addColumn`/`addForeignKey` 등 DDL 메서드 집합 인터페이스. `Migration.up` 의 db 인자 타입.
41
+ - **`DbTransactionError`** — executor 가 트랜잭션 에러를 표준화해 throw 하는 클래스. 호출측은 `instanceof DbTransactionError` 로 분기.
42
+ - **`DbErrorCode`** — `NO_ACTIVE_TRANSACTION` / `TRANSACTION_ALREADY_STARTED` / `DEADLOCK` / `LOCK_TIMEOUT` enum. `err.code === DbErrorCode.DEADLOCK` 같은 분기에 사용.
43
+ - **`parseQueryResult(rows, meta)` (`./utils/result-parser`)** — flat row 배열 + `ResultMeta` → 타입 변환 + JOIN 그룹핑된 중첩 객체 배열. async. 빈 결과면 undefined. executor 가 SELECT/OUTPUT 결과를 사용자 객체로 가공할 때.
44
+ - **`pickResultSets(rawResults, buildResult)` (`./utils/pick-result-sets`)** — `QueryBuildResult` 의 `resultSetIndex`/`resultSetStride` 따라 여러 결과 셋 중 필요한 셋만 추출/concat. MySQL 배치 INSERT 의 OUTPUT SELECT 만 모을 때.
45
+ - **`_Migration`** — `_migration(code PK varchar(255))` 시스템 테이블의 TableBuilder. DbContext 가 자동 등록, 사용자 직접 import 불필요.
46
+ - **`SD_BUILDER`** — `queryable()`/`executable()` 팩토리 함수에 builder 를 부착하는 심볼. `initialize()` 가 DbContext 의 프로퍼티에서 builder 를 회수할 때 사용. 사용자 코드 직접 사용 X.
22
47
 
23
48
  ## QueryBuilder (인라인)
24
49
 
@@ -4,13 +4,23 @@ Node.js 환경에서 `@simplysm/orm-common` 의 `DbContext` 를 실제 DB(MSSQL/
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **`createOrm` / `Orm` / `OrmOptions`** — `DbContext` 서브클래스 + `DbConnConfig` 로 ORM 인스턴스를 만들고, 트랜잭션 유/무 콜백을 실행한다. 일반 ORM 사용의 진입점.
8
- - **`createDbConn`** — `DbConnConfig` 만으로 저수준 `DbConn` 인스턴스를 직접 만든다. `DbContext` 없이 raw SQL/bulk insert 필요할 때.
7
+ - **`createOrm`** — `DbContext` 서브클래스 + `DbConnConfig` 로 ORM 인스턴스 생성. 일반 ORM 사용의 진입점.
8
+ - **`Orm<T>`** — `createOrm` 반환 타입. 함수 시그니처·DI 토큰에서 참조하거나 `connect` / `connectWithoutTransaction` 호출 시.
9
+ - **`OrmOptions`** — `createOrm` 3번째 인자. `DbConnConfig` 의 `database` / `schema` 를 런타임에 덮어쓸 때.
10
+ - **`createDbConn`** — `DbConnConfig` 만으로 저수준 `DbConn` 인스턴스 직접 생성. `DbContext` 없이 raw SQL/bulk insert 가 필요할 때.
9
11
  - **`NodeDbContextExecutor`** — `DbContext` 의 executor 직접 주입이 필요할 때. 일반적으로는 `createOrm` 이 내부적으로 사용.
10
- - **`MysqlDbConn` / `MssqlDbConn` / `PostgresqlDbConn`** — dialect별 `DbConn` 구현. 직접 `new` 하지 말고 `createDbConn` 사용. 타입 참조용으로만 import.
11
- - **`DbConn` / `DbConnConfig` (+ `MysqlDbConnConfig` / `MssqlDbConnConfig` / `PostgresqlDbConnConfig`)** 설정/연결 타입. 함수 시그니처·DI 토큰 등에서 참조.
12
+ - **`MysqlDbConn`** — MySQL `DbConn` 구현 클래스. 직접 `new` 하지 말고 `createDbConn` 사용. 타입 참조용 import.
13
+ - **`MssqlDbConn`** — MSSQL/Azure SQL `DbConn` 구현 클래스. 직접 `new` 하지 말고 `createDbConn` 사용. 타입 참조용 import.
14
+ - **`PostgresqlDbConn`** — PostgreSQL 용 `DbConn` 구현 클래스. 직접 `new` 하지 말고 `createDbConn` 사용. 타입 참조용 import.
15
+ - **`DbConn`** — 저수준 연결 인터페이스. 모든 dialect 공통 메서드(`connect`/`close`/트랜잭션/`execute`/`executeParametrized`/`bulkInsert`) 와 `close` 이벤트.
16
+ - **`DbConnConfig`** — dialect 분기 union 타입. `createOrm`/`createDbConn` 입력.
17
+ - **`MysqlDbConnConfig`** — `dialect: "mysql"` 한정 설정 타입.
18
+ - **`MssqlDbConnConfig`** — `dialect: "mssql" | "mssql-azure"` 설정 타입. `schema?` 포함.
19
+ - **`PostgresqlDbConnConfig`** — `dialect: "postgresql"` 설정 타입. `schema?` 포함.
12
20
  - **`getDialectFromConfig`** — `DbConnConfig` → `Dialect` 변환(`mssql-azure` → `mssql`). 쿼리 빌더 선택 시.
13
- - **`DB_CONN_CONNECT_TIMEOUT` (10s) / `DB_CONN_DEFAULT_TIMEOUT` (10m) / `DB_CONN_ERRORS`** — 타임아웃·에러 메시지 상수.
21
+ - **`DB_CONN_CONNECT_TIMEOUT`** DB 연결 수립 타임아웃 상수 (10초).
22
+ - **`DB_CONN_DEFAULT_TIMEOUT`** — 쿼리/유휴 기본 타임아웃 상수 (10분).
23
+ - **`DB_CONN_ERRORS`** — `NOT_CONNECTED` / `ALREADY_CONNECTED` 에러 메시지 리터럴.
14
24
 
15
25
  ## createOrm
16
26
 
@@ -4,6 +4,6 @@ Claude Code 셋업 자산(`.claude/` 의 sd-* 스킬·룰·훅) 배포 및 `sd-c
4
4
 
5
5
  코드 API 없음 (npm 배포용). 외부에서 import 할 수 있는 라이브러리 심볼은 노출하지 않는다.
6
6
 
7
- - `bin`: `sd-claude` — `auth save` / `auth switch` (저장된 Claude 계정 전환).
8
- - `postinstall`: 설치 시 사용자 `~/.claude/` 로 자산 동기화.
9
- - `prepack`: 배포 전 `.claude/` → `packages/sd-claude/claude/` 증분 복사.
7
+ - `bin`: `sd-claude` — `auth save` (현재 Claude 계정 저장) / `auth switch` (저장된 계정 목록에서 선택해 전환).
8
+ - `postinstall`: 패키지 설치 시 `claude/` 의 sd-\* 에셋을 소비 프로젝트 루트의 `.claude/` 로 복사 (기존 sd-\* 항목은 정리 후 재복사, `settings.json`·`simplysm.json` 포함).
9
+ - `prepack`: 배포 전 워크스페이스의 `.claude/` → `packages/sd-claude/claude/` 증분 복사 (mtime+size 비교, `evals/`·`SKILL.eval.md`·`eval_*` 제외).
@@ -1,14 +1,44 @@
1
1
  # @simplysm/sd-cli
2
2
 
3
- 워크스페이스 빌드/배포 오케스트레이터. 라이브러리 export `sd.config.ts` 작성용 타입과 Vitest Angular 플러그인, 그리고 외부 도구가 재사용할 `SdTsCompiler` 다. CLI 서브커맨드는 이 문서 대상 아님 (`pnpm sd-cli --help`).
3
+ 워크스페이스 빌드/배포 오케스트레이터. 소비 표면은 갈래다 — `pnpm sd-cli <cmd>` 서브커맨드와 `sd.config.ts` 작성용 타입. 라이브러리 export 로는 Vitest Angular 플러그인과 외부 도구가 재사용할 `SdTsCompiler` 노출한다.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
- - **`SdConfigFn` / `SdConfig` / `SdConfigParams`** — `sd.config.ts` 의 default export 타이핑. 자세히: [sd-config.md](./sd-config.md)
7
- - **패키지별 설정 타입 (`SdBuildPackageConfig`, `SdClientPackageConfig`, `SdServerPackageConfig`, `SdScriptsPackageConfig`)** — `SdConfig.packages` 값 타이핑. 자세히: [sd-config.md](./sd-config.md)
8
- - **`SdPublishConfig` / `SdPostPublishScriptConfig`** — `publish`·`postPublish` 항목 타이핑. 자세히: [sd-config.md](./sd-config.md)
9
- - **`SdCapacitorConfig` / `SdElectronConfig` / `SdPwaConfig` / `SdBrowserSupportConfig`** 클라이언트 패키지의 모바일/데스크톱/PWA/브라우저 지원 옵션. 자세히: [sd-config.md](./sd-config.md)
10
- - **`sdAngularPlugin(options)`** — Vitest `vite.config` (또는 `vitest.config`) 에서 Angular 패키지 AOT 컴파일이 필요할 때 plugin 으로 추가.
11
- - **`SdTsCompiler` (+ `ISdTsCompilerOptions`, `ISdTsCompilerResult`)** 사용자 코드에서 직접 호출할 일은 거의 없음. Angular/Non-Angular TS 패키지를 증분 컴파일·진단·lint·SCSS 통합 처리하는 엔진. `sd-cli` 내부 엔진(`engines/`, `orchestrators/`) `sdAngularPlugin` 에서 사용. 외부에서 동일한 엔진을 재사용해야 할 때만 직접 import.
6
+
7
+ ### 서브커맨드 (`pnpm sd-cli <cmd>`)
8
+
9
+ - **`check`** 타입체크와 lint 병렬 실행. 일반 검증 명령으로 가장 자주 호출됨.
10
+ - **`watch`** — 워크스페이스 전체 또는 일부 패키지를 watch 모드로 증분 빌드.
11
+ - **`dev`**server 패키지를 dev 모드로 실행 (`tsx` 핫리로드 + client 패키지의 dev http 서버 동반 기동).
12
+ - **`device`** — client 패키지를 안드로이드 디바이스 또는 Electron 데스크톱에서 실행. dev 서버가 떠 있어야 함.
13
+ - **`build`** — 프로덕션 빌드. `sd.config.ts` 의 `packages` 전 항목 또는 `-t` 지정 항목.
14
+ - **`publish`** — 빌드 후 `publish` 설정을 따라 배포 (npm / 로컬 디렉토리 / FTP·SFTP). `--no-build` 로 기존 산출물만 배포.
15
+ - **`replace-deps`** — `sd.config.ts.replaceDeps` 에 따라 `node_modules` 패키지를 로컬 소스로 심링크 교체.
16
+ - **`init`** — 인터랙티브 프롬프트로 SI 워크스페이스 골격 생성.
17
+
18
+ 공통 옵션:
19
+ - `-t / --target <pkg>` (반복 가능) — 대상 패키지. `sd.config.ts.packages` 키 (= `@simplysm/` 접두사 **제외**한 짧은 이름). 미지정 시 전체.
20
+ - `-o / --opt <val>` (반복 가능) — `sd.config.ts` 함수의 `params.opt[]` 로 전달. `check` 와 `init` 제외 전 커맨드에서 지원.
21
+ - `--debug` — 디버그 로그 출력 (모든 커맨드).
22
+ - `--help / -h` — 단독 호출 시 모든 서브커맨드 종합 도움말.
23
+
24
+ 커맨드별 고유 옵션:
25
+ - `check`: `--type typecheck|lint` (반복 가능, 기본 둘 다), `--fix` (lint 자동 수정).
26
+ - `device`: `--target <pkg>` (단수, 미지정 시 유일 client 자동 선택), `--url <devServerUrl>` (미지정 시 `sd.config.ts` 의 server 설정에서 자동 도출).
27
+ - `publish`: `--no-build` (빌드 생략), `--dry-run` (실제 배포 없이 시뮬레이션).
28
+
29
+ 진실 근거: `packages/sd-cli/src/sd-cli-entry.ts` (yargs 등록부), `packages/sd-cli/src/commands/<cmd>.ts`.
30
+
31
+ ### 설정 타입 (`sd.config.ts`)
32
+
33
+ - **`SdConfigFn` / `SdConfig` / `SdConfigParams`** — `sd.config.ts` 의 default export 함수와 그 반환 타입. 자세히: [sd-config.md](./sd-config.md)
34
+ - **`SdBuildPackageConfig` / `SdClientPackageConfig` / `SdServerPackageConfig` / `SdScriptsPackageConfig`** — `SdConfig.packages` 의 값 타입. 각각 라이브러리 / Frontend 앱 / Fastify 서버 / 유틸 패키지에 대응. 자세히: [sd-config.md](./sd-config.md)
35
+ - **`SdPublishConfig` / `SdPostPublishScriptConfig`** — 패키지의 `publish` 와 전역 `postPublish` 항목 타입. 자세히: [sd-config.md](./sd-config.md)
36
+ - **`SdCapacitorConfig` / `SdElectronConfig` / `SdPwaConfig` / `SdBrowserSupportConfig`** — client 패키지의 모바일·데스크톱·PWA·브라우저 지원 옵션. 자세히: [sd-config.md](./sd-config.md)
37
+
38
+ ### 라이브러리 export
39
+
40
+ - **`sdAngularPlugin(options)`** — Vitest 의 `vite.config` / `vitest.config` 에서 Angular 패키지 AOT 컴파일이 필요할 때 plugin 으로 추가.
41
+ - **`SdTsCompiler` (+ `ISdTsCompilerOptions`, `ISdTsCompilerResult`)** — Angular/Non-Angular TS 패키지를 증분 컴파일·진단·lint·SCSS 통합 처리하는 엔진. `sd-cli` 내부와 `sdAngularPlugin` 이 사용. 외부에서 동일한 엔진을 재사용할 때만 직접 import.
12
42
 
13
43
  ## sdAngularPlugin
14
44
 
@@ -3,28 +3,33 @@
3
3
  `@simplysm/service-server` 와 WebSocket 으로 통신하는 클라이언트. RPC 호출·이벤트 구독·파일 업/다운로드·원격 ORM 실행을 단일 `ServiceClient` 에서 제공한다 (Node/브라우저 공용).
4
4
 
5
5
  ## 사용 트리거 인덱스
6
- - **createServiceClient / ServiceConnectionOptions** — 클라이언트 인스턴스 생성·서버 접속/종료할 때.
7
- - **getService / send / ServiceProxy** — 서버 서비스 메서드를 타입 안전 RPC호출할 때.
8
- - **auth** — 서버 인증 토큰을 등록하고 재연결 자동 재인증되게 때.
9
- - **getEvent / addListener / removeListener / emitEvent** — 서버 사이드 이벤트 구독 또는 다른 클라이언트로 이벤트 발행할 때.
10
- - **uploadFile / downloadFileBuffer** — 서버에 파일을 올리거나 정적 경로에서 바이트로 받을 때.
11
- - **createOrmClientConnector / OrmConnectOptions** — 서버의 ORM 서비스를 통해 DbContext 트랜잭션을 원격 실행할 때.
12
- - **ServiceClient 이벤트 (`state`, `request-progress`, `response-progress`, `server-progress`)** 연결 상태와 전송/응답/서버 진행률을 구독할 때.
6
+
7
+ - **createServiceClient / ServiceConnectionOptions** — 서버 접속용 `ServiceClient` 만들고 `connect()`/`close()`연결을 열고 닫을 때.
8
+ - **getService / send / ServiceProxy** — 서버에 등록된 서비스의 메서드를 타입 안전 RPC 호출할 때 (Proxy 기반 메서드 호출 또는 저수준 `send`).
9
+ - **auth** — 서버 인증 토큰을 등록하고 재연결 자동 재인증되도록 토큰을 내부에 보관할 때.
10
+ - **getEvent / addListener / removeListener / emitEvent** — 서버 push 이벤트를 구독·해지하거나, 다른 클라이언트에게 이벤트를 발행할 때.
11
+ - **uploadFile / downloadFileBuffer** — 서버에 multipart 파일을 올리거나 정적 경로에서 바이트(`Uint8Array`)로 받을 때.
12
+ - **createOrmClientConnector / OrmConnectOptions** 서버 `"Orm"` 서비스를 통해 `DbContext` 트랜잭션을 원격 실행할 때.
13
+ - **ServiceClient 이벤트 (`state` / `request-progress` / `response-progress` / `server-progress`)** — 연결 상태 변화와 전송/응답/서버 진행률을 인스턴스 레벨에서 구독할 때.
14
+ - **BlobInput / FileCollection / isWorkerSupported 외** — Node/브라우저 양쪽 환경에서 동일한 코드를 쓰기 위해 DOM 의존 타입을 대체하거나 Worker 지원 여부를 미리 확인할 때.
13
15
 
14
16
  ## 연결/생성
17
+
15
18
  ```ts
16
19
  createServiceClient(name: string, options: ServiceConnectionOptions): ServiceClient
17
20
  interface ServiceConnectionOptions { port: number; host: string; ssl?: boolean; maxReconnectCount?: number; }
18
21
  client.connect(): Promise<void>
19
- client.close(): Promise<void>
22
+ client.close(): Promise<void> // protocol wrapper 의 worker 자원도 dispose
20
23
  client.connected: boolean
21
- client.hostUrl: string // `${ssl?https:http}://host:port`
24
+ client.hostUrl: string // `${ssl?https:http}://host:port`
22
25
  ```
23
- - WebSocket URL 은 `${ws|wss}://host:port/ws` 로 고정.
26
+
27
+ - WebSocket URL 은 `${ws|wss}://host:port/ws?ver=2&clientId=<uuid>&clientName=<name>` 으로 접속한다.
24
28
  - `maxReconnectCount` 기본 10, `0` 이면 재연결 비활성.
25
- - 5s ping / 30s 무응답 시 강제 재연결, 재연결 성공 시 인증 토큰과 이벤트 리스너가 자동 복구됨.
29
+ - 5s 마다 ping, 30s 무응답 시 강제 재연결. 재연결 성공 시 인증 토큰(`auth()`)과 이벤트 리스너(`addListener`)가 자동 복구된다.
26
30
 
27
31
  ## RPC 호출
32
+
28
33
  ```ts
29
34
  client.getService<TService>(serviceName): ServiceProxy<TService>
30
35
  client.send(serviceName, methodName, params, progress?): Promise<unknown>
@@ -32,18 +37,25 @@ type ServiceProxy<T> = { [K in keyof T]: T[K] extends (...a:infer P)=>infer R ?
32
37
  interface ServiceProgress { request?(s); response?(s); server?(s); }
33
38
  interface ServiceProgressState { uuid: string; totalSize: number; completedSize: number; }
34
39
  ```
35
- - `getService` 는 Proxy 라 임의 메서드명 호출 가능. 타입은 `TService` 로만 보장됨.
36
- - 메시지 이름은 `"<serviceName>.<methodName>"` 으로 전송.
37
- - 30KB 초과 또는 `Uint8Array`/큰 배열 페이로드는 Worker 에서 인코딩/디코딩.
40
+
41
+ - `getService` Proxy 라 임의 메서드명 호출 가능. 타입은 `TService` 로만 보장된다.
42
+ - 와이어 메시지 이름은 `"<serviceName>.<methodName>"`.
43
+ - 인코딩/디코딩의 Worker 오프로딩 임계값:
44
+ - **인코드**: body 가 `Uint8Array`, 30KB 초과 문자열, 길이 100 초과 배열, 또는 첫 요소가 `Uint8Array` 인 배열일 때 Worker 사용.
45
+ - **디코드**: 수신 바이트가 30KB 초과일 때 Worker 사용.
46
+ - Worker 미지원 환경(또는 초기화 실패)에선 메인 스레드 fallback.
38
47
 
39
48
  ## 인증
49
+
40
50
  ```ts
41
51
  client.auth(token: string): Promise<void>
42
52
  ```
53
+
43
54
  - 서버에 `auth` 메시지를 보내고 토큰을 내부 저장. 재연결 시 자동으로 같은 토큰으로 재인증.
44
- - `uploadFile` 은 인증 토큰이 없으면 에러를 던짐.
55
+ - `uploadFile` 은 인증 토큰이 없으면 에러를 던진다.
45
56
 
46
57
  ## 이벤트 (서버 push)
58
+
47
59
  ```ts
48
60
  client.getEvent<TEventDef>(eventName): ClientEventProxy<TEventDef>
49
61
  client.addListener<TEventDef>(eventName, info, cb): Promise<string> // key 반환
@@ -51,22 +63,26 @@ client.removeListener(key): Promise<void>
51
63
  client.emitEvent<TEventDef>(eventName, infoSelector, data): Promise<void>
52
64
  interface ClientEventProxy<T> { addListener(info, cb): Promise<string>; removeListener(key): Promise<void>; emit(infoSelector, data): Promise<void>; }
53
65
  ```
54
- - `addListener` 는 연결 상태에서만 가능. 로컬 맵에도 보관해 재연결 시 자동 재구독.
55
- - `emitEvent` 는 서버에 `evt:gets` 후보를 받아 `infoSelector` 통과분에만 `evt:emit` 호출.
56
- - 핸들러 내부 에러는 로깅만 되고 다른 핸들러 호출은 계속됨.
66
+
67
+ - `addListener` 는 연결 상태에서만 호출 가능 (`!connected` throw). 로컬 맵에도 보관해 재연결 시 자동 재구독.
68
+ - `emitEvent` 서버에 `evt:gets` 후보를 받아 `infoSelector` 통과분에만 `evt:emit` 호출. 대상이 0건이면 emit 생략.
69
+ - 핸들러 내부 에러는 로깅만 되고 다른 핸들러 호출은 계속 진행.
57
70
 
58
71
  ## 파일 업/다운로드
72
+
59
73
  ```ts
60
74
  client.uploadFile(files: File[] | FileCollection | { name: string; data: BlobInput }[]): Promise<ServiceUploadResult[]>
61
75
  client.downloadFileBuffer(relPath: string): Promise<Uint8Array>
62
76
  type BlobInput = Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | string
63
77
  interface FileCollection { length; item(i); [i]; [Symbol.iterator]() } // 브라우저 FileList 호환
64
78
  ```
79
+
65
80
  - 업로드: `POST {hostUrl}/upload` (multipart). 헤더 `x-sd-client-name`, `Authorization: Bearer <token>`. 인증 필수.
66
- - 다운로드: `GET {hostUrl}/{relPath}` 의 응답 본문을 `Uint8Array` 로 반환.
81
+ - 다운로드: `GET {hostUrl}/{relPath}` 의 응답 본문을 `Uint8Array` 로 반환. `relPath` 가 `/` 로 시작하지 않으면 자동으로 붙임.
67
82
  - 브라우저 전용 `File`/`FileList` 대신 `BlobInput`/`FileCollection` 으로 Node 환경도 지원.
68
83
 
69
84
  ## ORM 클라이언트
85
+
70
86
  ```ts
71
87
  createOrmClientConnector(client: ServiceClient): OrmClientConnector
72
88
  interface OrmConnectOptions<T extends DbContext> {
@@ -77,16 +93,32 @@ interface OrmConnectOptions<T extends DbContext> {
77
93
  connector.connect(config, async db => ...) // 트랜잭션 래핑
78
94
  connector.connectWithoutTransaction(config, async db => ...)
79
95
  ```
80
- - 서버의 `"Orm"` 서비스에 RPC 를 걸어 `getInfo` → `connect` → `executeDefs` 등을 실행.
81
- - `dbContextOpt` 생략 서버가 알려준 `database`/`schema` 사용. 비면 에러.
82
- - 외래키 참조 위반 에러(`a parent row: a foreign key constraint` / `conflicted with the REFERENCE`)는 사용자 메시지로 변환되어 throw.
96
+
97
+ - 서버의 `"Orm"` 서비스에 RPC 걸어 `getInfo` `connect` `executeDefs`/`executeParametrized`/`bulkInsert` 등을 실행.
98
+ - `dbContextOpt` 생략 서버가 알려준 `database`/`schema` 사용. 최종 `database` 값이면 에러.
99
+ - 외래키 참조 위반 에러(`a parent row: a foreign key constraint` / `conflicted with the REFERENCE`)는 "경고! 연관된 작업으로 인해 작업이 거부되었습니다..." 사용자 메시지로 변환되어 throw (원본은 `cause`).
83
100
 
84
101
  ## 상태/진행률 이벤트
102
+
85
103
  ```ts
86
104
  client.on("state", (s: "connected" | "closed" | "reconnecting") => ...)
87
105
  client.on("request-progress", (s: ServiceProgressState) => ...) // 클라이언트 → 서버 전송 청크 진행
88
106
  client.on("response-progress", (s: ServiceProgressState) => ...) // 서버 → 클라이언트 응답 청크 진행
89
107
  client.on("server-progress", (s: ServiceProgressState) => ...) // 서버가 명시적으로 보내는 진행률
90
108
  ```
91
- - 단일 청크 메시지에는 progress 가 발행되지 않음(분할 메시지에서만).
92
- - `send` 호출 전달한 `progress?: ServiceProgress` 콜백은 인스턴스 이벤트와 별도로 호출됨.
109
+
110
+ - `request-progress` 인코드 결과 chunk 2개 이상일 때만 초기 0% 가 발행됨. 단일 청크는 progress 없음.
111
+ - `response-progress` 는 분할 수신 중에는 서버 progress 메시지로, 응답 완료 시 100% 보정값으로 발행됨.
112
+ - `send` 호출 시 전달한 `progress?: ServiceProgress` 콜백은 인스턴스 이벤트와 함께 호출된다.
113
+
114
+ ## 환경 호환 유틸
115
+
116
+ ```ts
117
+ import { BlobInput, FileCollection, BrowserWorker,
118
+ isBrowserWorkerSupported, isNodeWorkerSupported, isWorkerSupported }
119
+ from "@simplysm/service-client";
120
+ ```
121
+
122
+ - `BrowserWorker` — DOM `Worker` 의 구조적 호환 인터페이스. Node 환경 typecheck 통과용.
123
+ - `isBrowserWorkerSupported()` / `isNodeWorkerSupported()` / `isWorkerSupported()` — 현재 런타임에서 Worker 오프로딩이 가능한지 사전 확인.
124
+ - 보통 직접 호출할 필요 없음. `ServiceClient` 가 내부에서 분기하므로, 환경별 분기 로직을 사용자 코드에서 직접 짤 때만 사용.
@@ -7,7 +7,9 @@ FTP/FTPS/SFTP 원격 스토리지에 동일 인터페이스(`StorageClient`)로
7
7
  - **`StorageFactory.connect`** — FTP/FTPS/SFTP 어느 것이든 연결→작업→자동 종료를 한 번에 처리할 때 (권장 진입점).
8
8
  - **`StorageClient`** — 콜백 안에서 사용하는 공통 파일 작업 인터페이스(mkdir/list/readFile/put/uploadDir/remove/rename/exists).
9
9
  - **`FtpStorageClient` / `SftpStorageClient`** — 연결 생명주기를 직접 관리해야 할 때만 (장기 연결 풀 등). 그 외엔 `StorageFactory.connect` 사용.
10
- - **`StorageConnConfig`, `StorageProtocol`, `FileInfo`** API 호출 타입.
10
+ - **`StorageConnConfig`** `connect`/`StorageFactory.connect` 넘기는 접속 정보(host/port/user/password) 타입.
11
+ - **`StorageProtocol`** — `StorageFactory.connect` 의 `type` 인자로 쓰는 프로토콜 리터럴(`"ftp" | "ftps" | "sftp"`).
12
+ - **`FileInfo`** — `list` 결과 항목 타입(`{ name, isFile }`).
11
13
 
12
14
  ## StorageFactory.connect
13
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-claude",
3
- "version": "14.0.72",
3
+ "version": "14.0.73",
4
4
  "description": "심플리즘 패키지 - Claude Code 셋업",
5
5
  "author": "심플리즘",
6
6
  "license": "Apache-2.0",