jell-utils 0.0.18 → 0.2.0

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 (178) hide show
  1. package/.claude/commands/tm/add-dependency.md +58 -0
  2. package/.claude/commands/tm/add-subtask.md +79 -0
  3. package/.claude/commands/tm/add-task.md +81 -0
  4. package/.claude/commands/tm/analyze-complexity.md +124 -0
  5. package/.claude/commands/tm/analyze-project.md +100 -0
  6. package/.claude/commands/tm/auto-implement-tasks.md +100 -0
  7. package/.claude/commands/tm/command-pipeline.md +80 -0
  8. package/.claude/commands/tm/complexity-report.md +120 -0
  9. package/.claude/commands/tm/convert-task-to-subtask.md +74 -0
  10. package/.claude/commands/tm/expand-all-tasks.md +52 -0
  11. package/.claude/commands/tm/expand-task.md +52 -0
  12. package/.claude/commands/tm/fix-dependencies.md +82 -0
  13. package/.claude/commands/tm/help.md +101 -0
  14. package/.claude/commands/tm/init-project-quick.md +49 -0
  15. package/.claude/commands/tm/init-project.md +53 -0
  16. package/.claude/commands/tm/install-taskmaster.md +118 -0
  17. package/.claude/commands/tm/learn.md +106 -0
  18. package/.claude/commands/tm/list-tasks-by-status.md +42 -0
  19. package/.claude/commands/tm/list-tasks-with-subtasks.md +30 -0
  20. package/.claude/commands/tm/list-tasks.md +46 -0
  21. package/.claude/commands/tm/next-task.md +69 -0
  22. package/.claude/commands/tm/parse-prd-with-research.md +51 -0
  23. package/.claude/commands/tm/parse-prd.md +52 -0
  24. package/.claude/commands/tm/project-status.md +67 -0
  25. package/.claude/commands/tm/quick-install-taskmaster.md +23 -0
  26. package/.claude/commands/tm/remove-all-subtasks.md +94 -0
  27. package/.claude/commands/tm/remove-dependency.md +65 -0
  28. package/.claude/commands/tm/remove-subtask.md +87 -0
  29. package/.claude/commands/tm/remove-subtasks.md +89 -0
  30. package/.claude/commands/tm/remove-task.md +110 -0
  31. package/.claude/commands/tm/setup-models.md +52 -0
  32. package/.claude/commands/tm/show-task.md +85 -0
  33. package/.claude/commands/tm/smart-workflow.md +58 -0
  34. package/.claude/commands/tm/sync-readme.md +120 -0
  35. package/.claude/commands/tm/tm-main.md +147 -0
  36. package/.claude/commands/tm/to-cancelled.md +58 -0
  37. package/.claude/commands/tm/to-deferred.md +50 -0
  38. package/.claude/commands/tm/to-done.md +47 -0
  39. package/.claude/commands/tm/to-in-progress.md +39 -0
  40. package/.claude/commands/tm/to-pending.md +35 -0
  41. package/.claude/commands/tm/to-review.md +43 -0
  42. package/.claude/commands/tm/update-single-task.md +122 -0
  43. package/.claude/commands/tm/update-task.md +75 -0
  44. package/.claude/commands/tm/update-tasks-from-id.md +111 -0
  45. package/.claude/commands/tm/validate-dependencies.md +72 -0
  46. package/.claude/commands/tm/view-models.md +52 -0
  47. package/.claude/settings.local.json +30 -0
  48. package/.cursor/commands/tm/add-dependency.md +55 -0
  49. package/.cursor/commands/tm/add-subtask.md +76 -0
  50. package/.cursor/commands/tm/add-task.md +78 -0
  51. package/.cursor/commands/tm/analyze-complexity.md +121 -0
  52. package/.cursor/commands/tm/analyze-project.md +97 -0
  53. package/.cursor/commands/tm/auto-implement-tasks.md +97 -0
  54. package/.cursor/commands/tm/command-pipeline.md +77 -0
  55. package/.cursor/commands/tm/complexity-report.md +117 -0
  56. package/.cursor/commands/tm/convert-task-to-subtask.md +71 -0
  57. package/.cursor/commands/tm/expand-all-tasks.md +51 -0
  58. package/.cursor/commands/tm/expand-task.md +49 -0
  59. package/.cursor/commands/tm/fix-dependencies.md +81 -0
  60. package/.cursor/commands/tm/help.md +98 -0
  61. package/.cursor/commands/tm/init-project-quick.md +46 -0
  62. package/.cursor/commands/tm/init-project.md +50 -0
  63. package/.cursor/commands/tm/install-taskmaster.md +117 -0
  64. package/.cursor/commands/tm/learn.md +103 -0
  65. package/.cursor/commands/tm/list-tasks-by-status.md +39 -0
  66. package/.cursor/commands/tm/list-tasks-with-subtasks.md +29 -0
  67. package/.cursor/commands/tm/list-tasks.md +43 -0
  68. package/.cursor/commands/tm/next-task.md +66 -0
  69. package/.cursor/commands/tm/parse-prd-with-research.md +48 -0
  70. package/.cursor/commands/tm/parse-prd.md +49 -0
  71. package/.cursor/commands/tm/project-status.md +64 -0
  72. package/.cursor/commands/tm/quick-install-taskmaster.md +22 -0
  73. package/.cursor/commands/tm/remove-all-subtasks.md +93 -0
  74. package/.cursor/commands/tm/remove-dependency.md +62 -0
  75. package/.cursor/commands/tm/remove-subtask.md +84 -0
  76. package/.cursor/commands/tm/remove-subtasks.md +86 -0
  77. package/.cursor/commands/tm/remove-task.md +107 -0
  78. package/.cursor/commands/tm/setup-models.md +51 -0
  79. package/.cursor/commands/tm/show-task.md +82 -0
  80. package/.cursor/commands/tm/smart-workflow.md +55 -0
  81. package/.cursor/commands/tm/sync-readme.md +117 -0
  82. package/.cursor/commands/tm/tm-main.md +146 -0
  83. package/.cursor/commands/tm/to-cancelled.md +55 -0
  84. package/.cursor/commands/tm/to-deferred.md +47 -0
  85. package/.cursor/commands/tm/to-done.md +44 -0
  86. package/.cursor/commands/tm/to-in-progress.md +36 -0
  87. package/.cursor/commands/tm/to-pending.md +32 -0
  88. package/.cursor/commands/tm/to-review.md +40 -0
  89. package/.cursor/commands/tm/update-single-task.md +119 -0
  90. package/.cursor/commands/tm/update-task.md +72 -0
  91. package/.cursor/commands/tm/update-tasks-from-id.md +108 -0
  92. package/.cursor/commands/tm/validate-dependencies.md +71 -0
  93. package/.cursor/commands/tm/view-models.md +51 -0
  94. package/.cursor/mcp.json +20 -0
  95. package/.cursor/rules/cursor_rules.mdc +53 -0
  96. package/.cursor/rules/self_improve.mdc +72 -0
  97. package/.cursor/rules/taskmaster/dev_workflow.mdc +424 -0
  98. package/.cursor/rules/taskmaster/taskmaster.mdc +573 -0
  99. package/.env.example +12 -0
  100. package/.gemini/commands/tm/add-dependency.toml +58 -0
  101. package/.gemini/commands/tm/add-subtask.toml +79 -0
  102. package/.gemini/commands/tm/add-task.toml +81 -0
  103. package/.gemini/commands/tm/analyze-complexity.toml +124 -0
  104. package/.gemini/commands/tm/analyze-project.toml +100 -0
  105. package/.gemini/commands/tm/auto-implement-tasks.toml +100 -0
  106. package/.gemini/commands/tm/command-pipeline.toml +80 -0
  107. package/.gemini/commands/tm/complexity-report.toml +120 -0
  108. package/.gemini/commands/tm/convert-task-to-subtask.toml +74 -0
  109. package/.gemini/commands/tm/expand-all-tasks.toml +54 -0
  110. package/.gemini/commands/tm/expand-task.toml +52 -0
  111. package/.gemini/commands/tm/fix-dependencies.toml +84 -0
  112. package/.gemini/commands/tm/help.toml +101 -0
  113. package/.gemini/commands/tm/init-project-quick.toml +49 -0
  114. package/.gemini/commands/tm/init-project.toml +53 -0
  115. package/.gemini/commands/tm/install-taskmaster.toml +120 -0
  116. package/.gemini/commands/tm/learn.toml +106 -0
  117. package/.gemini/commands/tm/list-tasks-by-status.toml +42 -0
  118. package/.gemini/commands/tm/list-tasks-with-subtasks.toml +32 -0
  119. package/.gemini/commands/tm/list-tasks.toml +46 -0
  120. package/.gemini/commands/tm/next-task.toml +69 -0
  121. package/.gemini/commands/tm/parse-prd-with-research.toml +51 -0
  122. package/.gemini/commands/tm/parse-prd.toml +52 -0
  123. package/.gemini/commands/tm/project-status.toml +67 -0
  124. package/.gemini/commands/tm/quick-install-taskmaster.toml +25 -0
  125. package/.gemini/commands/tm/remove-all-subtasks.toml +96 -0
  126. package/.gemini/commands/tm/remove-dependency.toml +65 -0
  127. package/.gemini/commands/tm/remove-subtask.toml +87 -0
  128. package/.gemini/commands/tm/remove-subtasks.toml +89 -0
  129. package/.gemini/commands/tm/remove-task.toml +110 -0
  130. package/.gemini/commands/tm/setup-models.toml +54 -0
  131. package/.gemini/commands/tm/show-task.toml +85 -0
  132. package/.gemini/commands/tm/smart-workflow.toml +58 -0
  133. package/.gemini/commands/tm/sync-readme.toml +120 -0
  134. package/.gemini/commands/tm/tm-main.toml +149 -0
  135. package/.gemini/commands/tm/to-cancelled.toml +58 -0
  136. package/.gemini/commands/tm/to-deferred.toml +50 -0
  137. package/.gemini/commands/tm/to-done.toml +47 -0
  138. package/.gemini/commands/tm/to-in-progress.toml +39 -0
  139. package/.gemini/commands/tm/to-pending.toml +35 -0
  140. package/.gemini/commands/tm/to-review.toml +43 -0
  141. package/.gemini/commands/tm/update-single-task.toml +122 -0
  142. package/.gemini/commands/tm/update-task.toml +75 -0
  143. package/.gemini/commands/tm/update-tasks-from-id.toml +111 -0
  144. package/.gemini/commands/tm/validate-dependencies.toml +74 -0
  145. package/.gemini/commands/tm/view-models.toml +54 -0
  146. package/.gemini/settings.json +20 -0
  147. package/.mcp.json +15 -0
  148. package/.releaserc.json +115 -0
  149. package/.taskmaster/CLAUDE.md +435 -0
  150. package/.taskmaster/config.json +44 -0
  151. package/.taskmaster/docs/prd.txt +584 -0
  152. package/.taskmaster/state.json +6 -0
  153. package/.taskmaster/tasks/tasks.json +304 -0
  154. package/.taskmaster/templates/example_prd.txt +47 -0
  155. package/.taskmaster/templates/example_prd_rpg.txt +511 -0
  156. package/CLAUDE.md +5 -0
  157. package/README.md +551 -0
  158. package/coverage/clover.xml +412 -0
  159. package/coverage/coverage-final.json +2 -0
  160. package/coverage/lcov-report/base.css +224 -0
  161. package/coverage/lcov-report/block-navigation.js +87 -0
  162. package/coverage/lcov-report/favicon.png +0 -0
  163. package/coverage/lcov-report/index.html +116 -0
  164. package/coverage/lcov-report/index.js.html +2884 -0
  165. package/coverage/lcov-report/prettify.css +1 -0
  166. package/coverage/lcov-report/prettify.js +2 -0
  167. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  168. package/coverage/lcov-report/sorter.js +196 -0
  169. package/coverage/lcov.info +896 -0
  170. package/internal-docs/CLAUDE.md +139 -0
  171. package/internal-docs/DEPLOYMENT.md +351 -0
  172. package/internal-docs/NPM_DEPLOYMENT_GUIDE.md +203 -0
  173. package/internal-docs/OBSIDIAN_PROJECT_PROMPT.md +302 -0
  174. package/internal-docs/TEST_RESULTS.md +153 -0
  175. package/internal-docs/TEST_SIMULATION.md +27 -0
  176. package/lib/index.d.ts +226 -39
  177. package/lib/index.js +767 -121
  178. package/package.json +54 -17
package/README.md CHANGED
@@ -10,8 +10,559 @@
10
10
 
11
11
  This is personal project.
12
12
 
13
+ JavaScript/TypeScript 개발에 유용한 유틸리티 함수 모음입니다. 특히 한국어 처리, 날짜 포맷, 문자열 변환, 객체 조작 등의 기능을 제공합니다.
14
+
15
+ ## 설치
16
+
17
+ ```bash
18
+ npm install jell-utils
19
+ ```
20
+
21
+ 또는
22
+
23
+ ```bash
24
+ yarn add jell-utils
25
+ ```
26
+
27
+ ## 사용법
28
+
29
+ ```typescript
30
+ import util from 'jell-utils'
31
+
32
+ // 한국어 문자열 검사
33
+ util.isKorean('안녕하세요') // true
34
+ util.isKorean('hello') // false
35
+
36
+ // 날짜 포맷
37
+ util.getKoreanDate(new Date(), true) // "2024년 03월 15일"
38
+ util.formatDate(new Date()) // "2024-03-15"
39
+
40
+ // 문자열 변환
41
+ util.toCamelCase('hello_world') // "helloWorld"
42
+ util.toSnakeCase('Hello World') // "hello_world"
43
+ ```
44
+
45
+ ## API 레퍼런스
46
+
47
+ ### 한국어 유틸리티
48
+
49
+ #### `isKorean(message: string): boolean`
50
+
51
+ 문자열에 한국어가 포함되어 있는지 확인합니다.
52
+
53
+ ```typescript
54
+ util.isKorean('안녕하세요') // true
55
+ util.isKorean('한글test') // true
56
+ util.isKorean('hello') // false
57
+ ```
58
+
59
+ #### `isBusinessNumber(businessNumber: string): boolean`
60
+
61
+ 사업자등록번호의 유효성을 검증합니다.
62
+
63
+ ```typescript
64
+ util.isBusinessNumber('1018626554') // true
65
+ util.isBusinessNumber('101-86-26554') // true (하이픈 자동 제거)
66
+ util.isBusinessNumber('1234567890') // false
67
+ ```
68
+
69
+ #### `chosungSearch(str: string, search: string): boolean`
70
+
71
+ 한글 초성 검색을 지원합니다.
72
+
73
+ ```typescript
74
+ util.chosungSearch('사과', 'ㅅㄱ') // true
75
+ util.chosungSearch('김철수', 'ㄱㅊㅅ') // true
76
+ util.chosungSearch('바나나', 'ㅅㄱ') // false
77
+ ```
78
+
79
+ ### 날짜 유틸리티
80
+
81
+ #### `getNowDate(): string`
82
+
83
+ 현재 시간을 `YYYY-MM-DD HH:mm:ss` 형식으로 반환합니다.
84
+
85
+ ```typescript
86
+ util.getNowDate() // "2024-03-15 14:30:25"
87
+ ```
88
+
89
+ #### `getKoreanDate(dateString?: Date | string, isYear?: boolean): string`
90
+
91
+ 날짜를 한국어 형식으로 변환합니다.
92
+
93
+ ```typescript
94
+ util.getKoreanDate(new Date('2024-03-15'), false) // "03월 15일"
95
+ util.getKoreanDate(new Date('2024-03-15'), true) // "2024년 03월 15일"
96
+ ```
97
+
98
+ #### `formatDate(date?: Date | string): string`
99
+
100
+ 날짜를 `YYYY-MM-DD` 형식으로 변환합니다.
101
+
102
+ ```typescript
103
+ util.formatDate(new Date('2024-03-15')) // "2024-03-15"
104
+ ```
105
+
106
+ #### `calDDay(date: Date | string): number`
107
+
108
+ D-day를 계산합니다.
109
+
110
+ ```typescript
111
+ const targetDate = new Date('2024-12-25')
112
+ util.calDDay(targetDate) // 목표 날짜까지 남은 일수
113
+ ```
114
+
115
+ #### `dateDiff(date1: Date | string, date2?: Date | string): object`
116
+
117
+ 두 날짜 사이의 차이를 계산합니다.
118
+
119
+ ```typescript
120
+ util.dateDiff('2024-01-01', '2024-01-02')
121
+ // { days: 1, hours: 0, minutes: 0, seconds: 0 }
122
+ ```
123
+
124
+ #### `formatDateAdvanced(date: Date | string, format: string): string` 🆕
125
+
126
+ 날짜를 원하는 형식으로 포맷합니다.
127
+
128
+ ```typescript
129
+ util.formatDateAdvanced(new Date(), 'YYYY-MM-DD') // "2025-12-26"
130
+ util.formatDateAdvanced(new Date(), 'YYYY년 MM월 DD일') // "2025년 12월 26일"
131
+ util.formatDateAdvanced(new Date(), 'YYYY/MM/DD HH:mm') // "2025/12/26 14:30"
132
+ util.formatDateAdvanced(new Date(), 'dddd') // "금요일"
133
+ util.formatDateAdvanced(new Date(), 'ddd') // "금"
134
+ ```
135
+
136
+ **지원 토큰**: `YYYY`, `MM`, `DD`, `HH`, `mm`, `ss`, `ddd`, `dddd`
137
+
138
+ #### `timeAgo(date: Date | string): string` 🆕
139
+
140
+ 상대적 시간을 한국어로 표시합니다.
141
+
142
+ ```typescript
143
+ util.timeAgo(new Date(Date.now() - 30000)) // "방금 전"
144
+ util.timeAgo(new Date(Date.now() - 3600000)) // "1시간 전"
145
+ util.timeAgo(new Date(Date.now() - 86400000)) // "어제"
146
+ util.timeAgo(new Date(Date.now() - 172800000)) // "2일 전"
147
+ ```
148
+
149
+ #### `getDaysInMonth(year: number, month: number): number` 🆕
150
+
151
+ 해당 월의 일수를 반환합니다.
152
+
153
+ ```typescript
154
+ util.getDaysInMonth(2025, 2) // 28 (평년)
155
+ util.getDaysInMonth(2024, 2) // 29 (윤년)
156
+ util.getDaysInMonth(2025, 12) // 31
157
+ ```
158
+
159
+ #### `isLeapYear(year: number): boolean` 🆕
160
+
161
+ 윤년 여부를 확인합니다.
162
+
163
+ ```typescript
164
+ util.isLeapYear(2024) // true
165
+ util.isLeapYear(2025) // false
166
+ util.isLeapYear(2000) // true
167
+ util.isLeapYear(1900) // false
168
+ ```
169
+
170
+ ### 문자열 유틸리티
171
+
172
+ #### `toCamelCase(txt: string): string`
173
+
174
+ 문자열을 camelCase로 변환합니다.
175
+
176
+ ```typescript
177
+ util.toCamelCase('hello_world') // "helloWorld"
178
+ util.toCamelCase('my variable name') // "myVariableName"
179
+ ```
180
+
181
+ #### `toSnakeCase(txt: string): string`
182
+
183
+ 문자열을 snake_case로 변환합니다.
184
+
185
+ ```typescript
186
+ util.toSnakeCase('Hello World') // "hello_world"
187
+ util.toSnakeCase('camelCase') // "camelcase"
188
+ ```
189
+
190
+ #### `toTitleCase(txt: string): string`
191
+
192
+ 문자열을 Title Case로 변환합니다.
193
+
194
+ ```typescript
195
+ util.toTitleCase('hello_world') // "Hello World"
196
+ util.toTitleCase('helloWorld') // "Hello World"
197
+ ```
198
+
199
+ #### `parseTag(txt: string): string`
200
+
201
+ HTML 엔티티를 파싱합니다.
202
+
203
+ ```typescript
204
+ util.parseTag('&lt;div&gt;') // "<div>"
205
+ ```
206
+
207
+ #### `toText(txt: string): string`
208
+
209
+ HTML `<br>` 태그를 줄바꿈 문자로 변환합니다.
210
+
211
+ ```typescript
212
+ util.toText('안녕<br>하세요') // "안녕\n하세요"
213
+ ```
214
+
215
+ #### `toHtml(txt: string): string`
216
+
217
+ 줄바꿈 문자를 HTML `<br>` 태그로 변환합니다.
218
+
219
+ ```typescript
220
+ util.toHtml('안녕\n하세요') // "안녕<br>하세요"
221
+ ```
222
+
223
+ #### `clearTag(txt: string, preserveErrorTags?: boolean): string`
224
+
225
+ HTML 태그를 안전하게 제거합니다.
226
+
227
+ ```typescript
228
+ util.clearTag('<div>안녕하세요</div>') // "안녕하세요"
229
+ ```
230
+
231
+ #### `replaceBetween(str: string, txt: string, startIndex: number, endIndex: number): string`
232
+
233
+ 문자열의 특정 구간을 다른 문자열로 교체합니다.
234
+
235
+ ```typescript
236
+ util.replaceBetween('hello world', 'XXX', 6, 11) // "hello XXX"
237
+ ```
238
+
239
+ #### `extractNumbers(str: string): string`
240
+
241
+ 문자열에서 숫자만 추출합니다.
242
+
243
+ ```typescript
244
+ util.extractNumbers('010-1234-5678') // "01012345678"
245
+ util.extractNumbers('abc123def456') // "123456"
246
+ ```
247
+
248
+ #### `maskString(str: string, visibleStart?: number, visibleEnd?: number, maskChar?: string): string`
249
+
250
+ 민감한 정보를 마스킹합니다.
251
+
252
+ ```typescript
253
+ util.maskString('01012345678', 3, 4) // "010****5678"
254
+ util.maskString('1234567890123456', 4, 4) // "1234********3456"
255
+ ```
256
+
257
+ #### `getFileExtension(filename: string): string`
258
+
259
+ 파일명에서 확장자를 추출합니다.
260
+
261
+ ```typescript
262
+ util.getFileExtension('image.jpg') // "jpg"
263
+ util.getFileExtension('document.PDF') // "pdf"
264
+ ```
265
+
266
+ ### 숫자 유틸리티
267
+
268
+ #### `parseNumber(target: string, defaultValue: number, isFloat?: boolean): number`
269
+
270
+ 문자열을 숫자로 안전하게 파싱합니다.
271
+
272
+ ```typescript
273
+ util.parseNumber('123', 0) // 123
274
+ util.parseNumber('123.45', 0, true) // 123.45
275
+ util.parseNumber('abc', 0) // 0 (기본값 반환)
276
+ ```
277
+
278
+ #### `parseTime(target: string, defaultValue: number): number`
279
+
280
+ 시간 문자열을 밀리초로 변환합니다.
281
+
282
+ ```typescript
283
+ util.parseTime('01:30', 0) // 90000 (1분 30초 = 90000ms)
284
+ util.parseTime('01:30:45', 0) // 90045 (1분 30초 45ms)
285
+ ```
286
+
287
+ #### `formatNumber(num: number): string` 🆕
288
+
289
+ 천 단위 쉼표 포맷을 적용합니다.
290
+
291
+ ```typescript
292
+ util.formatNumber(1000) // "1,000"
293
+ util.formatNumber(1234567) // "1,234,567"
294
+ util.formatNumber(1234.56) // "1,234.56"
295
+ ```
296
+
297
+ #### `formatCurrency(amount: number, currency?: string): string` 🆕
298
+
299
+ 통화 포맷을 적용합니다 (기본: 원화).
300
+
301
+ ```typescript
302
+ util.formatCurrency(1000) // "1,000원"
303
+ util.formatCurrency(1234567) // "1,234,567원"
304
+ util.formatCurrency(1000, 'USD') // "$1,000"
305
+ util.formatCurrency(1000, 'EUR') // "€1,000"
306
+ util.formatCurrency(1000, 'JPY') // "¥1,000"
307
+ ```
308
+
309
+ #### `formatFileSize(bytes: number, precision?: number): string` 🆕
310
+
311
+ 파일 크기를 읽기 쉬운 형식으로 변환합니다.
312
+
313
+ ```typescript
314
+ util.formatFileSize(1024) // "1 KB"
315
+ util.formatFileSize(1048576) // "1 MB"
316
+ util.formatFileSize(1073741824) // "1 GB"
317
+ util.formatFileSize(1536, 2) // "1.50 KB"
318
+ ```
319
+
320
+ #### `numberToKorean(num: number): string` 🆕
321
+
322
+ 숫자를 한글로 변환합니다.
323
+
324
+ ```typescript
325
+ util.numberToKorean(123) // "백이십삼"
326
+ util.numberToKorean(1234) // "천이백삼십사"
327
+ util.numberToKorean(12345) // "만이천삼백사십오"
328
+ util.numberToKorean(100000000) // "일억"
329
+ util.numberToKorean(0) // "영"
330
+ ```
331
+
332
+ #### `parseNumberFromString(str: string): number` 🆕
333
+
334
+ 문자열에서 숫자를 추출합니다.
335
+
336
+ ```typescript
337
+ util.parseNumberFromString('1,234') // 1234
338
+ util.parseNumberFromString('1,234.56') // 1234.56
339
+ util.parseNumberFromString('$1,000') // 1000
340
+ util.parseNumberFromString('1,000원') // 1000
341
+ ```
342
+
343
+ ### 객체 유틸리티
344
+
345
+ #### `clone<T>(obj: T): T`
346
+
347
+ 객체를 깊은 복사합니다.
348
+
349
+ ```typescript
350
+ const original = { a: 1, b: { c: 2 } }
351
+ const copied = util.clone(original)
352
+ copied.b.c = 3
353
+ console.log(original.b.c) // 2 (원본은 변경되지 않음)
354
+ ```
355
+
356
+ #### `getByPath<T>(obj: object, path: string, defaultValue?: T): T`
357
+
358
+ 경로 문자열로 중첩된 객체의 값을 가져옵니다.
359
+
360
+ ```typescript
361
+ const obj = { user: { profile: { name: 'John' } } }
362
+ util.getByPath(obj, 'user.profile.name') // "John"
363
+ util.getByPath(obj, 'user.age', 0) // 0 (기본값)
364
+ ```
365
+
366
+ #### `setByPath(obj: object, path: string, value: any): void`
367
+
368
+ 경로 문자열로 중첩된 객체의 값을 설정합니다.
369
+
370
+ ```typescript
371
+ const obj = {}
372
+ util.setByPath(obj, 'user.profile.name', 'John')
373
+ // obj = { user: { profile: { name: 'John' } } }
374
+ ```
375
+
376
+ #### `deepMerge<T>(target: T, source: Partial<T>): T`
377
+
378
+ 두 객체를 깊게 병합합니다.
379
+
380
+ ```typescript
381
+ const target = { a: 1, b: { c: 2, d: 3 } }
382
+ const source = { b: { d: 4, e: 5 }, f: 6 }
383
+ util.deepMerge(target, source)
384
+ // { a: 1, b: { c: 2, d: 4, e: 5 }, f: 6 }
385
+ ```
386
+
387
+ #### `objectToQueryString(obj: object): string`
388
+
389
+ 객체를 URL 쿼리 스트링으로 변환합니다.
390
+
391
+ ```typescript
392
+ util.objectToQueryString({ name: 'John', age: 30 })
393
+ // "name=John&age=30"
394
+ ```
395
+
396
+ ### 배열 유틸리티
397
+
398
+ #### `equalArrays(a: unknown[], b: unknown[]): boolean`
399
+
400
+ 두 배열이 같은지 비교합니다 (얕은 비교).
401
+
402
+ ```typescript
403
+ util.equalArrays([1, 2, 3], [1, 2, 3]) // true
404
+ util.equalArrays([1, 2, 3], [1, 2, 4]) // false
405
+ ```
406
+
407
+ #### `groupBy<T>(array: T[], key: keyof T): Record<string, T[]>`
408
+
409
+ 배열을 특정 키 기준으로 그룹화합니다.
410
+
411
+ ```typescript
412
+ const items = [
413
+ { name: 'Apple', category: 'fruit' },
414
+ { name: 'Carrot', category: 'vegetable' },
415
+ { name: 'Banana', category: 'fruit' }
416
+ ]
417
+ util.groupBy(items, 'category')
418
+ // {
419
+ // fruit: [{ name: 'Apple', ... }, { name: 'Banana', ... }],
420
+ // vegetable: [{ name: 'Carrot', ... }]
421
+ // }
422
+ ```
423
+
424
+ #### `sortBy<T>(array: T[], key: keyof T, order?: 'asc' | 'desc'): T[]`
425
+
426
+ 배열을 특정 키 기준으로 정렬합니다.
427
+
428
+ ```typescript
429
+ const items = [
430
+ { name: 'Charlie', age: 30 },
431
+ { name: 'Alice', age: 25 }
432
+ ]
433
+ util.sortBy(items, 'age', 'asc')
434
+ // [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 30 }]
435
+ ```
436
+
437
+ ### 비동기 유틸리티
438
+
439
+ #### `retry<T>(fn: () => Promise<T>, maxRetries?: number, delay?: number): Promise<T>`
440
+
441
+ 실패한 비동기 함수를 지수 백오프로 재시도합니다.
442
+
443
+ ```typescript
444
+ const fetchData = async () => {
445
+ const response = await fetch('https://api.example.com/data')
446
+ return response.json()
447
+ }
448
+
449
+ // 최대 3번 재시도, 초기 지연 1초
450
+ await util.retry(fetchData, 3, 1000)
451
+ ```
452
+
453
+ ### 브라우저 유틸리티
454
+
455
+ #### `isiOS(): boolean`
456
+
457
+ 현재 디바이스가 iOS인지 확인합니다.
458
+
459
+ ```typescript
460
+ if (util.isiOS()) {
461
+ console.log('iOS 디바이스입니다')
462
+ }
463
+ ```
464
+
465
+ ### 검증 유틸리티 🆕
466
+
467
+ #### `isEmail(email: string): boolean`
468
+
469
+ 이메일 주소를 검증합니다.
470
+
471
+ ```typescript
472
+ util.isEmail('test@example.com') // true
473
+ util.isEmail('invalid-email') // false
474
+ ```
475
+
476
+ #### `isPhoneNumber(phone: string): boolean`
477
+
478
+ 한국 전화번호를 검증합니다.
479
+
480
+ ```typescript
481
+ util.isPhoneNumber('010-1234-5678') // true
482
+ util.isPhoneNumber('01012345678') // true
483
+ util.isPhoneNumber('02-123-4567') // true (지역번호)
484
+ util.isPhoneNumber('1234-5678') // false
485
+ ```
486
+
487
+ #### `isUrl(url: string): boolean`
488
+
489
+ URL을 검증합니다.
490
+
491
+ ```typescript
492
+ util.isUrl('https://example.com') // true
493
+ util.isUrl('http://localhost:3000') // true
494
+ util.isUrl('ftp://files.example.com') // true
495
+ util.isUrl('not-a-url') // false
496
+ ```
497
+
498
+ #### `formatPhoneNumber(phone: string): string`
499
+
500
+ 전화번호를 하이픈으로 포맷합니다.
501
+
502
+ ```typescript
503
+ util.formatPhoneNumber('01012345678') // "010-1234-5678"
504
+ util.formatPhoneNumber('0212345678') // "02-1234-5678"
505
+ util.formatPhoneNumber('03112345678') // "031-1234-5678"
506
+ ```
507
+
508
+ #### `formatBusinessNumber(brn: string): string`
509
+
510
+ 사업자등록번호를 포맷합니다.
511
+
512
+ ```typescript
513
+ util.formatBusinessNumber('1234567890') // "123-45-67890"
514
+ ```
515
+
516
+ ## 타입스크립트 지원
517
+
518
+ 이 라이브러리는 TypeScript로 작성되었으며 완전한 타입 정의를 제공합니다.
519
+
520
+ ```typescript
521
+ import util from 'jell-utils'
522
+
523
+ // 타입 추론이 자동으로 작동합니다
524
+ const result = util.parseNumber('123', 0) // number
525
+ const date = util.getKoreanDate() // string
526
+ ```
527
+
13
528
  ## Change log
14
529
 
530
+ ### v0.2.0 (2025-12-26)
531
+
532
+ - 🆕 **날짜/시간 유틸리티 추가**
533
+ - `formatDateAdvanced`: 커스텀 포맷 토큰 지원
534
+ - `timeAgo`: 한국어 상대 시간 표시
535
+ - `getDaysInMonth`: 월별 일수 반환
536
+ - `isLeapYear`: 윤년 확인
537
+ - 🆕 **숫자 포맷 유틸리티 추가**
538
+ - `formatNumber`: 천 단위 쉼표
539
+ - `formatCurrency`: 다국어 통화 포맷 (KRW, USD, EUR, JPY, CNY)
540
+ - `formatFileSize`: 파일 크기 포맷
541
+ - `numberToKorean`: 숫자를 한글로 변환
542
+ - `parseNumberFromString`: 문자열에서 숫자 추출
543
+ - 🆕 **검증 유틸리티 추가**
544
+ - `isEmail`: 이메일 검증
545
+ - `isPhoneNumber`: 한국 전화번호 검증
546
+ - `isUrl`: URL 검증
547
+ - `formatPhoneNumber`: 전화번호 포맷
548
+ - `formatBusinessNumber`: 사업자등록번호 포맷
549
+ - 📈 테스트 커버리지 개선 (79.67% → 83.16%)
550
+ - 📝 114개 테스트 케이스
551
+
552
+ ### v0.1.0 (2024)
553
+
554
+ - 🎉 초기 릴리즈
555
+ - ✨ 한국어 유틸리티 (isKorean, isBusinessNumber, chosungSearch)
556
+ - ✨ 날짜 유틸리티 (getNowDate, getKoreanDate, formatDate, calDDay, dateDiff)
557
+ - ✨ 문자열 유틸리티 (toCamelCase, toSnakeCase, toTitleCase, parseTag, toText, toHtml, clearTag, replaceBetween, extractNumbers, maskString, getFileExtension)
558
+ - ✨ 숫자 유틸리티 (parseNumber, parseTime)
559
+ - ✨ 객체 유틸리티 (clone, getByPath, setByPath, deepMerge, objectToQueryString)
560
+ - ✨ 배열 유틸리티 (equalArrays, groupBy, sortBy)
561
+ - ✨ 비동기 유틸리티 (retry)
562
+ - ✨ 브라우저 유틸리티 (isiOS)
563
+ - ✅ Jest를 사용한 테스트 커버리지 추가
564
+ - 📝 TypeScript 타입 정의 포함
565
+
15
566
  ## License
16
567
 
17
568
  [MIT](LICENSE)