XML을 바이트로 압축하는 법 — EXI와 이진 인코딩
W3C EXI를 중심으로 XML을 이진 형식으로 직렬화하는 토큰화·스키마 인지 인코딩 기법과 Java 구현 예시, IoT·메시징 활용 사례를 정리한다.
2026-08-13 · 최초 발행 2025-12-03
태그를 사전으로 바꾸면 벌어지는 일
XML은 태그와 속성 이름을 매 요소마다 반복해서 텍스트로 적는다. 같은 구조가 수백, 수천 번 반복되는 메시지라면 이 반복 자체가 낭비다. Binary XML은 XML Infoset이 담고 있는 정보를 유지하면서 이 반복을 이진 형식으로 직렬화하는 포맷들의 총칭이다. 텍스트 XML 대비 전송 크기를 줄이고 파싱 지연을 단축하며 CPU·메모리 효율을 개선하는 것이 목적이다. W3C EXI(Efficient XML Interchange), ITU-T Fast Infoset, WAP의 WBXML, Oracle Binary XML 같은 공급업체 구현까지 표준·변형이 여럿 존재하는데, 서로 다른 생태계이므로 상호운용성이 필요한 경우 EXI 채택이 권장된다. 네트워크 제약 환경, 초고빈도 메시징, 데이터베이스 내부 저장, 로그·텔레메트리, 모바일·차량·산업용 디바이스 통신이 주 대상 영역이다.
핵심 구성 요소
태그·속성·네임스페이스를 토큰과 공용 사전으로 매핑해 반복을 제거하는 것이 토큰화·사전 기반 인코딩이다. 동일한 구조가 반복될수록 압축 효율과 파서 캐시 히트율이 올라간다. 스키마 인지(schema-informed) 인코딩은 XSD 지식으로 타입·값 범위를 축약 표현해 정수·불리언·열거형 등을 비트 단위로 최적화하는데, 스키마가 바뀌면 버전 관리와 호환성 정책이 함께 필요해진다. 숫자·날짜/시간·QName·URI 같은 도메인별 코딩 룰을 적용하는 타입·값 전용 코덱은 문자열 처리 대비 디코딩 비용과 메모리 임시 객체를 줄인다. 이벤트 기반 파싱(SAX/EXI 이벤트)으로 저메모리 처리를 가능하게 하는 스트리밍·랜덤 액세스 지원도 있고, 일부 구현은 블록 인덱싱으로 문서 조각 단위 접근까지 제공한다. 마지막으로 헤더·사전·데이터 블록을 분리해 전송·저장 매체 특성에 맞춰 블록 크기를 조정하는 선택적 압축·블록화가 있으며, 델타·런렝스 같은 전처리와 범용 압축을 조합하면 추가 이득도 가능하다.
입력부터 출력까지, 그리고 실패했을 때
파이프라인은 입력 → 처리 → 출력으로 이어지되, 스키마 불일치·토큰 사전 충돌·무결성 검사 실패 같은 조건에서는 페일오버 경로가 정의돼 있어야 한다. 데이터베이스에 저장할 때는 같은 트랜잭션 경계 안에서 변환·검증을 수행하는 편이 일관성 유지에 유리하다.
텍스트·바이너리·EXI를 나란히 놓으면
| 항목 | 텍스트 XML | 바이너리 XML(일반) | EXI(스키마 기반) |
|---|---|---|---|
| 성능 | 파싱 비용 높음, 문자열 처리 과다 | 파싱/직렬화 비용 중간, 크기 30~60% 축소 | 파싱 비용 낮음, 크기 50~90% 축소 |
| 확장성 | CPU 병목, GC 압력 증가 | 네트워크/스토리지 효율 개선 | 대규모 스트리밍·IoT에 유리 |
| 일관성 | 사람 가독성 높음, 도구 다양 | 구현별 상호운용성 차이 | 표준 기반 상호운용성 우수 |
| 안정성 | 디버깅 용이 | 디버깅/관찰성 추가 도구 필요 | 스키마 의존, 버전 관리 중요 |
| 운영 편의 | 텍스트 기반 툴 풍부 | 모니터링/프록시 추가 구성 필요 | 게이트웨이·변환 서비스 권장 |
실무에서는 어디에 쓰이나
Oracle XML DB는 XMLType 컬럼에 Binary XML 저장 모델을 적용해 인덱싱·쿼리 효율을 높인다. 트랜잭션 경계 안에서 변환·검증을 수행하고 오류가 나면 텍스트 XML을 보존하는 전략이 권장된다. 메시징·스트리밍에서는 MQTT/AMQP/Kafka에 EXI 페이로드를 적용해 단말→엣지→클라우드 경로의 네트워크 비용을 줄이고, 게이트웨이에서 텍스트 XML↔EXI 양방향 트랜스코딩 서비스를 운영한다. 모바일·임베디드·차량 통신에서는 제한된 대역폭·전력 환경에서 OTA 업데이트 메타데이터나 진단 프레임에 EXI·Fast Infoset을 쓰고, 고정 사전과 스키마 사전을 미리 로딩해 부팅 지연을 최소화한다. 웹서비스·엔터프라이즈 통합에서는 SOAP with Fast Infoset으로 레거시 XML 웹서비스의 크기·지연을 줄이고, 서비스 레지스트리·프록시에서 application/exi 같은 Content-Type 협상을 적용한다.
운영 측면에서는 미러 포트에서 디코더 탭을 운영하며 샘플링 기반으로 텍스트를 재구성해 로깅하는 관찰성이 필요하고, 클라이언트 기능 협상(HTTP Accept)과 기능 플래그 롤아웃, 비지원 클라이언트를 위한 텍스트 폴백으로 호환성을 확보해야 한다. 보안 측면에서는 TLS 전송 계층 암호화 외에 무결성 태그와 사전 업데이트 서명 검증이 필요하며, 압축과 암호화의 순서를 정할 때 CRIME/BREACH 유사 리스크를 고려해야 한다.
Java로 직접 인코딩·디코딩해보기
전제조건은 JDK 17, Maven 3.9 이상, 네트워크 차단이 없는 빌드 환경이며, 라이브러리는 EXIficient 1.0.4를 쓴다.
Maven 의존성:
<dependency>
<groupId>com.siemens.ct.exi</groupId>
<artifactId>exificient</artifactId>
<version>1.0.4</version>
</dependency>
XML을 EXI로 인코딩하고 다시 디코딩하는 예제는 다음과 같다.
import com.siemens.ct.exi.EXIFactory;
import com.siemens.ct.exi.api.sax.EXIResult;
import com.siemens.ct.exi.api.sax.SAXFactory;
import com.siemens.ct.exi.core.EXIFactoryDefault;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import java.io.*;
public class ExiDemo {
public static byte[] encodeExi(String xml) throws Exception {
EXIFactory exiFactory = new EXIFactoryDefault();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
EXIResult exiResult = new EXIResult(exiFactory);
exiResult.setOutputStream(baos);
SAXParserFactory spf = SAXFactory.newSAXParserFactory();
XMLReader xmlReader = spf.newSAXParser().getXMLReader();
xmlReader.setContentHandler(exiResult.getHandler());
xmlReader.parse(new InputSource(new StringReader(xml)));
return baos.toByteArray();
}
public static String decodeExi(byte[] exiBytes) throws Exception {
EXIFactory exiFactory = new EXIFactoryDefault();
org.xml.sax.XMLReader exiReader = SAXFactory.createEXIReader(exiFactory);
StringWriter sw = new StringWriter();
org.xml.sax.helpers.DefaultHandler handler = SAXFactory.createXMLSerializer(sw);
exiReader.setContentHandler(handler);
exiReader.parse(new InputSource(new ByteArrayInputStream(exiBytes)));
return sw.toString();
}
public static void main(String[] args) throws Exception {
String xml = "<sensor><id>42</id><temp unit=\"C\">23.7</temp></sensor>";
byte[] exi = encodeExi(xml);
System.out.println("EXI size bytes = " + exi.length);
String roundtrip = decodeExi(exi);
System.out.println("XML roundtrip = " + roundtrip);
}
}
스키마 인지 모드를 쓰려면 XSD 로딩과 alignment 옵션 설정이 필요하고, 콘텐츠 협상에는 HTTP Content-Type: application/exi와 Vary 헤더 설정이 권장된다. 운영 환경에서는 오류에 대비해 텍스트 XML 보관·재생성 경로를 유지해야 한다.
수치로 보는 절감 폭
전송·저장 비용 측면에서는 EXI 스키마 기반 기준 페이로드 크기가 5090% 줄고, 동일 대역폭 대비 처리량이 25배까지 상승할 수 있다. 저장소 I/O가 줄면서 로그·이벤트 적재 TPS가 3070% 향상되는 것도 기대할 수 있는 효과다. 지연 시간·CPU 효율에서는 파싱·직렬화 시간이 3060% 단축되고 GC 힙 압력이 줄어 tail-latency가 안정화되며, 엣지·모바일 디바이스의 배터리 소모도 감소한다. 신뢰성·확장성 측면에서는 블록화·스트리밍이 백프레셔 대응을 쉽게 하고, 사전을 고정화하면 콜드스타트 지연이 최소화되며 장애 시 텍스트 폴백으로 복구 경로를 확보할 수 있다. 다만 이 수치들은 워크로드·스키마·라이브러리에 따라 변동하므로 사전 POC가 필요하다.
표준을 고를 것인가, 단순함을 고를 것인가
Binary XML은 XML의 구조적 장점을 유지하면서 네트워크·CPU·스토리지 비용을 동시에 줄이는 실용적 대안이다. 상호운용성이 핵심이면 EXI를 채택하고, 폐쇄형 내부 시스템이라면 구현이 단순한 바이너리 포맷을 고려하는 편이 낫다. 도입은 대상 트래픽을 식별하고 스키마 정합성을 확보한 뒤 게이트웨이 변환으로 파일럿을 돌리고, 관찰성·폴백을 구축한 다음 점진적으로 롤아웃하는 순서를 권장한다.