@simplysm/excel 13.0.99 → 14.0.1

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 (95) hide show
  1. package/dist/excel-cell.d.ts +28 -28
  2. package/dist/excel-cell.d.ts.map +1 -1
  3. package/dist/excel-cell.js +273 -264
  4. package/dist/excel-cell.js.map +1 -6
  5. package/dist/excel-col.d.ts +4 -4
  6. package/dist/excel-col.d.ts.map +1 -1
  7. package/dist/excel-col.js +33 -35
  8. package/dist/excel-col.js.map +1 -6
  9. package/dist/excel-row.d.ts +3 -3
  10. package/dist/excel-row.d.ts.map +1 -1
  11. package/dist/excel-row.js +28 -30
  12. package/dist/excel-row.js.map +1 -6
  13. package/dist/excel-workbook.d.ts +23 -23
  14. package/dist/excel-workbook.d.ts.map +1 -1
  15. package/dist/excel-workbook.js +151 -125
  16. package/dist/excel-workbook.js.map +1 -6
  17. package/dist/excel-worksheet.d.ts +32 -32
  18. package/dist/excel-worksheet.d.ts.map +1 -1
  19. package/dist/excel-worksheet.js +281 -253
  20. package/dist/excel-worksheet.js.map +1 -6
  21. package/dist/excel-wrapper.d.ts +7 -7
  22. package/dist/excel-wrapper.d.ts.map +1 -1
  23. package/dist/excel-wrapper.js +190 -226
  24. package/dist/excel-wrapper.js.map +1 -6
  25. package/dist/index.js +4 -1
  26. package/dist/index.js.map +1 -6
  27. package/dist/types.d.ts +13 -13
  28. package/dist/types.d.ts.map +1 -1
  29. package/dist/types.js +3 -1
  30. package/dist/types.js.map +1 -6
  31. package/dist/utils/excel-utils.d.ts +23 -23
  32. package/dist/utils/excel-utils.d.ts.map +1 -1
  33. package/dist/utils/excel-utils.js +183 -151
  34. package/dist/utils/excel-utils.js.map +1 -6
  35. package/dist/utils/zip-cache.d.ts +6 -6
  36. package/dist/utils/zip-cache.js +78 -60
  37. package/dist/utils/zip-cache.js.map +1 -6
  38. package/dist/xml/excel-xml-content-type.d.ts +2 -2
  39. package/dist/xml/excel-xml-content-type.js +55 -53
  40. package/dist/xml/excel-xml-content-type.js.map +1 -6
  41. package/dist/xml/excel-xml-drawing.d.ts +2 -2
  42. package/dist/xml/excel-xml-drawing.js +74 -73
  43. package/dist/xml/excel-xml-drawing.js.map +1 -6
  44. package/dist/xml/excel-xml-relationship.d.ts +2 -2
  45. package/dist/xml/excel-xml-relationship.js +67 -67
  46. package/dist/xml/excel-xml-relationship.js.map +1 -6
  47. package/dist/xml/excel-xml-shared-string.d.ts +2 -2
  48. package/dist/xml/excel-xml-shared-string.js +57 -55
  49. package/dist/xml/excel-xml-shared-string.js.map +1 -6
  50. package/dist/xml/excel-xml-style.d.ts +2 -2
  51. package/dist/xml/excel-xml-style.js +311 -295
  52. package/dist/xml/excel-xml-style.js.map +1 -6
  53. package/dist/xml/excel-xml-unknown.d.ts +2 -2
  54. package/dist/xml/excel-xml-unknown.js +11 -10
  55. package/dist/xml/excel-xml-unknown.js.map +1 -6
  56. package/dist/xml/excel-xml-workbook.d.ts +2 -2
  57. package/dist/xml/excel-xml-workbook.js +87 -90
  58. package/dist/xml/excel-xml-workbook.js.map +1 -6
  59. package/dist/xml/excel-xml-worksheet.d.ts +6 -6
  60. package/dist/xml/excel-xml-worksheet.js +450 -393
  61. package/dist/xml/excel-xml-worksheet.js.map +1 -6
  62. package/package.json +5 -7
  63. package/src/excel-cell.ts +36 -36
  64. package/src/excel-col.ts +4 -4
  65. package/src/excel-row.ts +3 -3
  66. package/src/excel-workbook.ts +38 -38
  67. package/src/excel-worksheet.ts +69 -51
  68. package/src/excel-wrapper.ts +55 -50
  69. package/src/index.ts +3 -3
  70. package/src/types.ts +17 -17
  71. package/src/utils/excel-utils.ts +47 -41
  72. package/src/utils/zip-cache.ts +6 -6
  73. package/src/xml/excel-xml-content-type.ts +3 -3
  74. package/src/xml/excel-xml-drawing.ts +2 -2
  75. package/src/xml/excel-xml-relationship.ts +3 -3
  76. package/src/xml/excel-xml-shared-string.ts +2 -2
  77. package/src/xml/excel-xml-style.ts +11 -11
  78. package/src/xml/excel-xml-unknown.ts +2 -2
  79. package/src/xml/excel-xml-workbook.ts +5 -5
  80. package/src/xml/excel-xml-worksheet.ts +43 -43
  81. package/README.md +0 -119
  82. package/docs/core-classes.md +0 -541
  83. package/docs/types.md +0 -297
  84. package/docs/utilities.md +0 -128
  85. package/docs/wrapper.md +0 -100
  86. package/tests/excel-cell.spec.ts +0 -393
  87. package/tests/excel-col.spec.ts +0 -81
  88. package/tests/excel-row.spec.ts +0 -61
  89. package/tests/excel-workbook.spec.ts +0 -205
  90. package/tests/excel-worksheet.spec.ts +0 -469
  91. package/tests/excel-wrapper.spec.ts +0 -273
  92. package/tests/fixtures/logo.png +0 -0
  93. package/tests/fixtures//354/264/210/352/270/260/355/231/224.xlsx +0 -0
  94. package/tests/image-insert.spec.ts +0 -190
  95. package/tests/utils/excel-utils.spec.ts +0 -198
@@ -14,8 +14,8 @@ import type { ExcelXmlWorkbook } from "./xml/excel-xml-workbook";
14
14
  import type { ExcelXmlWorksheet } from "./xml/excel-xml-worksheet";
15
15
 
16
16
  /**
17
- * Class representing an Excel worksheet.
18
- * Provides cell access, row/column copying, data table processing, and image insertion.
17
+ * Excel 워크시트를 나타내는 클래스.
18
+ * 접근, 행/열 복사, 데이터 테이블 처리, 이미지 삽입 기능을 제공한다.
19
19
  */
20
20
  export class ExcelWorksheet {
21
21
  private readonly _rowMap = new Map<number, ExcelRow>();
@@ -29,17 +29,17 @@ export class ExcelWorksheet {
29
29
 
30
30
  //#region Name Methods
31
31
 
32
- /** Return worksheet name */
32
+ /** 워크시트 이름 반환 */
33
33
  async getName(): Promise<string> {
34
34
  const wbXmlData = await this._getWbData();
35
35
  const name = wbXmlData.getWorksheetNameById(this._relId);
36
36
  if (name == null) {
37
- throw new Error(`Cannot find name for worksheet ID ${this._relId}`);
37
+ throw new Error(`워크시트 ID ${this._relId}의 이름을 찾을 수 없습니다`);
38
38
  }
39
39
  return name;
40
40
  }
41
41
 
42
- /** Rename worksheet */
42
+ /** 워크시트 이름 변경 */
43
43
  async setName(newName: string): Promise<void> {
44
44
  const wbXmlData = await this._getWbData();
45
45
  wbXmlData.setWorksheetNameById(this._relId, newName);
@@ -49,17 +49,17 @@ export class ExcelWorksheet {
49
49
 
50
50
  //#region Cell Access Methods
51
51
 
52
- /** Return row object (0-based) */
52
+ /** 객체 반환 (0 기반) */
53
53
  row(r: number): ExcelRow {
54
54
  return this._rowMap.getOrCreate(r, new ExcelRow(this._zipCache, this._targetFileName, r));
55
55
  }
56
56
 
57
- /** Return cell object (0-based row/column) */
57
+ /** 객체 반환 (0 기반 행/열) */
58
58
  cell(r: number, c: number): ExcelCell {
59
59
  return this.row(r).cell(c);
60
60
  }
61
61
 
62
- /** Return column object (0-based) */
62
+ /** 객체 반환 (0 기반) */
63
63
  col(c: number): ExcelCol {
64
64
  return this._colMap.getOrCreate(c, new ExcelCol(this._zipCache, this._targetFileName, c));
65
65
  }
@@ -68,7 +68,7 @@ export class ExcelWorksheet {
68
68
 
69
69
  //#region Copy Methods
70
70
 
71
- /** Copy style from source row to target row */
71
+ /** 원본 행에서 대상 행으로 스타일 복사 */
72
72
  async copyRowStyle(srcR: number, targetR: number): Promise<void> {
73
73
  const range = await this.getRange();
74
74
 
@@ -77,7 +77,7 @@ export class ExcelWorksheet {
77
77
  }
78
78
  }
79
79
 
80
- /** Copy style from source cell to target cell */
80
+ /** 원본 셀에서 대상 셀로 스타일 복사 */
81
81
  async copyCellStyle(srcAddr: ExcelAddressPoint, targetAddr: ExcelAddressPoint): Promise<void> {
82
82
  const wsData = await this._getWsData();
83
83
 
@@ -87,55 +87,68 @@ export class ExcelWorksheet {
87
87
  }
88
88
  }
89
89
 
90
- /** Copy source row to target row (overwrite) */
90
+ /** 원본 행을 대상 행으로 복사 (덮어쓰기) */
91
91
  async copyRow(srcR: number, targetR: number): Promise<void> {
92
92
  const wsData = await this._getWsData();
93
93
  wsData.copyRow(srcR, targetR);
94
94
  }
95
95
 
96
- /** Copy source cell to target cell */
96
+ /** 원본 셀을 대상 셀로 복사 */
97
97
  async copyCell(srcAddr: ExcelAddressPoint, targetAddr: ExcelAddressPoint): Promise<void> {
98
98
  const wsData = await this._getWsData();
99
99
  wsData.copyCell(srcAddr, targetAddr);
100
100
  }
101
101
 
102
102
  /**
103
- * Insert-copy the source row at the target position.
104
- * Existing rows at and below the target are shifted down by one.
105
- * @param srcR Source row index to copy (0-based)
106
- * @param targetR Target row index to insert at (0-based)
103
+ * 원본 행을 대상 위치에 삽입 복사한다.
104
+ * 대상 위치 이하의 기존 행은 아래로 밀린다.
105
+ * @param srcR 복사할 원본 인덱스 (0 기반)
106
+ * @param targetR 삽입할 대상 인덱스 (0 기반)
107
107
  */
108
108
  async insertCopyRow(srcR: number, targetR: number): Promise<void> {
109
109
  const wsData = await this._getWsData();
110
110
  const range = wsData.range;
111
111
 
112
- // Shift merge cells at or below targetR down by 1
112
+ // targetR 이하의 병합 셀을 1칸 아래로 이동
113
+ // 삽입 지점을 관통하는 다중행 병합은 자동으로 1행 확장됨
113
114
  wsData.shiftMergeCells(targetR, 1);
114
115
 
115
- // When srcR >= targetR, adjust for the shifted position of srcR
116
+ // srcR >= targetR 경우, srcR의 이동된 위치를 보정
116
117
  const adjustedSrcR = srcR >= targetR ? srcR + 1 : srcR;
117
118
 
118
- // Shift existing rows down (from bottom to top to avoid overwriting)
119
- // Use skipMerge: true because merge cells are already shifted above
119
+ // 기존 행을 아래로 이동 (덮어쓰기 방지를 위해 아래에서 위로)
120
+ // 병합 셀은 위에서 이미 이동했으므로 skipMerge: true 사용
120
121
  for (let r = range.e.r; r >= targetR; r--) {
121
122
  wsData.copyRow(r, r + 1, { skipMerge: true });
122
123
  }
123
124
 
124
- // Copy source row to target position (includes merge handling)
125
- wsData.copyRow(adjustedSrcR, targetR);
125
+ // 원본 행을 대상 위치에 복사 (병합은 skipMerge로 건너뜀)
126
+ wsData.copyRow(adjustedSrcR, targetR, { skipMerge: true });
127
+
128
+ // 원본 행의 단일행 병합만 대상 행에 복사
129
+ // (다중행 병합은 shiftMergeCells에서 이미 확장 처리됨)
130
+ const allMergeCells = wsData.getMergeCells();
131
+ const sourceMergeCells = allMergeCells.filter(
132
+ (mc) => mc.s.r === adjustedSrcR && mc.e.r === adjustedSrcR,
133
+ );
134
+ for (const mergeCell of sourceMergeCells) {
135
+ const newStartAddr = { r: targetR, c: mergeCell.s.c };
136
+ const newEndAddr = { r: targetR, c: mergeCell.e.c };
137
+ wsData.setMergeCells(newStartAddr, newEndAddr);
138
+ }
126
139
  }
127
140
 
128
141
  //#endregion
129
142
 
130
143
  //#region Range Methods
131
144
 
132
- /** Return data range of the worksheet */
145
+ /** 워크시트의 데이터 범위 반환 */
133
146
  async getRange(): Promise<ExcelAddressRangePoint> {
134
147
  const xml = await this._getWsData();
135
148
  return xml.range;
136
149
  }
137
150
 
138
- /** Return all cells as a 2D array */
151
+ /** 모든 셀을 2차원 배열로 반환 */
139
152
  async getCells(): Promise<ExcelCell[][]> {
140
153
  const xml = await this._getWsData();
141
154
  const range = xml.range;
@@ -153,10 +166,10 @@ export class ExcelWorksheet {
153
166
  //#region Data Methods
154
167
 
155
168
  /**
156
- * Return worksheet data as a table (record array).
157
- * @param opt.headerRowIndex Header row index (default: first row)
158
- * @param opt.checkEndColIndex Column index to determine data end. Data ends when this column is empty.
159
- * @param opt.usableHeaderNameFn Function to filter usable headers
169
+ * 워크시트 데이터를 테이블(레코드 배열)로 반환한다.
170
+ * @param opt.headerRowIndex 헤더 인덱스 (기본값: 번째 행)
171
+ * @param opt.checkEndColIndex 데이터 끝을 판단할 인덱스. 열이 비어있으면 데이터가 끝난 것으로 판단한다.
172
+ * @param opt.usableHeaderNameFn 사용 가능한 헤더를 필터링하는 함수
160
173
  */
161
174
  async getDataTable(opt?: {
162
175
  headerRowIndex?: number;
@@ -174,6 +187,11 @@ export class ExcelWorksheet {
174
187
  const headerName = await this.cell(startRow, c).getValue();
175
188
  if (typeof headerName === "string") {
176
189
  if (opt?.usableHeaderNameFn == null || opt.usableHeaderNameFn(headerName)) {
190
+ if (headerMap.has(headerName)) {
191
+ throw new Error(
192
+ `중복된 헤더: "${headerName}" (열 ${headerMap.get(headerName)!}과 열 ${c})`,
193
+ );
194
+ }
177
195
  headerMap.set(headerName, c);
178
196
  }
179
197
  }
@@ -200,8 +218,8 @@ export class ExcelWorksheet {
200
218
  }
201
219
 
202
220
  /**
203
- * Write 2D array data to the worksheet
204
- * @param matrix 2D array data (row-major, index 0 is the first row)
221
+ * 2차원 배열 데이터를 워크시트에 쓰기
222
+ * @param matrix 2차원 배열 데이터 ( 우선, 인덱스 0 번째 )
205
223
  */
206
224
  async setDataMatrix(matrix: ExcelValueType[][]): Promise<void> {
207
225
  for (let r = 0; r < matrix.length; r++) {
@@ -212,8 +230,8 @@ export class ExcelWorksheet {
212
230
  }
213
231
 
214
232
  /**
215
- * Write record array to the worksheet
216
- * @param records Record array. Headers are auto-generated in the first row, data follows in subsequent rows.
233
+ * 레코드 배열을 워크시트에 쓰기
234
+ * @param records 레코드 배열. 번째 행에 헤더가 자동 생성되고, 이후 행에 데이터가 기록된다.
217
235
  */
218
236
  async setRecords(records: Record<string, ExcelValueType>[]): Promise<void> {
219
237
  const headers = records
@@ -236,7 +254,7 @@ export class ExcelWorksheet {
236
254
 
237
255
  //#region View Methods
238
256
 
239
- /** Set worksheet zoom scale (percent) */
257
+ /** 워크시트 확대/축소 비율 설정 (퍼센트) */
240
258
  async setZoom(percent: number): Promise<void> {
241
259
  const wbXml = await this._getWbData();
242
260
  wbXml.initializeView();
@@ -245,7 +263,7 @@ export class ExcelWorksheet {
245
263
  wsXml.setZoom(percent);
246
264
  }
247
265
 
248
- /** Set freeze panes for rows/columns */
266
+ /** 행/열 고정 설정 */
249
267
  async freezeAt(point: { r?: number; c?: number }): Promise<void> {
250
268
  const wbXml = await this._getWbData();
251
269
  wbXml.initializeView();
@@ -259,11 +277,11 @@ export class ExcelWorksheet {
259
277
  //#region Image Methods
260
278
 
261
279
  /**
262
- * Insert an image into the worksheet.
263
- * @param opts.bytes Image binary data
264
- * @param opts.ext Image extension (png, jpg, etc.)
265
- * @param opts.from Image start position (0-based row/column index, rOff/cOff in EMU offset)
266
- * @param opts.to Image end position (if omitted, inserted at from position with original size)
280
+ * 워크시트에 이미지를 삽입한다.
281
+ * @param opts.bytes 이미지 바이너리 데이터
282
+ * @param opts.ext 이미지 확장자 (png, jpg )
283
+ * @param opts.from 이미지 시작 위치 (0 기반 행/열 인덱스, rOff/cOff EMU 오프셋)
284
+ * @param opts.to 이미지 위치 (생략 from 위치에 원본 크기로 삽입)
267
285
  */
268
286
  async addImage(opts: {
269
287
  bytes: Bytes;
@@ -273,10 +291,10 @@ export class ExcelWorksheet {
273
291
  }): Promise<void> {
274
292
  const mimeType = mime.getType(opts.ext);
275
293
  if (mimeType == null) {
276
- throw new Error(`Cannot determine MIME type for extension '${opts.ext}'`);
294
+ throw new Error(`확장자 '${opts.ext}'에 대한 MIME 타입을 결정할 수 없습니다`);
277
295
  }
278
296
 
279
- // 1. Determine media filename and save
297
+ // 1. 미디어 파일명 결정 저장
280
298
  let mediaIndex = 1;
281
299
  while ((await this._zipCache.get(`xl/media/image${mediaIndex}.${opts.ext}`)) !== undefined) {
282
300
  mediaIndex++;
@@ -284,16 +302,16 @@ export class ExcelWorksheet {
284
302
  const mediaPath = `xl/media/image${mediaIndex}.${opts.ext}`;
285
303
  this._zipCache.set(mediaPath, opts.bytes);
286
304
 
287
- // 2. Update [Content_Types].xml
305
+ // 2. [Content_Types].xml 갱신
288
306
  const typeXml = (await this._zipCache.get("[Content_Types].xml")) as ExcelXmlContentType;
289
307
  typeXml.add(`/xl/media/image${mediaIndex}.${opts.ext}`, mimeType);
290
308
 
291
- // 3. Check for existing drawing in worksheet
309
+ // 3. 워크시트의 기존 drawing 확인
292
310
  const wsXml = await this._getWsData();
293
311
  const sheetRelsPath = `xl/worksheets/_rels/${this._targetFileName}.rels`;
294
312
  let sheetRels = (await this._zipCache.get(sheetRelsPath)) as ExcelXmlRelationship | undefined;
295
313
 
296
- // Find existing drawing
314
+ // 기존 drawing 찾기
297
315
  let drawingIndex: number | undefined;
298
316
  let drawingPath: string | undefined;
299
317
  let drawing: ExcelXmlDrawing | undefined;
@@ -306,7 +324,7 @@ export class ExcelWorksheet {
306
324
  "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
307
325
  );
308
326
  if (existingDrawingRel != null) {
309
- // Extract index from existing drawing path
327
+ // 기존 drawing 경로에서 인덱스 추출
310
328
  const match = existingDrawingRel.$.Target.match(/drawing(\d+)\.xml$/);
311
329
  if (match != null) {
312
330
  drawingIndex = parseInt(match[1], 10);
@@ -319,7 +337,7 @@ export class ExcelWorksheet {
319
337
  }
320
338
  }
321
339
 
322
- // 4. Create new drawing if none exists
340
+ // 4. 기존 drawing 없으면 새로 생성
323
341
  if (drawingIndex == null || drawingPath == null || drawing == null) {
324
342
  drawingIndex = 1;
325
343
  while ((await this._zipCache.get(`xl/drawings/drawing${drawingIndex}.xml`)) !== undefined) {
@@ -328,10 +346,10 @@ export class ExcelWorksheet {
328
346
  drawingPath = `xl/drawings/drawing${drawingIndex}.xml`;
329
347
  drawing = new ExcelXmlDrawing();
330
348
 
331
- // Add drawing type to [Content_Types].xml
349
+ // [Content_Types].xml에 drawing 타입 추가
332
350
  typeXml.add("/" + drawingPath, "application/vnd.openxmlformats-officedocument.drawing+xml");
333
351
 
334
- // Add drawing to worksheet rels
352
+ // 워크시트 rels에 drawing 추가
335
353
  sheetRels = sheetRels ?? new ExcelXmlRelationship();
336
354
  const sheetRelNum = sheetRels.addAndGetId(
337
355
  `../drawings/drawing${drawingIndex}.xml`,
@@ -340,7 +358,7 @@ export class ExcelWorksheet {
340
358
  const drawingRelIdOnWorksheet = `rId${sheetRelNum}`;
341
359
  this._zipCache.set(sheetRelsPath, sheetRels);
342
360
 
343
- // Add drawing to worksheet XML
361
+ // 워크시트 XML에 drawing 추가
344
362
  wsXml.data.worksheet.$["xmlns:r"] =
345
363
  wsXml.data.worksheet.$["xmlns:r"] ??
346
364
  "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
@@ -349,7 +367,7 @@ export class ExcelWorksheet {
349
367
  this._zipCache.set(`xl/worksheets/${this._targetFileName}`, wsXml);
350
368
  }
351
369
 
352
- // 5. Prepare drawing rels (create if not exists)
370
+ // 5. drawing rels 준비 (없으면 생성)
353
371
  drawingRels = drawingRels ?? new ExcelXmlRelationship();
354
372
  const mediaFileName = mediaPath.slice(3);
355
373
  const drawingTarget = `../${mediaFileName}`;
@@ -359,7 +377,7 @@ export class ExcelWorksheet {
359
377
  );
360
378
  this._zipCache.set(`xl/drawings/_rels/drawing${drawingIndex}.xml.rels`, drawingRels);
361
379
 
362
- // 6. Add image to drawing
380
+ // 6. drawing에 이미지 추가
363
381
  const blipRelId = `rId${relNum}`;
364
382
  drawing.addPicture({
365
383
  from: opts.from,
@@ -13,18 +13,18 @@ import { ExcelWorkbook } from "./excel-workbook";
13
13
  import type { ExcelValueType } from "./types";
14
14
 
15
15
  /**
16
- * Zod schema-based Excel wrapper
16
+ * Zod 스키마 기반 Excel 래퍼
17
17
  *
18
- * Infers type information from schema to provide type-safe read/write
18
+ * 스키마에서 타입 정보를 추론하여 타입 안전한 읽기/쓰기를 제공한다
19
19
  */
20
20
  export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
21
21
  /**
22
- * @param _schema Zod schema (defines record structure, use `.describe()` to specify Excel header names)
22
+ * @param _schema Zod 스키마 (레코드 구조를 정의하며, `.describe()`로 Excel 헤더 이름을 지정)
23
23
  */
24
24
  constructor(private readonly _schema: TSchema) {}
25
25
 
26
26
  /**
27
- * Read Excel file into record array
27
+ * Excel 파일을 레코드 배열로 읽기
28
28
  */
29
29
  async read(
30
30
  file: Bytes | Blob,
@@ -46,7 +46,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
46
46
 
47
47
  if (rawData.length === 0) {
48
48
  throw new Error(
49
- `[${wsName}] No data found in Excel file. (Expected headers: ${displayNames.join(", ")})`,
49
+ `[${wsName}] Excel 파일에서 데이터를 찾을 없습니다. (기대하는 헤더: ${displayNames.join(", ")})`,
50
50
  );
51
51
  }
52
52
 
@@ -73,13 +73,13 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
73
73
  continue;
74
74
  }
75
75
 
76
- // Validate with Zod schema
76
+ // Zod 스키마로 유효성 검사
77
77
  const parseResult = this._schema.safeParse(record);
78
78
  if (!parseResult.success) {
79
79
  const errors = parseResult.error.issues
80
80
  .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
81
81
  .join(", ");
82
- throw new Error(`[${wsName}] Data validation failed: ${errors}`);
82
+ throw new Error(`[${wsName}] 데이터 유효성 검사 실패: ${errors}`);
83
83
  }
84
84
 
85
85
  result.push(parseResult.data);
@@ -89,11 +89,11 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
89
89
  }
90
90
 
91
91
  /**
92
- * Record array to Excel workbook
92
+ * 레코드 배열을 Excel 워크북으로 변환
93
93
  *
94
94
  * @remarks
95
- * The caller is responsible for managing the returned workbook's resources.
96
- * Use `await using` or call `close()` after use.
95
+ * 반환된 워크북의 리소스 관리는 호출자의 책임이다.
96
+ * `await using`을 사용하거나 사용 `close()`를 호출해야 한다.
97
97
  *
98
98
  * @example
99
99
  * ```typescript
@@ -107,53 +107,58 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
107
107
  options?: { excludes?: (keyof z.infer<TSchema>)[] },
108
108
  ): Promise<ExcelWorkbook> {
109
109
  const wb = new ExcelWorkbook();
110
- const ws = await wb.addWorksheet(wsName);
110
+ try {
111
+ const ws = await wb.addWorksheet(wsName);
111
112
 
112
- const displayNameMap = this._getDisplayNameMap(options?.excludes as string[] | undefined);
113
- const keys = Object.keys(displayNameMap) as (keyof z.infer<TSchema>)[];
114
- const headers = keys.map((key) => displayNameMap[key as string]);
113
+ const displayNameMap = this._getDisplayNameMap(options?.excludes as string[] | undefined);
114
+ const keys = Object.keys(displayNameMap) as (keyof z.infer<TSchema>)[];
115
+ const headers = keys.map((key) => displayNameMap[key as string]);
115
116
 
116
- // Write header row
117
- for (let c = 0; c < headers.length; c++) {
118
- await ws.cell(0, c).setValue(headers[c]);
119
- }
117
+ // 헤더 쓰기
118
+ for (let c = 0; c < headers.length; c++) {
119
+ await ws.cell(0, c).setValue(headers[c]);
120
+ }
120
121
 
121
- // Write data rows
122
- for (let r = 0; r < records.length; r++) {
123
- for (let c = 0; c < keys.length; c++) {
124
- const key = keys[c];
125
- const value = records[r][key] as ExcelValueType;
126
- await ws.cell(r + 1, c).setValue(value);
122
+ // 데이터 쓰기
123
+ for (let r = 0; r < records.length; r++) {
124
+ for (let c = 0; c < keys.length; c++) {
125
+ const key = keys[c];
126
+ const value = records[r][key] as ExcelValueType;
127
+ await ws.cell(r + 1, c).setValue(value);
128
+ }
127
129
  }
128
- }
129
130
 
130
- // Apply border style
131
- for (let r = 0; r < records.length + 1; r++) {
132
- for (let c = 0; c < keys.length; c++) {
133
- await ws.cell(r, c).setStyle({
134
- border: ["left", "right", "top", "bottom"],
135
- });
131
+ // 테두리 스타일 적용
132
+ for (let r = 0; r < records.length + 1; r++) {
133
+ for (let c = 0; c < keys.length; c++) {
134
+ await ws.cell(r, c).setStyle({
135
+ border: ["left", "right", "top", "bottom"],
136
+ });
137
+ }
136
138
  }
137
- }
138
139
 
139
- // Highlight required field headers (yellow)
140
- const shape = this._schema.shape;
141
- for (let c = 0; c < keys.length; c++) {
142
- const fieldKey = keys[c] as string;
143
- const fieldSchema = shape[fieldKey] as z.ZodType;
144
-
145
- if (this._isRequired(fieldSchema) && !this._isBoolean(fieldSchema)) {
146
- await ws.cell(0, c).setStyle({
147
- background: "00FFFF00",
148
- });
140
+ // 필수 필드 헤더 강조 (노란색)
141
+ const shape = this._schema.shape;
142
+ for (let c = 0; c < keys.length; c++) {
143
+ const fieldKey = keys[c] as string;
144
+ const fieldSchema = shape[fieldKey] as z.ZodType;
145
+
146
+ if (this._isRequired(fieldSchema) && !this._isBoolean(fieldSchema)) {
147
+ await ws.cell(0, c).setStyle({
148
+ background: "00FFFF00",
149
+ });
150
+ }
149
151
  }
150
- }
151
152
 
152
- // View settings
153
- await ws.setZoom(85);
154
- await ws.freezeAt({ r: 0 });
153
+ // 보기 설정
154
+ await ws.setZoom(85);
155
+ await ws.freezeAt({ r: 0 });
155
156
 
156
- return wb;
157
+ return wb;
158
+ } catch (e) {
159
+ await wb.close();
160
+ throw e;
161
+ }
157
162
  }
158
163
 
159
164
  //#region Private Methods
@@ -198,7 +203,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
198
203
  return Boolean(rawValue);
199
204
  }
200
205
 
201
- // DateOnly, DateTime, Time are handled via instanceof
206
+ // DateOnly, DateTime, Time instanceof로 처리
202
207
  if (rawValue instanceof DateOnly || rawValue instanceof DateTime || rawValue instanceof Time) {
203
208
  return rawValue;
204
209
  }
@@ -218,7 +223,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
218
223
 
219
224
  private _getDefaultForSchema(schema: z.ZodType): unknown {
220
225
  if (schema instanceof ZodDefault) {
221
- // ZodDefault.parse(undefined) returns the default value
226
+ // ZodDefault.parse(undefined) 기본값을 반환한다
222
227
  return schema.parse(undefined);
223
228
  }
224
229
 
@@ -226,7 +231,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
226
231
  return undefined;
227
232
  }
228
233
 
229
- // Default value for required boolean fields is false
234
+ // 필수 boolean 필드의 기본값은 false
230
235
  const innerSchema = this._unwrapSchema(schema);
231
236
  if (innerSchema instanceof ZodBoolean) {
232
237
  return false;
package/src/index.ts CHANGED
@@ -1,13 +1,13 @@
1
- // Types and utilities
1
+ // 타입 유틸리티
2
2
  export * from "./types";
3
3
  export * from "./utils/excel-utils";
4
4
 
5
- // Core classes
5
+ // 핵심 클래스
6
6
  export * from "./excel-cell";
7
7
  export * from "./excel-row";
8
8
  export * from "./excel-col";
9
9
  export * from "./excel-worksheet";
10
10
  export * from "./excel-workbook";
11
11
 
12
- // Wrapper classes
12
+ // 래퍼 클래스
13
13
  export * from "./excel-wrapper";
package/src/types.ts CHANGED
@@ -134,16 +134,16 @@ export interface ExcelXmlWorksheetData {
134
134
 
135
135
  export interface ExcelRowData {
136
136
  $: {
137
- r: string; // address (1~)
137
+ r: string; // 주소 (1~)
138
138
  };
139
139
  c?: ExcelCellData[];
140
140
  }
141
141
 
142
142
  export interface ExcelCellData {
143
143
  $: {
144
- r: string; // address (A~)
145
- s?: string; // styleId
146
- t?: ExcelCellType; // type: s(sharedString)
144
+ r: string; // 주소 (A~)
145
+ s?: string; // 스타일 ID
146
+ t?: ExcelCellType; // 타입: s(sharedString)
147
147
  };
148
148
  v?: [string];
149
149
  f?: [string];
@@ -324,13 +324,13 @@ export type ExcelValueType = number | string | DateOnly | DateTime | Time | bool
324
324
  export type ExcelNumberFormat = "number" | "string" | "DateOnly" | "DateTime" | "Time";
325
325
 
326
326
  /**
327
- * Excel cell type
328
- * - s: shared string (SharedString)
327
+ * Excel 타입
328
+ * - s: 공유 문자열 (SharedString)
329
329
  * - b: boolean
330
- * - str: formula result string
331
- * - n: number
332
- * - inlineStr: inline string (rich text)
333
- * - e: error
330
+ * - str: 수식 결과 문자열
331
+ * - n: 숫자
332
+ * - inlineStr: 인라인 문자열 (서식 있는 텍스트)
333
+ * - e: 에러
334
334
  */
335
335
  export type ExcelCellType = "s" | "b" | "str" | "n" | "inlineStr" | "e";
336
336
 
@@ -366,11 +366,11 @@ export type ExcelHorizontalAlign = "center" | "left" | "right";
366
366
  export type ExcelVerticalAlign = "center" | "top" | "bottom";
367
367
 
368
368
  /**
369
- * Cell style options
369
+ * 스타일 옵션
370
370
  * @example
371
371
  * ```typescript
372
372
  * await cell.setStyle({
373
- * background: "00FF0000", // red
373
+ * background: "00FF0000", // 빨강
374
374
  * border: ["left", "right", "top", "bottom"],
375
375
  * horizontalAlign: "center",
376
376
  * verticalAlign: "center",
@@ -379,15 +379,15 @@ export type ExcelVerticalAlign = "center" | "top" | "bottom";
379
379
  * ```
380
380
  */
381
381
  export interface ExcelStyleOptions {
382
- /** Background color (ARGB format, e.g. "00FF0000") */
382
+ /** 배경색 (ARGB 형식, 예: "00FF0000") */
383
383
  background?: string;
384
- /** Border positions */
384
+ /** 테두리 위치 */
385
385
  border?: ExcelBorderPosition[];
386
- /** Horizontal alignment */
386
+ /** 가로 정렬 */
387
387
  horizontalAlign?: ExcelHorizontalAlign;
388
- /** Vertical alignment */
388
+ /** 세로 정렬 */
389
389
  verticalAlign?: ExcelVerticalAlign;
390
- /** Number format */
390
+ /** 숫자 형식 */
391
391
  numberFormat?: ExcelNumberFormat;
392
392
  }
393
393