원클릭으로
hwp-com-writer
한컴 COM API + HWPX XML 후처리로 행정문서를 정밀 자동 생성하는 하이브리드 스킬. 레퍼런스 서식 분석 → COM으로 문서 생성 → XML 후처리로 COM의 한계 보완.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
한컴 COM API + HWPX XML 후처리로 행정문서를 정밀 자동 생성하는 하이브리드 스킬. 레퍼런스 서식 분석 → COM으로 문서 생성 → XML 후처리로 COM의 한계 보완.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | hwp-com-writer |
| description | 한컴 COM API + HWPX XML 후처리로 행정문서를 정밀 자동 생성하는 하이브리드 스킬. 레퍼런스 서식 분석 → COM으로 문서 생성 → XML 후처리로 COM의 한계 보완. |
| status | ACTIVE |
한글(HWP) COM API로 문서 구조와 서식을 생성하고, HWPX XML 후처리로 COM이 못하는 부분을 보정하는 하이브리드 방식 스킬.
- Windows (한글 프로그램은 Windows 전용)
- 한글과컴퓨터 한글(HWP) 또는 한컴오피스 설치
- Python 3.10+ (pywin32 호환 필요)
- pip install pywin32
hwp-com-writer/
├── SKILL.md ← 이 파일 (에이전트 메인 지침)
├── skill.yaml ← Gemini 트리거 정의
├── references/
│ └── full-guide.md ← 1,100줄짜리 완전 가이드 (심층 참조용)
└── scripts/
├── com_core.py ← COM 초기화 + 기본 함수 (set_char, set_para 등)
├── xml_postprocess.py ← XML 후처리 4종 (배경색, indent, 표, 자간)
└── template_filler.py ← HWPX 템플릿 빈칸 채우기
입력 (마크다운/텍스트/데이터)
│
▼
[COM 단계] win32com으로 한글 프로그램 직접 제어
│ ✅ 페이지 설정, 표 생성, 텍스트 삽입
│ ✅ 글꼴/크기/색상/자간/정렬/굵기
│ ✅ 내어쓰기/들여쓰기, 음영(ShadeColor)
│ ❌ 셀 배경색, treatAsChar, indent 단위
│
▼
.hwpx 저장 (SaveAs)
│
▼
[XML 후처리 단계] HWPX = ZIP → XML 수정 → 다시 ZIP
│ ✅ 셀 배경색 (borderFill 추가)
│ ✅ 표 treatAsChar (글자처럼 취급)
│ ✅ 내어쓰기 단위 (CHAR → HWPUNIT)
│ ✅ 자간 최적화 (마지막 줄 1~4글자 올리기)
│ ✅ 표 너비 강제 설정
│
▼
최종 .hwpx 완성
왜 하이브리드인가:
import win32com.client as win32
# 1) EnsureDispatch (Dispatch 아님!)
hwp = win32.gencache.EnsureDispatch("HWPFrame.HwpObject")
# 2) 보안모듈 등록 (없으면 SaveAs 보안오류)
hwp.RegisterModule("FilePathCheckDLL", "SecurityModule")
# 3) 창 표시 (선택)
try: hwp.XHwpWindows.Item(0).Visible = True
except: pass
# 4) 새 문서
hwp.Run("FileNew")
Dispatch → 속성 접근 실패. 반드시 gencache.EnsureDispatchRegisterModule 빠지면 저장 시 보안 오류def rgb_to_hwp(r, g, b):
return (b << 16) | (g << 8) | r
# 빨간색: rgb_to_hwp(255, 0, 0) = 255 (RGB와 동일하게 보이지만 우연)
# 파란색: rgb_to_hwp(0, 0, 255) = 16711680 (RGB와 다름!)
# 음영 적용 후 다음 텍스트에서 반드시 리셋
hcs.ShadeColor = 0xFFFFFFFF # "none"
# 안 하면 이전 음영이 계속 적용됨 (COM 내부 캐시 문제)
COM이 생성한 HWPX와 HWP가 직접 저장한 HWPX의 네임스페이스가 다르다:
<hp:tbl>, <hh:charPr>, <hc:left><ns0:tbl>, <ns1:charPr>, <ns2:switch># ❌ 틀림 — 한쪽에서만 동작
re.search(r'<hp:tbl', content)
# ✅ 맞음 — 어느 네임스페이스든 매칭
re.search(r'<[^>]*:tbl\b', content)
header.xml에 charPr, paraPr, borderFill을 추가하면 부모 태그의 itemCnt를 반드시 수정:
# borderFill 3개 → 6개로 늘렸으면:
header = re.sub(
r'(<[^>]*:borderFills\b[^>]*)\bitemCnt="\d+"',
r'\1itemCnt="6"', header
)
# 이거 빠지면 HWP가 추가된 항목을 무시함
def force_out_of_table(hwp):
for _ in range(4):
try: hwp.Run("TableOut")
except: pass
try: hwp.Run("CloseEx")
except: pass
hwp.Run("MoveDown")
# 안 하면 다음 표가 이전 표 안에 중첩됨
<*:t> 태그 정규식 주의# ❌ :t 뒤에 다른 문자 매칭 (table, text 등)
re.compile(r'<[^>]*?:t[^>]*?>')
# ✅ 정확히 :t 태그만
re.compile(r'<[^>]*?:t>|<[^>]*?:t\s[^>]*?>')
| API | 문제 |
|---|---|
CellBorderFill Execute()로 배경색 | Execute()가 FillAttr을 HSet에 직렬화 안 함 → 저장 시 무시 |
SetPosBySet(ctrl.GetAnchorPos(0)) | HWP 프로세스 종료 (크래시) |
HParameterSet.HTableDef | 존재하지 않는 속성 |
InitScan()/GetText() | 파라미터 형식 문제로 텍스트 미반환 |
MoveNextChar로 표 진입 | 커서가 표 안으로 안 들어감 → MoveDown 사용 |
| bat 파일로 Python 실행 | exit 0 반환하지만 미실행 → bash에서 직접 실행 |
import zipfile, re
with zipfile.ZipFile("reference.hwpx") as z:
hdr = z.read("Contents/header.xml").decode("utf-8")
sec = z.read("Contents/section0.xml").decode("utf-8")
# charPr에서 폰트/크기/색상 추출
for m in re.finditer(r'<[^>]*charPr\b[^>]*id="(\d+)"[^>]*height="(\d+)"[^>]*textColor="([^"]*)"', hdr):
print(f"charPr id={m.group(1)}, height={int(m.group(2))/100}pt, color={m.group(3)}")
# borderFill에서 배경색 추출
for m in re.finditer(r'faceColor="([^"]*)"', hdr):
print(f"배경색: {m.group(1)}")
scripts/com_core.py의 함수들을 사용:
import sys; sys.path.insert(0, "SKILL_DIR/scripts")
from com_core import init_hwp, set_char, set_para, insert_text, setup_page, force_out_of_table
hwp = init_hwp()
setup_page(hwp)
set_char(hwp, face_h="함초롬돋움", pt=14, bold=1, color=(0, 0, 255))
set_para(hwp, align=3, line_spacing=170)
insert_text(hwp, "제목 텍스트")
hwp.Run("BreakPara")
hwp.SaveAs("output.hwpx", "HWPX", "")
hwp.Quit()
from xml_postprocess import apply_table_postprocess, apply_indent_unit_postprocess, apply_spacing_optimization
apply_table_postprocess("output.hwpx")
apply_indent_unit_postprocess("output.hwpx")
apply_spacing_optimization("output.hwpx")
기존 HWPX 서식의 빈 불릿(❍, ◦, - 등)에 내용을 채워넣는 방식. COM 없이 XML 직접 편집만으로 동작. 한글 프로그램 불필요.
from template_filler import fill_template
fill_template(
template="양식.hwpx",
output="완성본.hwpx",
summary=["◦ 요약1", "◦ 요약2"],
body=[" ❍ 본문1", " ❍ 본문2", " - 세부사항"]
)
동작 원리:
section0.xml 읽기<*:t> 태그에서 빈 불릿 기호(◦, ❍, -) 찾기1 pt = 100 HWPUNIT
1 mm = 283.46 HWPUNIT
167 mm (표 너비) = 47339 HWPUNIT
A4 가로 (210mm) = 59528 HWPUNIT
A4 세로 (297mm) = 84188 HWPUNIT
19mm 여백 = 5386 HWPUNIT
15mm 여백 = 4252 HWPUNIT
모든 COM 서식 조작은 이 패턴:
param_set = hwp.HParameterSet.H서식종류
hwp.HAction.GetDefault("액션이름", param_set.HSet)
param_set.속성명 = 값
hwp.HAction.Execute("액션이름", param_set.HSet)
hcs = hwp.HParameterSet.HCharShape
hwp.HAction.GetDefault("CharShape", hcs.HSet)
hcs.FaceNameHangul = "함초롬돋움"
hcs.FaceNameLatin = "Times New Roman"
hcs.Height = 1400 # 14pt (pt × 100)
hcs.Bold = 1
hcs.TextColor = rgb_to_hwp(0, 0, 255) # BGR!
# 자간: 언어별 각각 설정
for lang in ("Hangul", "Latin", "Hanja", "Japanese", "Other", "Symbol", "User"):
setattr(hcs, f"Spacing{lang}", -3)
# ★ ShadeColor 항상 설정 (리셋 포함)
hcs.ShadeColor = 0xFFFFFFFF # none (리셋)
hwp.HAction.Execute("CharShape", hcs.HSet)
hps = hwp.HParameterSet.HParaShape
hwp.HAction.GetDefault("ParagraphShape", hps.HSet)
hps.AlignType = 0 # 0=양쪽, 1=왼쪽, 2=오른쪽, 3=가운데, 4=배분
hps.LineSpacingType = 0 # 0=PERCENT
hps.LineSpacing = 170
hps.LeftMargin = 1000 # 왼쪽 여백 10pt (줄바꿈 후 시작 위치)
hps.Indentation = -2150 # 내어쓰기 (음수=내어쓰기)
hwp.HAction.Execute("ParagraphShape", hps.HSet)
tc = hwp.HParameterSet.HTableCreation
hwp.HAction.GetDefault("TableCreate", tc.HSet)
tc.Rows = 1; tc.Cols = 4
tc.WidthType = 2 # 절대 크기
tc.WidthValue = 47339 # 167mm
tc.ColWidth.SetItem(0, 2400)
tc.ColWidth.SetItem(1, 600)
# ...
hwp.HAction.Execute("TableCreate", tc.HSet)
# 셀 이동
hwp.Run("TableRightCell")
hwp.Run("TableLowerCell")
sec = hwp.HParameterSet.HSecDef
hwp.HAction.GetDefault("PageSetup", sec.HSet)
sec.PageDef.PaperWidth = 59528 # A4 가로 (210mm)
sec.PageDef.PaperHeight = 84188 # A4 세로 (297mm)
sec.PageDef.LeftMargin = 5386 # 19mm
sec.PageDef.RightMargin = 5386
sec.PageDef.TopMargin = 4252 # 15mm
sec.PageDef.BottomMargin = 4252
hwp.HAction.Execute("PageSetup", sec.HSet)
hwp.SaveAs(str(output_path), "HWPX", "")
hwp.Quit()
파일.hwpx (ZIP)
├── Contents/
│ ├── header.xml ← 스타일 정의 (charPr, paraPr, borderFill, fontface)
│ └── section0.xml ← 본문 (단락, 표, 텍스트)
├── META-INF/
│ ├── container.xml
│ └── manifest.xml
├── settings.xml
└── version.xml
charPr id="5"를 정의하면 본문에서 charPrIDRef="5"로 참조<hp:p> 단락 안에 <hp:run charPrIDRef="N"> → <hp:t>텍스트</hp:t><hp:tc borderFillIDRef="N">으로 참조<ns2:switch> 블록 (HwpUnitChar/default 이중 표현)더 자세한 내용은 references/full-guide.md를 참조.
1,100줄 분량으로 Phase 0~5 전체 시행착오, 디버깅 방법, 버전 히스토리 포함.
Use this skill as the default fallback when hwpx reaches structural validity but still fails visually in Hancom.
Work from the reference document layout and replace visible text in place. Do not rebuild the document from scratch unless the document is structurally simple.
hwpx only for analysis and hand the final write-back to COM.hwpxanalyze_template.py or XML inspection to identify paragraph, run, and cell structure.