HwpForge ๐ฅ
Rust๋ก ํ๊ธ(HWP/HWPX) ๋ฌธ์๋ฅผ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ์ ์ด
Hancom ํ๊ธ ํ์ผ ์ฝ๊ธฐ, ์ฐ๊ธฐ, ๋ณํ
HwpForge๋?
HwpForge๋ HWPX ๋ฌธ์(ZIP + XML, KS X 6101)๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ ์คํ์์ค ์์ Rust ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ํ๊ตญ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ์๋ํ๋ก์ธ์์ธ Hancom ํ๊ธ์ ์ต์ ํฌ๋งท์ ์ง์ํฉ๋๋ค.
์ง์ ๋ฒ์
| ํ๊ธ ๋ฒ์ | ํฌ๋งท | ์ฝ๊ธฐ | ์ฐ๊ธฐ | ์คํ์ผ ์ธํธ |
|---|---|---|---|---|
| ํ๊ธ 2014 ~ 2020 | HWPX (.hwpx) | โ | โ | Classic (18 styles) |
| ํ๊ธ 2022 ~ 2024 | HWPX (.hwpx) | โ | โ | Modern (22 styles, ๊ธฐ๋ณธ๊ฐ) |
| ํ๊ธ 2025+ | HWPX (.hwpx) | โ | โ | Latest (23 styles) |
| ํ๊ธ 97 ~ 2010 | HWP5 (.hwp) | โ | โ | โ |
- HWPX: OWPML ๊ตญ๊ฐํ์ค (KS X 6101) ๊ธฐ๋ฐ, ZIP + XML ์ปจํ ์ด๋
- HWP5: ๊ตฌํ ๋ฐ์ด๋๋ฆฌ ํฌ๋งท. ํ์ฌ๋ CLI ์ค์ฌ์ ์ฝ๊ธฐ/์ ๊ฒ/์ฌ์ถ๋ ฅ ๊ฒฝ๋ก๋ฅผ ์ ๊ณตํฉ๋๋ค (
convert-hwp5,audit-hwp5,census-hwp5) - ์คํ์ผ ์ธํธ๋
HancomStyleSetenum์ผ๋ก ์ ํ ๊ฐ๋ฅ (๊ธฐ๋ณธ: Modern)
LLM-first ์ค๊ณ ๐ฅ โ AI ์นํ์ ์ธ Markdown๊ณผ ๊ณต์ ํ๊ธ ๋ฌธ์ ํฌ๋งท(HWPX), ๋ ์ธ๊ณ๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ์์ต๋๋ค. LLM์ด Markdown์ผ๋ก ์์ฑํ ๋ด์ฉ์ ๊ณต๋ฌธ์ ๊ท๊ฒฉ์ HWPX๋ก ์ปดํ์ผ๋๊ณ ๐, ๋ฐ๋๋ก ๊ธฐ์กด HWPX ๋ฌธ์๋ AI๊ฐ ์ฝ๊ฒ ์ฝ์ ์ ์๋ ๊ตฌ์กฐ๋ก ๊บผ๋ผ ์ ์์ต๋๋ค โ๏ธ.
- ๐ HWPX ์์ ๊ฐ์ด๋ ๋ค์ด๋ก๋ โ HwpForge API๋ก ์์ฑํ 4์น์ ๋ฐ๋ชจ ๋ฌธ์ (ํ๊ธ์์ ์ด์ด๋ณด์ธ์)
- HWPX Reader for AI โ ๊ธฐ์กด ํ๊ธ ๋ฌธ์(.hwpx)๋ฅผ Markdown์ผ๋ก ๋ณํํ์ฌ LLM์ด ์ฆ์ ์ดํด ๊ฐ๋ฅ
- Full HWPX codec โ HWPX ํ์ผ์ ์์ค ์์ด ๋์ฝ๋ฉ/์ธ์ฝ๋ฉ (lossless roundtrip)
- Markdown bridge โ GFM Markdown๊ณผ HWPX ๊ฐ ์๋ฐฉํฅ ๋ณํ (์ฝ๊ธฐ + ์ฐ๊ธฐ)
- YAML style template โ Figma Design Token์ฒ๋ผ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์คํ์ผ ์ ์ (ํฐํธ, ํฌ๊ธฐ, ์์)
- Type-safe API โ branded index, typestate validation, zero unsafe code
๋น ๋ฅธ ์์
์ค์น
# Cargo.toml์ ์ถ๊ฐ
cargo add hwpforge
# Markdown ์ง์ ํฌํจ
cargo add hwpforge --features full
๋๋ Cargo.toml์ ์ง์ ์ถ๊ฐ:
hwpforge = "0.16"
๐จ Hammer โ CLI๋ก ์์ํ๊ธฐ
CLI ๋๊ตฌ hwpforge(Hammer)๋ฅผ ์ค์นํ๋ฉด ํฐ๋ฏธ๋์์ ๋ฐ๋ก ๋ฌธ์๋ฅผ ์์ฑํ๊ณ ํธ์งํ ์ ์์ต๋๋ค. hwpforge-bindings-cli๋ crates.io์ ๋ฐฐํฌ๋์ง ์์ผ๋ฏ๋ก(publish = false), git ๋๋ ๋ก์ปฌ ๊ฒฝ๋ก์์ ์ค์นํฉ๋๋ค. hwpforge-smithy-pdf(krilla) ์์กด์ผ๋ก ์ํฌ์คํ์ด์ค MSRV(1.88)๋ณด๋ค ๋์ Rust 1.92+๊ฐ ํ์ํฉ๋๋ค.
cargo install --git https://github.com/ai-screams/HwpForge hwpforge-bindings-cli
# ๋๋ clone ํ ๋ก์ปฌ ๊ฒฝ๋ก์์
git clone https://github.com/ai-screams/HwpForge && cd HwpForge
cargo install --path crates/hwpforge-bindings-cli
# Markdown โ HWPX ๋ณํ
# HWPX ๊ตฌ์กฐ ํ์ธ
# ๋ฌธ์ ๋ด๋น๊ฒ์ด์
๋งต ํ์ธ (์ ๋ชฉยทํยทํ๋ยท์ฑ
๊ฐํผ)
# HWPX โ Markdown ๋ณํ (AI๊ฐ ํ๊ธ ๋ฌธ์ ์ฝ๊ธฐ)
# HWPX โ JSON ์ถ์ถ (AI ํธ์ง์ฉ) โ -o ๋ ํ์, stdout ๋ด๋ณด๋ด๊ธฐ๋ ์์ต๋๋ค
# ์น์
JSON์ ์๋ณธ์ ๋์ฐ๊ธฐ (ํ
์คํธ๋ง ๋ฐ๋, ์๋ณธ์ด base)
# ๋ฌธ์ ์ ์ฒด JSON์ผ๋ก ์ ๋ฌธ์ ๋ง๋ค๊ธฐ (๊ตฌ์กฐ ๋ณ๊ฒฝ๊น์ง ๊ฐ๋ฅ)
# ๋ฌธ์ ๊ตฌ์กฐ ๊ฒ์ฆ (Core ๋ถ๋ณ์กฐ๊ฑด ํต๊ณผ ์ฌ๋ถ)
# JSON Schema ์ถ๋ ฅ (AI agent์ฉ)
๋ JSON์ ์๋ก ๋ค๋ฅธ ํ์์
๋๋ค. --section ์์ด ๋ฝ์ ๋ฌธ์ ์ ์ฒด JSON์ from-json์ด ์ฝ๊ณ , --section N์ผ๋ก ๋ฝ์ ์น์
JSON์ patch๊ฐ ์๋ณธ ๋ฌธ์๋ฅผ base๋ก ์ผ์ ๋์๋๋ค. ์๋ก ๋ฐ๊ฟ ๋ฃ์ผ๋ฉด ์คํค๋ง ๋ถ์ผ์น๋ก ๊ฑฐ๋ถ๋ฉ๋๋ค.
๋๋ฆํ ์ฑ์ฐ๊ธฐ๋ ๋๋ฆํ(click-here field)์ด ๋ค์ด ์๋ ์์ ๋ฌธ์์์๋ง ๋์ํฉ๋๋ค. ๋ฐฉ๊ธ convert๋ก ๋ง๋ ๋ฌธ์์๋ ๋๋ฆํ์ด ์์ด FIELD_NOT_FOUND๊ฐ ๋ฉ๋๋ค โ fields๋ก ์ด๋ฆ์ ๋จผ์ ํ์ธํ์ธ์.
PDF ๋ด๋ณด๋ด๊ธฐ๋ ๋ฌธ์์ ๋ค์ด ์๋ ์กฐํ ์บ์๋ฅผ ์ฌ์ํ๋ ๋ฐฉ์์ด๋ผ, ์บ์๊ฐ ์๋ ๋ฌธ์๋ง ๋ ๋ํ ์ ์์ต๋๋ค. ์บ์์ ์ถ์ฒ๋ ๋์
๋๋ค โ ํ์ปด์ด ์ ์ฅํ HWPX, ๊ทธ๋ฆฌ๊ณ HWP5์์ ์บ์๋ฅผ ์ค์ด ๋ณํํ HWPX(convert-hwp5 --carry-layout-cache). to-pdf๋ .hwp๋ฅผ ์ง์ ๋ฐ์ ๊ทธ ๋ณํ์ ๋์ ํด ์ฃผ๊ธฐ๋ ํฉ๋๋ค. ๋ฐ๋ฉด convert๋ from-json์ด ์๋ก ๋ง๋ ๋ฌธ์์๋ ์บ์๊ฐ ์์ด PDF_RENDER_FAILED๋ก ๊ฑฐ๋ถ๋ฉ๋๋ค. ๋ฌธ์๊ฐ ์ฐ๋ ํฐํธ๋ ํธ์คํธ์ ์์ด์ผ ํฉ๋๋ค(--font-dirยท--discovery๋ก ์ง์ , ์๋ ํฐํธ๋ฅผ ๋์ฒด ๊ธ๊ผด๋ก ๋ ๋ํ๋ ค๋ฉด --degraded).
HWP5์์ ์ค์ด ์จ ์บ์๋ PDF ์ฌ์ยท๋์กฐ ์ ์ฉ์
๋๋ค. ๊ทธ๋ ๊ฒ ๋ง๋ .hwpx๋ฅผ ํ์ปด์์ ๋ค์ ์ด ์ฉ๋๋ก ์ฐ์ง ๋ง์ธ์.
AI-first ์ค๊ณ: CLI๋ AI agent(Claude Code ๋ฑ)๊ฐ ์ฃผ ์ฌ์ฉ์์ ๋๋ค. Markdown์ผ๋ก ๋ฌธ์๋ฅผ ์์ฑํ ๋ค, JSON round-trip์ผ๋ก ๊ธฐ์กด ์คํ์ผ์ ๋ณด์กดํ๋ฉด์ section ๋จ์๋ก ์ ๋ฐํ๊ฒ ํธ์งํ ์ ์์ต๋๋ค.
--jsonํ๋๊ทธ๋ก ๋ชจ๋ ๋ช ๋ น์ด๊ฐ machine-readable ์ถ๋ ฅ์ ์ง์ํฉ๋๋ค.
โ๏ธ Anvil โ MCP Server (Beta)๋ก AI๊ฐ ์ง์ ํ๊ธ ๋ฌธ์๋ฅผ ๋ค๋ฃจ๋ค
Claude Code, Codex CLI, Claude, ChatGPT, Cursor, Antigravity ๋ฑ MCP ์ง์ AI ๋๊ตฌ์์ ํ๊ธ ๋ฌธ์๋ฅผ ์ง์ ์์ฑํ๊ณ ํธ์งํ ์ ์์ต๋๋ค. ํ์ฌ MCP surface๋ ๋ฒ ํ์ด๋ฉฐ, HWP5 ๊ฒฝ๋ก๋ MCP๊ฐ ์๋๋ผ CLI workflow๋ฅผ ์ฐ์ ํฉ๋๋ค. "๋ณด๊ณ ์ ๋ง๋ค์ด์ค"๋ผ๊ณ ๋งํ๋ฉด, AI๊ฐ ์์์ .hwpx ํ์ผ์ ๋๋ฑ ๋ง๋ค์ด๋
๋๋ค.
AI ๋๊ตฌ์ ๋ฑ๋ก
ํ ์ค์ด๋ฉด ์ค์น + ๋ฑ๋ก์ด ๋๋ฉ๋๋ค. npm์ npx -y๊ฐ ์๋์ผ๋ก ๋ฐ์ด๋๋ฆฌ๋ฅผ ๋ค์ด๋ก๋ํฉ๋๋ค.
# npm (๊ถ์ฅ โ Rust ํด์ฒด์ธ ๋ถํ์)
claude mcp add hwpforge -- npx -y @hwpforge/mcp
# Cargo (Rust ๊ฐ๋ฐ์์ฉ)
cargo install hwpforge-bindings-mcp && claude mcp add hwpforge hwpforge-mcp
# ๋ชจ๋ ํ๋ก์ ํธ์์ ์ฌ์ฉ (๊ธ๋ก๋ฒ)
claude mcp add --global hwpforge -- npx -y @hwpforge/mcp
~/.codex/config.toml์ ์ถ๊ฐ:
[mcp_servers.hwpforge]
command = "npx"
args = ["-y", "@hwpforge/mcp"]
๋๋ CLI๋ก:
codex mcp add hwpforge -- npx -y @hwpforge/mcp
์ค์ ํ์ผ์ ํธ์งํฉ๋๋ค:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Settings โ Tools โ Add MCP Server์์:
- Name:
hwpforge - Command:
npx -y @hwpforge/mcp
๋๋ ์ค์ ํ์ผ์ ์ง์ ํธ์ง:
ํ๋ก์ ํธ ๋ฃจํธ์ .cursor/mcp.json ์์ฑ:
... ๋๋กญ๋ค์ด โ MCP Store โ Manage MCP Servers โ View raw config (mcp_config.json)์ ์ถ๊ฐ:
๋ฑ๋กํ๋ฉด 19๊ฐ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค
| ๋๊ตฌ | ํ๋ ์ผ | ํ๋ง๋ |
|---|---|---|
hwpforge_convert |
Markdown โ HWPX ๋ณํ | "์ด ๋งํฌ๋ค์ด์ ํ๊ธ ํ์ผ๋ก!" |
hwpforge_inspect |
HWPX ๊ตฌ์กฐ ํ์ธ | "์ด ๋ฌธ์ ๋ญ๊ฐ ๋ค์ด์์ด?" |
hwpforge_to_json |
HWPX โ JSON ์ถ์ถ | "์ด ์น์ ๋ด์ฉ ์ข ๊บผ๋ด๋ด" |
hwpforge_patch |
JSON์ผ๋ก ์น์ ๊ต์ฒด | "์ด ๋ถ๋ถ๋ง ๋ฐ๊ฟ์ ๋ค์ ์ ์ฅํด" |
hwpforge_templates |
์คํ์ผ ํ๋ฆฌ์ ์กฐํ | "์ด๋ค ํ ํ๋ฆฟ ์ธ ์ ์์ด?" |
hwpforge_validate |
HWPX ๊ตฌ์กฐ/๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ | "์ด ํ์ผ ๋ฌธ์ ์๋์ง ํ์ธํด" |
hwpforge_restyle |
์คํ์ผ ํ๋ฆฌ์ ์ผ๊ด ์ ์ฉ | "์ด ๋ฌธ์ ํฐํธ ๋ฐ๊ฟ์ค" |
hwpforge_from_json |
JSON โ HWPX ์ง์ ์์ฑ | "์ด JSON์ผ๋ก ํ๊ธ ํ์ผ ๋ง๋ค์ด" |
hwpforge_to_md |
HWPX โ Markdown ๋ณํ | "์ด ํ๊ธ ๋ฌธ์๋ฅผ Markdown์ผ๋ก ๊บผ๋ด์ค" |
hwpforge_outline |
๋ฌธ์ ๋ด๋น๊ฒ์ด์ ๋งต(์ ๋ชฉยทํยทํ๋ยท์ฑ ๊ฐํผ) ์กฐํ | "์ด ๋ฌธ์ ๊ตฌ์กฐ๊ฐ ์ด๋ป๊ฒ ์๊ฒผ์ด?" |
hwpforge_diff |
๋ HWPX ํ์ผ์ semantic/package ์ฑ๋๋ก ๋น๊ต | "์ด ํธ์ง์ด ๋ญ ๋ฐ๊ฟจ๋์ง ํ์ธํด์ค" |
hwpforge_delete_para |
์ต์์ ๋ฌธ๋จ ์ญ์ (๊ตฌ์กฐ ํธ์ง) | "์ด ๋ฌธ๋จ ์ง์์ค" |
hwpforge_insert_para |
์ต์ปค ๋ฌธ๋จ ๊ธฐ์ค ์ ๋ฌธ๋จ ์ฝ์ | "์ด ๋ฌธ๋จ ๋ค์์ ๋ด์ฉ ์ถ๊ฐํด์ค" |
hwpforge_read |
๋ฌธ๋จ/ํ/ํ๋ ์ค ํ๋๋ฅผ ํ๊ฒ ์ฝ๊ธฐ | "์ด ๋ถ๋ถ๋ง ๋ฑ ์ฝ์ด์ค" |
hwpforge_fields |
๋๋ฆํ ํ๋ ๋ชฉ๋ก ์กฐํ | "์ฑ์ธ ์ ์๋ ์นธ์ด ๋ญ๊ฐ ์์ด?" |
hwpforge_fill |
๋๋ฆํ ํ๋์ ๊ฐ ์ฑ์ฐ๊ธฐ | "์ด ํ๋๋ค ์ฑ์์ค" |
hwpforge_stamp_plan |
๋ฌธ์ฅ ์ ๋น์นธ ์คํฌํ ํ๋ณด ํ์ | "์ฑ์ธ ์ ์๋ ๋น์นธ ํ๋ณด ์ฐพ์์ค" |
hwpforge_stamp |
ํ๋ณด๋ฅผ ๋๋ฆํ ํ๋๋ก ์น๊ฒฉ | "์ด ๋น์นธ๋ค์ ํ๋๋ก ๋ง๋ค์ด์ค" |
hwpforge_set_cell |
ํ ์ ์ ๊ทธ๋ฆฌ๋ ์ฃผ์๋ก ํธ์ง | "์ด ํ ์นธ ๊ฐ ๋ฐ๊ฟ์ค" |
์ ๋ฐ์ดํธ / ์ญ์
npm์ npx -y๊ฐ ํญ์ ์ต์ ๋ฒ์ ์ ๊ฐ์ ธ์ค๋ฏ๋ก ๋ณ๋ ์
๋ฐ์ดํธ๊ฐ ํ์ ์์ต๋๋ค.
# Cargo ์ฌ์ฉ์๋ง ํด๋น
์ MCP? CLI(Hammer)๋ AI๊ฐ
bash๋ช ๋ น์ ์คํํด์ผ ํ์ง๋ง, MCP(Anvil)๋ AI๊ฐ ๋ค์ดํฐ๋ธ ๋๊ตฌ๋ก ์ง์ ํธ์ถํฉ๋๋ค. ํ์ผ ๊ฒฝ๋ก ํ์ฑ๋, stdout ํด์๋ ํ์ ์์ต๋๋ค. JSON-RPC๋ก ์์ฒญํ๋ฉด ๊ตฌ์กฐํ๋ JSON์ผ๋ก ์๋ต โ ๊น๋ํฉ๋๋ค.
๐ Python์ผ๋ก ์์ํ๊ธฐ
pip install hwpforge
CPython 3.9+ ์ ์ฉ abi3 wheel์ Linux(manylinux_2_28 x86_64/aarch64)ยทmacOS(11+ arm64, 10.12+ x86_64)ยทWindows(x64)์ ๋ฐฐํฌํ๋ฉฐ, ๋ฐํ์ ์์กด์ฑ์ 0๊ฐ์
๋๋ค. pyproject.toml์ ์ง์ ํํ ๋๋ ์ ํ ํ(==)๋ณด๋ค ~=๋ฅผ ๊ถ์ฅํฉ๋๋ค โ Python ์ ์ฉ ํจ์น(X.Y.Z.N)๋ ์ ํ ํ์ผ๋ก๋ ๋ฐ์ ์ ์์ต๋๋ค.
=
= .
Document๋ ๋ถ๋ณ ๊ฐ์
๋๋ค โ ๋ชจ๋ ํธ์ง ๋ฉ์๋๋ ์๋ณธ์ ๊ทธ๋๋ก ๋ ์ฑ ์ ๋ฌธ์์ ๋ณด๊ณ ์๋ฅผ ๋ด์ DocumentResult๋ฅผ ๋ฐํํฉ๋๋ค. ๊ณต์ ์ฐ์ฐ ๊ณ์ธต์ ์ฐ๋ 18๊ฐ document ๋ฉ์๋ + 5๊ฐ ๋ชจ๋ ํจ์๋ฅผ ์ ๊ณตํ๋ฉฐ, ํธ์ถ ์ด๋ฆยท์ต์
ํ๊ธฐยทํธํ ์๋ฌ ์ฝ๋๋ ๋ฐ์ธ๋ฉ๋ง๋ค ๋ค๋ฅผ ์ ์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ hwpforge ํจํค์ง README๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๐จ ๋ฌธ์ ์์ฑ
use ;
use ;
let mut doc = new;
doc.add_section;
โ๏ธ HWPX๋ก ์ธ์ฝ๋ฉ
use ;
use ImageStore;
let validated = doc.validate.unwrap;
let style_store = with_default_fonts;
let image_store = new;
let bytes = encode.unwrap;
write.unwrap;
โ๏ธ HWPX ๋์ฝ๋ฉ
use HwpxDecoder;
let result = decode_file.unwrap;
println!;
โ๏ธ HWPX โ Markdown ๋ณํ (AI๊ฐ ํ๊ธ ๋ฌธ์ ์ฝ๊ธฐ)
use HwpxDecoder;
use MdEncoder;
let decoded = decode_file.unwrap;
let validated = decoded.document.validate.unwrap;
let markdown = encode_lossy.unwrap;
println!; // LLM์ด ๋ฐ๋ก ์ดํดํ ์ ์๋ Markdown
๊ธฐ์กด .hwpx ํ์ผ์ Markdown์ผ๋ก ๋ณํํ๋ฉด Claude, GPT ๋ฑ ์ด๋ค LLM์ด๋ ํ๊ธ ๊ณต๋ฌธ์๋ฅผ ์ฆ์ ์ฝ๊ณ ๋ถ์ํ ์ ์์ต๋๋ค.
โ๏ธ Markdown โ HWPX ๋ณํ
use MdDecoder;
use ;
let md_doc = decode_with_default.unwrap;
let bridge = from_registry.unwrap;
let rebound = bridge.rebind_draft_document.unwrap;
let validated = rebound.validate.unwrap;
let image_store = new;
let bytes = encode.unwrap;
Feature Flags
| Feature | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
hwpx |
Yes | HWPX encoder/decoder |
md |
โ | Markdown โ Core ๋ณํ |
full |
โ | ๋ชจ๋ ๊ธฐ๋ฅ ํฌํจ |
# Markdown ์ง์ ํฌํจ
= { = "0.16", = ["full"] }
๐ ์ง์ ์ฝํ ์ธ
| ์นดํ ๊ณ ๋ฆฌ | ์์ |
|---|---|
| ํ ์คํธ | Run, character shape, paragraph shape, style (22๊ฐ ํ์ปด ๊ธฐ๋ณธ ์คํ์ผ) |
| ๊ตฌ์กฐ | Table (์ค์ฒฉ), Image (๋ฐ์ด๋๋ฆฌ + ๊ฒฝ๋ก), TextBox, Caption |
| ๋ ์ด์์ | ๋ค๋จ, ํ์ด์ง ์ค์ , ๊ฐ๋ก/์ธ๋ก ๋ฐฉํฅ, ์ ๋ณธ ์ฌ๋ฐฑ, master page |
| ๋จธ๋ฆฌ๊ธ/๋ฐ๋ฅ๊ธ | Header, Footer, ์ชฝ๋ฒํธ (autoNum) |
| ๊ฐ์ฃผ/๋ฏธ์ฃผ | ๊ฐ์ฃผ, ๋ฏธ์ฃผ |
| ๋ํ | ์ , ํ์, ๋ค๊ฐํ, ํธ, ๊ณก์ , ์ฐ๊ฒฐ์ (์ฑ์, ํ์ , ํ์ดํ ์ง์) |
| ์์ | HancomEQN script ํ์ |
| ์ฐจํธ | 18์ข chart type (OOXML ํธํ) |
| ์ฐธ์กฐ | ์ฑ ๊ฐํผ, ์ํธ ์ฐธ์กฐ, ํ๋ (๋ ์ง/์๊ฐ/์์ฝ), ๋ฉ๋ชจ, ์์ธ |
| ๋ง๋ง/๊ฒน์นจ | ๋ง๋ง (dutmal), ๊ธ์ ๊ฒน์นจ |
| Markdown | GFM decode, lossy + lossless encode, YAML frontmatter |
| PDF ๋ด๋ณด๋ด๊ธฐ | ํ์ปด ์กฐํ ์บ์ ์ฌ์(๊ณ์ฐ ์๋) ๋ ๋ โ ํยท๋จธ๋ฆฌ๊ธ/๋ฐ๋ฅ๊ธยท์ชฝ๋ฒํธยทํฐํธ ํ์ดํ๋ผ์ธ |
์ํคํ ์ฒ
HwpForge๋ ๋ ์ด์ด๋ฅผ ๋๋ ์ ์๊ฐํ๋ ํ๋ก์ ํธ์ ๋๋ค.
foundation: ๊ณตํต primitive, unit, index, errorcore: ํฌ๋งท ๋ ๋ฆฝ ๋ฌธ์ ๋ชจ๋ธ๊ณผ shared semanticsblueprint: ์คํ์ผ ์ ์์ ํ ํ๋ฆฟ ๊ณ์ธตsmithy-*: ํฌ๋งท๋ณ codec๊ณผ bridge (๊ฐ ํฌ๋ ์ดํธ๋ ๋จ์ผ ํฌ๋งท๋ง ๋ด๋น)convert: ํฌ๋งท ๊ฐ ๋ณํ ์ค์ผ์คํธ๋ ์ดํฐ (HWP5 โ HWPX, smithy ์์์ ๋ ํฌ๋งท์ ์ฎ์)bindings-*: CLI / MCP / Python ์ง์ ์
hwpforge(umbrella crate)์ ops ๋ชจ๋๊ณผ hwpforge-convert์ ops ๋ชจ๋์ ์ธ ๋ฐ์ธ๋ฉ์ด ๊ณต์ ํ๋ ์ฐ์ฐ ๊ณ์ธต์
๋๋ค โ CLIยทMCPยทPython ๋ชจ๋ ์ด ๊ณ์ธต์ ๊ฑฐ์ณ ํธ์ถํฉ๋๋ค.
๋ ์ด์ด ๊ตฌ์กฐ
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '14px', 'lineColor': '#BDBDBD'}}}%%
flowchart TB
F["foundation<br/>primitive types<br/>units / indices / errors"]:::foundation
C["core<br/>shared document model<br/>list / tab / paragraph semantics"]:::core
B["blueprint<br/>style registry<br/>template authoring"]:::blueprint
SHX["smithy-hwpx<br/>HWPX read/write"]:::smithy
SH5["smithy-hwp5<br/>HWP5 read / audit / re-emission path"]:::smithy
SMD["smithy-md<br/>Markdown bridge"]:::smithy
SPDF["smithy-pdf<br/>layout-cache replay renderer"]:::smithy
CONV["convert<br/>HWP5 โ HWPX orchestrator"]:::convert
CLI["bindings-cli<br/>Hammer"]:::binding
MCP["bindings-mcp<br/>Anvil MCP Server"]:::binding
PY["bindings-py<br/>Python"]:::binding
F --> C
C --> B
C --> SHX
C --> SH5
C --> SMD
F --> SPDF
C --> SPDF
B --> SHX
B --> SMD
C --> CONV
SHX --> CONV
SH5 --> CONV
SHX --> CLI
SH5 --> CLI
SMD --> CLI
SPDF --> CLI
CONV --> CLI
SHX --> MCP
SMD --> MCP
SHX --> PY
classDef file fill:#FFFDE7,stroke:#F9A825,color:#5D4037
classDef smithy fill:#FFF3E0,stroke:#FB8C00,color:#E65100
classDef convert fill:#E0F2F1,stroke:#00897B,color:#004D40
classDef core fill:#E3F2FD,stroke:#42A5F5,color:#0D47A1
classDef blueprint fill:#F3E5F5,stroke:#AB47BC,color:#4A148C
classDef foundation fill:#FAFAFA,stroke:#BDBDBD,color:#424242
classDef binding fill:#E8F5E9,stroke:#43A047,color:#1B5E20
๋ฐ์ดํฐ ํ๋ฆ
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '14px', 'lineColor': '#BDBDBD'}}}%%
flowchart LR
HWP5[".hwp"]:::file --> SH5["smithy-hwp5"]:::smithy
MD[".md"]:::file --> SMD["smithy-md"]:::smithy
HWPX[".hwpx"]:::file <--> SHX["smithy-hwpx"]:::smithy
SH5 --> CORE["core document"]:::core
SHX <--> CORE
SMD <--> CORE
CORE --> SHX
CONV["convert<br/>HWP5โHWPX ์ค์ผ์คํธ๋ ์ด์
"]:::convert --> SH5
CONV --> SHX
CLI["CLI"]:::binding --> CONV
CLI --> SH5
CLI --> SHX
CLI --> SMD
MCP["MCP"]:::binding --> SHX
MCP --> SMD
classDef file fill:#FFFDE7,stroke:#F9A825,color:#5D4037
classDef smithy fill:#FFF3E0,stroke:#FB8C00,color:#E65100
classDef convert fill:#E0F2F1,stroke:#00897B,color:#004D40
classDef core fill:#E3F2FD,stroke:#42A5F5,color:#0D47A1
classDef binding fill:#E8F5E9,stroke:#43A047,color:#1B5E20
ํต์ฌ ์์น: ๊ตฌ์กฐ(Structure)์ ์คํ์ผ(Style)์ ๋ถ๋ฆฌํฉ๋๋ค. core๋ shared semantics์ ์คํ์ผ ์ฐธ์กฐ๋ฅผ ๋ค๊ณ , blueprint๋ ์คํ์ผ ์ ์๋ฅผ ๊ด๋ฆฌํฉ๋๋ค. smithy-* ๊ณ์ธต์ด ํฌ๋งท surface๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค.
์ค์ ์์ ๊ฐ์ฅ ์์ฃผ ์ฐ๋ ๊ฒฝ๋ก๋ ๋ค์ ์ ์ ๋๋ค.
- Markdown โ HWPX
- HWPX โ Markdown / JSON
- HWP5 โ HWPX โ audit / inspect
ํ๋ก์ ํธ ํํฉ
| ์งํ | ๊ฐ |
|---|---|
Tracked Rust src LOC |
~114,421 |
| ํ ์คํธ | ~3,476 passed + 14 skipped (cargo-nextest, 2026-08-28 make ci ๊ธฐ์ค) |
| ์์ค ํ์ผ | 228 .rs |
| Crate ์ | 12๊ฐ |
| ์ปค๋ฒ๋ฆฌ์ง | 90%+ |
| Clippy ๊ฒฝ๊ณ | 0 |
| Unsafe ์ฝ๋ | 0 |
๊ฐ๋ฐ
ํ์ ์๊ตฌ์ฌํญ
- Rust 1.88+ (์ํฌ์คํ์ด์ค MSRV) โ
hwpforge-bindings-cliCLI๋ฅผ ์ง์ ๋น๋/์ค์นํ๋ ค๋ฉด krilla ์์กด์ผ๋ก 1.92+ ํ์ - (๊ถ์ฅ) cargo-nextest โ ๋ณ๋ ฌ ํ ์คํธ ์คํ
- (์ ํ) pre-commit โ git hook ์๋ํ
MSRV ์ ์ฑ
- ์ํฌ์คํ์ด์ค ๊ธฐ๋ณธ MSRV๋ Rust 1.88์ด๋ฉฐ, stable์์ 4 ๋ฆด๋ฆฌ์ค ๋ค์ฒ์ง ๋ฒ์ ์ ๊ธฐ๋ณธ ์ ์ฑ ์ผ๋ก ์ ์งํฉ๋๋ค.
- krilla ์์กด ๊ฒฝ๋ก์ ์๋ ๋ค ํฌ๋ ์ดํธ(
hwpforge-smithy-pdfยทhwpforge-convertยทhwpforge-bindings-cliยทhwpforge-bindings-py)๋ Rust 1.92+๊ฐ ํ์ํฉ๋๋ค(rust-version์ ํฌ๋ ์ดํธ๋ณ๋ก ์ํฅ ์ง์ ). CI์Verify โบ MSRV (1.88)job์ ์ด ๋ท์ 1.88 ๊ฒ์ฆ ํจ์ค์์๋ ์ ์ธํ์ง๋ง, ๊ฐ์ job ์์์cargo +1.92 check๋ก ๋ฐ๋ก ๊ฒ์ฆํฉ๋๋ค โ ๊ฒ์ฆ ๋์์์ ๋น ์ง๋ ๊ฒ์ด ์๋๋๋ค. - ๊ฐ ํฌ๋ ์ดํธ์
Cargo.toml์rust-version์ด ๊ทธ ํฌ๋ ์ดํธ์ ์ค์ MSRV์ด๋ฉฐ, CI์Verify โบ MSRVjob์ด ์ํฌ์คํ์ด์ค ๊ธฐ๋ณธ๊ฐ(1.88)์ ๊ฒ์ฆํฉ๋๋ค. - MSRV ์ํฅ์ด ํ์ํ๋ฉด PR์์ ์ด์ ๋ฅผ ๋ช
์ํ๊ณ ,
Cargo.toml, CI, CHANGELOG๋ฅผ ํจ๊ป ๊ฐฑ์ ํฉ๋๋ค. - ๊ฐ๋ฐ์ฉ ๊ธฐ๋ณธ ํด์ฒด์ธ์ ๋ ์ต์ ์ผ ์ ์์ต๋๋ค. ํธํ์ฑ ํ๋จ ๊ธฐ์ค์ ์ต์ stable์ด ์๋๋ผ MSRV + CI ํต๊ณผ ์ฌ๋ถ์ ๋๋ค.
โ๏ธ ๋ช ๋ น์ด
make ci๋ CI ์ ์ฒด๊ฐ ์๋๋ผ ๊ทธ์ค ๋น ๋ฅธ ๋ค์ฏ ๋ ์ธ์
๋๋ค. CI๋ ์ฌ๊ธฐ์ CoverageยทMSRVยทHWP5 Audit GateยทDocs BuildยทPythonยทWorkflow Lint๋ฅผ ๋ ๋๋ฆฝ๋๋ค. ๋ฆด๋ฆฌ์ค๋ ํฐ ๋ณ๊ฒฝ ์ ์๋ make ci-full๊ณผ make py-all์, ๋ฌธ์๋ฅผ ๊ณ ์ณค๋ค๋ฉด mdbook build๋ฅผ ํจ๊ป ๋๋ฆฌ์ธ์.
๋น๋ ๊ฐ์ (์ ํ):
sccache๊ฐ PATH์ ์์ผ๋ฉดmakeํ๊น์ด ์๋์ผ๋ก ์ปดํ์ผ ์บ์๋ก ์ฌ์ฉํฉ๋๋ค(์์ผ๋ฉด ๊ทธ๋๋ก ๋์ โ ์๋ฌด๊ฒ๋ ๊นจ์ง์ง ์์). ๋ฐ๋ณตmake ci๊ฐ ํฌ๊ฒ ๋นจ๋ผ์ง๋๋ค. ์ค์น:cargo install sccache๋๋brew install sccache. (๋ฆด๋ฆฌ์ค ํ์ดํ๋ผ์ธ release-plz ๋ ์ํฅ๋ฐ์ง ์์ต๋๋ค.)
ํ๋ก์ ํธ ๊ตฌ์กฐ
HwpForge/
โโโ crates/
โ โโโ hwpforge/ # Umbrella crate (re-exports)
โ โโโ hwpforge-foundation/ # ๊ธฐ๋ณธ ํ์
(HwpUnit, Color, Index<T>)
โ โโโ hwpforge-core/ # ๋ฌธ์ ๋ชจ๋ธ (์คํ์ผ ์ฐธ์กฐ๋ง)
โ โโโ hwpforge-blueprint/ # YAML ํ
ํ๋ฆฟ (Figma ํจํด)
โ โโโ hwpforge-smithy-hwpx/ # HWPX codec (ZIP+XML โ Core)
โ โโโ hwpforge-smithy-md/ # Markdown codec (MD โ Core)
โ โโโ hwpforge-smithy-hwp5/ # HWP5 decode/projection + inspect helpers
โ โโโ hwpforge-smithy-pdf/ # PDF ๋ ๋๋ฌ (์กฐํ ์บ์ ์ฌ์ ๋ ๋๋ฌ, to-pdf ๊ฐ ์ฌ์ฉ)
โ โโโ hwpforge-convert/ # ํฌ๋งท ๊ฐ ๋ณํ ์ค์ผ์คํธ๋ ์ดํฐ (HWP5 โ HWPX)
โ โโโ hwpforge-bindings-py/ # Python bindings (shipped, PyPI)
โ โโโ hwpforge-bindings-cli/ # CLI ๋๊ตฌ (hwpforge, shipped)
โ โโโ hwpforge-bindings-mcp/ # MCP Server (hwpforge-mcp)
โโโ tests/ # ํตํฉ ํ
์คํธ + golden fixture
โโโ examples/ # curated showcase + interop artifacts
โโโ showcase/
โโโ interop/
๊ธฐ์ฌ
๋ฒ๊ทธ ์์ , ํฌ๋งท ๋ฆฌ์์น, ํ ์คํธ ๋ณด๊ฐ, ๋ฌธ์ ๊ฐ์ ๋ชจ๋ ํ์ํฉ๋๋ค.
- ์์ ์ ๊ฐ์ด๋: CONTRIBUTING.md
- ํนํ ํ์ธํ ๊ฒ: release-plz๊ฐ ์ฐ๋ ์ปค๋ฐ prefix (
feat,fix,perf,refactor) - ํนํ ํ์ธํ ๊ฒ: MSRV ์ ์ฑ ๊ณผ dependency/MSRV ์์น ๊ธฐ์ค
- ํนํ ํ์ธํ ๊ฒ: ๋ฌธ์ ๋ณ๊ฒฝ ์
mdbook build์ markdown lint ๊ฒ์ฆ - ํนํ ํ์ธํ ๊ฒ: ๋ก์ปฌ docs toolchain์ CI์ ๊ฐ์ pinned ๋ฒ์ (
mdbook 0.4.52,mdbook-admonish 1.20.0,mdbook-mermaid 0.16.2)์ ์ฐ๋ ํธ์ด ๋ซ์ต๋๋ค. ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์make install-tools - ํนํ ํ์ธํ ๊ฒ: CI required checks๋ฅผ ๊นจ์ง ์๋ ๋ฒ์์์์ ๋ณ๊ฒฝ ๋ถ๋ฆฌ
๋ก๋๋งต
ํ์ฌ ์ํ์ ๋ค์ ๋จ๊ณ
- HWP5 ์ฝ๊ธฐ/์ ๊ฒ/์ฌ์ถ๋ ฅ ๊ฒฝ๋ก โ
convert-hwp5,audit-hwp5,census-hwp5 - HWP5 public API ํ๋ โ umbrella crate surface์ broader parity ์ ๋ฆฌ
- MCP ์๋ฒ โ Claude, Cursor ๋ฑ AI ๋๊ตฌ๊ฐ tool๋ก ์ง์ HWPX ์์ฑยท๊ฒ์ฆยทํธ์ง (19๊ฐ ๋๊ตฌ + 4 ๋ฆฌ์์ค + 3 ํ๋กฌํํธ)
- CLI ๋๊ตฌ โ
hwpforge convert doc.md -o doc.hwpxํ ์ค ๋ณํ (23๊ฐ ๋ช ๋ น์ด: 20 core + 3 HWP5) - HWPX ์์ ์ง์ โ ์์ ์ปจํธ๋กค, ๋ณ๊ฒฝ ์ถ์ , OLE ๊ฐ์ฒด
- Python ๋ฐ์ธ๋ฉ โ
pip install hwpforge๋ก ์ค์น (PyPI, 0.16.5๋ถํฐ wheel ๋ฐฐํฌ)
๋ผ์ด์ ์ค
๋ค์ ์ค ํ๋๋ฅผ ์ ํํ ์ ์์ต๋๋ค:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
Acknowledgements
HwpForge๋ ๊ฑฐ์ธ๋ค์ ์ด๊นจ ์์ ์ ์์ต๋๋ค.
- Hancom โ HWPX ํฌ๋งท์ ๊ณต๊ฐ ๋ฌธ์์ KS X 6101 (OWPML) ๊ตญ๊ฐ ํ์ค์ด ์์๋ค๋ฉด ์ด ํ๋ก์ ํธ๋ ์์์กฐ์ฐจ ํ ์ ์์์ต๋๋ค. ํฌ๋งท์ ๊ณต๊ฐํด ์ฃผ์ Hancom์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
- openhwp โ Rust๋ก HWP/HWPX๋ฅผ ๋ค๋ฃจ๋ IR ๊ธฐ๋ฐ ์ํคํ ์ฒ ์ค๊ณ์์ ํฐ ์๊ฐ์ ๋ฐ์์ต๋๋ค. HwpForge์ Core ๋ ์ด์ด๊ฐ ์กด์ฌํ ์ ์์๋ ๊ฒ์ openhwp์ด ๋จผ์ ๊ทธ ๊ธธ์ ๊ฑธ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
- hwpxlib โ Java๋ก ์์ฑ๋ ๊ฐ์ฅ ์ฑ์ํ HWPX ๊ตฌํ์ฒด์ ๋๋ค. ์คํ๊ณผ ์ค์ ๋์์ ์ฐจ์ด๋ฅผ ํ์ ํ๋ ๋ฐ ๊ฒฐ์ ์ ์ธ ์ฐธ๊ณ ๊ฐ ๋์์ต๋๋ค.
- hwp.js โ HWP5 ํฌ๋งท์ quirks์ edge case๋ฅผ ๊ผผ๊ผผํ ๋ฌธ์ํํ ํ๋ก์ ํธ์ ๋๋ค. ๋ฐ์ด๋๋ฆฌ ํฌ๋งท์ ์ด๋์ด ๊ตฌ์์ ๋ฐํ ์ค ๋๋ถ์ ์ํ์ฐฉ์ค๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์์์ต๋๋ค.
- hwpx-owpml-model โ Hancom์ด ์ง์ ๊ณต๊ฐํ C++ OWPML ๋ชจ๋ธ ๊ตฌํ์ฒด๋ก, ์คํค๋ง ํด์์ ์ต์ข ๊ธฐ์ค์ผ๋ก ์ผ์์ต๋๋ค.
- Rust ์ํ๊ณ โ serde, quick-xml, pulldown-cmark, zip ๋ฑ ๋ฐ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค ๋๋ถ์ HwpForge ์ ์ฒด๋ฅผ zero unsafe ์์ Rust๋ก ๊ตฌํํ ์ ์์์ต๋๋ค. Rust ์ปค๋ฎค๋ํฐ์ Ferris ๐ฆ์๊ฒ ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
- Claude by Anthropic โ HwpForge์ ์ค๊ณ, ๊ตฌํ, ํ ์คํธ, ๋ฌธ์ํ ์ ๊ณผ์ ์์ Claude Code๊ฐ ๊ฐ๋ฐ ํํธ๋๋ก ํจ๊ปํ์ต๋๋ค. LLM-first๋ฅผ ํ๋ฐฉํ๋ ํ๋ก์ ํธ๋ต๊ฒ, AI์ ์ฌ๋์ด ํ์ ํ์ฌ ๋ง๋ค์ด๋ธ ๊ฒฐ๊ณผ๋ฌผ์ ๋๋ค.
- Codex by OpenAI โ HwpForge์ ๊ตฌํ ๊ฒํ , ๋ฆฌํฉํฐ๋ง, ํ๊ท ์ ๊ฒ, ์์ ํ๋ฆ ์ ๋ฆฌ ๊ณผ์ ์์ ์ค์ง์ ์ธ ๊ฐ๋ฐ ํํธ๋๋ก ๊ธฐ์ฌํ์ต๋๋ค.
์ ๋ถ๋ฆฌ Anvilscribe (SoeBuri Anvilscribe) ํ์ปด ๋ฌธ์๋ฅผ ๋ถ์ ๋ฌ๊ตฌ์ด ๋จ๋จํ๊ฒ ๋ฒผ๋ ค๋ด๋ ๋์ฅ์ฅ์ด ์ค๋ฆฌ๋๊ตฌ๋ฆฌ ๐ฅ