16. 틀 고정
특정 행/열을 고정하여 스크롤 시에도 보이도록 하는 기능을 다룹니다.
set_panes(sheet, cell) / setPanes(sheet, cell)
틀 고정을 설정합니다. 셀 참조는 스크롤 가능한 영역의 왼쪽 상단 셀을 나타냅니다.
Rust:
// Freeze first row
wb.set_panes("Sheet1", "A2")?;
// Freeze first column
wb.set_panes("Sheet1", "B1")?;
// Freeze first row and first column
wb.set_panes("Sheet1", "B2")?;TypeScript:
wb.setPanes("Sheet1", "A2"); // freeze row 1
wb.setPanes("Sheet1", "B1"); // freeze column A
wb.setPanes("Sheet1", "B2"); // freeze row 1 + column Aunset_panes(sheet) / unsetPanes(sheet)
틀 고정을 제거합니다.
Rust:
wb.unset_panes("Sheet1")?;TypeScript:
wb.unsetPanes("Sheet1");get_panes(sheet) / getPanes(sheet)
현재 틀 고정 설정을 조회합니다. 설정이 없으면 None / null을 반환합니다.
Rust:
if let Some(cell) = wb.get_panes("Sheet1")? {
println!("Frozen at: {}", cell); // e.g., "B2"
}TypeScript:
const pane = wb.getPanes("Sheet1");
if (pane) {
console.log(`Frozen at: ${pane}`);
}17. 페이지 레이아웃
인쇄 관련 설정을 다룹니다. 여백, 용지 크기, 방향, 배율, 머리글/바닥글, 인쇄 옵션, 페이지 나누기를 포함합니다.
여백 (Margins)
set_page_margins / get_page_margins로 페이지 여백을 인치 단위로 설정하거나 조회합니다.
Rust:
use sheetkit::page_layout::PageMarginsConfig;
wb.set_page_margins("Sheet1", &PageMarginsConfig {
left: 0.7,
right: 0.7,
top: 0.75,
bottom: 0.75,
header: 0.3,
footer: 0.3,
})?;
let margins = wb.get_page_margins("Sheet1")?;TypeScript:
wb.setPageMargins("Sheet1", {
left: 0.7,
right: 0.7,
top: 0.75,
bottom: 0.75,
header: 0.3,
footer: 0.3,
});
const margins = wb.getPageMargins("Sheet1");페이지 설정 (Page Setup)
용지 크기, 방향, 배율, 페이지 맞춤 설정을 다룹니다.
TypeScript:
wb.setPageSetup("Sheet1", {
paperSize: "a4",
orientation: "landscape",
scale: 80,
fitToWidth: 1,
fitToHeight: 0,
});
const setup = wb.getPageSetup("Sheet1");용지 크기 값: letter, tabloid, legal, a3, a4, a5, b4, b5
방향 값: portrait (세로), landscape (가로)
인쇄 옵션 (Print Options)
TypeScript:
wb.setPrintOptions("Sheet1", {
gridLines: true,
headings: true,
horizontalCentered: true,
verticalCentered: false,
});
const opts = wb.getPrintOptions("Sheet1");| 속성 | 타입 | 설명 |
|---|---|---|
grid_lines / gridLines | bool? / boolean? | 눈금선 인쇄 |
headings | bool? / boolean? | 행/열 머리글 인쇄 |
horizontal_centered / horizontalCentered | bool? / boolean? | 가로 가운데 정렬 |
vertical_centered / verticalCentered | bool? / boolean? | 세로 가운데 정렬 |
머리글/바닥글 (Header/Footer)
wb.setHeaderFooter("Sheet1", "&LLeft Text&CCenter Text&RRight Text", "&CPage &P of &N");
const hf = wb.getHeaderFooter("Sheet1");
// hf.header, hf.footerExcel 서식 코드:
&L(왼쪽),&C(가운데),&R(오른쪽),&P(현재 페이지),&N(총 페이지 수)
페이지 나누기 (Page Breaks)
wb.insertPageBreak("Sheet1", 20); // insert break before row 20
wb.insertPageBreak("Sheet1", 40);
const breaks: number[] = wb.getPageBreaks("Sheet1");
// [20, 40]
wb.removePageBreak("Sheet1", 20);Rust:
wb.insert_page_break("Sheet1", 20)?;
let breaks: Vec<u32> = wb.get_page_breaks("Sheet1")?;
wb.remove_page_break("Sheet1", 20)?;18. 정의된 이름
워크북 내에서 셀 범위에 이름을 부여하는 기능을 다룹니다. 워크북 범위(모든 시트에서 사용 가능) 또는 시트 범위(특정 시트에서만 사용 가능)로 정의할 수 있습니다.
set_defined_name / setDefinedName
정의된 이름을 추가하거나 업데이트합니다. 동일한 이름과 범위를 가진 항목이 이미 존재하면 값과 주석이 업데이트됩니다(중복 생성 없음).
Rust:
// Workbook-scoped name
wb.set_defined_name("SalesTotal", "Sheet1!$B$10", None, None)?;
// Sheet-scoped name with comment
wb.set_defined_name("LocalRange", "Sheet1!$A$1:$D$10", Some("Sheet1"), Some("Local data range"))?;TypeScript:
// Workbook-scoped name
wb.setDefinedName({ name: "SalesTotal", value: "Sheet1!$B$10" });
// Sheet-scoped name with comment
wb.setDefinedName({
name: "LocalRange",
value: "Sheet1!$A$1:$D$10",
scope: "Sheet1",
comment: "Local data range",
});get_defined_name / getDefinedName
이름과 선택적 범위로 정의된 이름을 조회합니다. 없으면 None/null을 반환합니다.
Rust:
if let Some(info) = wb.get_defined_name("SalesTotal", None)? {
println!("Refers to: {}", info.value);
}
// Sheet-scoped name
if let Some(info) = wb.get_defined_name("LocalRange", Some("Sheet1"))? {
println!("Sheet-scoped: {}", info.value);
}TypeScript:
const info = wb.getDefinedName("SalesTotal");
if (info) {
console.log(`Refers to: ${info.value}`);
}
const local = wb.getDefinedName("LocalRange", "Sheet1");get_all_defined_names / getDefinedNames
워크북의 모든 정의된 이름을 반환합니다.
Rust:
let names = wb.get_all_defined_names();
for dn in &names {
println!("{}: {} (scope: {:?})", dn.name, dn.value, dn.scope);
}TypeScript:
const names = wb.getDefinedNames();
for (const dn of names) {
console.log(`${dn.name}: ${dn.value} (scope: ${dn.scope ?? "workbook"})`);
}delete_defined_name / deleteDefinedName
이름과 선택적 범위로 정의된 이름을 삭제합니다. 해당 이름이 없으면 오류를 반환합니다.
Rust:
wb.delete_defined_name("SalesTotal", None)?;
wb.delete_defined_name("LocalRange", Some("Sheet1"))?;TypeScript:
wb.deleteDefinedName("SalesTotal");
wb.deleteDefinedName("LocalRange", "Sheet1");DefinedNameInfo
| 속성 | Rust 타입 | TypeScript 타입 | 설명 |
|---|---|---|---|
name | String | string | 정의된 이름 |
value | String | string | 참조 또는 수식 |
scope | DefinedNameScope | string? | 시트 이름(시트 범위) 또는 None/undefined(워크북 범위) |
comment | Option<String> | string? | 선택적 주석 |
정의된 이름에는
\ / ? * [ ]문자를 사용할 수 없으며, 앞뒤 공백도 허용되지 않습니다.
19. 문서 속성
워크북의 메타데이터를 설정하고 조회하는 기능을 다룹니다. 핵심 속성, 앱 속성, 사용자 정의 속성의 세 가지 유형이 있습니다.
핵심 속성 (Core Properties)
제목, 작성자 등 표준 문서 메타데이터를 다룹니다.
Rust:
use sheetkit::doc_props::DocProperties;
wb.set_doc_props(DocProperties {
title: Some("Annual Report".into()),
subject: Some("Financial Data".into()),
creator: Some("Finance Team".into()),
keywords: Some("finance, annual, 2024".into()),
description: Some("Annual financial report".into()),
last_modified_by: Some("Admin".into()),
revision: Some("3".into()),
created: Some("2024-01-01T00:00:00Z".into()),
modified: Some("2024-06-15T10:30:00Z".into()),
category: Some("Reports".into()),
content_status: Some("Final".into()),
});
let props = wb.get_doc_props();TypeScript:
wb.setDocProps({
title: "Annual Report",
subject: "Financial Data",
creator: "Finance Team",
keywords: "finance, annual, 2024",
description: "Annual financial report",
lastModifiedBy: "Admin",
revision: "3",
created: "2024-01-01T00:00:00Z",
modified: "2024-06-15T10:30:00Z",
category: "Reports",
contentStatus: "Final",
});
const props = wb.getDocProps();DocProperties 속성:
| 속성 | 타입 | 설명 |
|---|---|---|
title | string? | 제목 |
subject | string? | 주제 |
creator | string? | 작성자 |
keywords | string? | 키워드 |
description | string? | 설명 |
last_modified_by / lastModifiedBy | string? | 마지막 수정자 |
revision | string? | 수정 번호 |
created | string? | 생성 날짜 (ISO 8601) |
modified | string? | 수정 날짜 (ISO 8601) |
category | string? | 분류 |
content_status / contentStatus | string? | 콘텐츠 상태 |
앱 속성 (App Properties)
애플리케이션 관련 메타데이터를 다룹니다.
Rust:
use sheetkit::doc_props::AppProperties;
wb.set_app_props(AppProperties {
application: Some("SheetKit".into()),
doc_security: Some(0),
company: Some("ACME Corp".into()),
app_version: Some("1.0".into()),
manager: Some("Department Lead".into()),
template: None,
});
let app_props = wb.get_app_props();TypeScript:
wb.setAppProps({
application: "SheetKit",
docSecurity: 0,
company: "ACME Corp",
appVersion: "1.0",
manager: "Department Lead",
});
const appProps = wb.getAppProps();AppProperties 속성:
| 속성 | 타입 | 설명 |
|---|---|---|
application | string? | 애플리케이션 이름 |
doc_security / docSecurity | u32? / number? | 보안 수준 |
company | string? | 회사 이름 |
app_version / appVersion | string? | 앱 버전 |
manager | string? | 관리자 |
template | string? | 템플릿 |
사용자 정의 속성 (Custom Properties)
키-값 쌍으로 사용자 정의 메타데이터를 저장합니다. 값은 문자열, 숫자, 불리언 타입을 지원합니다.
Rust:
use sheetkit::doc_props::CustomPropertyValue;
wb.set_custom_property("Department", CustomPropertyValue::String("Engineering".into()));
wb.set_custom_property("Version", CustomPropertyValue::Int(3));
wb.set_custom_property("Approved", CustomPropertyValue::Bool(true));
wb.set_custom_property("Rating", CustomPropertyValue::Float(4.5));
let val = wb.get_custom_property("Department");
// Some(CustomPropertyValue::String("Engineering"))
let deleted = wb.delete_custom_property("Deprecated");
// true if existedTypeScript:
wb.setCustomProperty("Department", "Engineering");
wb.setCustomProperty("Version", 3);
wb.setCustomProperty("Approved", true);
wb.setCustomProperty("Rating", 4.5);
const val = wb.getCustomProperty("Department");
// "Engineering"
const deleted: boolean = wb.deleteCustomProperty("Deprecated");TypeScript에서 정수 값은 자동으로 Int로 변환되고, 소수점이 있는 숫자는 Float으로 저장됩니다.
20. 워크북 보호
워크북 구조(시트 추가/삭제/이름 변경)와 창 위치를 보호하는 기능을 다룹니다. 선택적으로 비밀번호를 설정할 수 있습니다.
protect_workbook(config) / protectWorkbook(config)
워크북 보호를 설정합니다.
Rust:
use sheetkit::protection::WorkbookProtectionConfig;
wb.protect_workbook(WorkbookProtectionConfig {
password: Some("secret123".into()),
lock_structure: true,
lock_windows: false,
lock_revision: false,
});TypeScript:
wb.protectWorkbook({
password: "secret123",
lockStructure: true,
lockWindows: false,
lockRevision: false,
});unprotect_workbook() / unprotectWorkbook()
워크북 보호를 해제합니다.
Rust:
wb.unprotect_workbook();TypeScript:
wb.unprotectWorkbook();is_workbook_protected() / isWorkbookProtected()
워크북이 보호되어 있는지 확인합니다.
Rust:
let protected: bool = wb.is_workbook_protected();TypeScript:
const isProtected: boolean = wb.isWorkbookProtected();WorkbookProtectionConfig 속성
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
password | string? | None | 보호 비밀번호 (레거시 해시로 저장) |
lock_structure / lockStructure | bool / boolean? | false | 시트 추가/삭제/이름 변경 차단 |
lock_windows / lockWindows | bool / boolean? | false | 창 위치/크기 변경 차단 |
lock_revision / lockRevision | bool / boolean? | false | 수정 추적 잠금 |
비밀번호는 Excel의 레거시 16비트 해시 알고리즘으로 저장됩니다. 이 해시는 암호학적으로 안전하지 않습니다.
21. 시트 보호
개별 시트의 편집을 제한하는 기능을 다룹니다. 선택적으로 비밀번호를 설정하고 특정 작업을 허용할 수 있습니다.
protect_sheet / protectSheet
시트를 보호합니다. 모든 권한 불리언 값은 기본적으로 false(금지)이며, true로 설정하면 보호 상태에서도 해당 작업이 허용됩니다.
Rust:
use sheetkit::sheet::SheetProtectionConfig;
wb.protect_sheet("Sheet1", &SheetProtectionConfig {
password: Some("mypass".to_string()),
format_cells: true,
insert_rows: true,
sort: true,
..SheetProtectionConfig::default()
})?;TypeScript:
wb.protectSheet("Sheet1", {
password: "mypass",
formatCells: true,
insertRows: true,
sort: true,
});
// Protect with defaults (all actions forbidden, no password)
wb.protectSheet("Sheet1");unprotect_sheet / unprotectSheet
시트 보호를 해제합니다.
Rust:
wb.unprotect_sheet("Sheet1")?;TypeScript:
wb.unprotectSheet("Sheet1");is_sheet_protected / isSheetProtected
시트가 보호되어 있는지 확인합니다.
Rust:
let protected: bool = wb.is_sheet_protected("Sheet1")?;TypeScript:
const isProtected: boolean = wb.isSheetProtected("Sheet1");SheetProtectionConfig 속성
| 속성 | Rust 타입 | TypeScript 타입 | 기본값 | 설명 |
|---|---|---|---|---|
password | Option<String> | string? | None | 보호 비밀번호 (레거시 해시로 저장) |
select_locked_cells / selectLockedCells | bool | boolean? | false | 잠긴 셀 선택 허용 |
select_unlocked_cells / selectUnlockedCells | bool | boolean? | false | 잠기지 않은 셀 선택 허용 |
format_cells / formatCells | bool | boolean? | false | 셀 서식 변경 허용 |
format_columns / formatColumns | bool | boolean? | false | 열 서식 변경 허용 |
format_rows / formatRows | bool | boolean? | false | 행 서식 변경 허용 |
insert_columns / insertColumns | bool | boolean? | false | 열 삽입 허용 |
insert_rows / insertRows | bool | boolean? | false | 행 삽입 허용 |
insert_hyperlinks / insertHyperlinks | bool | boolean? | false | 하이퍼링크 삽입 허용 |
delete_columns / deleteColumns | bool | boolean? | false | 열 삭제 허용 |
delete_rows / deleteRows | bool | boolean? | false | 행 삭제 허용 |
sort | bool | boolean? | false | 정렬 허용 |
auto_filter / autoFilter | bool | boolean? | false | 자동 필터 사용 허용 |
pivot_tables / pivotTables | bool | boolean? | false | 피벗 테이블 사용 허용 |
비밀번호는 Excel의 레거시 16비트 해시 알고리즘으로 저장됩니다. 이 해시는 암호학적으로 안전하지 않습니다.
22. 수식 평가
셀 수식을 파싱하고 실행하는 기능을 다룹니다. nom 파서로 수식을 AST로 변환한 후 평가 엔진이 결과를 계산합니다.
set_cell_formula / setCellFormula
단일 셀에 수식을 설정합니다.
Rust:
wb.set_cell_formula("Sheet1", "C1", "SUM(A1:B1)")?;TypeScript:
wb.setCellFormula("Sheet1", "C1", "SUM(A1:B1)");fill_formula / fillFormula
단일 열 범위에 수식을 채우며, 각 행에 맞게 행 참조를 자동으로 조정합니다. 절대 행 참조($1)는 조정되지 않습니다. 범위의 첫 번째 셀은 수식을 그대로 받고, 이후 셀들은 행 오프셋만큼 행 참조가 이동됩니다.
Rust:
// D2 = SUM(A2:C2), D3 = SUM(A3:C3), ..., D10 = SUM(A10:C10)으로 설정
wb.fill_formula("Sheet1", "D2:D10", "SUM(A2:C2)")?;
// 절대 참조는 보존된다:
// E2 = $A$1*B2, E3 = $A$1*B3, E4 = $A$1*B4
wb.fill_formula("Sheet1", "E2:E4", "$A$1*B2")?;TypeScript:
wb.fillFormula("Sheet1", "D2:D10", "SUM(A2:C2)");
wb.fillFormula("Sheet1", "E2:E4", "$A$1*B2");단일 열 범위만 지원됩니다 (예:
"D2:D10"). 다중 열 범위는 오류를 반환합니다.
evaluate_formula(sheet, formula) / evaluateFormula(sheet, formula)
주어진 시트 컨텍스트에서 수식 문자열을 평가하여 결과를 반환합니다. 워크북의 현재 셀 데이터를 참조할 수 있습니다.
Rust:
wb.set_cell_value("Sheet1", "A1", CellValue::Number(10.0))?;
wb.set_cell_value("Sheet1", "A2", CellValue::Number(20.0))?;
let result = wb.evaluate_formula("Sheet1", "SUM(A1:A2)")?;
// CellValue::Number(30.0)TypeScript:
wb.setCellValue("Sheet1", "A1", 10);
wb.setCellValue("Sheet1", "A2", 20);
const result = wb.evaluateFormula("Sheet1", "SUM(A1:A2)");
// 30calculate_all() / calculateAll()
워크북의 모든 수식 셀을 재계산합니다. 의존성 그래프를 구축하고 위상 정렬을 수행하여 올바른 순서로 평가합니다.
Rust:
wb.calculate_all()?;TypeScript:
wb.calculateAll();순환 참조가 발견되면 오류가 발생합니다. 최대 재귀 깊이는 256입니다.
지원 함수 목록 (164개, 10개 카테고리)
수학 함수 (Math) -- 20개
| 함수 | 설명 |
|---|---|
SUM | 합계 |
PRODUCT | 곱 |
ABS | 절대값 |
INT | 정수 변환 (내림) |
MOD | 나머지 |
POWER | 거듭제곱 |
SQRT | 제곱근 |
ROUND | 반올림 |
ROUNDUP | 올림 |
ROUNDDOWN | 내림 |
CEILING | 올림 (배수) |
FLOOR | 내림 (배수) |
SIGN | 부호 |
RAND | 난수 (0-1) |
RANDBETWEEN | 정수 난수 (범위) |
PI | 원주율 |
LOG | 로그 |
LOG10 | 상용 로그 |
LN | 자연 로그 |
EXP | 지수 함수 |
QUOTIENT | 정수 몫 |
FACT | 팩토리얼 |
SUMIF | 조건부 합계 |
SUMIFS | 다중 조건부 합계 |
통계 함수 (Statistical) -- 16개
| 함수 | 설명 |
|---|---|
AVERAGE | 평균 |
COUNT | 숫자 셀 개수 |
COUNTA | 비어 있지 않은 셀 개수 |
COUNTBLANK | 빈 셀 개수 |
COUNTIF | 조건부 개수 |
COUNTIFS | 다중 조건부 개수 |
MIN | 최소값 |
MAX | 최대값 |
MEDIAN | 중앙값 |
MODE | 최빈값 |
LARGE | N번째 큰 값 |
SMALL | N번째 작은 값 |
RANK | 순위 |
AVERAGEIF | 조건부 평균 |
AVERAGEIFS | 다중 조건부 평균 |
논리 함수 (Logical) -- 10개
| 함수 | 설명 |
|---|---|
IF | 조건 분기 |
AND | 논리곱 |
OR | 논리합 |
NOT | 부정 |
XOR | 배타적 논리합 |
TRUE | TRUE 상수 |
FALSE | FALSE 상수 |
IFERROR | 오류 시 대체값 |
IFNA | #N/A 시 대체값 |
IFS | 다중 조건 분기 |
SWITCH | 값 기반 분기 |
텍스트 함수 (Text) -- 15개
| 함수 | 설명 |
|---|---|
LEN | 문자열 길이 |
LOWER | 소문자 변환 |
UPPER | 대문자 변환 |
TRIM | 공백 제거 |
LEFT | 왼쪽 문자 추출 |
RIGHT | 오른쪽 문자 추출 |
MID | 중간 문자 추출 |
CONCATENATE | 문자열 연결 |
CONCAT | 문자열 연결 (최신) |
FIND | 문자열 찾기 (대소문자 구분) |
SEARCH | 문자열 찾기 (대소문자 무시) |
SUBSTITUTE | 문자열 치환 |
REPLACE | 위치 기반 문자열 교체 |
REPT | 문자열 반복 |
EXACT | 완전 일치 비교 |
T | 텍스트 변환 |
PROPER | 단어 첫 글자 대문자 |
정보 함수 (Information) -- 11개
| 함수 | 설명 |
|---|---|
ISNUMBER | 숫자 여부 |
ISTEXT | 텍스트 여부 |
ISBLANK | 빈 셀 여부 |
ISERROR | 오류 여부 (#N/A 포함) |
ISERR | 오류 여부 (#N/A 제외) |
ISNA | #N/A 여부 |
ISLOGICAL | 논리값 여부 |
ISEVEN | 짝수 여부 |
ISODD | 홀수 여부 |
TYPE | 값 유형 번호 |
N | 숫자 변환 |
NA | #N/A 생성 |
ERROR.TYPE | 오류 유형 번호 |
변환 함수 (Conversion) -- 2개
| 함수 | 설명 |
|---|---|
VALUE | 텍스트를 숫자로 변환 |
TEXT | 값을 서식 문자열로 변환 |
날짜/시각 함수 (Date/Time) -- 17개
| 함수 | 설명 |
|---|---|
DATE | 연/월/일로 날짜 생성 |
TODAY | 오늘 날짜 |
NOW | 현재 날짜 및 시각 |
YEAR | 연도 추출 |
MONTH | 월 추출 |
DAY | 일 추출 |
HOUR | 시 추출 |
MINUTE | 분 추출 |
SECOND | 초 추출 |
DATEDIF | 날짜 차이 계산 |
EDATE | N개월 후 날짜 |
EOMONTH | N개월 후 월말 |
DATEVALUE | 텍스트를 날짜로 변환 |
WEEKDAY | 요일 번호 |
WEEKNUM | 주차 번호 |
NETWORKDAYS | 근무일수 계산 |
WORKDAY | N 근무일 후 날짜 |
찾기/참조 함수 (Lookup) -- 11개
| 함수 | 설명 |
|---|---|
VLOOKUP | 세로 방향 조회 |
HLOOKUP | 가로 방향 조회 |
INDEX | 범위에서 값 추출 |
MATCH | 위치 찾기 |
LOOKUP | 벡터 조회 |
ROW | 행 번호 |
COLUMN | 열 번호 |
ROWS | 범위의 행 수 |
COLUMNS | 범위의 열 수 |
CHOOSE | 인덱스로 값 선택 |
ADDRESS | 셀 주소 문자열 생성 |
재무 함수 (Financial) -- 21개
| 함수 | 설명 |
|---|---|
FV | 미래 가치 |
PV | 현재 가치 |
NPV | 순현재가치 |
IRR | 내부수익률 |
PMT | 정기 납입금 |
IPMT | 이자 부분 |
PPMT | 원금 부분 |
RATE | 이자율 |
NPER | 납입 횟수 |
DB | 정률법 감가상각 |
DDB | 이중 체감법 감가상각 |
SLN | 정액법 감가상각 |
SYD | 연수합계법 감가상각 |
EFFECT | 실효 이자율 |
NOMINAL | 명목 이자율 |
DOLLARDE | 분수 가격을 소수로 변환 |
DOLLARFR | 소수 가격을 분수로 변환 |
CUMIPMT | 누적 이자 |
CUMPRINC | 누적 원금 |
XNPV | 비정기 순현재가치 |
XIRR | 비정기 내부수익률 |
공학 함수 (Engineering) -- 33개
| 함수 | 설명 |
|---|---|
BIN2DEC | 2진수를 10진수로 변환 |
BIN2HEX | 2진수를 16진수로 변환 |
BIN2OCT | 2진수를 8진수로 변환 |
DEC2BIN | 10진수를 2진수로 변환 |
DEC2HEX | 10진수를 16진수로 변환 |
DEC2OCT | 10진수를 8진수로 변환 |
HEX2BIN | 16진수를 2진수로 변환 |
HEX2DEC | 16진수를 10진수로 변환 |
HEX2OCT | 16진수를 8진수로 변환 |
OCT2BIN | 8진수를 2진수로 변환 |
OCT2DEC | 8진수를 10진수로 변환 |
OCT2HEX | 8진수를 16진수로 변환 |
DELTA | 두 값이 같은지 확인 |
GESTEP | 값이 임계값 이상인지 확인 |
ERF | 오차 함수 |
ERFC | 여오차 함수 |
COMPLEX | 복소수 생성 |
IMREAL | 복소수의 실수부 |
IMAGINARY | 복소수의 허수부 |
IMABS | 복소수의 절대값 |
IMARGUMENT | 복소수의 편각 |
IMCONJUGATE | 복소수의 켤레 |
IMSUM | 복소수 합 |
IMSUB | 복소수 차 |
IMPRODUCT | 복소수 곱 |
IMDIV | 복소수 나눗셈 |
IMPOWER | 복소수 거듭제곱 |
IMSQRT | 복소수 제곱근 |
CONVERT | 단위 변환 |
BESSELI | 수정 Bessel 함수 I |
BESSELJ | Bessel 함수 J |
BESSELK | 수정 Bessel 함수 K |
BESSELY | Bessel 함수 Y |
23. 피벗 테이블
피벗 테이블을 생성, 조회, 삭제하는 기능을 다룹니다. 소스 데이터 범위로부터 행/열/데이터 필드를 지정하여 피벗 테이블을 구성합니다.
add_pivot_table(config) / addPivotTable(config)
피벗 테이블을 추가합니다.
Rust:
use sheetkit::pivot::*;
wb.add_pivot_table(&PivotTableConfig {
name: "SalesPivot".into(),
source_sheet: "RawData".into(),
source_range: "A1:D100".into(),
target_sheet: "PivotSheet".into(),
target_cell: "A3".into(),
rows: vec![
PivotField { name: "Region".into() },
PivotField { name: "Product".into() },
],
columns: vec![
PivotField { name: "Quarter".into() },
],
data: vec![
PivotDataField {
name: "Revenue".into(),
function: AggregateFunction::Sum,
display_name: Some("Total Revenue".into()),
},
PivotDataField {
name: "Quantity".into(),
function: AggregateFunction::Count,
display_name: None,
},
],
})?;TypeScript:
wb.addPivotTable({
name: "SalesPivot",
sourceSheet: "RawData",
sourceRange: "A1:D100",
targetSheet: "PivotSheet",
targetCell: "A3",
rows: [
{ name: "Region" },
{ name: "Product" },
],
columns: [
{ name: "Quarter" },
],
data: [
{ name: "Revenue", function: "sum", displayName: "Total Revenue" },
{ name: "Quantity", function: "count" },
],
});Node.js에서
data[].function은 지원되는 집계 함수(sum,count,average,max,min,product,countNums,stdDev,stdDevP,var,varP)만 허용되며, 지원되지 않는 값은 오류를 반환합니다.
get_pivot_tables() / getPivotTables()
워크북의 모든 피벗 테이블 정보를 반환합니다.
Rust:
let tables = wb.get_pivot_tables();
for t in &tables {
println!("{}: {}!{} -> {}!{}", t.name, t.source_sheet, t.source_range, t.target_sheet, t.location);
}TypeScript:
const tables = wb.getPivotTables();
for (const t of tables) {
console.log(`${t.name}: ${t.sourceSheet}!${t.sourceRange} -> ${t.targetSheet}!${t.location}`);
}PivotTableInfo 구조:
| 속성 | 타입 | 설명 |
|---|---|---|
name | string | 피벗 테이블 이름 |
source_sheet / sourceSheet | string | 소스 데이터 시트 이름 |
source_range / sourceRange | string | 소스 데이터 범위 |
target_sheet / targetSheet | string | 대상 시트 이름 |
location | string | 피벗 테이블 위치 |
delete_pivot_table(name) / deletePivotTable(name)
이름으로 피벗 테이블을 삭제합니다.
Rust:
wb.delete_pivot_table("SalesPivot")?;TypeScript:
wb.deletePivotTable("SalesPivot");PivotTableConfig 구조
| 속성 | 타입 | 설명 |
|---|---|---|
name | string | 피벗 테이블 이름 |
source_sheet / sourceSheet | string | 소스 데이터가 있는 시트 |
source_range / sourceRange | string | 소스 데이터 범위 (예: "A1:D100") |
target_sheet / targetSheet | string | 피벗 테이블을 배치할 시트 |
target_cell / targetCell | string | 피벗 테이블 시작 셀 |
rows | PivotField[] | 행 필드 |
columns | PivotField[] | 열 필드 |
data | PivotDataField[] | 데이터(값) 필드 |
AggregateFunction (집계 함수)
| 값 | 설명 |
|---|---|
sum | 합계 |
count | 개수 |
average | 평균 |
max | 최대값 |
min | 최소값 |
product | 곱 |
countNums | 숫자 개수 |
stdDev | 표준편차 |
stdDevP | 모표준편차 |
var | 분산 |
varP | 모분산 |
24. 스트림 라이터
대용량 데이터를 메모리 효율적으로 쓰기 위한 스트리밍 API입니다. 행은 오름차순으로만 쓸 수 있으며, 전체 워크시트 XML을 메모리에 구축하지 않고 직접 버퍼에 기록합니다.
사용 흐름
new_stream_writer로 스트림 라이터 생성- 열 너비, 셀 병합 등 설정
write_row로 행 데이터를 순서대로 기록apply_stream_writer로 워크북에 적용
Rust:
use sheetkit::cell::CellValue;
let mut sw = wb.new_stream_writer("LargeData")?;
// Set column widths
sw.set_col_width(1, 15.0)?; // Column A
sw.set_col_width(2, 20.0)?; // Column B
// Add merge cells
sw.add_merge_cell("A1:B1")?;
// Write header row
sw.write_row(1, &[
CellValue::from("Name"),
CellValue::from("Value"),
])?;
// Write data rows (must be in ascending order)
for i in 2..=10000 {
sw.write_row(i, &[
CellValue::from(format!("Item {}", i - 1)),
CellValue::Number(i as f64 * 1.5),
])?;
}
// Apply to workbook
let sheet_index = wb.apply_stream_writer(sw)?;
wb.save("large_data.xlsx")?;TypeScript:
const sw = wb.newStreamWriter("LargeData");
// Set column widths
sw.setColWidth(1, 15); // Column A
sw.setColWidth(2, 20); // Column B
// Set width for a range of columns
sw.setColWidthRange(3, 10, 12); // Columns C-J
// Add merge cells
sw.addMergeCell("A1:B1");
// Write header row
sw.writeRow(1, ["Name", "Value"]);
// Write data rows
for (let i = 2; i <= 10000; i++) {
sw.writeRow(i, [`Item ${i - 1}`, i * 1.5]);
}
// Apply to workbook
const sheetIndex: number = wb.applyStreamWriter(sw);
await wb.save("large_data.xlsx");StreamWriter API
new_stream_writer(sheet_name) / newStreamWriter(sheetName)
새 시트를 위한 스트림 라이터를 생성합니다.
write_row(row, values) / writeRow(row, values)
행 데이터를 기록합니다. 행 번호는 1부터 시작하며 반드시 오름차순이어야 합니다.
set_col_width(col, width) / setColWidth(col, width)
열 너비를 설정합니다. col은 1부터 시작하는 열 번호입니다.
set_col_width_range(min_col, max_col, width) / setColWidthRange(minCol, maxCol, width)
열 범위의 너비를 한 번에 설정합니다.
add_merge_cell(reference) / addMergeCell(reference)
셀 병합을 추가합니다 (예: "A1:C3").
apply_stream_writer(writer) / applyStreamWriter(writer)
스트림 라이터의 결과를 워크북에 적용합니다. 시트 인덱스를 반환합니다. 적용 후 스트림 라이터는 소비(consumed)되어 더 이상 사용할 수 없습니다.
StreamRowOptions (Rust 전용)
Rust에서는 write_row_with_options를 사용하여 행별 옵션을 지정할 수 있습니다.
| 속성 | 타입 | 설명 |
|---|---|---|
height | Option<f64> | 행 높이 (포인트) |
visible | Option<bool> | 행 표시 여부 |
outline_level | Option<u8> | 아웃라인 수준 (0-7) |
style_id | Option<u32> | 행 스타일 ID |
25. 유틸리티 함수
셀 참조 변환에 사용되는 유틸리티 함수들입니다. Rust에서는 sheetkit_core::utils::cell_ref 모듈에서 제공합니다.
cell_name_to_coordinates
A1 형식의 셀 참조를 (열, 행) 좌표로 변환합니다. 열과 행 모두 1부터 시작합니다.
Rust:
use sheetkit_core::utils::cell_ref::cell_name_to_coordinates;
let (col, row) = cell_name_to_coordinates("B3")?;
// col = 2, row = 3
let (col, row) = cell_name_to_coordinates("$AB$100")?;
// col = 28, row = 100 (absolute references are supported)coordinates_to_cell_name
(열, 행) 좌표를 A1 형식의 셀 참조로 변환합니다.
Rust:
use sheetkit_core::utils::cell_ref::coordinates_to_cell_name;
let name = coordinates_to_cell_name(2, 3)?;
// "B3"
let name = coordinates_to_cell_name(28, 100)?;
// "AB100"column_name_to_number
열 이름을 1부터 시작하는 열 번호로 변환합니다.
Rust:
use sheetkit_core::utils::cell_ref::column_name_to_number;
assert_eq!(column_name_to_number("A")?, 1);
assert_eq!(column_name_to_number("Z")?, 26);
assert_eq!(column_name_to_number("AA")?, 27);
assert_eq!(column_name_to_number("XFD")?, 16384); // maximum columncolumn_number_to_name
1부터 시작하는 열 번호를 열 이름으로 변환합니다.
Rust:
use sheetkit_core::utils::cell_ref::column_number_to_name;
assert_eq!(column_number_to_name(1)?, "A");
assert_eq!(column_number_to_name(26)?, "Z");
assert_eq!(column_number_to_name(27)?, "AA");
assert_eq!(column_number_to_name(16384)?, "XFD");유틸리티 함수는 현재 Rust 전용으로 제공됩니다. TypeScript에서는 문자열 기반 셀 참조("A1", "B2" 등)를 직접 사용합니다.
is_date_num_fmt(num_fmt_id) (Rust 전용)
내장 숫자 서식 ID가 날짜/시간 서식인지 확인합니다. ID 14-22 및 45-47에 대해 true를 반환합니다.
use sheetkit::is_date_num_fmt;
assert!(is_date_num_fmt(14)); // m/d/yyyy
assert!(is_date_num_fmt(22)); // m/d/yyyy h:mm
assert!(!is_date_num_fmt(0)); // General
assert!(!is_date_num_fmt(49)); // @is_date_format_code(code) (Rust 전용)
사용자 정의 숫자 서식 문자열이 날짜/시간 서식인지 확인합니다. 따옴표로 감싸진 문자열과 이스케이프된 문자를 제외하고, 서식 코드에 날짜/시간 토큰(y, m, d, h, s)이 포함되어 있으면 true를 반환합니다.
use sheetkit::is_date_format_code;
assert!(is_date_format_code("yyyy-mm-dd"));
assert!(is_date_format_code("h:mm:ss AM/PM"));
assert!(!is_date_format_code("#,##0.00"));
assert!(!is_date_format_code("0%"));부록: 제한 사항
| 항목 | 제한 |
|---|---|
| 최대 열 수 | 16,384 (XFD) |
| 최대 행 수 | 1,048,576 |
| 최대 셀 문자 수 | 32,767 |
| 최대 행 높이 | 409 포인트 |
| 최대 아웃라인 수준 | 7 |
| 최대 스타일 XF 수 | 65,430 |
| 수식 최대 재귀 깊이 | 256 |
| 지원 수식 함수 수 | 164 / 456 |
26. 스파크라인
스파크라인은 워크시트 셀에 삽입되는 미니 차트입니다. SheetKit은 Line, Column, Win/Loss 세 가지 스파크라인 유형을 지원합니다. Excel은 36가지 스타일 프리셋(인덱스 0-35)을 정의합니다.
스파크라인은 OOXML 패키지의 x14 워크시트 확장으로 저장되며 저장/열기 라운드트립을 통해 보존됩니다.
타입
SparklineType (Rust) / sparklineType (TypeScript)
| 값 | Rust | TypeScript | OOXML |
|---|---|---|---|
| Line | SparklineType::Line | "line" | (default, omitted) |
| Column | SparklineType::Column | "column" | "column" |
| Win/Loss | SparklineType::WinLoss | "winloss" or "stacked" | "stacked" |
SparklineConfig (Rust)
use sheetkit::SparklineConfig;
let config = SparklineConfig::new("Sheet1!A1:A10", "B1");필드:
| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
data_range | String | (필수) | 데이터 소스 범위 (예: "Sheet1!A1:A10") |
location | String | (필수) | 스파크라인이 렌더링되는 셀 (예: "B1") |
sparkline_type | SparklineType | Line | 스파크라인 차트 유형 |
markers | bool | false | 데이터 마커 표시 |
high_point | bool | false | 최고점 강조 |
low_point | bool | false | 최저점 강조 |
first_point | bool | false | 첫 번째 점 강조 |
last_point | bool | false | 마지막 점 강조 |
negative_points | bool | false | 음수 값 강조 |
show_axis | bool | false | 가로축 표시 |
line_weight | Option<f64> | None | 선 두께 (포인트) |
style | Option<u32> | None | 스타일 프리셋 인덱스 (0-35) |
JsSparklineConfig (TypeScript)
const config = {
dataRange: 'Sheet1!A1:A10',
location: 'B1',
sparklineType: 'line', // "line" | "column" | "winloss" | "stacked"
markers: true,
highPoint: false,
lowPoint: false,
firstPoint: false,
lastPoint: false,
negativePoints: false,
showAxis: false,
lineWeight: 0.75,
style: 1,
};Workbook.addSparkline / Workbook::add_sparkline
워크시트에 스파크라인을 추가합니다.
Rust:
use sheetkit::{Workbook, SparklineConfig, SparklineType};
let mut wb = Workbook::new();
let mut config = SparklineConfig::new("Sheet1!A1:A10", "B1");
config.sparkline_type = SparklineType::Column;
config.markers = true;
config.high_point = true;
wb.add_sparkline("Sheet1", &config).unwrap();TypeScript:
import { Workbook } from '@sheetkit/node';
const wb = new Workbook();
wb.addSparkline('Sheet1', {
dataRange: 'Sheet1!A1:A10',
location: 'B1',
sparklineType: 'column',
markers: true,
highPoint: true,
});Workbook.getSparklines / Workbook::get_sparklines
워크시트의 모든 스파크라인을 조회합니다.
Rust:
let sparklines = wb.get_sparklines("Sheet1").unwrap();
for s in &sparklines {
println!("{} -> {}", s.data_range, s.location);
}TypeScript:
const sparklines = wb.getSparklines('Sheet1');
for (const s of sparklines) {
console.log(`${s.dataRange} -> ${s.location}`);
}Workbook.removeSparkline / Workbook::remove_sparkline
위치 셀 참조로 스파크라인을 제거합니다.
Rust:
wb.remove_sparkline("Sheet1", "B1").unwrap();TypeScript:
wb.removeSparkline('Sheet1', 'B1');유효성 검사
validate_sparkline_config 함수(Rust)는 다음을 확인합니다:
data_range가 비어 있지 않음location이 비어 있지 않음line_weight(설정된 경우) 양수 여부style(설정된 경우) 0-35 범위 내 여부
add_sparkline 호출 시 유효성 검사가 자동으로 적용됩니다.
use sheetkit_core::sparkline::{SparklineConfig, validate_sparkline_config};
let config = SparklineConfig::new("Sheet1!A1:A10", "B1");
validate_sparkline_config(&config).unwrap(); // Ok27. 테마 색상
테마 색상 슬롯(dk1, lt1, dk2, lt2, accent1-6, hlink, folHlink)을 선택적 틴트 값과 함께 조회합니다.
Workbook.getThemeColor (Node.js) / Workbook::get_theme_color (Rust)
| Parameter | Type | Description |
|---|---|---|
| index | u32 / number | Theme color index (0-11) |
| tint | Option<f64> / number | null | Tint value: positive lightens, negative darkens |
Returns: ARGB hex string (e.g. "FF4472C4") or None/null if out of range.
Theme Color Indices:
| Index | Slot Name | Default Color |
|---|---|---|
| 0 | dk1 | FF000000 |
| 1 | lt1 | FFFFFFFF |
| 2 | dk2 | FF44546A |
| 3 | lt2 | FFE7E6E6 |
| 4 | accent1 | FF4472C4 |
| 5 | accent2 | FFED7D31 |
| 6 | accent3 | FFA5A5A5 |
| 7 | accent4 | FFFFC000 |
| 8 | accent5 | FF5B9BD5 |
| 9 | accent6 | FF70AD47 |
| 10 | hlink | FF0563C1 |
| 11 | folHlink | FF954F72 |
Node.js
const wb = new Workbook();
// Get accent1 color (no tint)
const color = wb.getThemeColor(4, null); // "FF4472C4"
// Lighten black by 50%
const lightened = wb.getThemeColor(0, 0.5); // "FF7F7F7F"
// Darken white by 50%
const darkened = wb.getThemeColor(1, -0.5); // "FF7F7F7F"
// Out of range returns null
const invalid = wb.getThemeColor(99, null); // nullRust
let wb = Workbook::new();
// Get accent1 color (no tint)
let color = wb.get_theme_color(4, None); // Some("FF4472C4")
// Apply tint
let tinted = wb.get_theme_color(0, Some(0.5)); // Some("FF7F7F7F")Gradient Fill
FillStyle type supports gradient fills via the gradient field.
Types
pub struct GradientFillStyle {
pub gradient_type: GradientType, // Linear or Path
pub degree: Option<f64>, // Rotation angle for linear gradients
pub left: Option<f64>, // Path gradient coordinates (0.0-1.0)
pub right: Option<f64>,
pub top: Option<f64>,
pub bottom: Option<f64>,
pub stops: Vec<GradientStop>, // Color stops
}
pub struct GradientStop {
pub position: f64, // Position (0.0-1.0)
pub color: StyleColor, // Color at this stop
}
pub enum GradientType {
Linear,
Path,
}Rust Example
use sheetkit::*;
let mut wb = Workbook::new();
let style_id = wb.add_style(&Style {
fill: Some(FillStyle {
pattern: PatternType::None,
fg_color: None,
bg_color: None,
gradient: Some(GradientFillStyle {
gradient_type: GradientType::Linear,
degree: Some(90.0),
left: None,
right: None,
top: None,
bottom: None,
stops: vec![
GradientStop {
position: 0.0,
color: StyleColor::Rgb("FFFFFFFF".to_string()),
},
GradientStop {
position: 1.0,
color: StyleColor::Rgb("FF4472C4".to_string()),
},
],
}),
}),
..Style::default()
})?;28. 서식 있는 텍스트
서식 있는 텍스트(Rich Text)를 사용하면 하나의 셀에 글꼴, 크기, 굵게, 기울임, 색상 등 서로 다른 서식을 가진 여러 텍스트 조각(run)을 넣을 수 있습니다.
RichTextRun 타입
각 run은 RichTextRun으로 기술됩니다.
Rust:
pub struct RichTextRun {
pub text: String,
pub font: Option<String>,
pub size: Option<f64>,
pub bold: bool,
pub italic: bool,
pub color: Option<String>,
}TypeScript:
interface RichTextRun {
text: string;
font?: string;
size?: number;
bold?: boolean;
italic?: boolean;
color?: string; // RGB hex string, e.g. "#FF0000"
}set_cell_rich_text / setCellRichText
셀에 여러 서식 run으로 구성된 서식 있는 텍스트를 설정합니다.
Rust:
use sheetkit::{Workbook, RichTextRun};
let mut wb = Workbook::new();
let runs = vec![
RichTextRun {
text: "Bold text".to_string(),
font: Some("Arial".to_string()),
size: Some(14.0),
bold: true,
italic: false,
color: Some("#FF0000".to_string()),
},
RichTextRun {
text: " normal text".to_string(),
font: None,
size: None,
bold: false,
italic: false,
color: None,
},
];
wb.set_cell_rich_text("Sheet1", "A1", runs)?;TypeScript:
const wb = new Workbook();
wb.setCellRichText("Sheet1", "A1", [
{ text: "Bold text", font: "Arial", size: 14, bold: true, color: "#FF0000" },
{ text: " normal text" },
]);get_cell_rich_text / getCellRichText
셀의 서식 있는 텍스트 run을 가져옵니다. 서식 있는 텍스트가 아닌 셀은 None/null을 반환합니다.
Rust:
let runs = wb.get_cell_rich_text("Sheet1", "A1")?;
if let Some(runs) = runs {
for run in &runs {
println!("Text: {:?}, Bold: {}", run.text, run.bold);
}
}TypeScript:
const runs = wb.getCellRichText("Sheet1", "A1");
if (runs) {
for (const run of runs) {
console.log(`Text: ${run.text}, Bold: ${run.bold ?? false}`);
}
}CellValue::RichString (Rust 전용)
서식 있는 텍스트 셀은 CellValue::RichString(Vec<RichTextRun>) variant를 사용합니다. get_cell_value로 읽으면 모든 run의 텍스트가 연결된 문자열로 표시됩니다.
match wb.get_cell_value("Sheet1", "A1")? {
CellValue::RichString(runs) => {
println!("Rich text with {} runs", runs.len());
}
_ => {}
}rich_text_to_plain
서식 있는 텍스트 run 슬라이스에서 연결된 일반 텍스트를 추출하는 유틸리티 함수입니다.
Rust:
use sheetkit::rich_text_to_plain;
let plain = rich_text_to_plain(&runs);29. 파일 암호화
파일 수준 암호화는 전체 .xlsx 파일을 비밀번호로 보호합니다. 암호화된 파일은 일반 ZIP 아카이브가 아닌 OLE/CFB 복합 컨테이너를 사용합니다. SheetKit은 다음을 지원합니다:
- 복호화: Standard Encryption (Office 2007, AES-128-ECB + SHA-1) 및 Agile Encryption (Office 2010+, AES-256-CBC + SHA-512)
- 암호화: Agile Encryption (AES-256-CBC + SHA-512, 100,000회 반복)
Rust에서는
encryptionfeature가 필요합니다:sheetkit = { features = ["encryption"] }. Node.js 바인딩에는 항상 암호화 지원이 포함됩니다.
open_with_password(path, password) / openWithPasswordSync(path, password)
주어진 비밀번호로 암호화된 .xlsx 파일을 엽니다. 비밀번호가 틀리거나 지원하지 않는 암호화 방식이면 에러를 반환합니다.
Rust:
let wb = Workbook::open_with_password("encrypted.xlsx", "secret")?;TypeScript:
// 동기
const wb = Workbook.openWithPasswordSync("encrypted.xlsx", "secret");
// 비동기
const wb2 = await Workbook.openWithPassword("encrypted.xlsx", "secret");save_with_password(path, password) / saveWithPassword(path, password)
Agile Encryption을 사용하여 워크북을 암호화된 .xlsx 파일로 저장합니다.
Rust:
wb.save_with_password("encrypted.xlsx", "secret")?;TypeScript:
// 동기
wb.saveWithPassword("encrypted.xlsx", "secret");
// 비동기
await wb.saveWithPassword("encrypted.xlsx", "secret");에러 타입
| 에러 | Rust | TypeScript | 설명 |
|---|---|---|---|
| 파일 암호화됨 | Error::FileEncrypted | 에러 메시지: "file is encrypted, password required" | open()으로 암호화된 파일을 열 때 반환 |
| 잘못된 비밀번호 | Error::IncorrectPassword | 에러 메시지: "incorrect password" | open_with_password()에 잘못된 비밀번호 전달 시 반환 |
| 미지원 방식 | Error::UnsupportedEncryption(String) | 에러 메시지: "unsupported encryption method: ..." | 지원하지 않는 암호화 버전 |
암호화된 파일 감지
open()이 암호화된 파일을 만나면 파싱을 시도하지 않고 Error::FileEncrypted를 반환합니다. 이런 파일을 열려면 open_with_password()를 사용합니다.
Rust:
match Workbook::open("file.xlsx") {
Ok(wb) => { /* 암호화되지 않은 파일 */ }
Err(sheetkit::Error::FileEncrypted) => {
let wb = Workbook::open_with_password("file.xlsx", "password")?;
}
Err(e) => return Err(e),
}TypeScript:
try {
const wb = Workbook.openSync("file.xlsx");
} catch (e) {
if (e instanceof Error && e.message.includes("encrypted")) {
const wb = Workbook.openWithPasswordSync("file.xlsx", "password");
}
}암호화 상세 사양
| 항목 | 값 |
|---|---|
| 알고리즘 | AES-256-CBC |
| 해시 | SHA-512 |
| 키 유도 반복 횟수 | 100,000 |
| 세그먼트 크기 | 4,096 bytes |
| 데이터 무결성 | HMAC-SHA512 |
| 컨테이너 형식 | OLE/CFB (Compound File Binary) |
30. 대량 데이터 전송
SheetKit은 Node.js 바인딩에서 시트 데이터를 읽기 위한 세 가지 접근 방식을 제공하며, 각각 메모리와 성능 특성이 다릅니다. 세 가지 모두 내부적으로 동일한 바이너리 Buffer 프로토콜을 사용합니다.
getRows(sheet) (TypeScript 전용)
셀 데이터를 기존 JsRowData[] 형식으로 반환합니다. 바이너리 Buffer가 투명하게 디코딩되므로 반환 타입은 이전 버전과 하위 호환됩니다. 각 행은 열 이름, 타입, 값을 가진 셀 객체 배열을 포함합니다.
const rows = wb.getRows('Sheet1');
for (const row of rows) {
for (const cell of row.cells) {
console.log(`${cell.column}: ${cell.value ?? cell.numberValue ?? cell.boolValue}`);
}
}가장 단순한 API로 기존 코드를 변경할 필요가 없습니다. Buffer 전송은 이전의 셀별 napi 객체 생성에 따른 FFI 오버헤드를 제거하지만, 디코더가 여전히 모든 셀에 대해 JS 객체를 생성합니다.
getRowsBuffer(sheet) (TypeScript 전용)
Rust에서 생성된 raw 바이너리 Buffer를 반환합니다. 가장 저수준 API이며, 커스텀 디코더에 데이터를 전달하거나, 네트워크로 전송하거나, SheetData 클래스와 함께 지연 접근에 사용할 때 유용합니다.
const buf: Buffer = wb.getRowsBuffer('Sheet1');Buffer는 아키텍처 문서에 기술된 SKRD 바이너리 형식을 따릅니다.
SheetData 클래스 (TypeScript 전용)
SheetData 클래스는 raw Buffer를 래핑하여 전체 시트를 디코딩하지 않고도 개별 셀이나 행에 O(1) 임의 접근을 제공합니다. @sheetkit/node/sheet-data에서 임포트합니다.
import { SheetData } from '@sheetkit/node/sheet-data';
const buf = wb.getRowsBuffer('Sheet1');
const sheet = new SheetData(buf);속성:
| 속성 | 타입 | 설명 |
|---|---|---|
rowCount | number | Buffer 내 행 수 |
colCount | number | 열 수 (경계 사각형 너비) |
메서드:
getCell(row, col)
단일 셀의 디코딩된 값을 반환하며, 비어 있으면 null을 반환합니다. 행과 열은 1부터 시작합니다. 반환 타입은 셀 타입에 따라 달라집니다: 숫자 및 날짜 셀은 number, 문자열/에러/수식 셀은 string, 불리언 셀은 boolean.
const value = sheet.getCell(1, 1); // 1행, 1열 (A1)
const price = sheet.getCell(5, 3); // 5행, 3열 (C5)getCellType(row, col)
셀의 타입 이름을 문자열로 반환합니다: 'empty', 'number', 'string', 'boolean', 'date', 'error', 'formula', 또는 'string' (서식 있는 텍스트).
const type = sheet.getCellType(1, 1); // 'string'
if (type === 'number') {
const val = sheet.getCell(1, 1);
}getRow(rowNum)
단일 행의 디코딩된 값 배열을 반환합니다. 빈 셀은 null입니다. 행 번호는 1부터 시작합니다.
const row = sheet.getRow(1); // [value, value, null, value, ...]toArray()
모든 행을 디코딩하여 2차원 배열을 반환합니다. 각 요소는 빈 행이면 []이고, 그 외에는 빈 셀이 null인 값 배열입니다.
const data = sheet.toArray();
for (const row of data) {
console.log(row);
}rows() (제너레이터)
빈 행을 포함하여 각 행에 대해 { row: number, values: Array } 객체를 yield합니다. 스트리밍 방식의 반복에 유용합니다.
for (const { row, values } of sheet.rows()) {
console.log(`Row ${row}:`, values);
}columnName(colIndex)
0 기반 열 인덱스 (Buffer의 경계 사각형 기준)를 Excel 열 이름으로 변환합니다.
sheet.columnName(0); // 'A' (데이터가 A열에서 시작하는 경우)
sheet.columnName(25); // 'Z'API 사용 시기
| API | 메모리 | 지연 시간 | 적합한 경우 |
|---|---|---|---|
getRows() | 중간 (모든 셀을 객체로 디코딩) | 전체를 한 번에 | 하위 호환성, 모든 셀 순회 |
getRowsBuffer() + SheetData | 낮음 (Buffer + 필요 시 디코딩) | 접근 시마다 | 대용량 시트, 임의 접근, 셀 일부만 읽기 |
getRowsBuffer() (raw) | 최소 (Buffer만) | 없음 | 커스텀 디코더, 네트워크 전송, 캐싱 |
31. 시트 보기 옵션
시트 보기 옵션은 Excel UI에서 워크시트가 표시되는 방식을 제어합니다. 눈금선, 수식 표시, 확대/축소 수준, 보기 모드, 스크롤 위치 등을 포함합니다.
set_sheet_view_options(sheet, options) / setSheetViewOptions(sheet, options)
시트의 표시 옵션을 설정합니다. None/undefined가 아닌 필드만 적용되며 나머지 설정은 보존됩니다.
Rust:
use sheetkit::sheet::{SheetViewOptions, ViewMode};
wb.set_sheet_view_options("Sheet1", &SheetViewOptions {
show_gridlines: Some(false),
show_formulas: Some(true),
zoom_scale: Some(150),
view_mode: Some(ViewMode::PageBreak),
top_left_cell: Some("C10".to_string()),
..Default::default()
})?;TypeScript:
wb.setSheetViewOptions("Sheet1", {
showGridlines: false,
showFormulas: true,
zoomScale: 150,
viewMode: "pageBreak",
topLeftCell: "C10",
});get_sheet_view_options(sheet) / getSheetViewOptions(sheet)
현재 시트 보기 표시 옵션을 반환합니다.
Rust:
let opts = wb.get_sheet_view_options("Sheet1")?;
println!("Zoom: {:?}", opts.zoom_scale);TypeScript:
const opts = wb.getSheetViewOptions("Sheet1");
console.log("Zoom:", opts.zoomScale);SheetViewOptions
| 필드 | Rust 타입 | TS 타입 | 설명 |
|---|---|---|---|
show_gridlines / showGridlines | Option<bool> | boolean? | 눈금선 표시 (기본값: true) |
show_formulas / showFormulas | Option<bool> | boolean? | 결과 대신 수식 표시 (기본값: false) |
show_row_col_headers / showRowColHeaders | Option<bool> | boolean? | 행/열 헤더 표시 (기본값: true) |
zoom_scale / zoomScale | Option<u32> | number? | 확대/축소 비율, 10-400 (기본값: 100) |
view_mode / viewMode | Option<ViewMode> | string? | 보기 모드 |
top_left_cell / topLeftCell | Option<String> | string? | 표시되는 왼쪽 상단 셀 (예: "A1") |
ViewMode
| Rust | TypeScript | 설명 |
|---|---|---|
ViewMode::Normal | "normal" | 일반 편집 보기 (기본값) |
ViewMode::PageBreak | "pageBreak" | 페이지 나누기 미리 보기 |
ViewMode::PageLayout | "pageLayout" | 페이지 레이아웃 보기 |
확대/축소 값이 10-400 범위 밖이면 오류를 반환합니다. 보기 옵션을 설정해도 기존 틀 고정 설정에는 영향을 주지 않습니다.
32. 시트 표시 여부
시트 표시 여부는 Excel UI에서 시트 탭이 보이는지를 제어합니다. 세 가지 상태가 있습니다: 표시(기본값), 숨김(사용자가 UI를 통해 숨김 해제 가능), 매우 숨김(코드를 통해서만 숨김 해제 가능).
set_sheet_visibility(sheet, visibility) / setSheetVisibility(sheet, visibility)
시트의 표시 상태를 설정합니다. 최소 하나의 시트는 항상 표시 상태여야 합니다. 이 시트를 숨기면 표시 가능한 시트가 없게 되는 경우 오류를 반환합니다.
Rust:
use sheetkit::sheet::SheetVisibility;
wb.new_sheet("Hidden")?;
wb.set_sheet_visibility("Hidden", SheetVisibility::Hidden)?;
wb.new_sheet("Secret")?;
wb.set_sheet_visibility("Secret", SheetVisibility::VeryHidden)?;TypeScript:
wb.newSheet("Hidden");
wb.setSheetVisibility("Hidden", "hidden");
wb.newSheet("Secret");
wb.setSheetVisibility("Secret", "veryHidden");get_sheet_visibility(sheet) / getSheetVisibility(sheet)
시트의 현재 표시 상태를 반환합니다.
Rust:
let vis = wb.get_sheet_visibility("Hidden")?;
assert_eq!(vis, SheetVisibility::Hidden);TypeScript:
const vis = wb.getSheetVisibility("Hidden"); // "hidden"SheetVisibility
| Rust | TypeScript | 설명 |
|---|---|---|
SheetVisibility::Visible | "visible" | 시트 탭이 표시됩니다 (기본값) |
SheetVisibility::Hidden | "hidden" | 숨김 상태이며 사용자가 UI를 통해 숨김 해제할 수 있습니다 |
SheetVisibility::VeryHidden | "veryHidden" | 숨김 상태이며 코드를 통해서만 숨김 해제할 수 있습니다 |
마지막으로 남은 표시 가능한 시트는 숨길 수 없습니다. 유일하게 표시된 시트를 숨기려고 하면 오류가 반환됩니다.
33. VBA 프로젝트 추출
.xlsm 파일에 저장된 VBA 매크로에 대한 읽기 전용 접근을 제공합니다.
get_vba_project() / getVbaProject()
xl/vbaProject.bin의 raw 바이너리 내용을 반환합니다. VBA 프로젝트가 없는 워크북의 경우 None/null을 반환합니다.
Rust:
let raw: Option<&[u8]> = wb.get_vba_project();TypeScript:
const raw: Buffer | null = wb.getVbaProject();get_vba_modules() / getVbaModules()
VBA 프로젝트 바이너리를 파싱하고, 모듈 소스 코드를 압축 해제하여 모듈 배열을 반환합니다. VBA 프로젝트가 없으면 None/null을 반환합니다. VBA 프로젝트가 손상된 경우 에러를 발생시킵니다.
Rust:
use sheetkit::vba::{VbaModule, VbaModuleType};
if let Some(modules) = wb.get_vba_modules()? {
for m in &modules {
println!("{}: {:?}", m.name, m.module_type);
println!("{}", m.source_code);
}
}TypeScript:
const modules = wb.getVbaModules();
if (modules) {
for (const m of modules) {
console.log(`${m.name}: ${m.moduleType}`);
console.log(m.sourceCode);
}
}VbaModule / JsVbaModule
| 필드 | Rust 타입 | TypeScript 타입 | 설명 |
|---|---|---|---|
name | String | string | 모듈 이름 |
source_code / sourceCode | String | string | 압축 해제된 VBA 소스 코드 |
module_type / moduleType | VbaModuleType | string | standard, class, form, document, thisWorkbook 중 하나 |
34. Threaded Comments
Threaded comments(Excel 2019+)는 대화형 댓글 스레드를 지원합니다. 답글, 공유 person list를 통한 작성자 추적, 해결(done) 상태를 지원합니다. 레거시 댓글과 별도로 xl/threadedComments/threadedComment{N}.xml 파트에 저장됩니다.
add_threaded_comment / addThreadedComment
셀에 threaded comment를 추가합니다. 작성자가 person list에 없으면 자동으로 추가됩니다. 생성된 댓글 ID를 반환합니다.
Rust:
use sheetkit::ThreadedCommentInput;
let comment_id = wb.add_threaded_comment(
"Sheet1",
"A1",
&ThreadedCommentInput {
author: "Alice".into(),
text: "Please review this value.".into(),
parent_id: None,
},
)?;
// Reply to an existing comment
wb.add_threaded_comment(
"Sheet1",
"A1",
&ThreadedCommentInput {
author: "Bob".into(),
text: "Looks correct to me.".into(),
parent_id: Some(comment_id.clone()),
},
)?;TypeScript:
const commentId = wb.addThreadedComment("Sheet1", "A1", {
author: "Alice",
text: "Please review this value.",
});
// Reply to an existing comment
wb.addThreadedComment("Sheet1", "A1", {
author: "Bob",
text: "Looks correct to me.",
parentId: commentId,
});get_threaded_comments / getThreadedComments
시트의 모든 threaded comment를 반환합니다.
Rust:
let comments = wb.get_threaded_comments("Sheet1")?;
for c in &comments {
println!("{}: {} (by {})", c.cell_ref, c.text, c.author);
}TypeScript:
const comments = wb.getThreadedComments("Sheet1");get_threaded_comments_by_cell / getThreadedCommentsByCell
특정 셀의 threaded comment를 반환합니다.
Rust:
let comments = wb.get_threaded_comments_by_cell("Sheet1", "A1")?;TypeScript:
const comments = wb.getThreadedCommentsByCell("Sheet1", "A1");delete_threaded_comment / deleteThreadedComment
댓글 ID로 threaded comment를 삭제합니다. 댓글을 찾을 수 없으면 오류를 반환합니다.
Rust:
wb.delete_threaded_comment("Sheet1", &comment_id)?;TypeScript:
wb.deleteThreadedComment("Sheet1", commentId);resolve_threaded_comment / resolveThreadedComment
threaded comment의 해결(done) 상태를 설정합니다.
Rust:
wb.resolve_threaded_comment("Sheet1", &comment_id, true)?;TypeScript:
wb.resolveThreadedComment("Sheet1", commentId, true);add_person / addPerson
공유 person list에 사람을 추가합니다. 같은 표시 이름의 사람이 이미 있으면 기존 ID를 반환합니다.
Rust:
use sheetkit::PersonInput;
let person_id = wb.add_person(&PersonInput {
display_name: "Alice".into(),
user_id: Some("alice@example.com".into()),
provider_id: Some("ADAL".into()),
});TypeScript:
const personId = wb.addPerson({
displayName: "Alice",
userId: "alice@example.com",
providerId: "ADAL",
});get_persons / getPersons
person list의 모든 사람을 반환합니다.
Rust:
let persons = wb.get_persons();TypeScript:
const persons = wb.getPersons();ThreadedCommentInput / JsThreadedCommentInput
| 필드 | Rust 타입 | TypeScript 타입 | 설명 |
|---|---|---|---|
author | String | string | 작성자 표시 이름 (person list에 자동 추가) |
text | String | string | 댓글 텍스트 |
parent_id / parentId | Option<String> | string? | 답글 시 부모 댓글 ID |
ThreadedCommentData / JsThreadedCommentData
| 필드 | Rust 타입 | TypeScript 타입 | 설명 |
|---|---|---|---|
id | String | string | 고유 댓글 ID |
cell_ref / cellRef | String | string | 셀 참조 (예: "A1") |
text | String | string | 댓글 텍스트 |
author | String | string | 작성자 표시 이름 |
person_id / personId | String | string | person list의 사람 ID |
date_time / dateTime | String | string | ISO 8601 타임스탬프 |
parent_id / parentId | Option<String> | string? | 부모 댓글 ID (답글인 경우) |
done | bool | boolean | 해결(done) 상태 |
PersonInput / JsPersonInput
| 필드 | Rust 타입 | TypeScript 타입 | 설명 |
|---|---|---|---|
display_name / displayName | String | string | 사람 표시 이름 |
user_id / userId | Option<String> | string? | 사용자 식별자 (예: 이메일) |
provider_id / providerId | Option<String> | string? | ID 공급자 식별자 |
PersonData / JsPersonData
| 필드 | Rust 타입 | TypeScript 타입 | 설명 |
|---|---|---|---|
id | String | string | 고유 사람 ID |
display_name / displayName | String | string | 사람 표시 이름 |
user_id / userId | Option<String> | string? | 사용자 식별자 |
provider_id / providerId | Option<String> | string? | ID 공급자 식별자 |
35. 에러 타입
실패할 수 있는 모든 작업은 Rust에서 Result<T, Error>를 반환합니다. TypeScript에서는 Rust 에러 메시지를 포함한 JavaScript Error 객체가 throw됩니다.
Error Enum 레퍼런스
셀 및 참조 에러
| Variant | 메시지 | 설명 |
|---|---|---|
InvalidCellReference(String) | invalid cell reference: {0} | 유효하지 않은 A1 스타일 참조 |
InvalidRowNumber(u32) | invalid row number: {0} | 행 번호가 1..=1,048,576 범위 밖 |
InvalidColumnNumber(u32) | invalid column number: {0} | 열 번호가 1..=16,384 범위 밖 |
InvalidReference { reference } | invalid reference: {reference} | 유효하지 않은 셀 범위(sqref) |
InvalidMergeCellReference(String) | invalid merge cell reference: {0} | 잘못된 형식의 병합 범위 |
CellValueTooLong { length, max } | cell value too long: {length} characters (max {max}) | 값이 32,767자 제한을 초과 |
시트 에러
| Variant | 메시지 | 설명 |
|---|---|---|
SheetNotFound { name } | sheet '{name}' does not exist | 해당 이름의 시트가 워크북에 없음 |
SheetAlreadyExists { name } | sheet '{name}' already exists | 중복된 시트 이름 |
InvalidSheetName(String) | invalid sheet name: {0} | Excel 이름 규칙을 위반 |
스타일 에러
| Variant | 메시지 | 설명 |
|---|---|---|
StyleNotFound { id } | style not found: {id} | 스타일 ID가 스타일시트에 없음 |
CellStylesExceeded { max } | cell styles exceeded maximum ({max}) | 등록된 스타일 수가 너무 많음 |
ColumnWidthExceeded { width, max } | column width {width} exceeds maximum {max} | 열 너비 > 255 |
RowHeightExceeded { height, max } | row height {height} exceeds maximum {max} | 행 높이 > 409 |
OutlineLevelExceeded { level, max } | outline level {level} exceeds maximum {max} | 개요 수준 > 7 |
병합 셀 에러
| Variant | 메시지 | 설명 |
|---|---|---|
MergeCellOverlap { new, existing } | merge cell range '{new}' overlaps with existing range '{existing}' | 병합 범위가 겹침 |
MergeCellNotFound(String) | merge cell range '{0}' not found | 병합 범위가 존재하지 않음 |
수식 에러
| Variant | 메시지 | 설명 |
|---|---|---|
CircularReference { cell } | circular reference detected at {cell} | 수식에서 순환 참조 감지 |
UnknownFunction { name } | unknown function: {name} | 인식할 수 없는 함수 이름 |
WrongArgCount { name, expected, got } | function {name} expects {expected} arguments, got {got} | 잘못된 인수 개수 |
FormulaError(String) | formula evaluation error: {0} | 일반적인 수식 평가 실패 |
이름 정의 에러
| Variant | 메시지 | 설명 |
|---|---|---|
InvalidDefinedName(String) | invalid defined name: {0} | 이름에 금지된 문자 포함 |
DefinedNameNotFound { name } | defined name '{name}' not found | 정의된 이름이 존재하지 않음 |
기능별 에러
| Variant | 메시지 | 설명 |
|---|---|---|
PivotTableNotFound { name } | pivot table '{name}' not found | 피벗 테이블이 존재하지 않음 |
PivotTableAlreadyExists { name } | pivot table '{name}' already exists | 중복된 피벗 테이블 이름 |
TableNotFound { name } | table '{name}' not found | 테이블이 존재하지 않음 |
TableAlreadyExists { name } | table '{name}' already exists | 중복된 테이블 이름 |
TableColumnNotFound { table, column } | column '{column}' not found in table '{table}' | 테이블에 열이 없음 |
InvalidSourceRange(String) | invalid source range: {0} | 피벗 테이블 소스 범위가 유효하지 않음 |
SlicerNotFound { name } | slicer '{name}' not found | 슬라이서가 존재하지 않음 |
SlicerAlreadyExists { name } | slicer '{name}' already exists | 중복된 슬라이서 이름 |
ThreadedCommentNotFound { id } | threaded comment '{id}' not found | 댓글 ID가 존재하지 않음 |
ChartNotFound { sheet, cell } | no chart found at cell '{cell}' on sheet '{sheet}' | 해당 위치에 차트가 없음 |
PictureNotFound { sheet, cell } | no picture found at cell '{cell}' on sheet '{sheet}' | 해당 위치에 이미지가 없음 |
UnsupportedImageFormat { format } | unsupported image format: {format} | 지원하지 않는 이미지 형식 |
StreamWriter 에러
| Variant | 메시지 | 설명 |
|---|---|---|
StreamRowAlreadyWritten { row } | row {row} has already been written | 행은 오름차순으로 작성해야 합니다 |
StreamAlreadyFinished | stream writer already finished | Writer가 이미 완료됨 |
StreamColumnsAfterRows | cannot set column width after rows have been written | 열 설정은 행 작성 전에 해야 합니다 |
파일 I/O 및 ZIP 에러
| Variant | 메시지 | 설명 |
|---|---|---|
Io(std::io::Error) | I/O error: {0} | OS 수준의 I/O 에러 |
Zip(String) | ZIP error: {0} | ZIP 아카이브 읽기/쓰기 에러 |
XmlParse(String) | XML parse error: {0} | 잘못된 형식의 XML |
XmlDeserialize(String) | XML deserialization error: {0} | XML이 예상 스키마와 일치하지 않음 |
UnsupportedFileExtension(String) | unsupported file extension: {0} | .xlsx/.xlsm/.xltx/.xltm/.xlam이 아님 |
ZipSizeExceeded { size, limit } | ZIP decompressed size {size} bytes exceeds limit of {limit} bytes | 압축 해제 크기 안전 제한 초과 |
ZipEntryCountExceeded { count, limit } | ZIP entry count {count} exceeds limit of {limit} | 항목 수 안전 제한 초과 |
암호화 에러
| Variant | 메시지 | 설명 |
|---|---|---|
FileEncrypted | file is encrypted, password required | 파일에 비밀번호가 필요합니다 |
IncorrectPassword | incorrect password | 잘못된 복호화 비밀번호 |
UnsupportedEncryption(String) | unsupported encryption method: {0} | 알 수 없는 암호화 알고리즘 |
기타
| Variant | 메시지 | 설명 |
|---|---|---|
InvalidArgument(String) | invalid argument: {0} | 일반적인 유효하지 않은 매개변수 |
Internal(String) | internal error: {0} | 분류되지 않은 내부 에러 |
TypeScript 에러 처리
TypeScript에서 모든 에러는 표준 Error 객체로 throw됩니다. 메시지 문자열로 에러를 매칭할 수 있습니다:
try {
wb.getCellValue("NonExistent", "A1");
} catch (e) {
if (e instanceof Error && e.message.includes("does not exist")) {
console.log("Sheet not found");
}
}