@simplysm/sd-claude 14.0.98 β†’ 14.0.100

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 (77) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -16
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +81 -153
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +179 -205
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +71 -57
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +49 -109
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +58 -86
  7. package/claude/references/sd-simplysm14/apis/angular/kanban.md +32 -40
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +38 -52
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +86 -110
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -86
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +82 -74
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +56 -80
  13. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +15 -15
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -21
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +79 -53
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +9 -11
  17. package/claude/references/sd-simplysm14/apis/core-browser/README.md +15 -15
  18. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +20 -20
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +18 -18
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +20 -49
  21. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +66 -55
  22. package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +83 -56
  23. package/claude/references/sd-simplysm14/apis/core-common/errors.md +32 -21
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +57 -39
  25. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +36 -30
  26. package/claude/references/sd-simplysm14/apis/core-common/value-types.md +69 -41
  27. package/claude/references/sd-simplysm14/apis/core-node/README.md +4 -4
  28. package/claude/references/sd-simplysm14/apis/core-node/consola.md +15 -13
  29. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +11 -7
  30. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +8 -8
  31. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +29 -20
  32. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -6
  33. package/claude/references/sd-simplysm14/apis/core-node/worker.md +3 -3
  34. package/claude/references/sd-simplysm14/apis/excel/README.md +3 -3
  35. package/claude/references/sd-simplysm14/apis/excel/cell.md +32 -32
  36. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +23 -24
  37. package/claude/references/sd-simplysm14/apis/excel/style.md +24 -30
  38. package/claude/references/sd-simplysm14/apis/excel/utils.md +20 -23
  39. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +60 -71
  40. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +36 -36
  41. package/claude/references/sd-simplysm14/apis/lint/README.md +7 -9
  42. package/claude/references/sd-simplysm14/apis/lint/recommended.md +59 -37
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +81 -74
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -6
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +112 -78
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +131 -75
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +126 -82
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +170 -113
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +102 -48
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +12 -13
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +3 -3
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +5 -5
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +67 -65
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +130 -123
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +63 -63
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +22 -22
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +30 -26
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +8 -8
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +13 -6
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +1 -1
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +43 -47
  62. package/claude/references/sd-simplysm14/apis/service-server/built-in-services.md +35 -0
  63. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +20 -19
  64. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +23 -25
  65. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +9 -9
  66. package/claude/references/sd-simplysm14/apis/storage/README.md +26 -26
  67. package/claude/references/sd-simplysm14/manuals/client-component.md +9 -1
  68. package/claude/references/sd-simplysm14/manuals/client-crud.md +1 -1
  69. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -0
  70. package/claude/references/sd-simplysm14/manuals/client-service.md +1 -0
  71. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +1 -0
  72. package/claude/references/sd-simplysm14/manuals/client-ssg.md +1 -0
  73. package/claude/sd-system-prompt.md +11 -26
  74. package/claude/skills/sd-docs/references/subagent-prompt.md +4 -3
  75. package/claude/skills/sd-spec/SKILL.md +87 -18
  76. package/claude/skills/sd-spec/references/format.md +2 -2
  77. package/package.json +1 -1
@@ -1,39 +1,39 @@
1
1
  # @simplysm/service-client
2
2
 
3
- WebSocket 으둜 μ„œλΉ„μŠ€ μ„œλ²„μ— 접속해 μ„œλΉ„μŠ€ RPC ν˜ΈμΆœΒ·μ„œλ²„ ν‘Έμ‹œ 이벀트 ꡬ독/λ°œν–‰Β·νŒŒμΌ μ—…/λ‹€μš΄λ‘œλ“œΒ·μ„œλ²„μΈ‘ ORM 원격 싀행을 μˆ˜ν–‰ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈ. λΈŒλΌμš°μ €μ™€ Node.js μ–‘μͺ½μ—μ„œ λ™μž‘(Node μ—μ„œ κΈ€λ‘œλ²Œ `WebSocket` 이 μ—†μœΌλ©΄ λͺ¨λ“ˆ λ‘œλ“œ μ‹œ `ws` 둜 polyfill).
3
+ WebSocket 으둜 μ„œλΉ„μŠ€ μ„œλ²„μ— λΆ™μ–΄ μ„œλΉ„μŠ€ λ©”μ„œλ“œ RPC ν˜ΈμΆœΒ·μ„œλ²„ ν‘Έμ‹œ 이벀트 ꡬ독/λ°œν–‰Β·νŒŒμΌ μ—…/λ‹€μš΄λ‘œλ“œΒ·μ„œλ²„μΈ‘ ORM 원격 싀행을 μˆ˜ν–‰ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈ. λΈŒλΌμš°μ €Β·Node.js μ–‘μͺ½μ—μ„œ λ™μž‘ν•˜λ©°, Node μ—μ„œ κΈ€λ‘œλ²Œ `WebSocket` 이 μ—†μœΌλ©΄ μ†ŒμΌ“ λͺ¨λ“ˆ λ‘œλ“œ μ‹œμ μ— `ws` νŒ¨ν‚€μ§€λ‘œ polyfill ν•œλ‹€.
4
4
 
5
5
  ## μ‚¬μš© 트리거 인덱슀
6
6
 
7
- - **createServiceClient / ServiceClient** β€” μ„œλ²„ 접속, μ„œλΉ„μŠ€ 호좜, 인증, μ—°κ²° μƒνƒœ 좔적이 ν•„μš”ν•  λ•Œ. 이 νŒ¨ν‚€μ§€μ˜ μ£Ό μ§„μž…μ . (μ•„λž˜ 인라인 μ„Ήμ…˜)
8
- - **ServiceConnectionOptions** β€” ν΄λΌμ΄μ–ΈνŠΈ 생성 μ‹œ 접속 λŒ€μƒ(host/port/ssl)Β·μž¬μ—°κ²° 정책을 μ •ν•  λ•Œ. (μ•„λž˜ 인라인 μ„Ήμ…˜)
9
- - **getService / ServiceProxy** β€” μ„œλ²„ μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό νƒ€μž… μ•ˆμ „ ν”„λ‘μ‹œλ‘œ ν˜ΈμΆœν•  λ•Œ. (μ•„λž˜ 인라인 μ„Ήμ…˜)
10
- - **이벀트 κ΅¬λ…Β·λ°œν–‰ (addListener / removeListener / emitEvent / getEvent / ClientEventProxy / EventClient)** β€” μ„œλ²„ ν‘Έμ‹œ 이벀트λ₯Ό κ΅¬λ…Β·λ°œν–‰ν•  λ•Œ. (μ•„λž˜ 인라인 μ„Ήμ…˜)
11
- - **파일 μ—…/λ‹€μš΄λ‘œλ“œ (uploadFile / downloadFileBuffer / FileClient)** β€” 인증된 파일 μ—…λ‘œλ“œ, μ„œλ²„ μƒλŒ€κ²½λ‘œ 파일 λ‹€μš΄λ‘œλ“œ μ‹œ. (μ•„λž˜ 인라인 μ„Ήμ…˜)
12
- - **μ§„ν–‰λ₯  (ServiceProgress / ServiceProgressState)** β€” λŒ€μš©λŸ‰ μš”μ²­Β·μ‘λ‹΅μ˜ 청크 전솑 μ§„ν–‰λ₯ μ„ 좔적할 λ•Œ. (μ•„λž˜ 인라인 μ„Ήμ…˜)
13
- - **ν™˜κ²½ ν˜Έν™˜ νƒ€μž…Β·ν—¬νΌ (BlobInput / FileCollection / BrowserWorker / isWorkerSupported λ“±)** β€” Node/browser 곡용 μ½”λ“œμ—μ„œ DOM μ „μš© νƒ€μž… νšŒν”Ό, Worker 지원 λΆ„κΈ° μ‹œ. (μ•„λž˜ 인라인 μ„Ήμ…˜)
14
- - **ORM 원격 μ‹€ν–‰ (createOrmClientConnector / OrmClientConnector / OrmConnectOptions / OrmClientDbContextExecutor)** β€” μ„œλ²„μΈ‘ ORM DbContext λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ νŠΈλžœμž­μ…˜ λ‹¨μœ„λ‘œ μ‹€ν–‰ν•  λ•Œ. μžμ„Ένžˆ: [orm.md](./orm.md)
15
- - **μ €μˆ˜μ€€ 전솑 계측 (SocketProvider / ServiceTransport / ClientProtocolWrapper 및 create\*)** β€” 일반적으둜 직접 μ“°μ§€ μ•ŠμŒ. `ServiceClient` κ°€ λ‚΄λΆ€μ—μ„œ 쑰립. μ†ŒμΌ“Β·ν•˜νŠΈλΉ„νŠΈΒ·ν”„λ‘œν† μ½œΒ·μ²­ν¬ λ™μž‘μ„ 이해해야 ν•  λ•Œ. μžμ„Ένžˆ: [transport.md](./transport.md)
7
+ - **createServiceClient / ServiceClient** β€” μ„œλ²„ μ ‘μ†Β·μ„œλΉ„μŠ€ ν˜ΈμΆœΒ·μΈμ¦Β·μ—°κ²° μƒνƒœ μΆ”μ μ˜ μ£Ό μ§„μž…μ . (μ•„λž˜ 인라인)
8
+ - **ServiceConnectionOptions** β€” 접속 λŒ€μƒ(host/port/ssl)Β·μž¬μ—°κ²° μ •μ±… μ§€μ •. (μ•„λž˜ 인라인)
9
+ - **getService / ServiceProxy** β€” μ„œλ²„ μ„œλΉ„μŠ€λ₯Ό νƒ€μž… μ•ˆμ „ ν”„λ‘μ‹œλ‘œ 호좜. (μ•„λž˜ 인라인)
10
+ - **이벀트 κ΅¬λ…Β·λ°œν–‰ (getEvent / addListener / removeListener / emitEvent / ClientEventProxy / EventClient)** β€” μ„œλ²„ ν‘Έμ‹œ 이벀트 κ΅¬λ…Β·λ°œν–‰. (μ•„λž˜ 인라인)
11
+ - **파일 μ—…/λ‹€μš΄λ‘œλ“œ (uploadFile / downloadFileBuffer / FileClient)** β€” 인증 μ—…λ‘œλ“œ, μ„œλ²„ μƒλŒ€κ²½λ‘œ λ‹€μš΄λ‘œλ“œ. (μ•„λž˜ 인라인)
12
+ - **μ§„ν–‰λ₯  (ServiceProgress / ServiceProgressState)** β€” 청크 λΆ„ν• λ˜λŠ” λŒ€μš©λŸ‰ μš”μ²­/μ‘λ‹΅μ˜ μ§„ν–‰ 좔적. (μ•„λž˜ 인라인)
13
+ - **ν™˜κ²½ ν˜Έν™˜ νƒ€μž…Β·ν—¬νΌ (BlobInput / FileCollection / BrowserWorker / isBrowserWorkerSupported / isNodeWorkerSupported / isWorkerSupported)** β€” Node/browser 곡용 μ½”λ“œμ˜ DOM νƒ€μž… νšŒν”ΌΒ·Worker λΆ„κΈ°. (μ•„λž˜ 인라인)
14
+ - **ORM 원격 μ‹€ν–‰ (createOrmClientConnector / OrmClientConnector / OrmConnectOptions / OrmClientDbContextExecutor)** β€” μ„œλ²„ DbContext λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ νŠΈλžœμž­μ…˜ λ‹¨μœ„λ‘œ μ‹€ν–‰. μžμ„Ένžˆ: [orm.md](./orm.md)
15
+ - **μ €μˆ˜μ€€ 전솑 계측 (SocketProvider / ServiceTransport / ClientProtocolWrapper 및 create\*)** β€” `ServiceClient` κ°€ λ‚΄λΆ€μ—μ„œ μ‘°λ¦½ν•˜λŠ” μ†ŒμΌ“Β·ν•˜νŠΈλΉ„νŠΈΒ·ν”„λ‘œν† μ½œΒ·μ²­ν¬ λͺ¨λ“ˆ. 직접 μ“°μ§€ μ•ŠμŒ. μžμ„Ένžˆ: [transport.md](./transport.md)
16
16
 
17
- > μ•±(Angular) λ ˆμ΄μ–΄μ—μ„œλŠ” `ServiceClient` λ₯Ό ν™”λ©΄μ—μ„œ 직접 λ§Œλ“€μ§€ μ•Šκ³  `AppServiceProvider`(root provider) 의 `client` getter λ₯Ό κ²½μœ ν•΄ μ„œλΉ„μŠ€Β·μ΄λ²€νŠΈΒ·ORM μ§„μž…μ μ„ λͺ¨μ€λ‹€. μ•„λž˜ μ˜ˆμ‹œλŠ” client 직접 호좜 ν˜•νƒœλ‘œ λ³΄μ—¬μ£Όμ§€λ§Œ, μ‹€μ œ μ•± μ½”λ“œλŠ” manuals/client-service.mdΒ·client-orm.md 의 provider νŒ¨ν„΄μ„ λ”°λ₯Έλ‹€.
17
+ > μ•±(Angular)μ—μ„œλŠ” 화면이 `ServiceClient` λ₯Ό 직접 λ§Œλ“€μ§€ μ•Šκ³  `AppServiceProvider`(root provider)의 `client` getter λ₯Ό κ²½μœ ν•΄ μ„œλΉ„μŠ€Β·μ΄λ²€νŠΈΒ·ORM μ§„μž…μ μ„ λͺ¨μ€λ‹€. μ•„λž˜ μ˜ˆμ‹œλŠ” client 직접 호좜 ν˜•νƒœμ§€λ§Œ, μ‹€μ œ μ•± μ½”λ“œλŠ” manuals/client-service.mdΒ·client-orm.mdΒ·event.md 의 provider νŒ¨ν„΄μ„ λ”°λ₯Έλ‹€.
18
18
 
19
19
  ## 메인 ν΄λΌμ΄μ–ΈνŠΈ (createServiceClient / ServiceClient)
20
20
 
21
21
  `createServiceClient(name, options): ServiceClient` β€” ν΄λΌμ΄μ–ΈνŠΈ μΈμŠ€ν„΄μŠ€ 생성. `new ServiceClient(name, options)` 와 동일.
22
22
 
23
- - name: string β€” ν΄λΌμ΄μ–ΈνŠΈ 식별 이름. WebSocket 접속 쿼리의 `clientName`·파일 μ—…λ‘œλ“œ 헀더 `x-sd-client-name` 으둜 μ„œλ²„μ— 전달. μ„œλ²„ λ‘œκ·ΈΒ·μ—°κ²° ꡬ뢄에 μ‚¬μš©.
24
- - options: ServiceConnectionOptions β€” 접속 λŒ€μƒΒ·μž¬μ—°κ²° μ •μ±… (μ•„λž˜ μ„Ήμ…˜).
23
+ - `name: string` β€” ν΄λΌμ΄μ–ΈνŠΈ 식별 이름. WebSocket 접속 쿼리의 `clientName`·파일 μ—…λ‘œλ“œ 헀더 `x-sd-client-name` 으둜 μ„œλ²„μ— 전달. μ„œλ²„μΈ‘ μ—°κ²° κ΅¬λΆ„Β·λ‘œκΉ…μš©.
24
+ - `options: ServiceConnectionOptions` β€” 접속 λŒ€μƒΒ·μž¬μ—°κ²° μ •μ±… (μ•„λž˜ μ„Ήμ…˜).
25
25
 
26
26
  `ServiceClient` λŠ” `EventEmitter<ServiceClientEvents>` λ₯Ό μƒμ†ν•˜λ©° λ‹€μŒμ„ λ…ΈμΆœ:
27
27
 
28
- - name: string (readonly) β€” 생성 μ‹œ 받은 ν΄λΌμ΄μ–ΈνŠΈ 이름.
29
- - options: ServiceConnectionOptions (readonly) β€” 생성 μ‹œ 받은 접속 μ˜΅μ…˜.
30
- - connected: boolean (getter) β€” ν˜„μž¬ WebSocket 이 OPEN μƒνƒœμΈμ§€. μž¬μ—°κ²° μ€‘Β·μ’…λ£Œ μ‹œ false. 이벀트 등둝 κ°€λŠ₯ μ—¬λΆ€ νŒλ‹¨μ— μ‚¬μš© (`addListener` λŠ” false λ©΄ throw).
31
- - hostUrl: string (getter) β€” `http(s)://host:port` ν˜•νƒœμ˜ HTTP 베이슀 URL. `ssl` 이 true λ©΄ https. 파일 μ—…/λ‹€μš΄λ‘œλ“œκ°€ 이 URL 을 베이슀둜 μ‚¬μš©.
32
- - connect(): Promise\<void\> β€” μ„œλ²„μ— WebSocket μ—°κ²°. 초기 μ—°κ²° μ‹€νŒ¨ μ‹œ throw. 톡신(μ„œλΉ„μŠ€ 호좜·이벀트 등둝) 전에 λ°˜λ“œμ‹œ 1회 호좜.
33
- - close(): Promise\<void\> β€” μ—°κ²° μˆ˜λ™ μ’…λ£Œ(이후 μžλ™ μž¬μ—°κ²° μ•ˆ 함) 및 ν”„λ‘œν† μ½œ μ›Œμ»€ μžμ› ν•΄μ œ. μ’…λ£Œν•œ μΈμŠ€ν„΄μŠ€λŠ” μž¬μ‚¬μš©ν•˜μ§€ 말 것.
34
- - send(serviceName, methodName, params, progress?): Promise\<unknown\> β€” μ €μˆ˜μ€€ μ„œλΉ„μŠ€ 호좜. `serviceName.methodName` λ©”μ‹œμ§€λ₯Ό 보내고 응닡 λ°˜ν™˜. 보톡 `getService` ν”„λ‘μ‹œλ‘œ κ°„μ ‘ 호좜. progress 인자λ₯Ό μ£Όμ§€ μ•Šμ•„λ„ client 의 `request/response/server-progress` μ΄λ²€νŠΈλŠ” 항상 λ°œμƒ.
35
- - auth(token): Promise\<void\> β€” 인증 토큰 전솑 ν›„ λ‚΄λΆ€ 보관. 보관 토큰은 μž¬μ—°κ²° μ‹œ μžλ™ 재인증·파일 μ—…λ‘œλ“œ Bearer 인증에 μž¬μ‚¬μš©. 파일 μ—…λ‘œλ“œ μ „μ—λŠ” λ°˜λ“œμ‹œ μ„ ν–‰.
36
- - getService / getEvent / addListener / removeListener / emitEvent / uploadFile / downloadFileBuffer β€” μ•„λž˜ 각 μ„Ήμ…˜ μ°Έμ‘°.
28
+ - `name: string` (readonly) β€” 생성 μ‹œ 받은 이름.
29
+ - `options: ServiceConnectionOptions` (readonly) β€” 생성 μ‹œ 받은 접속 μ˜΅μ…˜.
30
+ - `connected: boolean` (getter) β€” μ†ŒμΌ“μ΄ OPEN μƒνƒœμΈμ§€. μž¬μ—°κ²° μ€‘Β·μ’…λ£Œ μ‹œ false. `addListener` λŠ” false λ©΄ throw ν•˜λ―€λ‘œ 등둝 κ°€λŠ₯ μ—¬λΆ€ νŒλ‹¨μ— μ‚¬μš©.
31
+ - `hostUrl: string` (getter) β€” `ssl` 이면 `https://`, μ•„λ‹ˆλ©΄ `http://` 둜 μ‹œμž‘ν•˜λŠ” `ν”„λ‘œν† μ½œ://host:port` HTTP 베이슀 URL. 파일 μ—…/λ‹€μš΄λ‘œλ“œκ°€ 이 URL 을 베이슀둜 μ‚¬μš©.
32
+ - `connect(): Promise<void>` β€” μ„œλ²„μ— WebSocket μ—°κ²°. 초기 μ—°κ²° μ‹€νŒ¨ μ‹œ throw. λͺ¨λ“  톡신(μ„œλΉ„μŠ€ 호좜·이벀트 등둝) 전에 1회 호좜.
33
+ - `close(): Promise<void>` β€” μ—°κ²° μˆ˜λ™ μ’…λ£Œ(이후 μžλ™ μž¬μ—°κ²° μ•ˆ 함) ν›„ ν”„λ‘œν† μ½œ μ›Œμ»€ μžμ› ν•΄μ œ(`protocolWrapper.dispose()`). μ’…λ£Œν•œ μΈμŠ€ν„΄μŠ€λŠ” μž¬μ‚¬μš©ν•˜μ§€ μ•ŠμŒ.
34
+ - `send(serviceName, methodName, params, progress?): Promise<unknown>` β€” μ €μˆ˜μ€€ μ„œλΉ„μŠ€ 호좜. `serviceName.methodName` λ©”μ‹œμ§€λ₯Ό 보내고 응닡 λ°˜ν™˜. 보톡 `getService` ν”„λ‘μ‹œλ‘œ κ°„μ ‘ 호좜. progress 인자λ₯Ό μ£Όμ§€ μ•Šμ•„λ„ client 의 `request/response/server-progress` μ΄λ²€νŠΈλŠ” 항상 λ°œμƒ.
35
+ - `auth(token): Promise<void>` β€” 인증 토큰을 μ„œλ²„μ— μ „μ†‘ν•˜κ³  λ‚΄λΆ€ 보관. 보관 토큰은 μž¬μ—°κ²° μ‹œ μžλ™ 재인증·파일 μ—…λ‘œλ“œ Bearer 인증에 μž¬μ‚¬μš©λ¨. 파일 μ—…λ‘œλ“œ μ „ μ„ ν–‰ ν•„μˆ˜.
36
+ - `getService / getEvent / addListener / removeListener / emitEvent / uploadFile / downloadFileBuffer` β€” μ•„λž˜ 각 μ„Ήμ…˜.
37
37
 
38
38
  ```ts
39
39
  const client = createServiceClient("my-app", { host: "localhost", port: 50080, ssl: false });
@@ -43,10 +43,10 @@ await client.auth(jwtToken);
43
43
 
44
44
  **ServiceClientEvents** (EventEmitter 이벀트):
45
45
 
46
- - "request-progress": ServiceProgressState β€” μš”μ²­(μ—…λ‘œλ“œ) 청크 μ§„ν–‰λ₯ . μš”μ²­ 본문이 청크 2개 μ΄μƒμœΌλ‘œ 뢄할될 λ•Œλ§Œ λ°œμƒ.
47
- - "response-progress": ServiceProgressState β€” 응닡(λ‹€μš΄λ‘œλ“œ) 청크 μˆ˜μ‹  μ§„ν–‰λ₯ . λΆ„ν•  응닡 μ™„λ£Œ μ‹œ 100% ν•œ 번 더 보고.
48
- - "server-progress": ServiceProgressState β€” μ„œλ²„κ°€ 처리 쀑 직접 λ³΄κ³ ν•˜λŠ” μ§„ν–‰λ₯ (μ„œλ²„μΈ‘ `progress` λ©”μ‹œμ§€ μˆ˜μ‹  μ‹œ).
49
- - "state": "connected" | "closed" | "reconnecting" β€” μ—°κ²° μƒνƒœ λ³€ν™”. "connected" = μ—°κ²°/μž¬μ—°κ²° 성곡(이 전이 μ‹œ 보관 ν† ν°μœΌλ‘œ μžλ™ 재인증 + 이벀트 λ¦¬μŠ€λ„ˆ μžλ™ 볡ꡬ), "closed" = 정상 μ’…λ£Œ λ˜λŠ” μž¬μ—°κ²° ν•œλ„ 초과, "reconnecting" = μž¬μ—°κ²° μ‹œλ„ 쀑. μ˜€ν”„λΌμΈ λ°°λ„ˆ ν† κΈ€ 등에 μ‚¬μš©.
46
+ - `"request-progress": ServiceProgressState` β€” μš”μ²­(μ—…λ‘œλ“œ) 청크 μ§„ν–‰λ₯ . μš”μ²­ 본문이 청크 2개 μ΄μƒμœΌλ‘œ 뢄할될 λ•Œλ§Œ λ°œμƒ.
47
+ - `"response-progress": ServiceProgressState` β€” 응닡(λ‹€μš΄λ‘œλ“œ) 청크 μˆ˜μ‹  μ§„ν–‰λ₯ . λΆ„ν•  응닡이면 μ™„λ£Œ μ‹œ 100% λ₯Ό ν•œ 번 더 보고.
48
+ - `"server-progress": ServiceProgressState` β€” μ„œλ²„κ°€ 처리 쀑 λ³΄κ³ ν•˜λŠ” μ§„ν–‰λ₯ (μ„œλ²„μΈ‘ `name: "progress"` λ©”μ‹œμ§€ μˆ˜μ‹  μ‹œ).
49
+ - `"state": "connected" | "closed" | "reconnecting"` β€” μ—°κ²° μƒνƒœ 전이. `"connected"` = μ—°κ²°/μž¬μ—°κ²° 성곡(이 전이 μ‹œ 보관 ν† ν°μœΌλ‘œ μžλ™ 재인증 + 등둝 λ¦¬μŠ€λ„ˆ μžλ™ 볡ꡬ), `"closed"` = 정상 μ’…λ£Œ λ˜λŠ” μž¬μ—°κ²° ν•œλ„ 초과, `"reconnecting"` = μž¬μ—°κ²° μ‹œλ„ 쀑. μ˜€ν”„λΌμΈ λ°°λ„ˆ ν† κΈ€ 등에 μ‚¬μš©.
50
50
 
51
51
  ```ts
52
52
  client.on("state", (s) => { if (s === "reconnecting") showOfflineBanner(); });
@@ -56,50 +56,50 @@ client.on("state", (s) => { if (s === "reconnecting") showOfflineBanner(); });
56
56
 
57
57
  `createServiceClient` 의 두 번째 인자.
58
58
 
59
- - port: number β€” μ„œλ²„ 포트. ν•„μˆ˜.
60
- - host: string β€” μ„œλ²„ 호슀트. ν•„μˆ˜.
61
- - ssl?: boolean β€” TLS μ‚¬μš© μ—¬λΆ€. true λ©΄ `wss`/`https`, false·미지정이면 `ws`/`http`. TLS μ„œλ²„μ— 뢙을 λ•Œλ§Œ true.
62
- - maxReconnectCount?: number β€” μ—°κ²° λŠκΉ€ μ‹œ μ΅œλŒ€ μž¬μ—°κ²° μ‹œλ„ 횟수. λ―Έμ§€μ • μ‹œ 10. 0 이면 μž¬μ—°κ²° λΉ„ν™œμ„±ν™”(끊기면 μ¦‰μ‹œ 포기). ν…ŒμŠ€νŠΈΒ·λ‹¨λ°œμ„± 연결이면 0.
59
+ - `port: number` β€” μ„œλ²„ 포트. ν•„μˆ˜.
60
+ - `host: string` β€” μ„œλ²„ 호슀트. ν•„μˆ˜.
61
+ - `ssl?: boolean` β€” TLS μ‚¬μš© μ—¬λΆ€. true λ©΄ `wss`/`https`, false·미지정이면 `ws`/`http`. TLS μ„œλ²„μ— 뢙을 λ•Œλ§Œ true.
62
+ - `maxReconnectCount?: number` β€” μ—°κ²° λŠκΉ€ μ‹œ μ΅œλŒ€ μž¬μ—°κ²° μ‹œλ„ 횟수. λ―Έμ§€μ • μ‹œ 10. 0 이면 μž¬μ—°κ²°μ„ λΉ„ν™œμ„±ν™”ν•˜κ³  끊기면 μ¦‰μ‹œ 포기. ν…ŒμŠ€νŠΈΒ·λ‹¨λ°œμ„± 연결이면 0.
63
63
 
64
64
  ## getService / ServiceProxy
65
65
 
66
- μ„œλ²„ μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€μ˜ 각 λ©”μ„œλ“œλ₯Ό `Promise` λ°˜ν™˜ ν•¨μˆ˜λ‘œ λ…ΈμΆœν•˜λŠ” νƒ€μž… μ•ˆμ „ ν”„λ‘μ‹œ.
66
+ μ„œλ²„ μ„œλΉ„μŠ€μ˜ 각 λ©”μ„œλ“œλ₯Ό `Promise` λ°˜ν™˜ ν•¨μˆ˜λ‘œ λ…ΈμΆœν•˜λŠ” νƒ€μž… μ•ˆμ „ ν”„λ‘μ‹œ.
67
67
 
68
- `getService<TService>(serviceName): ServiceProxy<TService>` β€” `serviceName` 으둜 λ“±λ‘λœ μ„œλ²„ μ„œλΉ„μŠ€μ˜ ν”„λ‘μ‹œ λ°˜ν™˜. ν”„λ‘μ‹œ λ©”μ„œλ“œ ν˜ΈμΆœμ€ λ‚΄λΆ€μ μœΌλ‘œ `client.send(serviceName, λ©”μ„œλ“œλͺ…, μΈμžλ°°μ—΄)` 둜 μœ„μž„.
68
+ `getService<TService>(serviceName): ServiceProxy<TService>` β€” `serviceName` 으둜 λ“±λ‘λœ μ„œλ²„ μ„œλΉ„μŠ€μ˜ ν”„λ‘μ‹œ λ°˜ν™˜. ν”„λ‘μ‹œ λ©”μ„œλ“œ ν˜ΈμΆœμ€ λ‚΄λΆ€μ μœΌλ‘œ `client.send(serviceName, λ©”μ„œλ“œλͺ…, μΈμžλ°°μ—΄)` 둜 μœ„μž„(`Proxy` 기반, λͺ¨λ“  속성 접근을 비동기 RPC ν•¨μˆ˜λ‘œ λ³€ν™˜).
69
69
 
70
- - TService β€” μ„œλ²„ μ„œλΉ„μŠ€ λ©”μ„œλ“œ μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…. 컴파일 νƒ€μž„ μ‹œκ·Έλ‹ˆμ²˜ κ²€μ¦μš©(λŸ°νƒ€μž„ 검증 μ•„λ‹˜). 앱에선 server νŒ¨ν‚€μ§€κ°€ export ν•œ `ServiceMethods<typeof XxxService>` μ‚¬μš©.
71
- - serviceName: string β€” μ„œλ²„μ˜ `defineService("XxxName", ...)` 이름과 μΌμΉ˜ν•΄μ•Ό 함.
72
- - ServiceProxy\<TService\> β€” TService 의 각 ν•¨μˆ˜ 멀버λ₯Ό `(...args) => Promise<Awaited<R>>` 둜 λ§€ν•‘ν•˜λŠ” νƒ€μž… λ³€ν™˜κΈ°. 원본이 동기 λ°˜ν™˜μ΄μ–΄λ„ Promise 둜 λž˜ν•‘λ¨. ν•¨μˆ˜ μ•„λ‹Œ 속성은 `never` 둜 μ œμ™Έ.
70
+ - `TService` β€” μ„œλ²„ μ„œλΉ„μŠ€ λ©”μ„œλ“œ μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…. 컴파일 νƒ€μž„ μ‹œκ·Έλ‹ˆμ²˜ 검증 μ „μš©(λŸ°νƒ€μž„ 검증 μ•„λ‹˜). 앱에선 server νŒ¨ν‚€μ§€κ°€ export ν•œ `ServiceMethods<typeof XxxService>` μ‚¬μš©.
71
+ - `serviceName: string` β€” μ„œλ²„μ˜ `defineService("XxxName", ...)` 이름과 μΌμΉ˜ν•΄μ•Ό 함.
72
+ - `ServiceProxy<TService>` β€” `TService` 의 각 ν•¨μˆ˜ 멀버λ₯Ό `(...args) => Promise<Awaited<R>>` 둜 λ§€ν•‘ν•˜λŠ” νƒ€μž… λ³€ν™˜κΈ°. 원본이 동기 λ°˜ν™˜μ΄μ–΄λ„ Promise 둜 λž˜ν•‘. ν•¨μˆ˜ μ•„λ‹Œ 속성은 `never` 둜 μ œμ™Έ.
73
73
 
74
74
  ```ts
75
75
  const svc = client.getService<TestServiceMethods>("TestService");
76
- const result = await svc.echo("hi"); // μ„œλ²„ TestService.echo("hi") 호좜, Promise<string>
76
+ const result = await svc.echo("hi"); // μ„œλ²„ TestService.echo("hi") 호좜 β†’ Promise<string>
77
77
  ```
78
78
 
79
- ## 이벀트 κ΅¬λ…Β·λ°œν–‰ (addListener / removeListener / emitEvent / getEvent)
79
+ ## 이벀트 κ΅¬λ…Β·λ°œν–‰ (getEvent / addListener / removeListener / emitEvent)
80
80
 
81
- μ„œλ²„ ν‘Έμ‹œ μ΄λ²€νŠΈλŠ” `@simplysm/service-common` 의 `defineEvent` μ‚°μΆœλ¬Ό(`ServiceEventDef`. `$info` = ꡬ독 ν•„ν„° 정보 νƒ€μž…, `$data` = νŽ˜μ΄λ‘œλ“œ νƒ€μž…) λ‹¨μœ„λ‘œ 닀룬닀. λ“±λ‘ν•œ λ¦¬μŠ€λ„ˆλŠ” μž¬μ—°κ²° μ‹œ μžλ™ 볡ꡬ됨.
81
+ μ„œλ²„ ν‘Έμ‹œ μ΄λ²€νŠΈλŠ” `@simplysm/service-common` 의 `defineEvent` μ‚°μΆœλ¬Ό(`ServiceEventDef`. `$info` = ꡬ독 ν•„ν„° 정보 νƒ€μž…, `$data` = νŽ˜μ΄λ‘œλ“œ νƒ€μž…) λ‹¨μœ„λ‘œ 닀룬닀. λ“±λ‘ν•œ λ¦¬μŠ€λ„ˆλŠ” μž¬μ—°κ²° μ‹œ μžλ™ μž¬κ΅¬λ…λœλ‹€.
82
82
 
83
83
  `addListener<TEventDef>(eventDef, info, cb): Promise<string>` β€” λ¦¬μŠ€λ„ˆ 등둝. λ―Έμ—°κ²°(`connected === false`)이면 throw("μ„œλ²„μ— μ—°κ²°λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."). λ°˜ν™˜ key 둜 λ‚˜μ€‘μ— 제거.
84
84
 
85
- - eventDef: TEventDef β€” 이벀트 μ •μ˜(`defineEvent` κ²°κ³Ό). `$info`/`$data` νƒ€μž…μ˜ 좜처.
86
- - info: TEventDef["$info"] β€” 이 ꡬ독을 식별·필터링할 정보. λ°œν–‰μΈ‘ selector κ°€ 이 값을 보고 전달 μ—¬λΆ€ κ²°μ •.
87
- - cb: (data: $data) => PromiseLike\<void\> β€” 이벀트 μˆ˜μ‹  콜백. 콜백 λ‚΄ μ˜ˆμ™ΈλŠ” λ‘œκΉ…λ§Œ 되고 ν˜ΈμΆœλΆ€λ‘œ μ „νŒŒλ˜μ§€ μ•ŠμŒ.
85
+ - `eventDef: TEventDef` β€” 이벀트 μ •μ˜(`defineEvent` κ²°κ³Ό). `$info`/`$data` νƒ€μž…μ˜ 좜처이자 λΌμš°νŒ… ν‚€(`eventName`).
86
+ - `info: TEventDef["$info"]` β€” 이 ꡬ독을 식별·필터링할 정보. λ°œν–‰μΈ‘ selector κ°€ 이 값을 보고 전달 μ—¬λΆ€ κ²°μ •.
87
+ - `cb: (data: $data) => PromiseLike<void>` β€” 이벀트 μˆ˜μ‹  콜백. 콜백 λ‚΄ μ˜ˆμ™ΈλŠ” λ‘œκΉ…λ§Œ 되고 ν˜ΈμΆœλΆ€λ‘œ μ „νŒŒλ˜μ§€ μ•ŠμŒ.
88
88
 
89
- `removeListener(key): Promise<void>` β€” 등둝 key 둜 λ¦¬μŠ€λ„ˆ 제거. μ„œλ²„ 전솑 μ‹€νŒ¨(μ—°κ²° λŠκΉ€ λ“±)λŠ” λ¬΄μ‹œ(μ„œλ²„κ°€ λŠκΉ€ μ‹œ λ¦¬μŠ€λ„ˆλ₯Ό μžλ™ μ •λ¦¬ν•˜λ―€λ‘œ μ•ˆμ „).
89
+ `removeListener(key): Promise<void>` β€” 등둝 key 둜 λ¦¬μŠ€λ„ˆ 제거(둜컬 λ§΅ μ‚­μ œ + μ„œλ²„ `evt:remove` 전솑). μ„œλ²„ 전솑 μ‹€νŒ¨(μ—°κ²° λŠκΉ€ λ“±)λŠ” λ¬΄μ‹œ β€” μ„œλ²„κ°€ λŠκΉ€ μ‹œ λ¦¬μŠ€λ„ˆλ₯Ό μžλ™ μ •λ¦¬ν•˜λ―€λ‘œ μ•ˆμ „.
90
90
 
91
91
  `emitEvent<TEventDef>(eventDef, infoSelector, data): Promise<void>` β€” 이벀트 λ°œν–‰. μ„œλ²„μ—μ„œ 동일 이벀트 κ΅¬λ…μž λͺ©λ‘μ„ μ‘°νšŒν•œ λ’€ `infoSelector(info)` κ°€ true 인 λŒ€μƒμ—κ²Œλ§Œ data 전솑. λ§€μΉ­ λŒ€μƒμ΄ 0개면 전솑 자체λ₯Ό μƒλž΅.
92
92
 
93
- - infoSelector: (item: $info) => boolean β€” λ°œν–‰ λŒ€μƒ κ΅¬λ…μžλ₯Ό info κΈ°μ€€μœΌλ‘œ ν•„ν„°. true λ°˜ν™˜ κ΅¬λ…μžμ—κ²Œλ§Œ 전달. 전체 전솑이면 `() => true`.
94
- - data: TEventDef["$data"] β€” 전솑 νŽ˜μ΄λ‘œλ“œ.
93
+ - `infoSelector: (item: $info) => boolean` β€” λ°œν–‰ λŒ€μƒ κ΅¬λ…μžλ₯Ό info κΈ°μ€€μœΌλ‘œ ν•„ν„°. true λ°˜ν™˜ κ΅¬λ…μžμ—κ²Œλ§Œ 전달. 전체 전솑이면 `() => true`.
94
+ - `data: TEventDef["$data"]` β€” 전솑 νŽ˜μ΄λ‘œλ“œ.
95
95
 
96
96
  `getEvent<TEventDef>(eventDef): ClientEventProxy<TEventDef>` β€” νŠΉμ • eventDef 에 λ°”μΈλ”©λœ ν”„λ‘μ‹œ λ°˜ν™˜. eventDef λ₯Ό 맀번 λ„˜κΈ°μ§€ μ•Šκ³  짧게 μ“°λ € ν•  λ•Œ(μ•±μ˜ `AppServiceProvider.xxxEvent` getter νŒ¨ν„΄).
97
97
 
98
98
  `ClientEventProxy<TEventDef>` 멀버(μœ„ client λ©”μ„œλ“œμ˜ eventDef κ³ μ •νŒ):
99
99
 
100
- - addListener(info, cb): Promise\<string\> β€” λ¦¬μŠ€λ„ˆ 등둝.
101
- - removeListener(key): Promise\<void\> β€” λ¦¬μŠ€λ„ˆ 제거.
102
- - emit(infoSelector, data): Promise\<void\> β€” 이벀트 λ°œν–‰.
100
+ - `addListener(info, cb): Promise<string>` β€” λ¦¬μŠ€λ„ˆ 등둝.
101
+ - `removeListener(key): Promise<void>` β€” λ¦¬μŠ€λ„ˆ 제거.
102
+ - `emit(infoSelector, data): Promise<void>` β€” 이벀트 λ°œν–‰.
103
103
 
104
104
  ```ts
105
105
  const chatEvent = defineEvent<{ channel: string }, string>("Chat");
@@ -108,17 +108,17 @@ await client.emitEvent(chatEvent, (info) => info.channel === "room1", "hello");
108
108
  await client.removeListener(key);
109
109
  ```
110
110
 
111
- `EventClient` / `createEventClient(transport)` λŠ” `ServiceClient` κ°€ λ‚΄λΆ€ 쑰립에 μ“°λŠ” μ €μˆ˜μ€€ κ΅¬ν˜„. μœ„ λ©”μ„œλ“œ(getEvent/addListener/removeListener/emit)에 더해 `resubscribeAll(): Promise<void>`(λ³΄κ΄€λœ λͺ¨λ“  λ¦¬μŠ€λ„ˆλ₯Ό μ„œλ²„μ— μž¬λ“±λ‘, μž¬μ—°κ²° 볡ꡬ용)λ₯Ό 가짐. 일반 μ‚¬μš©μ—μ„  직접 λ§Œλ“€μ§€ μ•ŠμŒ.
111
+ `EventClient` / `createEventClient(transport)` λŠ” `ServiceClient` κ°€ λ‚΄λΆ€ 쑰립에 μ“°λŠ” μ €μˆ˜μ€€ κ΅¬ν˜„. μœ„ λ©”μ„œλ“œ(getEvent/addListener/removeListener/emit)에 더해 `resubscribeAll(): Promise<void>`(λ³΄κ΄€λœ λͺ¨λ“  λ¦¬μŠ€λ„ˆλ₯Ό μ„œλ²„μ— μž¬λ“±λ‘, μž¬μ—°κ²° 볡ꡬ용. ν•­λͺ©λ³„ μ‹€νŒ¨λŠ” λ‘œκΉ… ν›„ 계속)λ₯Ό 가짐. 일반 μ‚¬μš©μ—μ„  직접 λ§Œλ“€μ§€ μ•ŠμŒ.
112
112
 
113
113
  ## 파일 μ—…/λ‹€μš΄λ‘œλ“œ (uploadFile / downloadFileBuffer)
114
114
 
115
- `uploadFile(files): Promise<ServiceUploadResult[]>` β€” `POST <hostUrl>/upload` (multipart/form-data) 둜 파일 μ—…λ‘œλ“œ. 보관 토큰을 `Authorization: Bearer` ν—€λ”λ‘œ μ „μ†‘ν•˜λ―€λ‘œ 사전 `auth()` ν•„μˆ˜(미인증 μ‹œ throw). 응닡 비정상 μ‹œ throw.
115
+ `uploadFile(files): Promise<ServiceUploadResult[]>` β€” `POST <hostUrl>/upload` (multipart/form-data) 둜 파일 μ—…λ‘œλ“œ. 보관 토큰을 `Authorization: Bearer` ν—€λ”λ‘œ μ „μ†‘ν•˜λ―€λ‘œ 사전 `auth()` ν•„μˆ˜(보관 토큰 μ—†μœΌλ©΄ throw). 응닡 비정상(`!res.ok`) μ‹œ throw.
116
116
 
117
- - files: `File[] | FileCollection | { name: string; data: BlobInput }[]` β€” μ—…λ‘œλ“œ λŒ€μƒ. λΈŒλΌμš°μ € `File` λ°°μ—΄, `FileCollection`(FileList ν˜Έν™˜), λ˜λŠ” `{ name, data }` μ»€μŠ€ν…€ 객체 λ°°μ—΄. μ»€μŠ€ν…€ 객체의 data κ°€ `Blob` 이 μ•„λ‹ˆλ©΄ `new Blob([data])` 둜 감싸 전솑.
117
+ - `files: File[] | FileCollection | { name: string; data: BlobInput }[]` β€” μ—…λ‘œλ“œ λŒ€μƒ. λΈŒλΌμš°μ € `File` λ°°μ—΄, `FileCollection`(FileList ν˜Έν™˜), λ˜λŠ” `{ name, data }` μ»€μŠ€ν…€ 객체 λ°°μ—΄. μ»€μŠ€ν…€ 객체의 data κ°€ `Blob` 이 μ•„λ‹ˆλ©΄ `new Blob([data])` 둜 감싸 전솑.
118
118
 
119
119
  `downloadFileBuffer(relPath): Promise<Bytes>` β€” `<hostUrl>/<relPath>` λ₯Ό GET ν•΄ `Uint8Array` λ°˜ν™˜. 응닡 비정상(`!res.ok`) μ‹œ throw.
120
120
 
121
- - relPath: string β€” μ„œλ²„ κΈ°μ€€ μƒλŒ€κ²½λ‘œ. μ„ ν–‰ `/` 유무 λͺ¨λ‘ ν—ˆμš©(μ—†μœΌλ©΄ μžλ™μœΌλ‘œ `/` μΆ”κ°€).
121
+ - `relPath: string` β€” μ„œλ²„ κΈ°μ€€ μƒλŒ€κ²½λ‘œ. μ„ ν–‰ `/` 유무 λͺ¨λ‘ ν—ˆμš©(μ—†μœΌλ©΄ μžλ™μœΌλ‘œ `/` 보정).
122
122
 
123
123
  ```ts
124
124
  await client.auth(token);
@@ -134,15 +134,15 @@ const bytes = await client.downloadFileBuffer("/files/a.txt");
134
134
 
135
135
  `ServiceProgress` β€” `send(..., progress)` 에 λ„˜κΈ°λŠ” 콜백 μ§‘ν•©(ν•΄λ‹Ή 호좜 단건 μΆ”μ μš©, μ „μ—­ μ΄λ²€νŠΈμ™€ λ³„κ°œ). 각 μ½œλ°±μ€ `(s: ServiceProgressState) => void`:
136
136
 
137
- - request? β€” μš”μ²­ 청크 μ—…λ‘œλ“œ μ§„ν–‰ μ‹œ 호좜. μš”μ²­ 청크가 2개 이상일 λ•Œλ§Œ λ°œμƒ.
138
- - response? β€” 응닡 청크 μˆ˜μ‹  μ§„ν–‰ μ‹œ 호좜. λΆ„ν•  μ‘λ‹΅μ΄μ—ˆμœΌλ©΄ μ™„λ£Œ μ‹œ 100%(`completedSize === totalSize`)λ₯Ό ν•œ 번 더 보고.
139
- - server? β€” μ„œλ²„κ°€ 처리 쀑 λ³΄κ³ ν•œ μ§„ν–‰λ₯  μˆ˜μ‹  μ‹œ 호좜(`name: "progress"` λ©”μ‹œμ§€).
137
+ - `request?` β€” μš”μ²­ 청크 μ—…λ‘œλ“œ μ§„ν–‰ μ‹œ 호좜. μš”μ²­ 청크가 2개 이상일 λ•Œλ§Œ λ°œμƒ.
138
+ - `response?` β€” 응닡 청크 μˆ˜μ‹  μ§„ν–‰ μ‹œ 호좜. λΆ„ν•  μ‘λ‹΅μ΄μ—ˆμœΌλ©΄ μ™„λ£Œ μ‹œ 100%(`completedSize === totalSize`)λ₯Ό ν•œ 번 더 보고.
139
+ - `server?` β€” μ„œλ²„κ°€ 처리 쀑 λ³΄κ³ ν•œ μ§„ν–‰λ₯  μˆ˜μ‹  μ‹œ 호좜(μ„œλ²„μΈ‘ `name: "progress"` λ©”μ‹œμ§€).
140
140
 
141
141
  `ServiceProgressState` β€” μ§„ν–‰λ₯  μŠ€λƒ…μƒ·.
142
142
 
143
- - uuid: string β€” ν•΄λ‹Ή μš”μ²­/응닡 μ‹λ³„μž. λ™μ‹œ μš”μ²­ κ΅¬λΆ„μš©.
144
- - totalSize: number β€” 전체 λ°”μ΄νŠΈ 수.
145
- - completedSize: number β€” μ™„λ£Œ λ°”μ΄νŠΈ 수. `completedSize === totalSize` λ©΄ μ™„λ£Œ.
143
+ - `uuid: string` β€” ν•΄λ‹Ή μš”μ²­/응닡 μ‹λ³„μž. λ™μ‹œ μš”μ²­ κ΅¬λΆ„μš©.
144
+ - `totalSize: number` β€” 전체 λ°”μ΄νŠΈ 수.
145
+ - `completedSize: number` β€” μ™„λ£Œ λ°”μ΄νŠΈ 수. `completedSize === totalSize` λ©΄ μ™„λ£Œ.
146
146
 
147
147
  μ „μ—­ 좔적이면 콜백 λŒ€μ‹  ServiceClient 의 `request/response/server-progress` 이벀트(`client.on("response-progress", ...)`)λ₯Ό 써도 됨 β€” `send` λŠ” progress 인자 μœ λ¬΄μ™€ λ¬΄κ΄€ν•˜κ²Œ 이 μ΄λ²€νŠΈλ“€μ„ 항상 λ°œμƒμ‹œν‚΄.
148
148
 
@@ -154,9 +154,9 @@ client.on("response-progress", (s) => updateBar(s.completedSize / s.totalSize));
154
154
 
155
155
  Node/browser 곡용 μ½”λ“œμ—μ„œ DOM μ „μš© νƒ€μž…μ„ ν”Όν•˜κ³  Worker 지원 μ—¬λΆ€λ₯Ό λΆ„κΈ°ν•˜κΈ° μœ„ν•œ νƒ€μž…Β·ν•¨μˆ˜. ν”„λ‘œν† μ½œ 인코딩/νŒŒμ‹± Worker μ˜€ν”„λ‘œλ”© νŒλ‹¨ μ‹œ λ‚΄λΆ€μ—μ„œ μ‚¬μš©.
156
156
 
157
- - BlobInput = `Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | string` β€” `Blob` μƒμ„±μžκ°€ λ°›λŠ” 데이터 νƒ€μž…(DOM `BlobPart` λŒ€μ²΄). `uploadFile` 의 μ»€μŠ€ν…€ 객체 data νƒ€μž….
158
- - FileCollection (interface) β€” DOM `FileList` λŒ€μ²΄. `length`, `item(index): File | null`, 인덱슀 μ ‘κ·Ό, `[Symbol.iterator]` 보유. λΈŒλΌμš°μ € `FileList` 와 ꡬ쑰적 ν˜Έν™˜.
159
- - BrowserWorker (interface) β€” DOM `Worker` μ΅œμ†Œ μΈν„°νŽ˜μ΄μŠ€(`onmessage`/`onerror` ν•Έλ“€λŸ¬, `postMessage(message, transfer?)`, `terminate()`). DOM lib 없이 νƒ€μž…μ²΄ν¬ ν†΅κ³Όμš©.
160
- - isBrowserWorkerSupported(): boolean β€” `globalThis` 에 `Worker` 쑴재 μ—¬λΆ€ λ°˜ν™˜. λΈŒλΌμš°μ € DOM Worker κ°€μš© νŒλ‹¨.
161
- - isNodeWorkerSupported(): boolean β€” `process.versions.node` 쑴재 μ—¬λΆ€ λ°˜ν™˜. Node `worker_threads` κ°€μš© νŒλ‹¨.
162
- - isWorkerSupported(): boolean β€” μœ„ λ‘˜ 쀑 ν•˜λ‚˜λΌλ„ true λ©΄ true. ν”„λ‘œν† μ½œ 인코딩/νŒŒμ‹± μ˜€ν”„λ‘œλ”© κ°€λŠ₯ μ—¬λΆ€ νŒλ‹¨(미지원 μ‹œ 메인 μŠ€λ ˆλ“œ 폴백).
157
+ - `BlobInput = Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | string` β€” `Blob` μƒμ„±μžκ°€ λ°›λŠ” 데이터 νƒ€μž…(DOM `BlobPart` λŒ€μ²΄). `uploadFile` μ»€μŠ€ν…€ 객체의 data νƒ€μž….
158
+ - `FileCollection` (interface) β€” DOM `FileList` λŒ€μ²΄. `length`, `item(index): File | null`, 인덱슀 μ ‘κ·Ό, `[Symbol.iterator]` 보유. λΈŒλΌμš°μ € `FileList` 와 ꡬ쑰적 ν˜Έν™˜.
159
+ - `BrowserWorker` (interface) β€” DOM `Worker` μ΅œμ†Œ μΈν„°νŽ˜μ΄μŠ€(`onmessage`/`onerror` ν•Έλ“€λŸ¬, `postMessage(message, transfer?)`, `terminate()`). DOM lib 없이 νƒ€μž…μ²΄ν¬ ν†΅κ³Όμš©.
160
+ - `isBrowserWorkerSupported(): boolean` β€” `globalThis` 에 `Worker` 쑴재 μ—¬λΆ€ λ°˜ν™˜. λΈŒλΌμš°μ € DOM Worker κ°€μš© νŒλ‹¨.
161
+ - `isNodeWorkerSupported(): boolean` β€” `process.versions.node` 쑴재 μ—¬λΆ€ λ°˜ν™˜. Node `worker_threads` κ°€μš© νŒλ‹¨.
162
+ - `isWorkerSupported(): boolean` β€” μœ„ λ‘˜ 쀑 ν•˜λ‚˜λΌλ„ true λ©΄ true. ν”„λ‘œν† μ½œ 인코딩/νŒŒμ‹± μ˜€ν”„λ‘œλ”© κ°€λŠ₯ μ—¬λΆ€ νŒλ‹¨(미지원 μ‹œ 메인 μŠ€λ ˆλ“œ 폴백).
@@ -1,8 +1,8 @@
1
1
  # @simplysm/service-client β€” ORM 원격 μ‹€ν–‰
2
2
 
3
- μ„œλ²„μΈ‘ ORM DbContext λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ›κ²©μœΌλ‘œ μ‹€ν–‰ν•˜λŠ” 묢음. 쿼리 μžμ²΄λŠ” μ„œλ²„ `Orm` μ„œλΉ„μŠ€κ°€ DB 에 λŒ€ν•΄ μˆ˜ν–‰ν•˜κ³ , ν΄λΌμ΄μ–ΈνŠΈλŠ” 컀λ„₯μ…˜Β·νŠΈλžœμž­μ…˜Β·μΏΌλ¦¬ μ •μ˜(`QueryDef`)만 RPC 둜 μ „μ†‘ν•œλ‹€. μΏΌλ¦¬λŠ” `connect`/`connectWithoutTransaction` 콜백 λ‚΄λΆ€μ—μ„œλ§Œ κ°€λŠ₯. ORM μž‘μ—…μ„ νŠΈλžœμž­μ…˜ κ²½κ³„λ‘œ λ¬Άμ–΄ μ‹€ν–‰ν•  λ•Œ ν•¨κ»˜ μ½νžŒλ‹€.
3
+ μ„œλ²„μΈ‘ ORM DbContext λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 원격 μ‹€ν–‰ν•˜λŠ” 묢음. 쿼리 μžμ²΄λŠ” μ„œλ²„ `Orm` μ„œλΉ„μŠ€κ°€ DB 에 λŒ€ν•΄ μˆ˜ν–‰ν•˜κ³ , ν΄λΌμ΄μ–ΈνŠΈλŠ” 컀λ„₯μ…˜Β·νŠΈλžœμž­μ…˜Β·μΏΌλ¦¬ μ •μ˜(`QueryDef`)만 RPC 둜 μ „μ†‘ν•œλ‹€. μΏΌλ¦¬λŠ” `connect`/`connectWithoutTransaction` 콜백 λ‚΄λΆ€μ—μ„œλ§Œ κ°€λŠ₯. ORM μž‘μ—…μ„ νŠΈλžœμž­μ…˜ κ²½κ³„λ‘œ λ¬Άμ–΄ μ‹€ν–‰ν•  λ•Œ ν•¨κ»˜ μ½νžŒλ‹€.
4
4
 
5
- > μ•±(Angular)μ—μ„œλŠ” `OrmConnectOptions`(DbClassΒ·connOptΒ·dbContextOpt)λ₯Ό 화면에 ν©λΏŒλ¦¬μ§€ μ•Šκ³  `AppOrmProvider`(root provider) ν•œ 곳에 κ³ μ •ν•œ λ’€ `connectAsync`/`connectWithoutTransAsync` 만 ν˜ΈμΆœν•œλ‹€. 쿼리 μž‘μ„±λ²•μ€ apis/orm-common μ°Έμ‘°. μ•„λž˜ μ˜ˆμ‹œλŠ” connector 직접 호좜 ν˜•νƒœμ§€λ§Œ μ‹€μ œ 앱은 manuals/client-orm.md 의 provider νŒ¨ν„΄μ„ λ”°λ₯Έλ‹€.
5
+ > μ•±(Angular)μ—μ„œλŠ” `OrmConnectOptions`(DbClassΒ·connOptΒ·dbContextOpt)λ₯Ό 화면에 ν©λΏŒλ¦¬μ§€ μ•Šκ³  `AppOrmProvider`(root provider) ν•œ 곳에 κ³ μ •ν•œ λ’€ `connectAsync` 만 ν˜ΈμΆœν•œλ‹€. 쿼리 μž‘μ„±λ²•μ€ apis/orm-commonΒ·manuals/orm.md μ°Έμ‘°. μ•„λž˜ μ˜ˆμ‹œλŠ” connector 직접 호좜 ν˜•νƒœμ§€λ§Œ μ‹€μ œ 앱은 manuals/client-orm.md 의 provider νŒ¨ν„΄μ„ λ”°λ₯Έλ‹€.
6
6
 
7
7
  ## createOrmClientConnector / OrmClientConnector
8
8
 
@@ -10,13 +10,13 @@
10
10
 
11
11
  `OrmClientConnector` λ©”μ„œλ“œ:
12
12
 
13
- - connect<T, R>(config, callback): Promise\<R\> β€” νŠΈλžœμž­μ…˜ μ•ˆμ—μ„œ callback μ‹€ν–‰. DbContext 생성 β†’ `db.connect(...)`(connect + beginTransaction + callback + commit/rollback) μˆ˜ν–‰. callback 정상 λ°˜ν™˜ μ‹œ 컀밋(λ°˜ν™˜κ°’μ΄ κ·ΈλŒ€λ‘œ λ°˜ν™˜λ¨), throw μ‹œ λ‘€λ°±λ˜μ–΄ 콜백 λ‚΄ 닀건 μž‘μ—…μ΄ μ›μž 처리됨. callback μ—μ„œ λ°œμƒν•œ μ—λŸ¬ 쀑 μ™Έλž˜ν‚€ μ œμ•½ μœ„λ°˜ λ©”μ‹œμ§€(MySQL `a parent row: a foreign key constraint` / MSSQL `conflicted with the REFERENCE` / PostgreSQL `violates foreign key constraint`)λŠ” "κ²½κ³ ! μ—°κ΄€λœ μž‘μ—…μœΌλ‘œ 인해 μž‘μ—…μ΄ κ±°λΆ€λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 후속 μž‘μ—…μ„ 확인해 μ£Όμ„Έμš”." 둜 감싸 `SdError` 둜 throw(원본은 cause 에 보쑴), κ·Έ μ™Έ μ—λŸ¬λŠ” κ·ΈλŒ€λ‘œ throw.
14
- - connectWithoutTransaction<T, R>(config, callback): Promise\<R\> β€” νŠΈλžœμž­μ…˜ 없이 callback μ‹€ν–‰(`db.connectWithoutTransaction`). νŠΈλžœμž­μ…˜ μ•ˆμ—μ„œ λ™μž‘ν•˜μ§€ μ•ŠλŠ” μž‘μ—…(예: DB initialize)·쑰회 μ „μš© μž‘μ—…μ— μ‚¬μš©. callback λ°˜ν™˜κ°’μ΄ κ·ΈλŒ€λ‘œ λ°˜ν™˜λ¨. μ™Έλž˜ν‚€ λ©”μ‹œμ§€ λž˜ν•‘ μ—†μŒ.
13
+ - `connect<T, R>(config, callback): Promise<R>` β€” νŠΈλžœμž­μ…˜ μ•ˆμ—μ„œ callback μ‹€ν–‰. DbContext 생성 β†’ `db.connect(...)`(connect + beginTransaction + callback + commit/rollback) μˆ˜ν–‰. callback 정상 λ°˜ν™˜ μ‹œ 컀밋(λ°˜ν™˜κ°’μ΄ κ·ΈλŒ€λ‘œ λ©”μ„œλ“œ λ°˜ν™˜κ°’), throw μ‹œ λ‘€λ°±λ˜μ–΄ 콜백 λ‚΄ 닀건 μž‘μ—…μ΄ μ›μž 처리됨. callback μ—μ„œ λ°œμƒν•œ μ—λŸ¬ 쀑 μ™Έλž˜ν‚€ μ œμ•½ μœ„λ°˜ λ©”μ‹œμ§€(MySQL `a parent row: a foreign key constraint` / MSSQL `conflicted with the REFERENCE` / PostgreSQL `violates foreign key constraint`)λŠ” "κ²½κ³ ! μ—°κ΄€λœ μž‘μ—…μœΌλ‘œ 인해 μž‘μ—…μ΄ κ±°λΆ€λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 후속 μž‘μ—…μ„ 확인해 μ£Όμ„Έμš”." 둜 감싼 `SdError` 둜 throw(원본은 cause 에 보쑴), κ·Έ μ™Έ μ—λŸ¬λŠ” κ·ΈλŒ€λ‘œ throw.
14
+ - `connectWithoutTransaction<T, R>(config, callback): Promise<R>` β€” νŠΈλžœμž­μ…˜ 없이 callback μ‹€ν–‰(`db.connectWithoutTransaction`). νŠΈλžœμž­μ…˜ μ•ˆμ—μ„œ λ™μž‘ν•˜μ§€ μ•ŠλŠ” μž‘μ—…(예: DB initialize)·쑰회 μ „μš© μž‘μ—…μ— μ‚¬μš©. callback λ°˜ν™˜κ°’μ΄ κ·ΈλŒ€λ‘œ λ°˜ν™˜λ¨. μ™Έλž˜ν‚€ λ©”μ‹œμ§€ λž˜ν•‘ μ—†μŒ.
15
15
 
16
16
  곡톡 인자:
17
17
 
18
- - config: OrmConnectOptions\<T\> β€” μ•„λž˜ μ„Ήμ…˜. DbContext ν΄λž˜μŠ€Β·μ„œλ²„ ORM μ„€μ •Β·DBλͺ…/μŠ€ν‚€λ§ˆ.
19
- - callback: (db: T) => Promise\<R\> | R β€” μƒμ„±Β·μ—°κ²°λœ DbContext λ₯Ό λ°›μ•„ μΏΌλ¦¬ν•˜λŠ” 콜백. 동기/비동기 λ°˜ν™˜ λͺ¨λ‘ ν—ˆμš©.
18
+ - `config: OrmConnectOptions<T>` β€” μ•„λž˜ μ„Ήμ…˜. DbContext ν΄λž˜μŠ€Β·μ„œλ²„ ORM μ„€μ •Β·DBλͺ…/μŠ€ν‚€λ§ˆ.
19
+ - `callback: (db: T) => Promise<R> | R` β€” μƒμ„±Β·μ—°κ²°λœ DbContext λ₯Ό λ°›μ•„ μΏΌλ¦¬ν•˜λŠ” 콜백. 동기/비동기 λ°˜ν™˜ λͺ¨λ‘ ν—ˆμš©.
20
20
 
21
21
  ```ts
22
22
  const connector = createOrmClientConnector(client);
@@ -30,24 +30,24 @@ const rows = await connector.connect(
30
30
 
31
31
  `connect`/`connectWithoutTransaction` 의 첫 인자. DbContext 1회 싀행에 ν•„μš”ν•œ μ„€μ •.
32
32
 
33
- - DbClass: `new (executor, opt) => T` β€” μ‹€ν–‰ν•  DbContext 클래슀 μƒμ„±μž. `executor`(μ•„λž˜ `OrmClientDbContextExecutor`)와 `{ database, schema? }` λ₯Ό λ°›μ•„ μΈμŠ€ν„΄μŠ€ν™”λ¨. 앱별 DbContext(예: `MainDbContext`)λ₯Ό κ·ΈλŒ€λ‘œ 전달.
34
- - connOpt: `DbConnOptions & { configName: string }` β€” μ„œλ²„μΈ‘ ORM μ—°κ²° μ„€μ •. `configName` 은 μ„œλ²„μ— λ“±λ‘λœ ORM μ„€μ • 이름(μ„œλ²„κ°€ 이 μ΄λ¦„μœΌλ‘œ μ‹€μ œ DB 접속 정보λ₯Ό 찾음). λ‚˜λ¨Έμ§€ ν•„λ“œλŠ” `@simplysm/service-common` 의 `DbConnOptions`.
35
- - dbContextOpt?: `{ database: string; schema?: string }` β€” DbContext 에 μ μš©ν•  DBλͺ…Β·μŠ€ν‚€λ§ˆ. μƒλž΅ μ‹œ μ„œλ²„ `getInfo()` κ°€ λŒλ €μ€€ `database`/`schema` λ₯Ό μ‚¬μš©. database κ°€ μ˜΅μ…˜Β·μ„œλ²„ μ–‘μͺ½ λͺ¨λ‘ λΉ„μ–΄ 있으면 throw("databaseλŠ” ν•„μˆ˜μž…λ‹ˆλ‹€." β€” 결츑을 μž„μ˜ λ³΄μ •ν•˜μ§€ μ•ŠμŒ).
36
- - database: string β€” λŒ€μƒ λ°μ΄ν„°λ² μ΄μŠ€λͺ….
37
- - schema?: string β€” λŒ€μƒ μŠ€ν‚€λ§ˆλͺ…. λ―Έμ§€μ • μ‹œ μ„œλ²„ `getInfo()` 의 schema λ₯Ό μ‚¬μš©.
33
+ - `DbClass: new (executor, opt) => T` β€” μ‹€ν–‰ν•  DbContext 클래슀 μƒμ„±μž. `executor`(μ•„λž˜ `OrmClientDbContextExecutor`)와 `{ database, schema? }` λ₯Ό λ°›μ•„ μΈμŠ€ν„΄μŠ€ν™”λ¨. 앱별 DbContext(예: `MainDbContext`)λ₯Ό κ·ΈλŒ€λ‘œ 전달.
34
+ - `connOpt: DbConnOptions & { configName: string }` β€” μ„œλ²„μΈ‘ ORM μ—°κ²° μ„€μ •. `configName` 은 μ„œλ²„μ— λ“±λ‘λœ ORM μ„€μ • 이름(μ„œλ²„κ°€ 이 μ΄λ¦„μœΌλ‘œ μ‹€μ œ DB 접속 정보λ₯Ό 찾음). λ‚˜λ¨Έμ§€ ν•„λ“œλŠ” `@simplysm/service-common` 의 `DbConnOptions`.
35
+ - `dbContextOpt?: { database: string; schema?: string }` β€” DbContext 에 μ μš©ν•  DBλͺ…Β·μŠ€ν‚€λ§ˆ. μƒλž΅ μ‹œ μ„œλ²„ `getInfo()` κ°€ λŒλ €μ€€ `database`/`schema` λ₯Ό μ‚¬μš©. database κ°€ μ˜΅μ…˜Β·μ„œλ²„ μ–‘μͺ½ λͺ¨λ‘ λΉ„μ–΄ 있으면 throw("databaseλŠ” ν•„μˆ˜μž…λ‹ˆλ‹€." β€” 결츑을 μž„μ˜ λ³΄μ •ν•˜μ§€ μ•ŠμŒ).
36
+ - `database: string` β€” λŒ€μƒ λ°μ΄ν„°λ² μ΄μŠ€λͺ….
37
+ - `schema?: string` β€” λŒ€μƒ μŠ€ν‚€λ§ˆλͺ…. λ―Έμ§€μ • μ‹œ μ„œλ²„ `getInfo()` 의 schema λ₯Ό μ‚¬μš©.
38
38
 
39
39
  ## OrmClientDbContextExecutor
40
40
 
41
41
  `DbContextExecutor`(`@simplysm/orm-common`) κ΅¬ν˜„μ²΄. λͺ¨λ“  λ©”μ„œλ“œλ₯Ό `client.getService<OrmService>("Orm")` RPC 둜 μœ„μž„. 컀λ„₯ν„°κ°€ λ‚΄λΆ€μ—μ„œ DbContext 에 μ£Όμž…ν•˜λ―€λ‘œ 직접 μƒμ„±Β·ν˜ΈμΆœμ€ 보톡 λΆˆν•„μš”.
42
42
 
43
- `new OrmClientDbContextExecutor(client, opt)` β€” 생성. opt = `DbConnOptions & { configName: string }`. 생성 μ‹œ `Orm` μ„œλΉ„μŠ€ ν”„λ‘μ‹œ 확보. μ•„λž˜ connect μ΄μ™Έμ˜ μ‹€ν–‰ λ©”μ„œλ“œλŠ” connId κ°€ μ—†μœΌλ©΄(λ―Έμ—°κ²°) throw("λ°μ΄ν„°λ² μ΄μŠ€μ— μ—°κ²°λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.").
44
-
45
- - getInfo(): `Promise<{ dialect; database?; schema? }>` β€” μ„œλ²„ ORM μ„€μ • 정보(λ°©μ–ΈΒ·κΈ°λ³Έ DBλͺ…Β·μŠ€ν‚€λ§ˆ) 쑰회. 컀λ„₯ν„°κ°€ dbContextOpt λ―Έμ§€μ • μ‹œ fallback 으둜 μ‚¬μš©.
46
- - connect(): Promise\<void\> β€” μ„œλ²„μ— 컀λ„₯μ…˜ 생성, λ°˜ν™˜λœ connId λ₯Ό λ‚΄λΆ€ 보관. 이후 νŠΈλžœμž­μ…˜Β·μΏΌλ¦¬ 호좜의 ν•Έλ“€.
47
- - beginTransaction(isolationLevel?): Promise\<void\> β€” νŠΈλžœμž­μ…˜ μ‹œμž‘. isolationLevel(orm-common `IsolationLevel`) λ―Έμ§€μ • μ‹œ μ„œλ²„ κΈ°λ³Έκ°’.
48
- - commitTransaction(): Promise\<void\> β€” νŠΈλžœμž­μ…˜ 컀밋.
49
- - rollbackTransaction(): Promise\<void\> β€” νŠΈλžœμž­μ…˜ λ‘€λ°±.
50
- - close(): Promise\<void\> β€” 컀λ„₯μ…˜ μ’…λ£Œ ν›„ 보관 connId ν•΄μ œ.
51
- - executeDefs\<T\>(defs, options?): Promise\<T[][]\> β€” 쿼리 μ •μ˜ λ°°μ—΄(`QueryDef[]`)을 μ„œλ²„μ—μ„œ μ‹€ν–‰. options λŠ” μ •μ˜λ³„ κ²°κ³Ό λ§€ν•‘ 메타(`(ResultMeta | undefined)[]`, ν•­λͺ©λ³„ nullable)둜 ν–‰ 역직렬화 방식 μ§€μ •. μ •μ˜ 1κ°œλ‹Ή κ²°κ³Ό λ°°μ—΄ 1개λ₯Ό κ°€μ§„ 2차원 λ°°μ—΄ λ°˜ν™˜.
52
- - executeParametrized(query, params?): Promise\<unknown[][]\> β€” νŒŒλΌλ―Έν„° 바인딩 raw SQL μ‹€ν–‰. query = SQL λ¬Έμžμ—΄, params = 바인딩 κ°’ λ°°μ—΄.
53
- - bulkInsert(tableName, columnDefs, records): Promise\<void\> β€” λŒ€λŸ‰ μ‚½μž…. tableName = λŒ€μƒ ν…Œμ΄λΈ”λͺ…, columnDefs(`Record<string, ColumnMeta>`)둜 μ»¬λŸΌλ³„ 메타λ₯Ό, records(`Record<string, unknown>[]`)둜 μ‚½μž…ν•  ν–‰ 객체 배열을 전달.
43
+ `new OrmClientDbContextExecutor(client, opt)` β€” 생성. `opt = DbConnOptions & { configName: string }`. 생성 μ‹œ `Orm` μ„œλΉ„μŠ€ ν”„λ‘μ‹œ 확보. μ•„λž˜ `getInfo`/`connect` μ΄μ™Έμ˜ μ‹€ν–‰ λ©”μ„œλ“œλŠ” connId κ°€ μ—†μœΌλ©΄(λ―Έμ—°κ²°) throw("λ°μ΄ν„°λ² μ΄μŠ€μ— μ—°κ²°λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.").
44
+
45
+ - `getInfo(): Promise<{ dialect; database?; schema? }>` β€” μ„œλ²„ ORM μ„€μ • 정보(λ°©μ–ΈΒ·κΈ°λ³Έ DBλͺ…Β·μŠ€ν‚€λ§ˆ) 쑰회. 컀λ„₯ν„°κ°€ dbContextOpt λ―Έμ§€μ • μ‹œ fallback 으둜 μ‚¬μš©.
46
+ - `connect(): Promise<void>` β€” μ„œλ²„μ— 컀λ„₯μ…˜ 생성, λ°˜ν™˜λœ connId λ₯Ό λ‚΄λΆ€ 보관. 이후 νŠΈλžœμž­μ…˜Β·μΏΌλ¦¬ 호좜의 ν•Έλ“€.
47
+ - `beginTransaction(isolationLevel?): Promise<void>` β€” νŠΈλžœμž­μ…˜ μ‹œμž‘. `isolationLevel`(orm-common `IsolationLevel`) λ―Έμ§€μ • μ‹œ μ„œλ²„ κΈ°λ³Έκ°’.
48
+ - `commitTransaction(): Promise<void>` β€” νŠΈλžœμž­μ…˜ 컀밋.
49
+ - `rollbackTransaction(): Promise<void>` β€” νŠΈλžœμž­μ…˜ λ‘€λ°±.
50
+ - `close(): Promise<void>` β€” 컀λ„₯μ…˜ μ’…λ£Œ ν›„ 보관 connId ν•΄μ œ.
51
+ - `executeDefs<T>(defs, options?): Promise<T[][]>` β€” 쿼리 μ •μ˜ λ°°μ—΄(`QueryDef[]`)을 μ„œλ²„μ—μ„œ μ‹€ν–‰. `options` λŠ” μ •μ˜λ³„ κ²°κ³Ό λ§€ν•‘ 메타(`(ResultMeta | undefined)[]`, ν•­λͺ©λ³„ nullable)둜 ν–‰ 역직렬화 방식 μ§€μ •. μ •μ˜ 1κ°œλ‹Ή κ²°κ³Ό λ°°μ—΄ 1개λ₯Ό κ°€μ§„ 2차원 λ°°μ—΄ λ°˜ν™˜.
52
+ - `executeParametrized(query, params?): Promise<unknown[][]>` β€” νŒŒλΌλ―Έν„° 바인딩 raw SQL μ‹€ν–‰. `query` = SQL λ¬Έμžμ—΄, `params` = 바인딩 κ°’ λ°°μ—΄.
53
+ - `bulkInsert(tableName, columnDefs, records): Promise<void>` β€” λŒ€λŸ‰ μ‚½μž…. `tableName` = λŒ€μƒ ν…Œμ΄λΈ”λͺ…, `columnDefs`(`Record<string, ColumnMeta>`)둜 μ»¬λŸΌλ³„ 메타, `records`(`Record<string, unknown>[]`)둜 μ‚½μž…ν•  ν–‰ 객체 배열을 전달.
@@ -1,52 +1,52 @@
1
1
  # @simplysm/service-client β€” μ €μˆ˜μ€€ 전솑 계측
2
2
 
3
- `ServiceClient` κ°€ μƒμ„±μžμ—μ„œ λ‚΄λΆ€μ μœΌλ‘œ μ‘°λ¦½ν•˜λŠ” μ €μˆ˜μ€€ λͺ¨λ“ˆλ“€. WebSocket μ—°κ²°Β·ν•˜νŠΈλΉ„νŠΈΒ·μž¬μ—°κ²°(SocketProvider), μš”μ²­/응닡 λ§€μΉ­κ³Ό λ©”μ‹œμ§€ λ””μŠ€νŒ¨μΉ˜(ServiceTransport), 인코딩/λ””μ½”λ”©μ˜ Worker μ˜€ν”„λ‘œλ”©(ClientProtocolWrapper). 일반 μ‚¬μš©μ—μ„œλŠ” `ServiceClient` 만 μ“°λ©΄ 되며, 이 계측은 μ†ŒμΌ“Β·μ²­ν¬Β·ν•˜νŠΈλΉ„νŠΈ λ™μž‘μ„ 이해해야 ν•  λ•Œλ§Œ μ½λŠ”λ‹€.
3
+ `ServiceClient` κ°€ μƒμ„±μžμ—μ„œ λ‚΄λΆ€μ μœΌλ‘œ μ‘°λ¦½ν•˜λŠ” μ €μˆ˜μ€€ λͺ¨λ“ˆλ“€. WebSocket μ—°κ²°Β·ν•˜νŠΈλΉ„νŠΈΒ·μžλ™ μž¬μ—°κ²°(SocketProvider), μš”μ²­/응닡 uuid λ§€μΉ­κ³Ό λ©”μ‹œμ§€ λ””μŠ€νŒ¨μΉ˜(ServiceTransport), 인코딩/λ””μ½”λ”©μ˜ Worker μ˜€ν”„λ‘œλ”©(ClientProtocolWrapper). 일반 μ‚¬μš©μ—μ„œλŠ” `ServiceClient` 만 μ“°λ©΄ 되며, 이 계측은 μ†ŒμΌ“Β·μ²­ν¬Β·ν•˜νŠΈλΉ„νŠΈ λ™μž‘μ„ 이해해야 ν•  λ•Œλ§Œ μ½λŠ”λ‹€.
4
4
 
5
5
  ## SocketProvider / createSocketProvider
6
6
 
7
7
  WebSocket 1개의 μ—°κ²°Β·ν•˜νŠΈλΉ„νŠΈΒ·μžλ™ μž¬μ—°κ²° λ‹΄λ‹Ή.
8
8
 
9
- `createSocketProvider(url, clientName, maxReconnectCount): SocketProvider` β€” ν”„λ‘œλ°”μ΄λ” 생성. Node ν™˜κ²½μ—μ„œ κΈ€λ‘œλ²Œ `WebSocket` 이 μ—†μœΌλ©΄ λͺ¨λ“ˆ λ‘œλ“œ μ‹œ `ws` νŒ¨ν‚€μ§€λ‘œ polyfill.
9
+ `createSocketProvider(url, clientName, maxReconnectCount): SocketProvider` β€” ν”„λ‘œλ°”μ΄λ” 생성. 이 λͺ¨λ“ˆμ€ import μ‹œμ μ— κΈ€λ‘œλ²Œ `WebSocket` 이 μ—†μœΌλ©΄ `ws` νŒ¨ν‚€μ§€λ‘œ polyfill ν•œλ‹€(Node ν™˜κ²½).
10
10
 
11
- - url: string β€” `ws(s)://host:port/ws`. 접속 μ‹œ `ver=2`, μƒμ„±λœ `clientId`(UUID), `clientName` 쿼리λ₯Ό λΆ™μž„.
12
- - clientName: string β€” 접속 쿼리에 μ‹€λ¦¬λŠ” 식별λͺ….
13
- - maxReconnectCount: number β€” μ΅œλŒ€ μž¬μ—°κ²° μ‹œλ„ 횟수. 0 이면 μž¬μ—°κ²° μ•ˆ 함.
11
+ - `url: string` β€” `ws(s)://host:port/ws`. 접속 μ‹œ `ver=2`, μƒμ„±λœ `clientId`(UUID), `clientName` 쿼리λ₯Ό λΆ™μž„.
12
+ - `clientName: string` β€” 접속 쿼리에 μ‹€λ¦¬λŠ” 식별λͺ….
13
+ - `maxReconnectCount: number` β€” μ΅œλŒ€ μž¬μ—°κ²° μ‹œλ„ 횟수. 0 이면 μž¬μ—°κ²° μ•ˆ 함.
14
14
 
15
- λ‚΄λΆ€ μƒμˆ˜: ν•˜νŠΈλΉ„νŠΈ ping 5초 간격, 30초 λ¬΄μˆ˜μ‹  μ‹œ νƒ€μž„μ•„μ›ƒ, μž¬μ—°κ²° 3초 간격. 1λ°”μ΄νŠΈ `0x01` ping 전솑, `0x02` pong μˆ˜μ‹ μ€ λ¬΄μ‹œ(νƒ€μž„μŠ€νƒ¬ν”„λ§Œ κ°±μ‹ ).
15
+ λ‚΄λΆ€ μƒμˆ˜: ping 5초 간격 전솑, 30초 λ¬΄μˆ˜μ‹  μ‹œ νƒ€μž„μ•„μ›ƒ, μž¬μ—°κ²° 3초 간격. 1λ°”μ΄νŠΈ `0x01` ping 전솑, μˆ˜μ‹ ν•œ 1λ°”μ΄νŠΈ `0x02` pong 은 λ¬΄μ‹œ(ν•˜νŠΈλΉ„νŠΈ νƒ€μž„μŠ€νƒ¬ν”„λ§Œ κ°±μ‹ ).
16
16
 
17
- 멀버:
17
+ `SocketProvider` 멀버:
18
18
 
19
- - clientName: string (readonly) β€” 생성 μ‹œ 받은 식별λͺ….
20
- - connected: boolean (getter) β€” μ†ŒμΌ“μ΄ OPEN μƒνƒœμΈμ§€.
21
- - connect(): Promise\<void\> β€” 접속 μ‹œμž‘. μ‹€νŒ¨ μ‹œ throw, 성곡 μ‹œ μž¬μ—°κ²° 카운트 λ¦¬μ…‹ν•˜κ³  `state: "connected"` emit.
22
- - close(): Promise\<void\> β€” μˆ˜λ™ μ’…λ£Œ. 이후 μžλ™ μž¬μ—°κ²° μ•ˆ 함. μ†ŒμΌ“ CLOSED κΉŒμ§€ λŒ€κΈ° ν›„ `state: "closed"` emit.
23
- - send(data: Bytes): Promise\<void\> β€” λ°”μ΄νŠΈ 전솑. 일정 μ‹œκ°„ λ‚΄ 미연결이면 throw("μ„œλ²„μ— μ—°κ²°λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. 인터넷 연결을 확인해 μ£Όμ„Έμš”.").
24
- - on(type, listener) / off(type, listener) β€” 이벀트 ꡬ독/ν•΄μ œ.
19
+ - `clientName: string` (readonly) β€” 생성 μ‹œ 받은 식별λͺ….
20
+ - `connected: boolean` (getter) β€” μ†ŒμΌ“μ΄ OPEN μƒνƒœμΈμ§€.
21
+ - `connect(): Promise<void>` β€” 접속 μ‹œμž‘. μ‹€νŒ¨ μ‹œ throw, 성곡 μ‹œ μž¬μ—°κ²° 카운트 리셋 ν›„ `state: "connected"` emit.
22
+ - `close(): Promise<void>` β€” μˆ˜λ™ μ’…λ£Œ. 이후 μžλ™ μž¬μ—°κ²° μ•ˆ 함. μ†ŒμΌ“ CLOSED κΉŒμ§€ λŒ€κΈ° ν›„ `state: "closed"` emit.
23
+ - `send(data: Bytes): Promise<void>` β€” λ°”μ΄νŠΈ 전솑. 일정 μ‹œκ°„ λ‚΄ 미연결이면 throw("μ„œλ²„μ— μ—°κ²°λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. 인터넷 연결을 확인해 μ£Όμ„Έμš”.").
24
+ - `on(type, listener)` / `off(type, listener)` β€” 이벀트 ꡬ독/ν•΄μ œ.
25
25
 
26
26
  `SocketProviderEvents`:
27
27
 
28
- - message: Bytes β€” μˆ˜μ‹  λ°”μ΄νŠΈ(ping/pong 1λ°”μ΄νŠΈ μ œμ–΄ ν”„λ ˆμž„ μ œμ™Έ).
29
- - state: "connected" | "closed" | "reconnecting" β€” μ—°κ²° μƒνƒœ λ³€ν™”. "connected" = μ—°κ²°/μž¬μ—°κ²° 성곡, "closed" = μˆ˜λ™ μ’…λ£Œ λ˜λŠ” μž¬μ—°κ²° ν•œλ„ 초과, "reconnecting" = μž¬μ—°κ²° μ‹œλ„ 쀑.
28
+ - `message: Bytes` β€” μˆ˜μ‹  λ°”μ΄νŠΈ(1λ°”μ΄νŠΈ ping/pong μ œμ–΄ ν”„λ ˆμž„ μ œμ™Έ).
29
+ - `state: "connected" | "closed" | "reconnecting"` β€” μ—°κ²° μƒνƒœ 전이. `"connected"` = μ—°κ²°/μž¬μ—°κ²° 성곡, `"closed"` = μˆ˜λ™ μ’…λ£Œ λ˜λŠ” μž¬μ—°κ²° ν•œλ„ 초과, `"reconnecting"` = μž¬μ—°κ²° μ‹œλ„ 쀑.
30
30
 
31
- ν•˜νŠΈλΉ„νŠΈ νƒ€μž„μ•„μ›ƒ 감지 μ‹œ μ†ŒμΌ“μ„ κ°•μ œ μ •λ¦¬ν•˜κ³ (λŠ¦μ€ onclose 둜 μΈν•œ 쀑볡 μž¬μ—°κ²° λ°©μ§€λ₯Ό μœ„ν•΄ ν•Έλ“€λŸ¬ ν•΄μ œ) μˆ˜λ™ μ’…λ£Œκ°€ μ•„λ‹ˆλ©΄ μž¬μ—°κ²°μ„ μ‹œλ„. μ΅œλŒ€ μ‹œλ„ 초과 μ‹œ `state: "closed"` emit.
31
+ ν•˜νŠΈλΉ„νŠΈ νƒ€μž„μ•„μ›ƒ 감지 μ‹œ μ†ŒμΌ“μ„ κ°•μ œ μ •λ¦¬ν•˜κ³ (λŠ¦μ€ `onclose` 둜 μΈν•œ 쀑볡 μž¬μ—°κ²° λ°©μ§€λ₯Ό μœ„ν•΄ ν•Έλ“€λŸ¬ ν•΄μ œ) μˆ˜λ™ μ’…λ£Œκ°€ μ•„λ‹ˆλ©΄ μž¬μ—°κ²°μ„ μ‹œλ„. μž¬μ—°κ²°μ€ μž¬κ·€ λŒ€μ‹  λ£¨ν”„λ‘œ μˆ˜ν–‰ν•˜λ©° μ΅œλŒ€ μ‹œλ„ 초과 μ‹œ `state: "closed"` emit.
32
32
 
33
33
  ## ServiceTransport / createServiceTransport
34
34
 
35
35
  μš”μ²­λ³„ uuid λ§€μΉ­, 응닡/μ—λŸ¬/μ§„ν–‰λ₯ /μ„œλ²„μ΄λ²€νŠΈ λ””μŠ€νŒ¨μΉ˜ λ‹΄λ‹Ή.
36
36
 
37
- `createServiceTransport(socket, protocol): ServiceTransport` β€” 트랜슀포트 생성. μ†ŒμΌ“ `message` λ₯Ό λ°›μ•„ decode ν›„ μ’…λ₯˜λ³„ λΆ„κΈ°. μ†ŒμΌ“μ΄ `closed`/`reconnecting` 되면 λŒ€κΈ° 쀑인 λͺ¨λ“  μš”μ²­μ„ reject(λ©”λͺ¨λ¦¬ ν•΄μ œ).
37
+ `createServiceTransport(socket, protocol): ServiceTransport` β€” 트랜슀포트 생성. μ†ŒμΌ“ `message` λ₯Ό λ°›μ•„ decode ν›„ μ’…λ₯˜λ³„ λΆ„κΈ°. μ†ŒμΌ“μ΄ `closed`/`reconnecting` 되면 λŒ€κΈ° 쀑인 λͺ¨λ“  μš”μ²­μ„ reject("μš”μ²­ μ·¨μ†Œλ¨: ...") ν•˜μ—¬ λ©”λͺ¨λ¦¬ ν•΄μ œ.
38
38
 
39
- - socket: SocketProvider β€” ν•˜μœ„ μ†ŒμΌ“.
40
- - protocol: ClientProtocolWrapper β€” μΈμ½”λ“œ/λ””μ½”λ“œ 래퍼.
39
+ - `socket: SocketProvider` β€” ν•˜μœ„ μ†ŒμΌ“.
40
+ - `protocol: ClientProtocolWrapper` β€” μΈμ½”λ“œ/λ””μ½”λ“œ 래퍼.
41
41
 
42
- 멀버:
42
+ `ServiceTransport` 멀버:
43
43
 
44
- - send(message, progress?): Promise\<unknown\> β€” μš”μ²­ 1건 전솑 ν›„ 응닡 Promise λ°˜ν™˜. uuid 생성 β†’ λ¦¬μŠ€λ„ˆ 등둝 β†’ encode β†’ 청크 순차 전솑. 응닡(`response`) μˆ˜μ‹  μ‹œ resolve, μ—λŸ¬(`error`) μˆ˜μ‹  μ‹œ μ„œλ²„ μ—λŸ¬ ν•„λ“œλ₯Ό λ¨Έμ§€ν•œ `Error` 둜 reject. message = `ServiceClientMessage`, progress = `ServiceProgress`(선택).
45
- - on(type, listener) / off(type, listener) β€” 이벀트 ꡬ독/ν•΄μ œ.
44
+ - `send(message, progress?): Promise<unknown>` β€” μš”μ²­ 1건 전솑 ν›„ 응닡 Promise λ°˜ν™˜. uuid 생성 β†’ λ¦¬μŠ€λ„ˆ 선등둝 β†’ encode β†’ 청크 순차 전솑. 응닡(`response`) μˆ˜μ‹  μ‹œ resolve, μ—λŸ¬(`error`) μˆ˜μ‹  μ‹œ μ„œλ²„ μ—λŸ¬ ν•„λ“œλ₯Ό λ¨Έμ§€ν•œ `Error` 둜 reject. `message = ServiceClientMessage`, `progress = ServiceProgress`(선택).
45
+ - `on(type, listener)` / `off(type, listener)` β€” 이벀트 ꡬ독/ν•΄μ œ.
46
46
 
47
47
  `ServiceTransportEvents`:
48
48
 
49
- - event: `{ keys: string[]; data: unknown }` β€” μ„œλ²„κ°€ ν‘Έμ‹œν•œ `evt:on` λ©”μ‹œμ§€. `EventClient` κ°€ 이걸 ꡬ독해 keys 에 λ§€μΉ­λ˜λŠ” 둜컬 λ¦¬μŠ€λ„ˆλ‘œ λ””μŠ€νŒ¨μΉ˜.
49
+ - `event: { keys: string[]; data: unknown }` β€” μ„œλ²„κ°€ ν‘Έμ‹œν•œ `evt:on` λ©”μ‹œμ§€. `EventClient` κ°€ 이걸 ꡬ독해 keys 에 λ§€μΉ­λ˜λŠ” 둜컬 λ¦¬μŠ€λ„ˆλ‘œ λ””μŠ€νŒ¨μΉ˜.
50
50
 
51
51
  decode μ‹€νŒ¨ μ‹œμ—λ„ 헀더 16λ°”μ΄νŠΈμ—μ„œ uuid λ₯Ό μ„ μΆ”μΆœν•΄ ν•΄λ‹Ή μš”μ²­λ§Œ reject. λΆ„ν•  응닡이면 μ™„λ£Œ μ‹œ `progress.response` 둜 100% λ₯Ό ν•œ 번 더 보고. μ„œλ²„μΈ‘ μ§„ν–‰ λ©”μ‹œμ§€(`name: "progress"`)λŠ” `progress.server` 둜 전달.
52
52
 
@@ -56,6 +56,10 @@ decode μ‹€νŒ¨ μ‹œμ—λ„ 헀더 16λ°”μ΄νŠΈμ—μ„œ uuid λ₯Ό μ„ μΆ”μΆœν•΄ ν•΄λ‹Ή
56
56
 
57
57
  `createClientProtocolWrapper(protocol): ClientProtocolWrapper` β€” 래퍼 생성.
58
58
 
59
- - encode(uuid, message): Promise\<{ chunks: Bytes[]; totalSize: number }\> β€” λ©”μ‹œμ§€λ₯Ό 청크 λ°°μ—΄λ‘œ μΈμ½”λ“œ. body κ°€ Uint8Array, 30KB 초과 λ¬Έμžμ—΄, 길이 100 초과 λ°°μ—΄, λ˜λŠ” 첫 ν•­λͺ©μ΄ Uint8Array 인 배열이면 Worker μ‚¬μš©. message = `ServiceMessage`.
60
- - decode(bytes): Promise\<ServiceMessageDecodeResult\<ServiceMessage\>\> β€” μˆ˜μ‹  λ°”μ΄νŠΈ λ””μ½”λ“œ. 청크 재쑰립(stateful)은 ν•œ λ©”μ‹œμ§€μ˜ 청크가 흩어지지 μ•Šλ„λ‘ 항상 메인 μŠ€λ ˆλ“œ 단일 λˆ„μ κΈ°μ—μ„œ μˆ˜ν–‰ν•˜κ³ (#35), 재쑰립 μ™„λ£Œ ν›„ 30KB 초과 JSON νŒŒμ‹±(stateless)만 Worker 에 μœ„μž„. λ―Έμ™„λ£Œ(progress) μƒνƒœλ©΄ κ·ΈλŒ€λ‘œ λ°˜ν™˜.
61
- - dispose(): void β€” ν”„λ‘œν† μ½œκ³Ό Worker 리쑸버 정리. `ServiceClient.close()` μ—μ„œ 호좜.
59
+ `ClientProtocolWrapper` 멀버:
60
+
61
+ - `encode(uuid, message): Promise<{ chunks: Bytes[]; totalSize: number }>` β€” λ©”μ‹œμ§€λ₯Ό 청크 λ°°μ—΄λ‘œ μΈμ½”λ“œ. body κ°€ `Uint8Array`, 30KB 초과 λ¬Έμžμ—΄, 길이 100 초과 λ°°μ—΄, λ˜λŠ” 첫 ν•­λͺ©μ΄ `Uint8Array` 인 배열이면 Worker μ‚¬μš©(κ·Έ μ™ΈΒ·Worker λ―Έκ°€μš© μ‹œ 메인 μŠ€λ ˆλ“œ). `message = ServiceMessage`.
62
+ - `decode(bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>` β€” μˆ˜μ‹  λ°”μ΄νŠΈ λ””μ½”λ“œ. 청크 재쑰립(stateful)은 ν•œ λ©”μ‹œμ§€μ˜ 청크가 μ„œλ‘œ λ‹€λ₯Έ λˆ„μ κΈ°λ‘œ 흩어지지 μ•Šλ„λ‘ 항상 메인 μŠ€λ ˆλ“œ 단일 λˆ„μ κΈ°μ—μ„œ μˆ˜ν–‰ν•˜κ³ (#35), 재쑰립 μ™„λ£Œ ν›„ 30KB 초과 JSON νŒŒμ‹±(stateless)만 Worker 에 μœ„μž„. λ―Έμ™„λ£Œ(progress) μƒνƒœλ©΄ κ·ΈλŒ€λ‘œ λ°˜ν™˜.
63
+ - `dispose(): void` β€” ν”„λ‘œν† μ½œκ³Ό Worker 리쑸버 정리. `ServiceClient.close()` μ—μ„œ 호좜.
64
+
65
+ Worker λŠ” browser DOM Worker λ˜λŠ” Node `worker_threads` 쀑 κ°€μš©ν•œ μͺ½μ„ lazy μ΄ˆκΈ°ν™”ν•˜λ©°, Worker μž‘μ—…μ΄ 60초 λ‚΄ μ‘λ‹΅ν•˜μ§€ μ•ŠμœΌλ©΄ ν•΄λ‹Ή μž‘μ—…μ„ μ‹œκ°„ 초과둜 reject ν•œλ‹€.
@@ -1,6 +1,6 @@
1
1
  # @simplysm/service-common
2
2
 
3
- μ„œλ²„Β·ν΄λΌμ΄μ–ΈνŠΈκ°€ κ³΅μœ ν•˜λŠ” μ„œλΉ„μŠ€ 톡신 계약. 이벀트 μ •μ˜(`defineEvent`), λ‚΄μž₯ RPC μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€ μ‹œκ·Έλ‹ˆμ²˜, μ•± λ©”λ‰΄Β·κΆŒν•œΒ·λͺ¨λ“ˆ ꡬ쑰(`AppStructure`), WebSocket λ°”μ΄λ„ˆλ¦¬ ν”„λ‘œν† μ½œΒ·λ©”μ‹œμ§€ νƒ€μž…μ„ ν•œκ³³μ— λ‘”λ‹€. λ°œμƒμΈ‘Β·κ΅¬λ…μΈ‘ λ˜λŠ” μ„œλ²„Β·ν΄λΌμ΄μ–ΈνŠΈκ°€ 같은 μ •μ˜ κ°μ²΄Β·νƒ€μž…μ„ import ν•΄ μ“°λŠ” 게 핡심.
3
+ μ„œλ²„Β·ν΄λΌμ΄μ–ΈνŠΈκ°€ κ³΅μœ ν•˜λŠ” μ„œλΉ„μŠ€ 톡신 계약. μ‹€μ‹œκ°„ 이벀트 μ •μ˜(`defineEvent`), λ‚΄μž₯ RPC μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€ μ‹œκ·Έλ‹ˆμ²˜, μ•± λ©”λ‰΄Β·κΆŒν•œΒ·λͺ¨λ“ˆ ꡬ쑰(`AppStructure`), WebSocket λ°”μ΄λ„ˆλ¦¬ ν”„λ‘œν† μ½œΒ·λ©”μ‹œμ§€ νƒ€μž…μ„ ν•œκ³³μ— λ‘”λ‹€. λ°œμƒμΈ‘Β·κ΅¬λ…μΈ‘ λ˜λŠ” μ„œλ²„Β·ν΄λΌμ΄μ–ΈνŠΈκ°€ 같은 μ •μ˜ κ°μ²΄Β·νƒ€μž…μ„ import ν•΄ μ“°λŠ” 게 핡심.
4
4
 
5
5
  ## μ‚¬μš© 트리거 인덱슀
6
6
 
@@ -21,9 +21,9 @@
21
21
  function defineEvent<TInfo = unknown, TData = unknown>(eventName: string): ServiceEventDef<TInfo, TData>
22
22
  ```
23
23
 
24
- - `TInfo` (μ œλ„€λ¦­) β€” κ΅¬λ…μžκ°€ "무엇을 κ΅¬λ…ν•˜λŠ”μ§€" μ‹λ³„ν•˜λŠ” 메타데이터 νƒ€μž…. λ°œμƒμΈ‘ selector κ°€ 이 값을 보고 전달 λŒ€μƒμ„ 골라냄. λ―Έμ§€μ • μ‹œ `unknown`. 예: `{ warehouseId: number }`.
25
- - `TData` (μ œλ„€λ¦­) β€” μ΄λ²€νŠΈκ°€ μ‹€μ–΄ λ‚˜λ₯΄λŠ” νŽ˜μ΄λ‘œλ“œ νƒ€μž…. ꡬ독 콜백이 λ°›λŠ” 인자 νƒ€μž…. λ―Έμ§€μ • μ‹œ `unknown`. 예: `{ orderId: number; status: string }`.
26
- - `eventName` (인자) β€” λΌμš°νŒ… ν‚€ λ¬Έμžμ—΄. 같은 이름이면 같은 이벀트둜 μ·¨κΈ‰λ˜λ―€λ‘œ μ•± λ‚΄μ—μ„œ κ³ μœ ν•΄μ•Ό 함.
24
+ - `TInfo` (μ œλ„€λ¦­) β€” κ΅¬λ…μžκ°€ "무엇을 κ΅¬λ…ν•˜λŠ”μ§€" μ‹λ³„ν•˜λŠ” 메타데이터 νƒ€μž…. λ°œμƒμΈ‘ selector κ°€ 이 값을 보고 전달 λŒ€μƒμ„ 골라냄. λ―Έμ§€μ • μ‹œ `unknown`.
25
+ - `TData` (μ œλ„€λ¦­) β€” μ΄λ²€νŠΈκ°€ μ‹€μ–΄ λ‚˜λ₯΄λŠ” νŽ˜μ΄λ‘œλ“œ νƒ€μž…. ꡬ독 콜백이 λ°›λŠ” 인자 νƒ€μž…. λ―Έμ§€μ • μ‹œ `unknown`.
26
+ - `eventName` (인자) β€” λΌμš°νŒ… ν‚€ λ¬Έμžμ—΄. `ServiceEventDef.eventName` 에 κ·ΈλŒ€λ‘œ λ“€μ–΄κ°€λ©° λŸ°νƒ€μž„μ— μ‹€μ œλ‘œ μ“°μ΄λŠ” μœ μΌν•œ κ°’. 같은 이름이면 같은 이벀트둜 μ·¨κΈ‰λ˜λ―€λ‘œ μ•± λ‚΄μ—μ„œ κ³ μœ ν•΄μ•Ό 함.
27
27
  - λ°˜ν™˜λœ `ServiceEventDef` λ₯Ό κ·ΈλŒ€λ‘œ λ°œμƒΒ·κ΅¬λ… API 의 첫 인자둜 λ„˜κΈ°λ©΄ μ΄λ¦„Β·νƒ€μž…μ΄ 좔둠됨.
28
28
 
29
29
  ```ts
@@ -49,8 +49,8 @@ interface ServiceEventDef<TInfo = unknown, TData = unknown> {
49
49
  ```
50
50
 
51
51
  - `eventName: string` β€” λΌμš°νŒ… ν‚€. `defineEvent` μΈμžκ°€ κ·ΈλŒ€λ‘œ λ“€μ–΄κ°€λ©° λŸ°νƒ€μž„μ— μ‹€μ œλ‘œ μ“°μ΄λŠ” μœ μΌν•œ κ°’.
52
- - `$info: TInfo` (readonly) β€” `TInfo` νƒ€μž… μΆ”μΆœ μ „μš© phantom 마컀. λŸ°νƒ€μž„ 값은 `undefined`(μ‚¬μš©λ˜μ§€ μ•ŠμŒ). λ°œμƒΒ·κ΅¬λ… API κ°€ 이 마컀둜 `TInfo` λ₯Ό λŒμ–΄λ‹€ 씀.
53
- - `$data: TData` (readonly) β€” `TData` νƒ€μž… μΆ”μΆœ μ „μš© phantom 마컀. λŸ°νƒ€μž„ 값은 `undefined`(μ‚¬μš©λ˜μ§€ μ•ŠμŒ). 직접 읽지 말 것.
52
+ - `$info: TInfo` (readonly) β€” `TInfo` νƒ€μž… μΆ”μΆœ μ „μš© 마컀. λŸ°νƒ€μž„ 값은 `undefined`(μ‚¬μš©λ˜μ§€ μ•ŠμŒ). λ°œμƒΒ·κ΅¬λ… API κ°€ 이 마컀둜 `TInfo` λ₯Ό λŒμ–΄λ‹€ 씀. 직접 읽지 말 것.
53
+ - `$data: TData` (readonly) β€” `TData` νƒ€μž… μΆ”μΆœ μ „μš© 마컀. λŸ°νƒ€μž„ 값은 `undefined`(μ‚¬μš©λ˜μ§€ μ•ŠμŒ). 직접 읽지 말 것.
54
54
 
55
55
  ## λ‚΄μž₯ RPC μ„œλΉ„μŠ€ μ‹œκ·Έλ‹ˆμ²˜ (OrmService / AutoUpdateService)
56
56
 
@@ -82,7 +82,7 @@ interface OrmService {
82
82
  - `rollbackTransaction(connId)` β€” νŠΈλžœμž­μ…˜ μ·¨μ†Œ.
83
83
  - `executeParametrized(connId, query, params?)` β€” νŒŒλΌλ―Έν„° 바인딩 raw SQL 직접 μ‹€ν–‰. `params` λŠ” ν”Œλ ˆμ΄μŠ€ν™€λ”μ— μˆœμ„œλŒ€λ‘œ 바인딩(μ—†μœΌλ©΄ 바인딩 μ—†λŠ” 쿼리). κ²°κ³ΌλŠ” 결과셋별 ν–‰ λ°°μ—΄(`unknown[][]`).
84
84
  - `executeDefs(connId, defs, options?)` β€” ORM μ½”μ–΄κ°€ λ§Œλ“  `QueryDef` 배열을 일괄 μ‹€ν–‰. `options[i]` λŠ” `defs[i]` κ²°κ³Όμ…‹μ˜ 컬럼 νŒŒμ‹± 메타(`ResultMeta`)둜, 메타 λΆˆν•„μš”ν•œ def μžλ¦¬λŠ” `undefined`.
85
- - `bulkInsert(connId, tableName, columnDefs, records)` β€” 닀건 λ ˆμ½”λ“œ λŒ€λŸ‰ μ‚½μž…. `columnDefs` λŠ” 컬럼λͺ…β†’`ColumnMeta`(νƒ€μž…Β·μΈμ½”λ”©) λ§€ν•‘, `records` λŠ” μ‚½μž…ν•  ν–‰ λ°°μ—΄.
85
+ - `bulkInsert(connId, tableName, columnDefs, records)` β€” 닀건 λ ˆμ½”λ“œ λŒ€λŸ‰ μ‚½μž…. `columnDefs` λŠ” 컬럼λͺ…β†’`ColumnMeta` λ§€ν•‘, `records` λŠ” μ‚½μž…ν•  ν–‰ λ°°μ—΄.
86
86
 
87
87
  `Dialect`/`IsolationLevel`/`QueryDef`/`ColumnMeta`/`ResultMeta` λŠ” `@simplysm/orm-common` νƒ€μž….
88
88
 
@@ -92,7 +92,7 @@ interface OrmService {
92
92
  type DbConnOptions = { configName?: string; config?: Record<string, unknown> }
93
93
  ```
94
94
 
95
- - `configName?: string` β€” μ„œλ²„μ— λ“±λ‘λœ DB μ—°κ²° μ„€μ •μ˜ 이름. 이 μ΄λ¦„μœΌλ‘œ μ„œλ²„κ°€ 접속 정보λ₯Ό 찾음. (`getInfo`/`connect` λŠ” `configName` 을 ν•„μˆ˜λ‘œ ꡐ차해 λ°›μŒ.)
95
+ - `configName?: string` β€” μ„œλ²„μ— λ“±λ‘λœ DB μ—°κ²° μ„€μ •μ˜ 이름. 이 μ΄λ¦„μœΌλ‘œ μ„œλ²„κ°€ 접속 정보λ₯Ό 찾음. `getInfo`/`connect` λŠ” 이 ν•„λ“œλ₯Ό ν•„μˆ˜λ‘œ ꡐ차해(`& { configName: string }`) λ°›μŒ.
96
96
  - `config?: Record<string, unknown>` β€” 등둝 μ„€μ • λŒ€μ‹  직접 λ„˜κΈ°λŠ” 즉석 접속 μ„€μ • 객체. `configName` 으둜 가리킬 수 없을 λ•Œ μ‚¬μš©.
97
97
 
98
98
  ### AutoUpdateService
@@ -51,7 +51,7 @@ interface AppStructureLeafItem<TModule> {
51
51
  ```
52
52
 
53
53
  - `code` / `title` / `modules` / `requiredModules` / `icon` β€” κ·Έλ£Ήκ³Ό 동일 의미.
54
- - `perms?: ("use" | "edit")[]` β€” 이 화면에 λΆ€μ—¬ κ°€λŠ₯ν•œ κΆŒν•œ μ’…λ₯˜. `"use"` = 쑰회 κΆŒν•œ, `"edit"` = νŽΈμ§‘ κΆŒν•œ. μ§€μ •ν•œ ν™”λ©΄λ§Œ κΆŒν•œ νŽ˜μ΄μ§€Β·κΆŒν•œ 체크 λŒ€μƒμ΄ 됨. μƒλž΅ν•˜λ©΄ μ œμ•½ μ—†λŠ” ν™”λ©΄(항상 λͺ¨λ“  κΆŒν•œ ν™œμ„±).
54
+ - `perms?: ("use" | "edit")[]` β€” 이 화면에 λΆ€μ—¬ κ°€λŠ₯ν•œ κΆŒν•œ μ’…λ₯˜. `"use"` = 쑰회 κΆŒν•œ, `"edit"` = νŽΈμ§‘ κΆŒν•œ. μ§€μ •ν•œ ν™”λ©΄λ§Œ κΆŒν•œ νŽ˜μ΄μ§€Β·κΆŒν•œ 체크 λŒ€μƒμ΄ 됨. μƒλž΅ν•˜λ©΄ μ œμ•½ μ—†λŠ” ν™”λ©΄.
55
55
  - `subPerms?: AppStructureSubPermission<TModule>[]` β€” ν•œ ν™”λ©΄ μ•ˆμ˜ μ„ΈλΆ€ κΈ°λŠ₯ κΆŒν•œ λͺ©λ‘.
56
56
  - `url?: string` β€” μ™ΈλΆ€ 링크 λ“± 이동 경둜. 일반 ν™”λ©΄ λΌμš°νŒ… λŒ€μ‹  μ™ΈλΆ€λ‘œ 보낼 λ•Œ.
57
57
  - `isNotMenu?: boolean` β€” 메뉴 λ…ΈμΆœ ν† κΈ€. `true` λ©΄ μ‚¬μ΄λ“œ λ©”λ‰΄μ—μ„œ μˆ¨κΉ€(λΌμš°νŒ… λŒ€μƒ ν™”λ©΄ μžμ²΄λŠ” μœ μ§€). ν™ˆΒ·λ‚΄μ •λ³΄ λ“± 직접 μ§„μž… 화면에 μ‚¬μš©. λ―Έμ§€μ •/`false` λ©΄ 메뉴에 λ…ΈμΆœ.
@@ -88,9 +88,9 @@ interface FlatPermission<TModule = unknown> {
88
88
  }
89
89
  ```
90
90
 
91
- - `titleChain: string[]` β€” λ£¨νŠΈλΆ€ν„° 이 κΆŒν•œκΉŒμ§€μ˜ ν‘œμ‹œλͺ… 경둜. κΆŒν•œ νŽ˜μ΄μ§€μ—μ„œ 계측 라벨둜 μ‚¬μš©.
91
+ - `titleChain: string[]` β€” λ£¨νŠΈλΆ€ν„° 이 κΆŒν•œκΉŒμ§€μ˜ ν‘œμ‹œλͺ…(`title`) 경둜. κΆŒν•œ νŽ˜μ΄μ§€μ—μ„œ 계측 라벨둜 μ‚¬μš©.
92
92
  - `codeChain: string[]` β€” λ£¨νŠΈλΆ€ν„°μ˜ μ½”λ“œ 경둜 + λ§ˆμ§€λ§‰μ— κΆŒν•œ μ’…λ₯˜(`"use"`/`"edit"`)Β·μ„ΈλΆ€μ½”λ“œκ°€ 뢙은 전체 ν‚€. κΆŒν•œ μ‹λ³„μž.
93
- - `modulesChain: TModule[][]` β€” κ²½λ‘œμƒ 각 레벨이 κ°€μ§„ `modules` λ°°μ—΄λ“€μ˜ λͺ¨μŒ. 이 κΆŒν•œμ΄ μ–΄λ–€ λͺ¨λ“ˆ 쑰건에 λ¬Άμ˜€λŠ”μ§€ ν‘œν˜„.
93
+ - `modulesChain: TModule[][]` β€” κ²½λ‘œμƒ `modules` λ₯Ό κ°€μ§„ 각 레벨의 `modules` λ°°μ—΄λ“€μ˜ λͺ¨μŒ. 이 κΆŒν•œμ΄ μ–΄λ–€ λͺ¨λ“ˆ 쑰건에 λ¬Άμ˜€λŠ”μ§€ ν‘œν˜„.
94
94
 
95
95
  ## isUsableModules
96
96
 
@@ -102,9 +102,16 @@ function isUsableModules<TModule>(modules: TModule[] | undefined, requiredModule
102
102
 
103
103
  - `modules` β€” OR 쑰건. λ‚˜μ—΄ 쀑 ν•˜λ‚˜λΌλ„ `usableModules` 에 있으면 톡과. `undefined`/빈 배열이면 무쑰건 톡과.
104
104
  - `requiredModules` β€” AND 쑰건. λ‚˜μ—΄ μ „λΆ€κ°€ `usableModules` 에 μžˆμ–΄μ•Ό 톡과. `undefined`/빈 배열이면 ν†΅κ³Όλ‘œ κ°„μ£Ό.
105
- - `usableModules` β€” ν˜„μž¬ μ•±μ—μ„œ ν™œμ„±μΈ λͺ¨λ“ˆ λͺ©λ‘.
105
+ - `usableModules` β€” ν˜„μž¬ μ•±μ—μ„œ ν™œμ„±μΈ λͺ¨λ“ˆ λͺ©λ‘. `undefined` 이면 `modules` λ₯Ό κ°€μ§„ ν•­λͺ©μ€ λ§€μΉ­ μ‹€νŒ¨(톡과 λΆˆκ°€).
106
106
  - λ°˜ν™˜: requiredModules(AND)와 modules(OR)λ₯Ό λͺ¨λ‘ λ§Œμ‘±ν•˜λ©΄ `true`.
107
107
 
108
+ ```ts
109
+ import { isUsableModules } from "@simplysm/service-common";
110
+
111
+ isUsableModules(["A", "B"], undefined, ["A"]); // true β€” OR
112
+ isUsableModules(undefined, ["A", "B"], ["A"]); // false β€” AND λ―ΈμΆ©μ‘±
113
+ ```
114
+
108
115
  ## isUsableModulesChain
109
116
 
110
117
  ```ts
@@ -116,7 +123,7 @@ function isUsableModulesChain<TModule>(modulesChain: TModule[][], requiredModule
116
123
  - `modulesChain` β€” 각 레벨의 `modules` λ°°μ—΄λ“€. λͺ¨λ“  레벨이 OR 쑰건을 톡과해야 함.
117
124
  - `requiredModulesChain` β€” 각 레벨의 `requiredModules` λ°°μ—΄λ“€. λͺ¨λ“  레벨이 AND 쑰건을 톡과해야 함.
118
125
  - `usableModules` β€” ν˜„μž¬ ν™œμ„± λͺ¨λ“ˆ λͺ©λ‘.
119
- - λ°˜ν™˜: ν•œ λ ˆλ²¨μ΄λΌλ„ λ§‰νžˆλ©΄ `false`. μžμ‹ ν‘œμ‹œ μ—¬λΆ€ νŒμ •μ— μ‚¬μš©.
126
+ - λ°˜ν™˜: 빈 체인이면 `true`, ν•œ λ ˆλ²¨μ΄λΌλ„ λ§‰νžˆλ©΄ `false`. μžμ‹ ν‘œμ‹œ μ—¬λΆ€ νŒμ •μ— μ‚¬μš©.
120
127
 
121
128
  ## getFlatPermissions
122
129
 
@@ -128,7 +135,7 @@ function getFlatPermissions<TModule>(items: AppStructureItem<TModule>[], usableM
128
135
 
129
136
  - `items` β€” μ•± ꡬ쑰 λ°°μ—΄(루트).
130
137
  - `usableModules` β€” ν˜„μž¬ ν™œμ„± λͺ¨λ“ˆ. λͺ¨λ“ˆ 쑰건을 ν†΅κ³Όν•˜μ§€ λͺ»ν•œ κ°€μ§€(κ·Έ μžμ‹Β·κΆŒν•œ 포함)λŠ” κ²°κ³Όμ—μ„œ 빠짐.
131
- - λ™μž‘: leaf 의 `perms` 각각, 그리고 `subPerms` 의 각 `perm` 을 ν•œ 쀄(`FlatPermission`)둜 펼침. `subPerms` λŠ” 자체 λͺ¨λ“ˆ 쑰건도 μΆ”κ°€λ‘œ 검사.
138
+ - λ™μž‘: leaf 의 `perms` 각각, 그리고 `subPerms` 의 각 `perm` 을 ν•œ 쀄(`FlatPermission`)둜 펼침. `subPerms` λŠ” 자체 λͺ¨λ“ˆ 쑰건도 μΆ”κ°€λ‘œ 검사. 빈 `items` λ©΄ 빈 λ°°μ—΄.
132
139
  - λ°˜ν™˜: ν‘œμ‹œ κ°€λŠ₯ν•œ κΆŒν•œλ“€μ˜ 평탄 λ°°μ—΄. κΆŒν•œ 관리 ν™”λ©΄(`<sd-permission-table>` μž…λ ₯)Β·κΆŒν•œ 맀칭의 기반.
133
140
 
134
141
  ```ts
@@ -37,7 +37,7 @@ interface ServiceProtocol {
37
37
  ```
38
38
 
39
39
  - `encode(uuid, message)` β€” λ©”μ‹œμ§€λ₯Ό 와이어 λ°”μ΄νŠΈλ‘œ 인코딩. 3MB 초과 μ‹œ μžλ™μœΌλ‘œ μ—¬λŸ¬ 청크둜 λΆ„ν• . λ°˜ν™˜ `chunks` λŠ” 전솑할 νŒ¨ν‚· λ°°μ—΄, `totalSize` λŠ” λ³Έλ¬Έ 총 λ°”μ΄νŠΈ. 같은 λ©”μ‹œμ§€μ˜ λͺ¨λ“  μ²­ν¬λŠ” 동일 `uuid` λ₯Ό 곡유. `MAX_TOTAL_SIZE` 초과 μ‹œ throw.
40
- - `accumulate(bytes)` β€” μˆ˜μ‹  청크 νŒ¨ν‚·μ„ λˆ„μ (stateful). 같은 uuid 의 청크λ₯Ό ν•œ λˆ„μ κΈ°μ— λͺ¨μŒ. 미완성이면 `progress`, μ „λΆ€ λ„μ°©ν•˜λ©΄ 재쑰립된 raw λ°”μ΄νŠΈλ₯Ό 담은 `complete` λ°˜ν™˜. JSON νŒŒμ‹±μ€ μ•ˆ 함. 헀더(28λ°”μ΄νŠΈ) λ―Έλ§Œμ΄κ±°λ‚˜ 크기 μœ„λ°˜ μ‹œ throw. 쀑볡 νŒ¨ν‚·μ€ λ¬΄μ‹œ(인덱슀 κΈ°μ€€ 1회만 반영).
40
+ - `accumulate(bytes)` β€” μˆ˜μ‹  청크 νŒ¨ν‚·μ„ λˆ„μ (stateful). 같은 uuid 의 청크λ₯Ό ν•œ λˆ„μ κΈ°μ— λͺ¨μŒ. 미완성이면 `progress`, μ „λΆ€ λ„μ°©ν•˜λ©΄ 재쑰립된 raw λ°”μ΄νŠΈλ₯Ό 담은 `complete` λ°˜ν™˜. JSON νŒŒμ‹±μ€ μ•ˆ 함. 헀더(28λ°”μ΄νŠΈ) λ―Έλ§Œμ΄κ±°λ‚˜ 크기 μœ„λ°˜ μ‹œ throw. 쀑볡 νŒ¨ν‚·μ€ 인덱슀 κΈ°μ€€ 1회만 반영(쀑볡은 λ¬΄μ‹œ). λˆ„μ λΆ„μ΄ `totalSize` λ₯Ό μ΄ˆκ³Όν•˜λ©΄ 무결성 μœ„λ°˜μœΌλ‘œ throw.
41
41
  - `parseMessage(resultBytes)` β€” 재쑰립된 raw λ°”μ΄νŠΈλ₯Ό `ServiceMessage` 둜 νŒŒμ‹±(stateless). λˆ„μ κΈ° μƒνƒœμ— μ˜μ‘΄ν•˜μ§€ μ•Šμ•„ worker λ“± λ‹€λ₯Έ μ»¨ν…μŠ€νŠΈμ— μœ„μž„ κ°€λŠ₯. νŒŒμ‹± μ‹€νŒ¨ μ‹œ throw.
42
42
  - `decode(bytes)` β€” `accumulate` ν›„ μ™„λ£Œ μ‹œ `parseMessage` κΉŒμ§€ μˆ˜ν–‰ν•˜λŠ” 톡합 경둜. 단일 μ»¨ν…μŠ€νŠΈμ—μ„œ ν•œ λ²ˆμ— λ””μ½”λ”©ν•  λ•Œ.
43
43
  - `dispose()` β€” λ‚΄λΆ€ 청크 λˆ„μ κΈ°μ˜ GC 타이머 ν•΄μ œΒ·λ©”λͺ¨λ¦¬ λ°˜ν™˜. μΈμŠ€ν„΄μŠ€λ₯Ό 더 μ•ˆ μ“Έ λ•Œ λ°˜λ“œμ‹œ 호좜.