Delphi 및 C++Builder용 기본 Excel 라이브러리인 HotXLS는 무손실 XLSX 라운드트립(lossless XLSX round-trip)을 위해 특별히 설계되었습니다. 워크북을 열어 단 하나의 셀만 변경하고 저장하더라도 사용자의 커스텀 테마, 타사 프로그램이 기입한 extLst 확장 블록 및 계산 체인이 고스란히 유지됩니다. 이 작업은 xl/theme/theme1.xml 파일의 원본 바이트 캐싱, 알 수 없는 <ext> 블록에 대한 이벤트 기반 재직렬화(re-serialization), 그리고 수식이 포함된 워크북 저장 시마다 표준에 부합하는 새로운 xl/calcChain.xml 재생성이라는 세 가지 메커니즘을 통해 완벽히 작동합니다
이 세 가지 기능이 절실해지는 시나리오는 실무에서 정말 흔히 발생합니다. 청구서 발송 서비스가 고객이 엑셀로 정성껏 디자인한 서식 파일(회사 고유 컬러 테마 지정, KPI 열의 스파크라인 배치, 최신 Excel 버전에서 추가된 특수 조건부 서식 포함)을 로드하여, B3 셀에 인보이스 총액 수치 하나를 기입하고 저장했습니다. 그런데 고객이 출력된 파일을 열어보니 회사 브랜드 고유 컬러는 온데간데없고 기본 오피스 블루 색상으로 되돌아가 있으며 스파크라인 정보는 모두 유실되었고 Excel 프로그램은 파일에 오류가 있다며 '복구'를 제안합니다. 소스 코드 상에서는 해당 서식을 건드린 적이 없는데도 말입니다. 라이브러리가 단순 저장 과정에서 이 서식 정보들을 누락시켜 버린 것입니다
왜 라이브러리로 편집한 후에 Excel 파일의 서식이 유실될까요?
왜 라이브러리로 편집한 후에 Excel 파일의 서식이 유실될까요? 시중의 대부분의 라이브러리들은 파일을 '수정'하는 것이 아니라 처음부터 새로 '재빌드'하기 때문입니다. .xlsx 패키지는 실질적으로 여러 XML 부품(xl/workbook.xml, 시트별 xl/worksheets/sheetN.xml, xl/styles.xml, xl/theme/theme1.xml, xl/calcChain.xml 등)이 묶여 있는 ZIP 압축 구조입니다. 대다수 일반 라이브러리는 로드 시에 이 부품들을 파싱해 객체 모델로 적재한 후, 저장 시에 해당 모델 정보를 기반으로 XML 파일들을 새로 컴파일해 재배치합니다. 이때 라이브러리가 지원하지 않는 포맷 정보(파싱되지 않은 커스텀 테마나 최신 버전 엑셀이 삽입한 알 수 없는 확장 기능 블록)는 메모리 상에서 소멸하므로, 다시 써진 파일에는 해당 정보가 소리 소문 없이 누락됩니다
ECMA-376 표준 설계진은 이 호환성 문제의 절반을 사전에 인지하고 설계에 대비해 두었습니다. SpreadsheetML 규격에서는 extLst(ECMA-376 Part 1, '향후 기능 데이터 저장 영역', 워크북 단위 요소 기준 §18.2.10 규정)라는 지정된 확장 진입점을 정의하고 있습니다. 최신 버전의 엑셀 프로그램들은 신규 독자 기능을 이 영역의 <ext> 요소 내부에 적재하며, 개별 식별을 위한 uri 특성을 동봉하고 구형 판독기는 자신이 이해할 수 없는 영역이더라도 이를 보존해 유지해 주어야 한다는 규약입니다. 스파크라인, 슬라이서(slicer), 신형 조건부 서식들이 모두 이 원리로 전송됩니다. 따라서 알 수 없는 <ext> 블록들을 그냥 버리는 라이브러리는 단순히 호환성 수준이 낮을 뿐만 아니라, 파일 포맷 설계의 핵심인 '하위 호환성 계약' 자체를 위반하는 결함 제품입니다. 엑셀 라이브러리를 비교 검증할 때 던져야 할 질문은 명확합니다. '셀 하나를 수정했을 때 파일 내의 다른 데이터 중 어떤 것들이 함께 바뀌는가?'
HotXLS가 커스텀 테마를 1바이트 오차 없이 완벽히 보존하는 방법
HotXLS는 로드 시점에 xl/theme/theme1.xml 파일의 원본 바이트 배열을 고스란히 캐싱한 후, 저장할 때 이를 단 1바이트의 변경 भी 없이 그대로 재기록하는 방식으로 테마 서식을 보존합니다. 테마 리소스(ECMA-376 Part 1, §14.2.7 규정)는 SpreadsheetML이 아닌 DrawingML 규격(컬러 구조, 글꼴 설정, 그래픽 포맷 등)에 속하므로, 일반적인 스프레드시트 엔진이 이를 메모리 상에 객체 모델로 무겁게 로드해 해석할 필요가 없습니다. 초기 HotXLS 버전에서는 저장할 때마다 고정된 기본 오피스 테마 파일을 매번 강제 재기록하여 위와 같은 서식 파손을 겪기도 했으나, v2.89.46 버전 이후부터는 로드된 문서 내의 원본 테마 정보를 원시 형태(raw byte) 그대로 메모리에 유지하여 출력물에 적용하며 기본 내장 테마는 무에서 완전히 새로 작성되는 새 워크북에만 가동됩니다. 원시 바이트 그대로의 인코딩 재기록 방식은 데이터 손상을 방지하는 가장 완벽한 해법입니다. 파싱 작업도, 재직렬화 작업도 거치지 않으므로 서식이 변형될 위험이 0%이기 때문입니다
이 원본 재기록 메커니즘은 테마 수정 코드 설정보다 우선 적용됩니다. TXLSXWorkbook 클래스는 새 워크북 작성 시 제목 및 본문 폰트 스타일 지정을 돕는 ThemeMajorFont 및 ThemeMinorFont 속성을 노출하지만, 파일 로드 과정에서 원본 테마 바이트가 획득된 경우 이 설정 변경은 무시되며 무손실 라운드트립이 무조건 최우선 처리됩니다. 기존 워크북의 전체 테마 레이아웃 디자인을 변경해야 하는 상황이라면, 소스 코드로 이를 억지로 가공하기보다 엑셀 프로그램 상에서 서식 템플릿을 수정해 두는 것이 적합합니다. 평범한 읽기/쓰기 구현의 경우 별도의 API 조작이 필요 없습니다
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('branded-invoice.xlsx');
Book.Sheets[0].Cells[3, 2].Value := 42750.00; // the one edit
Book.SaveAs('branded-invoice-out.xlsx');
// theme1.xml in the output is byte-identical to the input
finally
Book.Free;
end;
end;
알 수 없는 extLst 블록은 저장 시 어떻게 처리되나요?
HotXLS는 자신이 지원하지 않는 워크시트 수준의 <ext> 블록들을 유연하게 수집하여 저장되는 시트의 extLst 영역에 고스란히 재적재해 줌으로써 최신 엑셀 버전으로 작성된 특수 서식 데이터도 손상 없이 라운드트립을 완수할 수 있게 돕습니다. v2.131.0 버전 이후부터는 수집된 임시 구문 조각들이 개별 XLSX 시트의 읽기 전용 속성인 RawWorksheetExts(TStringList 구조)를 통해 표출되므로, 모호한 추측 대신 디버깅 소스 코드 상에서 감지 상태를 완벽히 확인하고 테스트할 수 있습니다
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
i: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('from-newer-excel.xlsx');
Sheet := Book.Sheets[0];
WriteLn(Format('%d foreign ext block(s) captured',
[Sheet.RawWorksheetExts.Count]));
for i := 0 to Sheet.RawWorksheetExts.Count - 1 do
WriteLn(Copy(Sheet.RawWorksheetExts[i], 1, 100)); // peek at each uri
finally
Book.Free;
end;
end;
구현 상 확인해 둘 점은 이 수집 작업이 원시 바이트 단순 복사가 아닌, 이벤트 단계의 재직렬화(re-serialization) 방식으로 처리된다는 점입니다. HotXLS의 고속 XML 스트리밍 리더는 소스 오프셋 좌표를 제공하지 않으므로, 스트림 판독 과정을 스쳐 지나가는 Element, Text, EndElement 이벤트 트리거 데이터를 수집해 하위 서브트리 구조를 실시간 재구성합니다. 이 과정에서 한 가지 기술적 함정이 있습니다. 즉, <ext/>와 같이 단독으로 끝나는 자가 폐쇄형(self-closing) 태그는 비어있음을 나타내는 빈 Element 이벤트만 트리거하고 별도의 EndElement 이벤트를 일으키지 않으므로, 단순히 EndElement 감지 수치에 의존해 트리 깊이를 측정하는 분석 엔진은 트리 닫힘 시점을 찾지 못해 충돌하게 됩니다. HotXLS는 이 문제를 완벽히 다루어, 원래 데이터와 논리적으로 완벽히 동일한 수집 조각을 재작성해 냅니다. 속성 감싸기나 자가 폐쇄 형식이 표준 규격에 맞게 자동 정형화되므로 파일 바이너리가 100% 같지는 않지만 Excel 프로그램은 올바르게 인식해 냅니다. Excel 생성 구조의 두 가지 특징 덕분에 이 재적재 방식이 안전하게 구동됩니다. 바로 Excel 프로그램이 필요한 네임스페이스 xmlns 선언을 <ext> 요소나 그 내부에 확실하게 기재해 두므로 각 수집 조각이 독립된 네임스페이스 영역을 유지할 수 있다는 점과, 이 독립성 덕분에 워크시트 복제 API 실행 시에도 문자열 복사 지정만으로 알 수 없는 외부 서식 블록 정보들을 손쉽게 보존하여 전송할 수 있다는 점입니다
Excel 수식 작동 신뢰성을 보장하는 calcChain.xml 작성법
HotXLS는 저장 대상 워크북에 수식이 포함된 경우 xl/calcChain.xml(계산 체인 부품, ECMA-376 Part 1, §12.3.1 규정)을 항상 기입하며, 두 가지 배치 방식 중 하나를 사용해 셀 순서를 정렬합니다. 만일 계산 의존성 그래프가 이미 생성되어 최신 상태로 유지되고 있는 경우(값 편집 후 Recalculate 메서드를 가동한 상태), 선행 종속 계산 순서에 완벽히 일치하는 위상 정렬 순서대로 체인을 출력하며 순환 참조 오류가 있는 셀들은 맨 끝에 배치시킵니다. 반면 계산을 거치지 않은 상태라면 일반적인 문서 스캔 배치 순서대로 셀 리스트를 작성해 출력합니다. 두 가지 형태 모두 정상 규격입니다. Microsoft의 포맷 기술 가이드라인 [MS-XLSX]에 따르면 계산 체인은 Excel 로드 단계에서 스스로 검증해 순서를 재구성하기 전의 임시 '힌트(hint)'에 불과하므로 형식적 목록 기재만으로도 무방하며, HotXLS는 저장 연산 시 성능을 보존하기 위해 SaveAs 메서드 내부에서 의존성 그래프 구축을 강제하지 않습니다. 100만 개 수준의 대량 셀 저장 시 의존성 에지 링크 추적에 불필요한 성능 손실을 초래하는 것을 차단하기 위해서입니다
Book.Open('model.xlsx');
Book.Sheets[0].Cells[10, 4].Formula := '=SUM(D2:D9)';
// Saved now, calcChain.xml lists formula cells in document order.
// After Recalculate the dependency graph exists, so the same save
// emits a full topological order instead:
Book.Recalculate;
Book.SaveAs('model-out.xlsx');
어차피 엑셀이 알아서 재조정할 권장 권고(advisory) 헤더 영역에 왜 이토록 신경을 쓸까요? 바로 이 파일의 존재 여부 자체가 문서 신뢰성의 척도가 되기 때문입니다. 일부 타사 파일 판독 뷰어나 비교(diff) 엔진, 시스템 복구 모듈의 경우 수식이 수록된 파일 내부에는 당연히 계산 체인 데이터가 있을 것이라 상정하고 검사하므로, 이 파일을 누락시켜 생성하면 타 프로그램 연계 가동 시 비정상 손상 파일로 분류될 위험이 생깁니다. 규격에 맞는 올바른 체인을 제공하여 파일 형식을 완전히 갖추어 생성하는 것이, 조용하지만 매우 중요한 무손실 라운드트립 호환성 기술의 기초가 됩니다
무손실 라운드트립 신뢰성의 기술적 경계
마케팅 카탈로그 식의 단순 선전보다 기술적인 한계 경계를 투명하게 설명해 드리는 것이 중요합니다. HotXLS는 파일 패키지 전체의 100% 바이너리 복제를 실행하지는 않습니다. 시트 본문 XML, 스타일 설정, 공용 문자열 테이블 등은 파싱된 내부 모델에 맞춰 새로 갱신되어 다시 써지므로, 논리 정보는 완전하게 유지되지만 ZIP 파일 헤더의 DOS 타임스탬프 정보 등 바이너리 세부 구성이 바뀔 수 있습니다. 수집된 <ext> 구문 역시 앞서 기술한 대로 자동 정형화되어 재구성됩니다. 테마 원본 바이트 보존 시 소스 코드 상의 폰트 강제 재정의는 무시됩니다. 또한 무손실 보존망의 타겟 한계는 HotXLS가 직접 파싱하고 해석하는 규격 범위(예컨대 스파크라인 정보는 단순 바이너리 복사가 아닌 논리 파싱 후 재정형화됨)와 extLst 수집 처리 영역 및 바이트 보존 부품들에 국한됩니다. 라이브러리가 해석하지 못하며 확장 구문 영역에도 포함되지 않는 특수한 독자 플러그인의 애드온 영역 등은 보존망 범위를 벗어날 수 있으므로, 실제 제품군에 탑재하기 전에 사용 중인 특정 서식 파일과의 정합성 테스트를 먼저 거쳐 확인해 보시는 것을 권장합니다
이러한 정보 보호 장치는 타 하위 모듈들에도 잘 정렬되어 적용되어 있습니다. VBA 매크로 프로젝트 및 외부 워크북 참조 정보 역시 미해석 원본 보존 정책에 일치하도록 설계되어 보호되며, VBA 매크로 및 외부 링크 정보 보존 안내서에서 상세 원리를 확인할 수 있습니다. 또한 docProps 파일 속성 메타데이터 역시 누락되지 않고 별도의 문서 메타 정보 읽기/쓰기 전용 API를 통해 정상 처리됩니다. 엑셀 라이브러리의 완성도를 평가할 때 가장 확실한 방법은 '단일 셀 편집 테스트'를 가동하는 것입니다. 복잡한 수식과 서식이 가득한 시트 파일을 연 뒤, 단 하나의 수치 셀만 바꾸고 다른 이름으로 저장한 후, 두 ZIP 파일 내부의 개별 XML 원본 데이터들을 압축 해제하여 디프(diff) 도구로 정밀 대조해 보십시오. 개발자가 의도하지 않았던 다른 스타일이나 헤더들이 어떻게 보존되고 혹은 변형되었는지 눈으로 직접 비교해 보면, 라이브러리의 완성도를 가장 확실히 체감할 수 있습니다
본 문서에 안내해 드린 테마 바이너리 보존(v2.89.46 이후 도입), 미해석 extLst 수집 처리 및 calcChain.xml 체인 신뢰성 빌드(v2.131.0 이후 도입) 등 고품질 무손실 순환 처리 사양들은 모두 HotXLS Delphi Excel Component에 탑재되어 무상으로 제공됩니다. 제품 페이지를 방문하시면 Delphi 및 C++Builder 개발을 위한 전체 읽기/쓰기 상세 사양 정보를 조회하실 수 있습니다