HotXLS는 나머지 파일을 파싱하거나 다시 압축하지 않고도 기존 XLSX 패키지 안의 워크시트 하나만 다시 쓸 수 있습니다. TXLSDirectWriter.BeginPatch는 원본 패키지를 열고, 대상 시트를 제외한 모든 항목을 압축된 바이트 그대로 옮긴 다음, 그 시트 하나만 평소처럼 AddSheet, AddRow, Write* 호출로 다시 작성하게 해 줍니다. 차트, 피벗 캐시, 테마, 스타일, 공유 문자열은 전혀 압축 해제되지 않습니다
이 워크플로가 해결하는 문제는 리포팅과 데이터 갱신에서 나타납니다. 비즈니스 팀에서 피벗 테이블, 슬라이서, 조건부 서식, 그리고 10년치 축적된 서식이 담긴 워크북이 도착합니다. 매일 밤 데이터 시트 하나를 최신 숫자로 교체해야 합니다. 워크북 전체를 로드하고 다시 저장하면 파일당 몇 분이 걸리고, 더 중요하게는 로딩 엔진이 재구성해야 하는 기능의 충실도까지 위험해집니다. 패치는 필요하지 않은 것은 건드리지 않음으로써 이 두 문제를 모두 피해 갑니다
압축된 바이트를 복사하는 것이 왜 흥미로운 부분일까?
압축된 수준에서 복사되는 zip 항목은 스트림 복사 비용만 듭니다. 같은 항목이 일반적인 쓰기 경로를 거치면 들어올 때 inflate, 나갈 때 deflate 비용이 들고, deflate가 비용이 더 큰 절반입니다. 큰 피벗 캐시와 임베드된 이미지 수십 개가 있는 워크북에서 이 차이는 새 시트를 쓰는 데 걸리는 시간만큼만 걸리는 패치와, 한 번도 들여다보지 않은 바이트를 다시 압축하는 데 대부분의 시간을 쓰는 패치의 차이입니다
HotXLS는 이를 위해 CopyCompressedFrom을 사용하며, 이는 원본 항목의 압축된 바이트를 대상 아카이브에 그대로 씁니다. 다른 압축 방식이나 취약한 암호화를 쓰는 등 그렇게 복사할 수 없는 항목이면, 작성기는 실패하는 대신 압축 해제된 스트림 복사로 후퇴합니다. 디렉터리 마커 항목은 건너뛰는데, 작성기가 자신만의 것을 만들어내기 때문입니다
제자리에서 교체하거나 새 파일로 쓰기
이 작업이 취하는 두 가지 형태는 오버로드 두 개로 다룹니다. 제자리 형태는 결과를 원본 옆의 임시 파일에 준비한 다음, 원본 핸들을 닫고, 삭제 후 이름을 바꾸므로 쓰는 도중 충돌이 나도 원본은 그대로 남습니다. 명시적 대상 형태는 원본을 건드리지 않고 시트를 교체하거나 새로 추가할 수 있습니다:
var
W: TXLSDirectWriter;
begin
W := TXLSDirectWriter.Create;
try
W.BeginPatch('monthly-dashboard.xlsx', 'Data'); // in place
W.AddSheet('Data');
W.AddRow(1);
W.WriteString(1, 'Region');
W.WriteString(2, 'Revenue');
W.AddRow(2);
W.WriteString(1, 'North');
W.WriteNumber(2, 184320.55);
W.AddRow(3);
W.WriteFormula(1, '=SUM(B2:B2)');
W.Close;
finally
W.Free;
end;
end;
삽입 변형은 원본과 대상 경로에 더해 InsertSheet를 받습니다:
// Source stays untouched; target gets an extra worksheet named Extra
W.BeginPatch('template.xlsx', 'output.xlsx', 'Extra', True);
W.AddSheet('Extra');
W.AddRow(1);
W.WriteString(1, 'appended by the nightly job');
W.Close;
삽입은 실제 장부 정리가 필요한 부분입니다. 작성기는 xl/workbook.xml의 시트 레지스트리와 각 시트를 자신의 파트에 묶는 관계 맵을 파싱한 다음, 다음으로 비어 있는 파트 번호, 시트 식별자, 관계 식별자를 선택합니다. 관계 타입은 원본 패키지의 관례를 따르므로, strict ISO 29500 워크북을 패치하면 strict 관계 타입이, 전이형 워크북을 패치하면 전이형 관계 타입이 나옵니다
패치가 의도적으로 버리고 제한하는 것
계산 체인은 두 모드 모두에서 버려집니다. 교체 모드에서는 그 항목들이 더는 그런 형태로 존재하지 않는 시트 안의 셀을 가리키고, 삽입 모드에서는 시트 인덱스가 밀리면서 아예 무효가 됩니다. Excel은 다음 재계산 때 체인을 다시 만들므로 이를 버리는 것은 손실이 아니라 올바른 처리입니다. 이 파트는 복사에서 제외되고, 그 관계 항목과 콘텐츠 타입 오버라이드도 정확히 함께 제거됩니다
패치 안에서는 두 가지 작성 규칙이 바뀌며, 둘 다 같은 원칙에서 나옵니다. 패치는 자신이 다시 쓰지 않은 파트를 건드려서는 안 됩니다. 문자열은 공유 문자열 테이블에 추가되는 대신 시트 안에 인라인으로 쓰이는데, 원본 테이블이 손대지 않은 채 그대로 넘어가기 때문입니다. 그리고 StyleIndex는 작성기가 만드는 스타일 테이블이 아니라 원본 패키지의 cellXfs 항목을 가리킵니다. 즉 원본 워크북이 이미 정의해 둔 서식을 참조할 수 있다는 뜻인데, 이는 데이터 갱신에서 대개 정확히 원하는 동작이지만, 동시에 어느 인덱스가 어떤 서식을 담고 있는지 알아야 한다는 뜻이기도 합니다
// Inside a patch, StyleIndex indexes the SOURCE package cellXfs.
// A date needs an explicit index that maps to a date format there:
W.WriteDateTime(3, EncodeDate(2026, 8, 22), DateStyleIndexFromTemplate);
// The style-free WriteDateTime overload is rejected in patch mode,
// because it assumes the writer's own style table, which a patch
// never creates
진입점 여섯 개는 잠겨 있습니다. 표, 차트, 이미지, 코멘트, 정의된 이름, 셀 스타일을 추가하려 하면 패치 모드에서는 예외가 발생하고, 닫는 시점에는 이들 카운터 중 하나라도 0이 아니면 실패하는 이중 안전장치가 하나 더 있습니다. 이 각각의 기능은 패치가 그대로 복사하는 파트를 편집해야 할 텐데, 절반만 편집된 패키지는 거부된 작업보다 나쁩니다. 한 번의 작업으로는 정확히 시트 하나만 패치할 수 있습니다
언제 패치하고 언제 로드해야 할까
워크북이 크고, 변경 사항이 시트 하나에 국한되며, 나머지 파일이 비트 단위로 그대로 살아남아야 할 때 패치가 옳은 도구입니다. 변경이 여러 시트에 걸쳐 있거나, 새로운 서식이나 새로운 객체가 필요하거나, 파일이 작아서 일반적인 로드와 저장에 비용이 거의 들지 않을 때는 잘못된 도구입니다. 처음부터 대량으로 생성하는 작업이라면 스트리밍 다이렉트 라이터에서 설명한 스트리밍 경로가 여전히 더 적합하며, 같은 AddRow와 Write* API를 공유하므로 둘 사이를 오가는 것은 기계적인 작업입니다
완전한 객체 모델을 원할 때 로드된 워크북 안에서 시트 단위로 조작하는 방법은 XLSX 패키지에서 워크시트 복제하기에서 다룹니다. 그리고 패치를 고려하는 이유가 워크북 전체 처리가 느려졌기 때문이라면, 대용량 워크북 성능의 측정치와 메모리 동작을 접근 방식을 선택하기 전에 읽어볼 가치가 있습니다
패치가 정말로 의도한 대로 됐는지 확인하기
세 가지 확인으로 거의 모든 실수를 잡아낼 수 있습니다. 살아남아야 할 파트가 아카이브에 여전히 있는지, xl/calcChain.xml이 사라졌는지, 그리고 TXLSXWorkbook으로 파일을 다시 열었을 때 예상한 시트 개수, 즉 교체라면 변화 없고 삽입이라면 하나 늘어난 개수가 보고되는지 확인하십시오. 패치된 시트를 다시 읽어 값과 수식 몇 개를 비교해 보면 검증의 고리가 닫힙니다
이 기능을 개발하며 얻은 구현 세부 사항 하나는 되풀이할 가치가 있습니다. 유사한 zip 수준 코드를 작성하는 누구든 발목을 잡을 수 있기 때문입니다. 워크시트 파트 이름은 접두사로 매칭되며, 접두사 길이의 사소한 오류 하나만 있어도 조건이 결코 매칭되지 않아, 새로 작성된 파트가 기존 이름과 충돌하고 같은 이름의 마지막 항목을 취하는 리더는 조용히 잘못된 시트를 골라잡게 됩니다. 패치가 두 시트의 내용을 뒤바꾼 것처럼 보인다면, XML보다 먼저 이름 매칭을 살펴보십시오
제자리 패치, 스트리밍 쓰기, 완전한 워크북 객체 모델은 모두 Delphi와 C++Builder용 같은 라이브러리 안에서 제공됩니다. 기능 목록은 HotXLS Delphi 스프레드시트 컴포넌트 페이지에 있습니다