Skip to main content
Glama
README.md
<div align="center">

# ๐Ÿฉน sanjae-apply

**์‚ฐ์žฌ ์‹ ์ฒญ ๋„์šฐ๋ฏธ ยท MCP ์„œ๋ฒ„ + CLI**

๊ธ‰์—ฌ ์ข…๋ฅ˜์™€ ์žฌํ•ด ์ƒํ™ฉ์„ ๋งํ•˜๋ฉด **ํ•„์š”ํ•œ ์„œ์‹ยท์ฒจ๋ถ€ยท๊ทผ๊ฑฐ ์กฐ๋ฌธ**์„ ์•Œ๋ ค ์ค๋‹ˆ๋‹ค.
๊ทผ๋กœ๋ณต์ง€๊ณต๋‹จ **์ •๋ณธ PDF 12์ข…**๋„ ๊ทธ ์ž๋ฆฌ์—์„œ ์ฑ„์›Œ ์ค๋‹ˆ๋‹ค.

[![release](https://img.shields.io/github/v/release/seojaehong/sanjae-apply?label=release&color=2ea44f)](https://github.com/seojaehong/sanjae-apply/releases)
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![node](https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=white)](https://nodejs.org)
[![MCP](https://img.shields.io/badge/MCP-stdio%20%2B%20http-7c3aed)](https://modelcontextprotocol.io)
[![tests](https://img.shields.io/badge/tests-227%20passing-2ea44f)](test/)

<img src="docs/img/demo_yoyang.jpg" alt="ํ•ฉ์„ฑ ๊ฐ’์œผ๋กœ ์ฑ„์šด ์š”์–‘๊ธ‰์—ฌ์‹ ์ฒญ์„œ. ์™ผ์ชฝ PDF ์ถœ๋ ฅ, ์˜ค๋ฅธ์ชฝ ํ•œ๊ธ€(hwpx) ์ถœ๋ ฅ" width="100%">

<sub>ํ•ฉ์„ฑ ๊ฐ’์œผ๋กœ ์ฑ„์šด ์š”์–‘๊ธ‰์—ฌ์‹ ์ฒญ์„œ. ์™ผ์ชฝ์ด PDF ์ถœ๋ ฅ, ์˜ค๋ฅธ์ชฝ์ด ํ•œ๊ธ€(.hwpx) ์ถœ๋ ฅ์„ ํ•œ๊ธ€์—์„œ ์—ฐ ๋ชจ์Šต์ž…๋‹ˆ๋‹ค.</sub>

</div>

---

## โœจ ๋ฌด์—‡์„ ํ•ด ์ฃผ๋‚˜์š”

์‚ฐ์žฌ๋ณดํ—˜์€ **์ฒญ๊ตฌ์ฃผ์˜**์ž…๋‹ˆ๋‹ค. ์žฌํ•ด์ž๋‚˜ ๋Œ€๋ฆฌ์ธ์ด ๊ธ‰์—ฌ ์ข…๋ฅ˜๋งˆ๋‹ค ๋‹ค๋ฅธ ์„œ์‹๊ณผ ์ฒจ๋ถ€๋ฅผ ์ง์ ‘ ์ฑ™๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ด ๋„๊ตฌ๋Š” ๊ทธ ใ€Œ์ฑ™๊ธฐ๋Š” ์ผใ€์„ ๋•์Šต๋‹ˆ๋‹ค.

| ํ•˜๋Š” ์ผ | ์„ค๋ช… |
|---|---|
| ๐Ÿ“‹ **ํ•„์š” ์„œ์‹ ํ™•์ธ** | ๊ธ‰์—ฌ 9์ข…(์š”์–‘ยทํœด์—…ยท์žฅํ•ดยท์œ ์กฑยท๊ฐ„๋ณ‘๋ฃŒ ๋“ฑ) ร— ์‚ฌ๊ณ /์ถœํ‡ด๊ทผ/์งˆ๋ณ‘ ร— ์ œ3์ž ์œ ๋ฌด๋ฅผ ๋ฐ›์•„ `REQUIRED` / `CONDITIONAL` / `NOT_APPLICABLE` ๋กœ ๋Œ๋ ค์ค๋‹ˆ๋‹ค |
| ๐Ÿ“Ž **์ฒจ๋ถ€ ์ ๊ฒ€** | ์ด๋ฏธ ๊ฐ€์ง„ ๊ฒƒ์€ `PROVIDED`, ์—†๋Š” ๊ฒƒ์€ `MISSING`, ๋‹ค๋ฅธ ์„œ๋ฅ˜๋กœ ๊ฐˆ์Œ๋˜๋Š” ๊ฒƒ์€ `SUBSTITUTABLE` |
| โš–๏ธ **๊ทผ๊ฑฐ ์กฐ๋ฌธ** | ์‚ฐ์žฌ๋ณดํ—˜๋ฒ•ยท์‹œํ–‰๋ นยท์‹œํ–‰๊ทœ์น™ ์กฐ๋ฌธ ๋ฒˆํ˜ธ์™€ ์ œ๋ชฉ์„ ํ•จ๊ป˜ ๋ถ™์ž…๋‹ˆ๋‹ค(๋ฒ•์ œ์ฒ˜ ๊ตญ๊ฐ€๋ฒ•๋ น์ •๋ณด Open API) |
| โ“ **๋‚จ์€ ์งˆ๋ฌธ** | ์•„์ง ๋‹ต์ด ์—†๋Š” ๋ถ„๊ธฐ๋Š” `open_confirmations` ๋กœ ๋˜๋Œ๋ ค ์ค๋‹ˆ๋‹ค. ๋ชจ๋ฅด๋Š” ๊ฒƒ์„ ์ง€์–ด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค |
| ๐Ÿ–จ๏ธ **PDF ์ฑ„์šฐ๊ธฐ** | ๊ณต๋‹จ ์„œ์‹์ž๋ฃŒ์‹ค ์ •๋ณธ PDF ์œ„์— ๊ฐ’์„ ์–น์–ด ๋Œ๋ ค์ค๋‹ˆ๋‹ค. ์‹ ์ฒญ์ธ ์นธ๋งŒ ์ฑ„์šฐ๊ณ  ์˜์‚ฌยท์‚ฌ์—…์ฃผยท๊ณต๋‹จ ์นธ์€ ๋น„์›๋‹ˆ๋‹ค |

> [!IMPORTANT]
> **์Šน์ธ ์—ฌ๋ถ€๋Š” ๋‹ค๋ฃจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.** ๊ฒฐ๊ณผ์˜ `READY_TO_SUBMIT` ์€ ใ€Œ์ œ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํƒœใ€์ด์ง€ ์Šน์ธ์ด ์•„๋‹™๋‹ˆ๋‹ค.
> ๊ฒฐ๊ณผ๋ฅผ ์ ์น˜๋Š” ํ‘œํ˜„์€ ์ฝ”๋“œ ํ•„ํ„ฐ๊ฐ€ ์ถœ๋ ฅ ๋‹จ๊ณ„์—์„œ ๋ง‰์Šต๋‹ˆ๋‹ค.

> [!NOTE]
> **์ž…๋ ฅ๊ฐ’์„ ์ €์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.** ๋กœ์ปฌ ์„ค์น˜๋Š” PC ๋ฐ–์œผ๋กœ ์•„๋ฌด๊ฒƒ๋„ ๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ ๊ฐ™์€ ์นธ์€ ๋น„์›Œ ๋‘๊ณ  ์ถœ๋ ฅํ•œ ๋’ค ์†์œผ๋กœ ์ ์–ด๋„ ๋ฉ๋‹ˆ๋‹ค.

---

## ๐Ÿš€ 30์ดˆ ์„ค์น˜ (๋‚ด PC, ํ† ํฐ ์—†์Œ)

Node.js 20 ์ด์ƒ๋งŒ ์žˆ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋‹จ๊ณ„๋ณ„ ๋”ฐ๋ผ ํ•˜๊ธฐ์™€ ๋ฌธ์ œ ํ•ด๊ฒฐ์€ [์‚ฌ์šฉ ๋งค๋‰ด์–ผ](docs/์‚ฌ์šฉ_๋งค๋‰ด์–ผ.md)์— ์žˆ์Šต๋‹ˆ๋‹ค.

### Claude Code

```bash
claude mcp add --scope user sanjae-apply -- npx -y sanjae-apply mcp
```

### Claude Desktop

`claude_desktop_config.json` ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

```json
{
  "mcpServers": {
    "sanjae-apply": { "command": "npx", "args": ["-y", "sanjae-apply", "mcp"] }
  }
}
```

<details>
<summary>๐ŸชŸ Windows ์—์„œ Claude Desktop ์ด npx ๋ฅผ ๋ชป ์ฐพ์„ ๋•Œ</summary>

`cmd /c` ๋กœ ๊ฐ์Œ‰๋‹ˆ๋‹ค.

```json
{
  "mcpServers": {
    "sanjae-apply": { "command": "cmd", "args": ["/c", "npx", "-y", "sanjae-apply", "mcp"] }
  }
}
```
</details>

<details>
<summary>๐Ÿงฉ Cursor ยท Windsurf ยท ๊ทธ ๋ฐ–์˜ MCP ํ˜ธ์ŠคํŠธ</summary>

๊ฐ™์€ `command` / `args` ๋ฅผ ๊ฐ ๋„๊ตฌ์˜ MCP ์„ค์ •์— ๋„ฃ์Šต๋‹ˆ๋‹ค. stdio ์ „์†ก์ด๋ฉด ์ „๋ถ€ ๋ฉ๋‹ˆ๋‹ค.
</details>

๋ถ™์ด๊ณ  ๋‚˜์„œ AI ์—๊ฒŒ ์ด๋ ‡๊ฒŒ ๋งํ•ด ๋ณด์„ธ์š”.

> ใ€Œ์ถœํ‡ด๊ทผ ์ค‘ ๊ตํ†ต์‚ฌ๊ณ ์ธ๋ฐ ์ƒ๋Œ€ ์šด์ „์ž๊ฐ€ ์žˆ์–ด์š”. ์š”์–‘๊ธ‰์—ฌ ์‹ ์ฒญํ•˜๋ ค๋ฉด ๋ญ๊ฐ€ ํ•„์š”ํ•ด์š”?ใ€
> ใ€Œ์š”์–‘๊ธ‰์—ฌ์‹ ์ฒญ์„œ์— ์ด ๊ฐ’์œผ๋กœ ์ฑ„์›Œ์„œ PDF ๋กœ ์ค˜.ใ€

---

## ๐Ÿ’ป ๋ช…๋ น์ค„๋กœ ์“ฐ๊ธฐ

```bash
# ํ•„์š” ์„œ์‹ยท์ฒจ๋ถ€ยท๊ทผ๊ฑฐ ์กฐ๋ฌธ
npx -y sanjae-apply check YOYANG --accident COMMUTE --third-party yes
npx -y sanjae-apply check JANGHAE --tags NOISE_HEARING_LOSS --json

# ์„œ์‹ ์ •๋ณธ ๋Œ€์žฅ๊ณผ ์ฑ„์šธ ์ˆ˜ ์žˆ๋Š” ์นธ ์ด๋ฆ„
npx -y sanjae-apply catalog F_YOYANG_APPLY

# ๊ณต๋‹จ ์ •๋ณธ PDF ์œ„์— ์ฑ„์šฐ๊ธฐ
npx -y sanjae-apply fill F_YOYANG_APPLY values.json --out ์‹ ์ฒญ์„œ.pdf

# ๊ฐ™์€ ๊ฐ’์„ ํ•œ๊ธ€ ๋ฌธ์„œ(.hwpx)๋กœ. ํ™•์žฅ์ž๋งŒ ๋ฐ”๊พธ๋ฉด ๋ฉ๋‹ˆ๋‹ค
npx -y sanjae-apply fill F_YOYANG_APPLY values.json --out ์‹ ์ฒญ์„œ.hwpx

# PDF ์— ๋„์žฅ๊นŒ์ง€. ์ด๋ฆ„๋งŒ ์ฃผ๋ฉด ์„ฑ๋ช…์ธ์„ ๋งŒ๋“ค์–ด ์‹ ์ฒญ์ธ ์„œ๋ช…๋ž€ ์ „๋ถ€์— ์ฐ์Šต๋‹ˆ๋‹ค
npx -y sanjae-apply fill F_YOYANG_APPLY values.json --out ์‹ ์ฒญ์„œ.pdf --stamp ํ™๊ธธ๋™
npx -y sanjae-apply fill F_YOYANG_APPLY values.json --out ์‹ ์ฒญ์„œ.pdf --stamp-png ๋‚ด๋„์žฅ.png --stamp-spot applicant

# ๋„์žฅ PNG ๋งŒ ๋”ฐ๋กœ (์„ฑ๋ช…์ธ ยท ์‚ฌ๊ฐ ยท ํƒ€์› ยท ๋ฒ•์ธํ˜•)
npx -y sanjae-apply seal --out ๋„์žฅ.png --name ํ™๊ธธ๋™
npx -y sanjae-apply seal --out ์ง์ธ.png --shape ellipse --arc "์ฃผ์‹ํšŒ์‚ฌ ๊ฐ€๋‚˜๋‹ค" --center ๋Œ€ํ‘œ์ด์‚ฌ --bottom 123-45-67890
```

`values.json` ์˜ ์นธ ์ด๋ฆ„์€ `catalog <FORM_ID>` ๊ฒฐ๊ณผ์˜ `slots` ์— ๋‚˜์˜ต๋‹ˆ๋‹ค. PDF ์™€ ํ•œ๊ธ€์€ ์นธ ์ด๋ฆ„์ด ๊ฐ™์Šต๋‹ˆ๋‹ค. ์„œ์‹์ด ๊ฐฑ์‹ ๋˜๋ฉด `npx -y sanjae-apply@latest` ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

### ๐Ÿ” ๋„์žฅ(๋‚ ์ธ)์€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜

- `--stamp ์ด๋ฆ„` ์ด๋ฉด ์„ฑ๋ช…์ธ(์›ํ˜• ๊ธฐ๋ณธ, `--stamp-shape square|ellipse`)์„ ๋งŒ๋“ค์–ด ์ฐ๊ณ  `--stamp-png` ๋ฉด ๊ฐ€์ง„ ๋„์žฅ PNG(ํˆฌ๋ช… ๋ฐฐ๊ฒฝ, 4096px ์ดํ•˜)๋ฅผ ๊ฒ€์‚ฌํ•œ ๋’ค ๊ทธ๋Œ€๋กœ ์ฐ์Šต๋‹ˆ๋‹ค. ์ธ์ฃผ์ƒ‰ ์ ์ƒ‰, ์ง€๋ฆ„ 42pt(์•ฝ 1.5cm). ๋™๋ด‰ ๊ธ€๊ผด(๋‚˜๋ˆ”๊ณ ๋”•)์— ์—†๋Š” ๊ธ€์ž(ํ•œ์ž ๋“ฑ)๋Š” ๋นˆ ๋„์žฅ์ด ๋˜์ง€ ์•Š๋„๋ก ์˜ค๋ฅ˜๋กœ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ์ด๋ฆ„์€ `--stamp-png` ๋กœ ๊ฐ€์ง„ ๋„์žฅ์„ ๋„ฃ์–ด ์ฃผ์„ธ์š”.
- ์ž๋ฆฌ๋Š” ์„œ์‹๋งˆ๋‹ค ์ง€๋„์— ์ •ํ•ด์ ธ ์žˆ๊ณ  `catalog <FORM_ID>` ์˜ `stamps` ์— ์ด๋ฆ„์ด ๋‚˜์˜ต๋‹ˆ๋‹ค. ๋น„์šฐ๋ฉด **์‹ ์ฒญ์ธ ๋ณธ์ธ ์ž๋ฆฌ ์ „๋ถ€**(`applicant` ๋ณธ๋ฌธ ยท `delegation` ์œ„์ž„์žฅ ยท `consent` ๊ฐœ์ธ์ •๋ณด ๋™์˜)์— ์ฐ๊ณ  `--stamp-spot a,b` ๋กœ ๊ณ ๋ฆ…๋‹ˆ๋‹ค. ํƒ€์ธ ์„œ๋ช…๋ž€(`confirm` ํ™•์ธ์ž ยท `commute_reporter` ์‹ ๊ณ ์ธ ยท `choice_confirmer` ๋“ฑ)์€ ์ด๋ฆ„์„ ์ ์–ด ์ค„ ๋•Œ๋งŒ ์ฐ์Šต๋‹ˆ๋‹ค. ์ง€๋„์— ์—†๋Š” ์ž๋ฆฌ๋Š” ๊ฑด๋„ˆ๋›ฐ๊ณ  ๊ฒฐ๊ณผ์˜ `stamps_skipped` ์— ์ ์Šต๋‹ˆ๋‹ค.
- PDF ์™€ ํ•œ๊ธ€(hwpx) ์ถœ๋ ฅ **๋‘˜ ๋‹ค** ์ฐํž™๋‹ˆ๋‹ค. ํ•œ๊ธ€์—์„œ๋Š” ์„œ๋ช…๋ž€ ๋ฌธ๋‹จ์— ใ€Œ๊ธ€ ์•ž์œผ๋กœใ€ ๋–  ์žˆ๋Š” ๊ทธ๋ฆผ์œผ๋กœ ๋“ค์–ด๊ฐ€ ๊ธ€์ž๋ฅผ ๋ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์ชฝ ๋ฐ€๋ฆผ 0, 11์ข… ์‹ค์ธก). ํ•œ๊ธ€์—์„œ ์—ด์–ด ์˜ฎ๊ธฐ๊ฑฐ๋‚˜ ์ง€์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
- ๋„์žฅ์€ ๋ช…์˜์ž์˜ ์ธ๊ฒฉ ํ‘œ์ง€์ž…๋‹ˆ๋‹ค. **๋ณธ์ธ ๋˜๋Š” ๋ช…์˜์ž ๋™์˜๊ฐ€ ์žˆ๋Š” ์ด๋ฆ„๋งŒ** ๋„ฃ์–ด ์ฃผ์„ธ์š”. ๋“ฑ๋ก ์ธ๊ฐ์„ ํ‰๋‚ด ๋‚ด๋Š” ์šฉ๋„๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค(์‚ฌ์šฉ์ธ๊ฐ ์„ฑ๊ฒฉ).

### ๐Ÿ–จ๏ธ ํ•œ๊ธ€(hwpx) ์ถœ๋ ฅ์€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜

- ๊ณต๋‹จ์ด ๋ฐฐํฌํ•œ **HWP ์›๋ณธ**์„ ํ•œ๊ธ€์—์„œ hwpx ๋กœ ํ•œ ๋ฒˆ ๋ณ€ํ™˜ํ•ด ๋™๋ด‰ํ–ˆ์Šต๋‹ˆ๋‹ค(`forms/hwpx/`). ์ฑ„์šธ ๋•Œ๋Š” ํ•œ๊ธ€์ด ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. zip ์•ˆ XML ์˜ ํ‘œ ์นธ์— ๊ฐ’์„ ๋„ฃ๋Š” ์ˆœ์ˆ˜ TypeScript ์ž…๋‹ˆ๋‹ค. ์œˆ๋„์šฐยท๋งฅยท๋ฆฌ๋ˆ…์Šค ์–ด๋””์„œ๋‚˜ ๋•๋‹ˆ๋‹ค.
- ๊ฒฐ๊ณผ๋Š” **.hwpx** ์ž…๋‹ˆ๋‹ค. ํ•œ๊ธ€ 2014 ์ด์ƒ์—์„œ ์—ด์–ด ๊ณ ์น˜๊ณ  ์ธ์‡„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ์‹์˜ ๊ธ€๊ผดยทํ‘œยท์ชฝ ๊ตฌ์„ฑ์€ ๊ณต๋‹จ ์›๋ณธ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค.
- ๋นˆ ์„œ์‹์— ์ˆจ์–ด ์žˆ๋˜ ์ž๋ฆฌํ‘œ์‹œ์ž ๊ธ€์ž์ƒ‰(ํšŒ์ƒ‰ยทํŒŒ๋ž‘)๊ณผ 1pt ์ƒ์ž ๊ธ€์ž๋Š” ๊ฒ€์ • ์ •์ฒด๋กœ ๋ฐ”๊ฟ” ๋„ฃ์Šต๋‹ˆ๋‹ค. ๊ฐ’์ด ์นธ๋ณด๋‹ค ๊ธธ๋ฉด ๊ธ€์ž๋ฅผ ์ค„์ž…๋‹ˆ๋‹ค(PDF ์™€ ๊ฐ™์€ ๊ทœ์น™).
- ์žฌํ™œ๋ณด์กฐ๊ธฐ๊ตฌ ์ฒ˜๋ฐฉ์ „์€ ๊ณต๋‹จ์ด PDF ๋งŒ ๋ฐฐํฌํ•ด ํ•œ๊ธ€ ์ถœ๋ ฅ์ด ์—†์Šต๋‹ˆ๋‹ค.
- ์ƒˆ ์„œ์‹์„ ํ•œ๊ธ€๋กœ ์ฑ„์šฐ๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฒ•์€ [`docs/ํ•œ๊ธ€์ง€๋„_์ž‘์„ฑ๋ฒ•.md`](docs/ํ•œ๊ธ€์ง€๋„_์ž‘์„ฑ๋ฒ•.md) ์— ์žˆ์Šต๋‹ˆ๋‹ค.

<details>
<summary>๐Ÿ”ง MCP ๋„๊ตฌ 5๊ฐœ</summary>

| ๋„๊ตฌ | ํ•˜๋Š” ์ผ |
|---|---|
| `list_required_forms` | ๊ธ‰์—ฌยท์žฌํ•ด ๋ถ„๊ธฐ โ†’ ์„œ์‹ยท์ฒจ๋ถ€ยท์กฐ๋ฌธยท์ ˆ์ฐจยท๋‚จ์€ ์งˆ๋ฌธ |
| `get_form_catalog` | ๊ณต๋‹จ ์ •๋ณธ ๋Œ€์žฅ(ํŒŒ์ผ๋ช…ยท๊ฐœ์ •์ผยทsha256ยท๊ฒŒ์‹œ๊ธ€ URL)๊ณผ ์ฑ„์šธ ์ˆ˜ ์žˆ๋Š” ์„œ์‹(`fillable` PDF ยท `fillable_hwpx` ํ•œ๊ธ€)ยท์นธ ์ด๋ฆ„ยท๋„์žฅ ์ž๋ฆฌ ์ด๋ฆ„(`stamps`) |
| `fill_form` | `form_id` + ๊ฐ’ (+ `format: "pdf" | "hwpx"`, + `stamp: { name }` ๋˜๋Š” `{ png_base64 }`, `spot`) โ†’ ์ฑ„์šด ํŒŒ์ผ(base64). ๋กœ์ปฌ ์„ค์น˜๋ฉด `save_to`(์ ˆ๋Œ€ ๊ฒฝ๋กœ)๋กœ ํŒŒ์ผ์— ๋ฐ”๋กœ ์ €์žฅ |
| `make_seal` | ์ด๋ฆ„ โ†’ ์„ฑ๋ช…์ธ(์›ยท์‚ฌ๊ฐยทํƒ€์›) ๋˜๋Š” ๋ฒ•์ธํ˜•(์›์ฃผ ๋ฒ•์ธ๋ช… + ์ค‘์‹ฌ ๋ฌธ์ž + ํ•˜๋‹จ ๋ถ€๋ฌธ์ž) ๋„์žฅ PNG(base64). ๋กœ์ปฌ ์„ค์น˜๋ฉด `save_to` ๋กœ ๋ฐ”๋กœ ์ €์žฅ |
| `fill_yoyang_apply` | v0.1 ํ˜ธํ™˜. `fill_form(form_id="F_YOYANG_APPLY")` ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค |
</details>

---

## ๐Ÿ“„ ์ฑ„์šธ ์ˆ˜ ์žˆ๋Š” ์„œ์‹ 12์ข…

| ์„œ์‹ | ์นธ ์ˆ˜ | PDF | ํ•œ๊ธ€ | ๋„์žฅ ์ž๋ฆฌ |
|---|---:|:-:|:-:|:-:|
| ์š”์–‘๊ธ‰์—ฌ์‹ ์ฒญ์„œ (๋ณ„์ง€ ์ œ2ํ˜ธ, 2026.7.7 ๊ฐœ์ •) | 74 | โœ… | โœ… | 3 |
| ์ถœํ‡ด๊ทผ ์žฌํ•ด ํ•ฉ๋ณธ (์‹ ์ฒญ์„œ + ๋ฐœ์ƒ์‹ ๊ณ ์„œ) | 118 | โœ… | โœ… | 4 |
| ์ œ3์ž ์žฌํ•ด ์‹ ๊ณ ์„œ + ํ™•์ธ์„œ | 62 | โœ… | โœ… | 2 |
| ์œ ์กฑ๊ธ‰์—ฌ ์ฒญ๊ตฌ์„œ | 79 | โœ… | โœ… | 2 |
| ๋ณธ์ธ๋ถ€๋‹ด ์น˜๋ฃŒ๋น„ ์ฒญ๊ตฌ์„œ | 75 | โœ… | โœ… | 4 |
| ํœด์—…๊ธ‰์—ฌ ์ฒญ๊ตฌ์„œ | 40 | โœ… | โœ… | 2 |
| ๊ฐ„๋ณ‘๋ฃŒยท์ด์†ก๋น„ยท๋ณด์กฐ๊ธฐ ์ฒญ๊ตฌ์„œ | 60 | โœ… | โœ… | 3 |
| ์žฌํ™œ๋ณด์กฐ๊ธฐ๊ตฌ ์ฒ˜๋ฐฉ์ „ (๊ธฐํƒ€) | 12 | โœ… | ๊ณต๋‹จ์ด PDF ๋งŒ ๋ฐฐํฌ | ์˜๋ฃŒ๊ธฐ๊ด€ ์ž‘์„ฑ |
| ๋ฏธ์ง€๊ธ‰ ๋ณดํ—˜๊ธ‰์—ฌ ์ฒญ๊ตฌ์„œ | 63 | โœ… | โœ… | 1 |
| ์žฅํ•ด๊ธ‰์—ฌ ๋ฌถ์Œ (์ฒญ๊ตฌ์„œ + ์†Œ์Œ์ž‘์—… ํ™•์ธ์„œ + ์„ ํƒ ํ™•์ธ์„œ) | 159 | โœ… | โœ… | 5 |
| ์ถ”๊ฐ€์ƒ๋ณ‘ ์‹ ์ฒญ์„œ | 40 | โœ… | โœ… | 3 |
| ์žฌ์š”์–‘ ์‹ ์ฒญ์„œ | 54 | โœ… | โœ… | 3 |

- ์ •๋ณธ์€ **๊ทผ๋กœ๋ณต์ง€๊ณต๋‹จ ์„œ์‹์ž๋ฃŒ์‹ค** ํŒŒ์ผ๋งŒ ์”๋‹ˆ๋‹ค. ์ขŒํ‘œ ์ง€๋„๋Š” ์ •๋ณธ์˜ `sha256` ์— ๋ฌถ์—ฌ ์žˆ์–ด ๊ณต๋‹จ์ด ์„œ์‹์„ ๋ฐ”๊พธ๋ฉด ์‹œํ—˜์ด ๋จผ์ € ๊นจ์ง‘๋‹ˆ๋‹ค.
- ๊ณต๋‹จ์ด HWP ๋งŒ ์ฃผ๋Š” ์„œ์‹์€ ํ•œ๊ธ€์—์„œ PDF ๋กœ ๋ฐ”๊ฟ” ๋™๋ด‰ํ–ˆ์Šต๋‹ˆ๋‹ค.
- ์ด ๋„๊ตฌ์˜ ๋ฒ”์œ„๋Š” ์ฑ„์šด ์ถœ๋ ฅ๋ฌผ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ๊นŒ์ง€์ž…๋‹ˆ๋‹ค. ์˜จ๋ผ์ธ ์ ‘์ˆ˜(ํ† ํƒˆ์„œ๋น„์Šค)๋Š” ๋ฒ”์œ„ ๋ฐ–์ž…๋‹ˆ๋‹ค.

---

## ๐ŸŒ ์›๊ฒฉ MCP (์šด์˜์ž๊ฐ€ ํ† ํฐ์„ ์ฃผ๋Š” ๊ฒฝ์šฐ)

```bash
claude mcp add --transport http --scope user sanjae-apply https://sanjae-apply.vercel.app/api/mcp/mcp \
  --header "Authorization: Bearer <๋ฐœ๊ธ‰๋ฐ›์€ ํ† ํฐ>"
```

๊ฐ™์€ ๋„๊ตฌ 5๊ฐœ๊ฐ€ ์›๊ฒฉ์œผ๋กœ ๋•๋‹ˆ๋‹ค. ์ž…๋ ฅ์€ ์„œ๋ฒ„์— ์ €์žฅ๋˜์ง€ ์•Š์ง€๋งŒ ๋„คํŠธ์›Œํฌ๋ฅผ ๊ฑฐ์นฉ๋‹ˆ๋‹ค.

<details>
<summary>๐Ÿ  ์ง์ ‘ ํ˜ธ์ŠคํŒ…ํ•˜๊ธฐ(Vercel)</summary>

์ด ๋ ˆํฌ๋ฅผ Vercel ์— ์˜ฌ๋ฆฌ๊ณ  ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋„ฃ์œผ๋ฉด `/api/mcp/mcp` ๊ฐ€ ์—ด๋ฆฝ๋‹ˆ๋‹ค.

| ๋ณ€์ˆ˜ | ๋œป |
|---|---|
| `SANJAE_MCP_TOKENS` | ํ—ˆ์šฉํ•  Bearer ํ† ํฐ. ์‰ผํ‘œ๋กœ ์—ฌ๋Ÿฌ ๊ฐœ |
| `LAW_GO_KR_OC` | ๋ฒ•์ œ์ฒ˜ Open API ์ธ์ฆํ‚ค. ์กฐ๋ฌธ ์‹ค์‹œ๊ฐ„ ์กฐํšŒ๋ฅผ ์“ธ ๋•Œ๋งŒ |
</details>

---

## ๐Ÿงช ๊ฒ€์ฆ ์ˆ˜์ค€

| ์ธต | ๋‚ด์šฉ | ์ˆ˜ |
|---|---|---:|
| ๊ทœ์น™ | ๊ธ‰์—ฌ 9์ข… ร— ๋ถ„๊ธฐ, ๊ธˆ์ง€์–ด ํ•„ํ„ฐ ์ž๊ธฐ์‹œํ—˜ | vitest |
| ์กฐ๋ฌธ ๋Œ€์กฐ | ์ฝ”๋“œ์˜ ๊ทผ๊ฑฐ ์กฐ๋ฌธ โ†” ๋ฒ•์ œ์ฒ˜์—์„œ ๋ฐ›์€ ์กฐ๋ฌธ XML ์ œ๋ชฉ | 18๊ฑด |
| ์„œ์‹ ๋Œ€์žฅ | ์ฝ”๋“œ์˜ ์„œ์‹ โ†” `forms/catalog.json` sha256 | 15์ข… |
| PDF ์ฑ„์šฐ๊ธฐ | 12์ข… ์ „๋ถ€ ๊ธ€์ž ์œ„์น˜ + **ํ”ฝ์…€ ๋น„๊ต**(๋ฐฑ์ง€ ์ถœ๋ ฅ ์‚ฌ๊ณ  ๋ฐฉ์ง€) + ์œก์•ˆ | ์•ฝ 590์นธ |
| ํ•œ๊ธ€ ์ฑ„์šฐ๊ธฐ | 11์ข… ์ „๋ถ€ zip ์•ˆ ๊ธ€์žยท์ฒดํฌ ์ˆ˜ยท๊ธ€์ž์ƒ‰ + **ํ•œ๊ธ€์ด ์‹ค์ œ๋กœ ์—ด์–ด PDF ๋กœ ๊ทธ๋ฆฌ๋Š”์ง€**(COM) + ์ชฝ์ˆ˜๊ฐ€ ์›๋ณธ๊ณผ ๊ฐ™์€์ง€ + ์œก์•ˆ | ์•ฝ 650์นธ |
| MCP | http ยท stdio ์–‘์ชฝ initialize / tools/list / tools/call | SDK ํด๋ผ์ด์–ธํŠธ |
| ์šด์˜ ๋“œ๋ผ์ด๋Ÿฐ | Vercel ์šด์˜ ๋์ ์— ๊ณต์‹ SDK ๋กœ 31๊ฑด | ์ „๋ถ€ ํ†ต๊ณผ |

์ „๋ถ€ **ํ•ฉ์„ฑ ๊ฐ’**์œผ๋กœ๋งŒ ๋Œ๋ ธ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ์‚ฌ๊ฑด์œผ๋กœ๋Š” ๊ฒ€์ฆํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

```bash
git clone https://github.com/seojaehong/sanjae-apply && cd sanjae-apply
npm ci && npm test

# ์ฑ„์šด PDF ๋ฅผ ๊ธ€์ž ์œ„์น˜ + ํ”ฝ์…€๋กœ ๊ฒ€์‚ฌ
python scripts/verify_fill.py out.pdf forms/maps/F_YOYANG_APPLY.json test/samples/F_YOYANG_APPLY.json out.jpg forms/bundled/F_YOYANG_APPLY.pdf

# ์ฑ„์šด ํ•œ๊ธ€ ํŒŒ์ผ์„ ๊ฒ€์‚ฌ (ํ•œ๊ธ€์ด ์žˆ๋Š” ์œˆ๋„์šฐ๋ฉด ์‹ค์ œ ๋ Œ๋”๊นŒ์ง€, ์—†์œผ๋ฉด --no-render)
python scripts/verify_hwpx_fill.py out.hwpx test/samples/F_YOYANG_APPLY.json forms/hwpx_maps/F_YOYANG_APPLY.json out.jpg
```

์„ค์น˜๋ถ€ํ„ฐ ํ™œ์šฉ๊นŒ์ง€ ๋”ฐ๋ผ ํ•˜๋Š” ์‚ฌ์šฉ ๋งค๋‰ด์–ผ๊ณผ ์‹ค์ธก ๊ธฐ๋ก์€ [`docs/์‚ฌ์šฉ_๋งค๋‰ด์–ผ.md`](docs/์‚ฌ์šฉ_๋งค๋‰ด์–ผ.md) ์— ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒˆ ์„œ์‹์˜ ์ขŒํ‘œ ์ง€๋„๋Š” [`docs/์ขŒํ‘œ์ง€๋„_์ž‘์„ฑ๋ฒ•.md`](docs/์ขŒํ‘œ์ง€๋„_์ž‘์„ฑ๋ฒ•.md), ํ•œ๊ธ€ ์ง€๋„๋Š” [`docs/ํ•œ๊ธ€์ง€๋„_์ž‘์„ฑ๋ฒ•.md`](docs/ํ•œ๊ธ€์ง€๋„_์ž‘์„ฑ๋ฒ•.md), ๋ฐฐํฌยท์—ฐ๊ฒฐ ์ „์ฒด๋Š” [`docs/ํŒจํ‚ค์ง•_๋ฐฐํฌ_์—ฐ๊ฒฐ_๊ฐ€์ด๋“œ.md`](docs/ํŒจํ‚ค์ง•_๋ฐฐํฌ_์—ฐ๊ฒฐ_๊ฐ€์ด๋“œ.md).

---

## ๐Ÿ—‚๏ธ ๊ตฌ์กฐ

```
src/core     ๊ทœ์น™ S-01~S-13 ยท ์„œ์‹/์ฒจ๋ถ€ ์นดํƒˆ๋กœ๊ทธ ยท ๊ธˆ์ง€์–ด ํ•„ํ„ฐ(์ˆœ์ˆ˜ TS, IO ์—†์Œ)
src/fill     overlay.ts  pdf-lib ์˜ค๋ฒ„๋ ˆ์ด(text / boxes / check / multiline)
             hwpx.ts     ํ•œ๊ธ€ hwpx ์ฑ„์šฐ๊ธฐ(cell / after / check / digits / boxes / group, ๊ธ€์ž์ƒ‰ยทํฌ๊ธฐ ๊ต์ •)
src/stamp    seal.ts ๋„์žฅ PNG ์ƒ์„ฑ(๊ธ€๋ฆฌํ”„ ์œค๊ณฝ ๋ž˜์Šคํ„ฐ) ยท resolve.ts ์ž๋ฆฌ ์„ ํƒ. PDFยทhwpx ๋‚ ์ธ์€ overlay.tsยทhwpx.ts ์˜ stamps
src/mcp      ๋„๊ตฌ 5๊ฐœ ยท ์›๊ฒฉ(mcp-handler, Bearer) ยท ๋กœ์ปฌ(stdio)
src/laws     ๋ฒ•์ œ์ฒ˜ Open API ํด๋ผ์ด์–ธํŠธ
api/mcp      Vercel ํ•จ์ˆ˜
web/         ์•ˆ๋‚ด ํŽ˜์ด์ง€ ์Šคํฌ๋ฆฝํŠธ(engine.ts = src/core ๋ธŒ๋ผ์šฐ์ € ๋ฒˆ๋“ค ยท main.ts = ํ™”๋ฉด). npm run build:web โ†’ public/app ยท public/data
forms/       catalog.json(์ •๋ณธ ๋Œ€์žฅ) ยท maps/(PDF ์ขŒํ‘œ ์ง€๋„ 12) ยท bundled/(๋™๋ด‰ PDF 12) ยท hwpx/(๋ณ€ํ™˜ hwpx 11) ยท hwpx_maps/(ํ•œ๊ธ€ ์ง€๋„ 11)
scripts/     comwel_forms.py(์„œ์‹์ž๋ฃŒ์‹ค ์ˆ˜์ง‘) ยท verify_fill.py ยท probe_form.py ยท hwp_to_hwpx.py ยท probe_hwpx.py ยท verify_hwpx_fill.py ยท hwpx_stamp_anchors.py(๋„์žฅ ์•ต์ปค) ยท verify_hwpx_stamps.py(ํ•œ๊ธ€ ๋‚ ์ธ ์‹ค์ธก) ยท mcp_dryrun.mjs
test/        vitest 227 (formal_rules.test.ts ์ „์ˆ˜ 10,368๊ฑด ํฌํ•จ)
docs/        ์ขŒํ‘œ์ง€๋„ ์ž‘์„ฑ๋ฒ• ยท ํ•œ๊ธ€์ง€๋„ ์ž‘์„ฑ๋ฒ• ยท ํŒจํ‚ค์ง•ยท๋ฐฐํฌยท์—ฐ๊ฒฐ ๊ฐ€์ด๋“œ
```

---

## โš–๏ธ ๊ฒฝ๊ณ„

- ๊ณต์ธ๋…ธ๋ฌด์‚ฌ๋ฒ• ์ œ27์กฐ์— ๋”ฐ๋ผ ์ฒญ๊ตฌ์˜ ๋Œ€ํ–‰ยท๋Œ€๋ฆฌ์™€ ์„œ๋ฅ˜์˜ ์ž‘์„ฑยทํ™•์ธ์€ ๊ณต์ธ๋…ธ๋ฌด์‚ฌ์˜ ์ง๋ฌด์ž…๋‹ˆ๋‹ค. ์ด ๋„๊ตฌ๋Š” **๊ทธ ์ผ์„ ๋•๋Š” ๋„๊ตฌ**์ด์ง€ ๋Œ€์‹ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
- ๊ทœ์น™์€ ์‚ฐ์žฌ ์‹ค๋ฌด ๋ฉ”๋ชจ(์‚ฐ์žฌ์ฐฝ๊ณ  ๋ธ”๋กœ๊ทธ, 2025.1.14 ๊ธฐ์ค€)๋ฅผ ์˜ฎ๊ธฐ๊ณ  ๋ฒ• ์กฐ๋ฌธ์œผ๋กœ ๊ทผ๊ฑฐ๋ฅผ ๋ถ™์ธ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ณต๋‹จ ๋‚ด๋ถ€ ๊ทœ์ •๊ณผ๋Š” ๋Œ€์กฐํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. **์ถœ๋ ฅ๋ฌผ์€ ๋‹ด๋‹น ๋…ธ๋ฌด์‚ฌ๊ฐ€ ํ™•์ธํ•œ ๋’ค ์“ฐ์‹ญ์‹œ์˜ค.**
- ์„œ์‹ ๊ฐœ์ •์€ ๊ณต๋‹จ์ด ์˜ˆ๊ณ  ์—†์ด ํ•ฉ๋‹ˆ๋‹ค. `catalog` ๋กœ ๊ฐœ์ •์ผ์„ ํ•œ ๋ฒˆ์”ฉ ํ™•์ธํ•˜์„ธ์š”.

---

## ๐Ÿค ๊ธฐ์—ฌ

- ์ƒˆ ์„œ์‹ ์ขŒํ‘œ ์ง€๋„๋Š” [`docs/์ขŒํ‘œ์ง€๋„_์ž‘์„ฑ๋ฒ•.md`](docs/์ขŒํ‘œ์ง€๋„_์ž‘์„ฑ๋ฒ•.md), ํ•œ๊ธ€ ์ง€๋„๋Š” [`docs/ํ•œ๊ธ€์ง€๋„_์ž‘์„ฑ๋ฒ•.md`](docs/ํ•œ๊ธ€์ง€๋„_์ž‘์„ฑ๋ฒ•.md) ๋ฅผ ๋”ฐ๋ผ ๋งŒ๋“ค๊ณ  `test/samples/` ์— ํ•ฉ์„ฑ ๊ฐ’์„ ํ•จ๊ป˜ ๋„ฃ์–ด ์ฃผ์„ธ์š”.
- ๊ทœ์น™(S-xx)์„ ๊ณ ์น  ๋•Œ๋Š” ์‹คํŒจํ•˜๋Š” ์‹œํ—˜์„ ๋จผ์ € ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
- ์‹ค์ œ ์‚ฌ๊ฑด์˜ ๊ฐ’ยท์‹ค๋ช…ยท์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ๋Š” ์ด์ŠˆยทPR ์–ด๋””์—๋„ ์˜ฌ๋ฆฌ์ง€ ๋งˆ์„ธ์š”.

## ๐Ÿ“œ ๋ผ์ด์„ ์Šค

MIT. ๋™๋ด‰ ๊ธ€๊ผด ๋‚˜๋ˆ”๊ณ ๋”•์€ SIL OFL 1.1([`fonts/OFL.txt`](fonts/OFL.txt)). ๊ณต๋‹จ ์„œ์‹ PDF ๋Š” ๊ทผ๋กœ๋ณต์ง€๊ณต๋‹จ ๊ณต๊ฐœ ์ž๋ฃŒ์ž…๋‹ˆ๋‹ค.