hwpforge-foundation 0.1.5

Foundation types for HwpForge (HwpUnit, Color, Interned IDs, Branded Index)
Documentation

HwpForge ๐Ÿ”ฅ

Rust๋กœ ํ•œ๊ธ€(HWP/HWPX) ๋ฌธ์„œ๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ์ œ์–ด

Hancom ํ•œ๊ธ€ ํŒŒ์ผ ์ฝ๊ธฐ, ์“ฐ๊ธฐ, ๋ณ€ํ™˜

CI codecov Tests unsafe forbidden Lines of Code

crates.io docs.rs crates.io downloads MSRV License: MIT OR Apache-2.0

MCP Ready GitHub release GitHub last commit GitHub stars

Security Policy Contributing PRs Welcome Made in Korea Buy Me a Coffee


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: ๊ตฌํ˜• ๋ฐ”์ด๋„ˆ๋ฆฌ ํฌ๋งท (v2.0์—์„œ ์ฝ๊ธฐ ์ง€์› ์˜ˆ์ •)
  • ์Šคํƒ€์ผ ์„ธํŠธ๋Š” HancomStyleSet enum์œผ๋กœ ์„ ํƒ ๊ฐ€๋Šฅ (๊ธฐ๋ณธ: 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์— ์ง์ ‘ ์ถ”๊ฐ€:

[dependencies]
hwpforge = "0.1"

๐Ÿ”จ Hammer โ€” CLI๋กœ ์‹œ์ž‘ํ•˜๊ธฐ

CLI ๋„๊ตฌ hwpforge(Hammer)๋ฅผ ์„ค์น˜ํ•˜๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋ฐ”๋กœ ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

cargo install hwpforge-bindings-cli
# Markdown โ†’ HWPX ๋ณ€ํ™˜
hwpforge convert report.md -o report.hwpx

# HWPX ๊ตฌ์กฐ ํ™•์ธ
hwpforge inspect report.hwpx

# HWPX โ†’ JSON ์ถ”์ถœ (AI ํŽธ์ง‘์šฉ)
hwpforge to-json report.hwpx --section 0 > section0.json

# JSON์œผ๋กœ ์„น์…˜ ๊ต์ฒด
hwpforge patch report.hwpx --section 0 < modified.json -o updated.hwpx

# JSON Schema ์ถœ๋ ฅ (AI agent์šฉ)
hwpforge schema document

AI-first ์„ค๊ณ„: CLI๋Š” AI agent(Claude Code ๋“ฑ)๊ฐ€ ์ฃผ ์‚ฌ์šฉ์ž์ž…๋‹ˆ๋‹ค. Markdown์œผ๋กœ ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•œ ๋’ค, JSON round-trip์œผ๋กœ ๊ธฐ์กด ์Šคํƒ€์ผ์„ ๋ณด์กดํ•˜๋ฉด์„œ section ๋‹จ์œ„๋กœ ์ •๋ฐ€ํ•˜๊ฒŒ ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. --json ํ”Œ๋ž˜๊ทธ๋กœ ๋ชจ๋“  ๋ช…๋ น์–ด๊ฐ€ machine-readable ์ถœ๋ ฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

โš™๏ธ Anvil โ€” MCP Server๋กœ AI๊ฐ€ ์ง์ ‘ ํ•œ๊ธ€ ๋ฌธ์„œ๋ฅผ ๋‹ค๋ฃจ๋‹ค

Claude Code, Codex CLI, ChatGPT, Cursor, Windsurf ๋“ฑ MCP ์ง€์› AI ๋„๊ตฌ์—์„œ ํ•œ๊ธ€ ๋ฌธ์„œ๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๊ณ  ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. "๋ณด๊ณ ์„œ ๋งŒ๋“ค์–ด์ค˜"๋ผ๊ณ  ๋งํ•˜๋ฉด, AI๊ฐ€ ์•Œ์•„์„œ .hwpx ํŒŒ์ผ์„ ๋š๋”ฑ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค.

1๋‹จ๊ณ„: ์„ค์น˜

npm (Node.js 20+) ๋˜๋Š” Cargo ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค:

# ๋ฐฉ๋ฒ• A: npm (๊ถŒ์žฅ โ€” Rust ํˆด์ฒด์ธ ๋ถˆํ•„์š”)
npx -y @hwpforge/mcp

# ๋ฐฉ๋ฒ• B: Cargo (Rust ๊ฐœ๋ฐœ์ž์šฉ)
cargo install hwpforge-bindings-mcp

2๋‹จ๊ณ„: AI ๋„๊ตฌ์— ๋“ฑ๋ก

# npm์œผ๋กœ ์„ค์น˜ํ•œ ๊ฒฝ์šฐ
claude mcp add hwpforge -- npx -y @hwpforge/mcp

# cargo๋กœ ์„ค์น˜ํ•œ ๊ฒฝ์šฐ
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
{
  "mcpServers": {
    "hwpforge": {
      "command": "npx",
      "args": ["-y", "@hwpforge/mcp"]
    }
  }
}

Settings โ†’ Tools โ†’ Add MCP Server์—์„œ:

  • Name: hwpforge
  • Command: npx -y @hwpforge/mcp

๋˜๋Š” ์„ค์ • ํŒŒ์ผ์„ ์ง์ ‘ ํŽธ์ง‘:

{
  "mcpServers": {
    "hwpforge": {
      "command": "npx",
      "args": ["-y", "@hwpforge/mcp"]
    }
  }
}

ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— .cursor/mcp.json ์ƒ์„ฑ:

{
  "mcpServers": {
    "hwpforge": {
      "command": "npx",
      "args": ["-y", "@hwpforge/mcp"]
    }
  }
}

~/.codeium/windsurf/mcp_config.json์— ์ถ”๊ฐ€:

{
  "mcpServers": {
    "hwpforge": {
      "command": "npx",
      "args": ["-y", "@hwpforge/mcp"]
    }
  }
}

๋“ฑ๋กํ•˜๋ฉด 5๊ฐœ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๋„๊ตฌ ํ•˜๋Š” ์ผ ํ•œ๋งˆ๋””
hwpforge_convert Markdown โ†’ HWPX ๋ณ€ํ™˜ "์ด ๋งˆํฌ๋‹ค์šด์„ ํ•œ๊ธ€ ํŒŒ์ผ๋กœ!"
hwpforge_inspect HWPX ๊ตฌ์กฐ ํ™•์ธ "์ด ๋ฌธ์„œ ๋ญ๊ฐ€ ๋“ค์–ด์žˆ์–ด?"
hwpforge_to_json HWPX โ†’ JSON ์ถ”์ถœ "์ด ์„น์…˜ ๋‚ด์šฉ ์ข€ ๊บผ๋‚ด๋ด"
hwpforge_patch JSON์œผ๋กœ ์„น์…˜ ๊ต์ฒด "์ด ๋ถ€๋ถ„๋งŒ ๋ฐ”๊ฟ”์„œ ๋‹ค์‹œ ์ €์žฅํ•ด"
hwpforge_templates ์Šคํƒ€์ผ ํ”„๋ฆฌ์…‹ ์กฐํšŒ "์–ด๋–ค ํ…œํ”Œ๋ฆฟ ์“ธ ์ˆ˜ ์žˆ์–ด?"

์—…๋ฐ์ดํŠธ / ์‚ญ์ œ

cargo install hwpforge-bindings-mcp --force   # ์—…๋ฐ์ดํŠธ
cargo uninstall hwpforge-bindings-mcp          # ์‚ญ์ œ

์™œ MCP? CLI(Hammer)๋Š” AI๊ฐ€ bash ๋ช…๋ น์„ ์‹คํ–‰ํ•ด์•ผ ํ•˜์ง€๋งŒ, MCP(Anvil)๋Š” AI๊ฐ€ ๋„ค์ดํ‹ฐ๋ธŒ ๋„๊ตฌ๋กœ ์ง์ ‘ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ๊ฒฝ๋กœ ํŒŒ์‹ฑ๋„, stdout ํ•ด์„๋„ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. JSON-RPC๋กœ ์š”์ฒญํ•˜๋ฉด ๊ตฌ์กฐํ™”๋œ JSON์œผ๋กœ ์‘๋‹ต โ€” ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”จ ๋ฌธ์„œ ์ƒ์„ฑ

use hwpforge::core::{Document, Draft, Paragraph, Run, Section, PageSettings};
use hwpforge::foundation::{CharShapeIndex, ParaShapeIndex};

let mut doc = Document::<Draft>::new();
doc.add_section(Section::with_paragraphs(
    vec![Paragraph::with_runs(
        vec![Run::text("Hello, ํ•œ๊ธ€!", CharShapeIndex::new(0))],
        ParaShapeIndex::new(0),
    )],
    PageSettings::a4(),
));

โš’๏ธ HWPX๋กœ ์ธ์ฝ”๋”ฉ

use hwpforge::hwpx::{HwpxEncoder, HwpxStyleStore};
use hwpforge::core::ImageStore;

let validated = doc.validate().unwrap();
let style_store = HwpxStyleStore::with_default_fonts("ํ•จ์ดˆ๋กฌ๋ฐ”ํƒ•");
let image_store = ImageStore::new();
let bytes = HwpxEncoder::encode(&validated, &style_store, &image_store).unwrap();
std::fs::write("output.hwpx", &bytes).unwrap();

โš’๏ธ HWPX ๋””์ฝ”๋”ฉ

use hwpforge::hwpx::HwpxDecoder;

let result = HwpxDecoder::decode_file("input.hwpx").unwrap();
println!("์„น์…˜ ์ˆ˜: {}", result.document.sections().len());

โš’๏ธ HWPX โ†’ Markdown ๋ณ€ํ™˜ (AI๊ฐ€ ํ•œ๊ธ€ ๋ฌธ์„œ ์ฝ๊ธฐ)

use hwpforge::hwpx::HwpxDecoder;
use hwpforge::md::MdEncoder;

let decoded = HwpxDecoder::decode_file("government_report.hwpx").unwrap();
let validated = decoded.document.validate().unwrap();
let markdown = MdEncoder::encode_lossy(&validated).unwrap();
println!("{}", markdown); // LLM์ด ๋ฐ”๋กœ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” Markdown

๊ธฐ์กด .hwpx ํŒŒ์ผ์„ Markdown์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด Claude, GPT ๋“ฑ ์–ด๋–ค LLM์ด๋“  ํ•œ๊ธ€ ๊ณต๋ฌธ์„œ๋ฅผ ์ฆ‰์‹œ ์ฝ๊ณ  ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โš’๏ธ Markdown โ†’ HWPX ๋ณ€ํ™˜

use hwpforge::md::MdDecoder;
use hwpforge::hwpx::{HwpxEncoder, HwpxStyleStore};

let md_doc = MdDecoder::decode_with_default("# ์ œ๋ชฉ\n\nMarkdown์—์„œ ๋ณ€ํ™˜!").unwrap();
let validated = md_doc.document.validate().unwrap();
let style_store = HwpxStyleStore::from_registry(&md_doc.style_registry);
let image_store = hwpforge::core::ImageStore::new();
let bytes = HwpxEncoder::encode(&validated, &style_store, &image_store).unwrap();

Feature Flags

Feature ๊ธฐ๋ณธ๊ฐ’ ์„ค๋ช…
hwpx Yes HWPX encoder/decoder
md โ€” Markdown โ†” Core ๋ณ€ํ™˜
full โ€” ๋ชจ๋“  ๊ธฐ๋Šฅ ํฌํ•จ
# Markdown ์ง€์› ํฌํ•จ
hwpforge = { version = "0.1", features = ["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

์•„ํ‚คํ…์ฒ˜

%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '14px', 'lineColor': '#BDBDBD'}}}%%
flowchart TD
    subgraph formats["ํฌ๋งท"]
        HF(["๐Ÿ“„ .hwpx<br/>ํ•œ๊ธ€ ํŒŒ์ผ"]):::file
        MF(["๐Ÿ“ .md<br/>Markdown"]):::file
    end

    subgraph smithy["โš’๏ธ Smithy โ€” ํฌ๋งท ๋ณ€ํ™˜๊ธฐ"]
        SH["hwpforge-smithy-hwpx"]:::smithy
        SM["hwpforge-smithy-md"]:::smithy
    end

    C["๐Ÿ”จ hwpforge-core<br/>ํฌ๋งท ๋…๋ฆฝ ๋ฌธ์„œ ๋ชจ๋ธ (IR)"]:::core
    BP["๐Ÿ“ hwpforge-blueprint<br/>YAML ์Šคํƒ€์ผ ยท ํฐํŠธ ยท ์ƒ‰์ƒ"]:::blueprint
    F["๐Ÿ”ฉ hwpforge-foundation<br/>HwpUnit ยท Color ยท Index"]:::foundation

    HF <--> SH
    MF <--> SM
    SH & SM <--> C
    BP --> SH & SM
    F --> C & BP

    classDef file     fill:#FFFDE7,stroke:#F9A825,color:#5D4037
    classDef smithy   fill:#FFF3E0,stroke:#FB8C00,color:#E65100
    classDef core     fill:#E3F2FD,stroke:#42A5F5,color:#0D47A1
    classDef blueprint fill:#F3E5F5,stroke:#AB47BC,color:#4A148C
    classDef foundation fill:#FAFAFA,stroke:#BDBDBD,color:#424242

ํ•ต์‹ฌ ์›์น™: ๊ตฌ์กฐ(Structure)์™€ ์Šคํƒ€์ผ(Style)์˜ ๋ถ„๋ฆฌ โ€” HTML + CSS์™€ ๊ฐ™์€ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. Core๋Š” ์Šคํƒ€์ผ ์ฐธ์กฐ(index)๋งŒ ๊ฐ€์ง€๊ณ , Blueprint๋Š” ์Šคํƒ€์ผ ์ •์˜(ํฐํŠธ, ํฌ๊ธฐ, ์ƒ‰์ƒ)๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. Smithy compiler๊ฐ€ Core + Blueprint๋ฅผ ํ•ฉ์ณ ์ตœ์ข… ํฌ๋งท์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ํ˜„ํ™ฉ

์ง€ํ‘œ ๊ฐ’
์ด LOC ~52,700
ํ…Œ์ŠคํŠธ 1,602๊ฐœ (cargo-nextest)
์†Œ์Šค ํŒŒ์ผ 116 .rs
Crate ์ˆ˜ 10๊ฐœ (7๊ฐœ ๋ฐฐํฌ)
์ปค๋ฒ„๋ฆฌ์ง€ 92.65%
Clippy ๊ฒฝ๊ณ  0
Unsafe ์ฝ”๋“œ 0

๊ฐœ๋ฐœ

ํ•„์ˆ˜ ์š”๊ตฌ์‚ฌํ•ญ

  • Rust 1.88+ (MSRV)
  • (๊ถŒ์žฅ) cargo-nextest โ€” ๋ณ‘๋ ฌ ํ…Œ์ŠคํŠธ ์‹คํ–‰
  • (์„ ํƒ) pre-commit โ€” git hook ์ž๋™ํ™”

MSRV ์ •์ฑ…

  • ํ˜„์žฌ MSRV๋Š” Rust 1.88์ž…๋‹ˆ๋‹ค.
  • HwpForge๋Š” stable์—์„œ 4 ๋ฆด๋ฆฌ์Šค ๋’ค์ฒ˜์ง„ ๋ฒ„์ „์„ ๊ธฐ๋ณธ MSRV ์ •์ฑ…์œผ๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • Cargo.toml์˜ rust-version์ด ๋‹จ์ผ ์ง„์‹ค์›์ด๋ฉฐ, CI์˜ Verify โ€บ MSRV job์ด ์ด ๊ณ„์•ฝ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  • MSRV ์ƒํ–ฅ์ด ํ•„์š”ํ•˜๋ฉด PR์—์„œ ์ด์œ ๋ฅผ ๋ช…์‹œํ•˜๊ณ , Cargo.toml, CI, CHANGELOG๋ฅผ ํ•จ๊ป˜ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐœ๋ฐœ์šฉ ๊ธฐ๋ณธ ํˆด์ฒด์ธ์€ ๋” ์ตœ์‹ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜ธํ™˜์„ฑ ํŒ๋‹จ ๊ธฐ์ค€์€ ์ตœ์‹  stable์ด ์•„๋‹ˆ๋ผ MSRV + CI ํ†ต๊ณผ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค.

โš’๏ธ ๋ช…๋ น์–ด

make ci          # fmt + clippy + test + deny + lint (CI์™€ ๋™์ผ)
make test        # cargo nextest run
make clippy      # cargo clippy (๋ชจ๋“  target, ๋ชจ๋“  feature, -D warnings)
make fmt-fix     # rustfmt ์ž๋™ ํฌ๋งท
make doc         # rustdoc ์ƒ์„ฑ (๋ธŒ๋ผ์šฐ์ €์—์„œ ์—ด๋ฆผ)
make cov         # coverage ๋ฆฌํฌํŠธ (90% gate)

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

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 decoder (์˜ˆ์ •)
โ”‚   โ”œโ”€โ”€ hwpforge-bindings-py/     # Python bindings (์˜ˆ์ •)
โ”‚   โ”œโ”€โ”€ hwpforge-bindings-cli/    # CLI ๋„๊ตฌ (hwpforge)
โ”‚   โ””โ”€โ”€ hwpforge-bindings-mcp/    # MCP Server (hwpforge-mcp)
โ”œโ”€โ”€ tests/                        # ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ + golden fixture
โ””โ”€โ”€ examples/                     # ๐Ÿ“œ ์‚ฌ์šฉ ์˜ˆ์ œ + ์ƒ์„ฑ๋œ HWPX ํŒŒ์ผ

๊ธฐ์—ฌ

๋ฒ„๊ทธ ์ˆ˜์ •, ํฌ๋งท ๋ฆฌ์„œ์น˜, ํ…Œ์ŠคํŠธ ๋ณด๊ฐ•, ๋ฌธ์„œ ๊ฐœ์„  ๋ชจ๋‘ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

  • ์‹œ์ž‘ ์ „ ๊ฐ€์ด๋“œ: CONTRIBUTING.md
  • ํŠนํžˆ ํ™•์ธํ•  ๊ฒƒ: release-plz๊ฐ€ ์“ฐ๋Š” ์ปค๋ฐ‹ prefix (feat, fix, perf, refactor)
  • ํŠนํžˆ ํ™•์ธํ•  ๊ฒƒ: MSRV ์ •์ฑ…๊ณผ dependency/MSRV ์ƒ์Šน ๊ธฐ์ค€
  • ํŠนํžˆ ํ™•์ธํ•  ๊ฒƒ: ๋ฌธ์„œ ๋ณ€๊ฒฝ ์‹œ mdbook build์™€ markdown lint ๊ฒ€์ฆ
  • ํŠนํžˆ ํ™•์ธํ•  ๊ฒƒ: CI required checks๋ฅผ ๊นจ์ง€ ์•Š๋Š” ๋ฒ”์œ„์—์„œ์˜ ๋ณ€๊ฒฝ ๋ถ„๋ฆฌ

๋กœ๋“œ๋งต

์ถœ์‹œ ์˜ˆ์ •

  • HWP5 ์ฝ๊ธฐ โ€” ๊ตฌํ˜• ๋ฐ”์ด๋„ˆ๋ฆฌ ํฌ๋งท(.hwp) ๋””์ฝ”๋”

  • MCP ์„œ๋ฒ„ โ€” Claude, Cursor ๋“ฑ AI ๋„๊ตฌ๊ฐ€ tool๋กœ ์ง์ ‘ HWPX ์ƒ์„ฑ (5๊ฐœ ๋„๊ตฌ)

  • CLI ๋„๊ตฌ โ€” hwpforge convert doc.md doc.hwpx ํ•œ ์ค„ ๋ณ€ํ™˜ (7๊ฐœ ๋ช…๋ น์–ด)

  • HWPX ์™„์ „ ์ง€์› โ€” ์–‘์‹ ์ปจํŠธ๋กค, ๋ณ€๊ฒฝ ์ถ”์ , OLE ๊ฐ์ฒด

  • Python ๋ฐ”์ธ๋”ฉ โ€” pip install hwpforge๋กœ ์„ค์น˜, PyPI ๋ฐฐํฌ

๋ผ์ด์„ ์Šค

๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

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์™€ ์‚ฌ๋žŒ์ด ํ˜‘์—…ํ•˜์—ฌ ๋งŒ๋“ค์–ด๋‚ธ ๊ฒฐ๊ณผ๋ฌผ์ž…๋‹ˆ๋‹ค.


์‡ ๋ถ€๋ฆฌ Anvilscribe (SoeBuri Anvilscribe) ํ•œ์ปด ๋ฌธ์„œ๋ฅผ ๋ถˆ์— ๋‹ฌ๊ตฌ์–ด ๋‹จ๋‹จํ•˜๊ฒŒ ๋ฒผ๋ ค๋‚ด๋Š” ๋Œ€์žฅ์žฅ์ด ์˜ค๋ฆฌ๋„ˆ๊ตฌ๋ฆฌ ๐Ÿ”ฅ