XML Minifier
공백과 주석을 제거하여 XML 압축
XML 입력
압축된 XML
XML 압축이란?
XML 압축(미니피케이션)은 XML 문서의 의미를 변경하지 않으면서 불필요한 문자를 모두 제거하는 과정입니다. XML 미니파이어는 태그 사이의 공백을 제거하고, 주석을 삭제하며, 줄바꿈을 없애고, 들여쓰기를 축약하여 간결한 한 줄 출력을 생성합니다. 결과물은 원본 형식화된 버전과 동일하게 파서가 읽는 XML 문자열로, 동일한 데이터 모델을 생성합니다.
XML 1.0 사양(W3C 권고안, 5판)은 2.10절에서 공백 처리 규칙을 정의합니다. 태그 사이에 의미 없는 공백을 "불필요한 공백"이라고 하며, XML 프로세서는 이를 버릴 수 있습니다. 그러나 텍스트 내용 안의 공백은 부모 요소가 xml:space="default"를 선언하지 않는 한 기본적으로 의미 있는 공백입니다. 올바른 XML 미니파이어는 이 두 경우를 구분하여 안전하게 제거할 수 있는 공백만 제거합니다.
압축은 Gzip 또는 Brotli 같은 전송 계층 압축과 다릅니다. Gzip이나 Brotli는 전송 계층에서 크기를 줄이지만 파싱 전에 압축 해제가 필요합니다. 미니피케이션은 원시 문서 크기 자체를 줄이므로, XML은 별도의 압축 해제 없이 어떤 파서로도 읽을 수 있는 유효한 형태를 유지합니다. 실제로는 압축 전에 미니피케이션을 적용하면 최상의 결과를 얻을 수 있습니다. 먼저 중복 문자를 제거한 뒤 압축 알고리즘이 더 간결한 입력에 작동합니다.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Product catalog for Q1 2026 -->
<catalog>
<product id="p101">
<name>Widget A</name>
<price currency="USD">29.99</price>
<!-- Temporarily discounted -->
<stock>142</stock>
</product>
<product id="p102">
<name>Widget B</name>
<price currency="EUR">19.50</price>
<stock>87</stock>
</product>
</catalog><?xml version="1.0" encoding="UTF-8"?><catalog><product id="p101"><name>Widget A</name><price currency="USD">29.99</price><stock>142</stock></product><product id="p102"><name>Widget B</name><price currency="EUR">19.50</price><stock>87</stock></product></catalog>
XML 미니파이어를 사용하는 이유
들여쓰기와 주석이 있는 형식화된 XML은 개발 및 코드 리뷰에 이상적입니다. 저장, 전송, 기계 처리에는 추가 서식이 아무런 이점 없이 바이트만 차지합니다. XML 미니파이어가 그 차이를 해소합니다.
XML 미니파이어 활용 사례
XML 압축이 제거하는 항목
XML 문서의 모든 내용을 안전하게 제거할 수 있는 것은 아닙니다. 이 참조 표는 제거 가능한 각 유형의 내용과 그 제거가 항상 안전한지, 아니면 사용 사례에 따라 다른지 보여줍니다.
| 항목 | 예시 | 안전성 |
|---|---|---|
| Indentation | Spaces/tabs before tags | Always safe to remove |
| Line breaks | \n and \r\n between tags | Always safe to remove |
| Comments | <!-- ... --> | Safe unless parsed by app |
| XML declaration | <?xml version="1.0"?> | Keep if encoding is non-UTF-8 |
| Processing instructions | <?xml-stylesheet ...?> | Keep if consumed downstream |
| Trailing whitespace | Spaces after closing tags | Always safe to remove |
| Text node whitespace | Spaces inside text content | Remove only between tags, not within |
미니피케이션 vs Gzip vs 이진 형식
미니피케이션, 압축, 이진 인코딩은 각각 크기 문제의 다른 계층을 대상으로 합니다. 미니피케이션은 출력을 유효하고 사람이 읽을 수 있는 XML로 유지합니다. 압축(Gzip, Brotli)은 더 작게 줄이지만 파싱 전에 압축 해제 단계가 필요합니다. 이진 형식이 가장 작지만 연결 양쪽에 호환 가능한 인코더/디코더가 필요합니다 — 주로 임베디드 시스템이나 WSDL 중심의 엔터프라이즈 서비스에서 사용됩니다.
코드 예시
프로그래밍 방식으로 XML을 압축하는 패턴은 모든 언어에서 동일합니다. 문서를 트리로 파싱하고, 선택적으로 주석 노드를 제거한 뒤, 들여쓰기 없이 직렬화합니다.
// Minify XML by parsing and re-serializing (strips formatting)
const raw = `<root>
<item id="1">
<!-- note -->
<name>Test</name>
</item>
</root>`
const parser = new DOMParser()
const doc = parser.parseFromString(raw, 'application/xml')
// Remove comment nodes
const walker = doc.createTreeWalker(doc, NodeFilter.SHOW_COMMENT)
const comments = []
while (walker.nextNode()) comments.push(walker.currentNode)
comments.forEach(c => c.parentNode.removeChild(c))
const minified = new XMLSerializer().serializeToString(doc)
// → "<root><item id=\"1\"><name>Test</name></item></root>"from lxml import etree
xml = """<root>
<item id="1">
<!-- note -->
<name>Test</name>
</item>
</root>"""
tree = etree.fromstring(xml.encode())
# Remove comments
for comment in tree.iter(etree.Comment):
comment.getparent().remove(comment)
# Serialize without pretty-print (minified)
result = etree.tostring(tree, xml_declaration=False).decode()
# → '<root><item id="1"><name>Test</name></item></root>'
# With xml.etree (stdlib, no lxml needed)
import xml.etree.ElementTree as ET
root = ET.fromstring(xml)
ET.indent(root, space='') # Python 3.9+
print(ET.tostring(root, encoding='unicode'))package main
import (
"encoding/xml"
"fmt"
"strings"
)
func minifyXML(input string) (string, error) {
decoder := xml.NewDecoder(strings.NewReader(input))
var out strings.Builder
encoder := xml.NewEncoder(&out)
// No indentation = minified output
for {
tok, err := decoder.Token()
if err != nil {
break
}
// Skip comments
if _, ok := tok.(xml.Comment); ok {
continue
}
// Skip whitespace-only char data
if cd, ok := tok.(xml.CharData); ok {
if strings.TrimSpace(string(cd)) == "" {
continue
}
}
encoder.EncodeToken(tok)
}
encoder.Flush()
return out.String(), nil
}
// minifyXML("<a>\n <b>1</b>\n</a>") → "<a><b>1</b></a>"# Minify XML with xmllint (part of libxml2) xmllint --noblanks input.xml > minified.xml # Minify from stdin echo '<root> <item>hello</item> </root>' | xmllint --noblanks - # → <?xml version="1.0"?><root><item>hello</item></root> # Strip comments too (combine with sed or xmlstarlet) xmlstarlet ed -d '//comment()' input.xml | xmllint --noblanks - # Check size reduction echo "Before: $(wc -c < input.xml) bytes" echo "After: $(xmllint --noblanks input.xml | wc -c) bytes"