Table Edit Improvement Plan¶
Status note: this is a historical design note for inaccurate HWP table-cell targeting, especially COM workflows that rely on tableIndex + row/col moves. Since this note was written, the HWPX package path gained a merge/nested-table-aware table model for form-map extraction and the submission-template filler. That newer HWPX path reconstructs grids from cellAddr plus cellSpan, separates parent-cell direct text from nested table text, and clones multi-row record groups when expanding known profile tables. The COM-side label resolver and generic dump-table / table-get / table-set-by-label APIs described below are still future work.
배경¶
초기 로컬 fixture 문서에 마크다운 내용을 적용하는 과정에서 표 셀을 정확히 선택하지 못하는 문제가 확인됐다.
현재 구현은 tableIndex + rowMoveCount + columnMoveCount 방식으로 셀을 고르는데, 실제 신청서 양식은 병합 셀과 불규칙한 표 구조가 많아서 이 방식만으로는 안정적인 편집이 어렵다.
관련 확인 결과:
- 원본 문서 첫 표 덤프: 로컬 검증 artifact였으며 공개 repo에는 포함하지 않는다.
- 현재 표 선택 로직:
src/OpenHwp.Automation/HwpSession.cs - 현재 CLI 진입점:
src/OpenHwp.Automation.Cli/Program.cs
현재 문제¶
1. 표가 단순 격자가 아니다¶
첫 표만 봐도 다음 현상이 있다.
- 같은 텍스트가 여러 좌표에서 반복된다.
- 병합 셀 때문에
row/col이동이 논리 셀과 일치하지 않는다. - 빈 셀처럼 보여도 실제로는 병합 영역 일부일 수 있다.
- 아래 행으로 내려갈수록 동일한 긴 블록이 반복 선택된다.
즉 지금 방식의 SelectTableCell(r, c, tableIndex)는 "시각적 표의 r행 c열"을 보장하지 않는다.
2. 현재 수정 경로가 내용 중심이다¶
초기 로컬 fixture 적용 경로는 크게 두 가지였다.
- 전역 치환: 안전한 문자열을 문서 전체에서 치환
- 구간 교체:
8. 사업목적이후를 평문으로 재삽입
이 방식은 내용 반영에는 유효하지만, 다음 문제가 있다.
- 표 셀 기준으로는 어디가 바뀌었는지 보장되지 않는다.
- 병합 셀 양식에서 잘못된 셀을 건드릴 수 있다.
- 본문 구간 교체는 서식을 유지하지 못한다.
3. 현재 관측 API가 약하다¶
현재는 다음 수준까지는 가능하다.
- 표 선택
- 셀 블록 선택
- 복사 후 클립보드 텍스트 읽기
- 문서 전체 텍스트 읽기
하지만 아직 없다.
- 특정 표의 논리 셀 구조 추출
- 셀별 고유 식별자
- 라벨 셀과 값 셀의 관계 분석
- "기관명 옆 값 셀" 같은 의미 기반 선택
원인 정리¶
핵심 원인은 편집보다 관측이 부족한 것이다.
현재는:
- 표를 선택할 수는 있음
- 셀을 대충 이동할 수는 있음
- 하지만 현재 셀이 문서상 어떤 의미를 가지는지는 모름
결과적으로 AI가 "기관명 옆 셀에 회사명을 넣는다"가 아니라 "어딘가의 4행 5열쯤 되는 셀"을 건드리게 된다.
목표¶
표 편집을 다음 수준으로 끌어올린다.
- 문서의 표를 안전하게 덤프한다.
- 병합 셀을 포함한 논리 셀 모델을 만든다.
- 라벨 기반으로 값 셀을 찾는다.
- 값 셀만 수정한다.
- 수정 전후를 다시 덤프해서 검증한다.
즉 최종 목표는 좌표 기반 편집이 아니라 의미 기반 편집이다.
제안 아키텍처¶
1. TableDump 계층¶
새 기능:
DumpTable(tableIndex)DumpTableRange(tableIndex, maxRows, maxCols)ReadSelectedCellText()ReadTableCell(tableIndex, row, col)
출력 포맷 예시:
{
"tableIndex": 0,
"cells": [
{ "row": 0, "col": 0, "text": "1) 공고번호", "selectionOk": true },
{ "row": 0, "col": 1, "text": "제2026-0295호", "selectionOk": true }
]
}
초기에는 완전한 병합 정보 없이도 괜찮다. 우선 "현재 좌표에서 선택했을 때 읽히는 텍스트"를 안정적으로 얻는 게 먼저다.
2. LogicalTable 계층¶
DumpTable 결과를 후처리해서 논리 테이블 모델을 만든다.
핵심 규칙:
- 동일 텍스트가 넓게 반복되면 병합 셀 후보로 본다.
- 라벨 후보와 값 후보를 분리한다.
- 빈 셀과 병합 확장 셀을 구분한다.
- 인접한 라벨-값 패턴을 추정한다.
예시:
{
"labelValuePairs": [
{ "label": "기관명", "value": "차라투(주)", "tableIndex": 0, "valueCell": { "row": 4, "col": 5 } },
{ "label": "사업자등록번호", "value": "624086-01323", "tableIndex": 0, "valueCell": { "row": 5, "col": 4 } }
]
}
3. Label Resolver 계층¶
새 기능:
FindCellByLabel(tableIndex, "기관명")FindValueCellByLabel(tableIndex, "기관명")FindBestMatchingCell("사업자등록번호")
매칭 규칙:
- 완전일치 우선
- 공백/개행 정규화 후 비교
기관명,사업자등록번호,부서/직위같은 핵심 라벨 사전 지원- 중복 시 같은 행 또는 우측 셀 우선
4. Safe Write 계층¶
새 기능:
WriteTableCell(tableIndex, row, col, text)WriteValueByLabel(tableIndex, label, text)ReplaceValueNextToLabel(tableIndex, label, expectedOldText, newText)
쓰기 규칙:
- 먼저 현재 셀 텍스트를 읽는다.
- 예상값과 다르면 바로 쓰지 않는다.
- diff 로그를 남긴다.
- 수정 후 다시 읽어서 검증한다.
구현 순서¶
Phase 1. 관측 강화¶
먼저 구현할 것:
ReadSelectedCellText()DumpTable(tableIndex, maxRows, maxCols)- CLI
dump-table <inputPath> <tableIndex> [maxRows] [maxCols]
산출물:
- JSON dump
- 사람이 읽을 수 있는
.txtdump
이 단계가 끝나면 수동으로 대상 문서의 표 맵을 만들 수 있다.
Phase 2. 의미 기반 셀 찾기¶
다음 구현:
FindValueCellByLabel(...)- 라벨 정규화 함수
- 표 내 라벨-값 관계 탐색기
우선 대상 라벨:
기관명사업자등록번호주 소부서/직위정부출연금현금현물합 계
Phase 3. 안전한 셀 쓰기¶
다음 구현:
WriteTableCell(...)WriteValueByLabel(...)UpdateFormSummary(test1 mapping spec)
여기서는 전역 치환을 줄이고, 앞쪽 신청서/요약서 표는 전부 셀 쓰기로 바꾼다.
Phase 4. 본문 편집 개선¶
표와 별개로 본문도 현재는 평문 치환만 된다. 나중에는 다음이 필요하다.
- 섹션 경계 탐색
- 섹션 단위 선택
- 선택 영역 삭제 후 삽입
- 문단 스타일 복사/붙여넣기
하지만 우선순위는 표다.
로컬 fixture에 필요했던 매핑 전략¶
해당 로컬 fixture는 다음 순서로 적용하는 것이 맞다.
- 표 덤프 생성
- 앞쪽 신청서 표 라벨-값 맵 작성
- 요약서 표 라벨-값 맵 작성
- 셀 단위 수정
- 문서 전체 텍스트 검증
- 본문 섹션 적용
즉 fixture 전용 구현 순서는:
DumpTable(0)DumpTable(1)또는 요약서 표 index 확인- 라벨 기반 수정기 작성
- fixture 적용 스크립트 작성
권장 API 초안¶
라이브러리:
public string ReadSelectedCellText();
public TableDump DumpTable(int tableIndex, int maxRows = 30, int maxCols = 10);
public TableCellRef FindValueCellByLabel(int tableIndex, string label);
public bool WriteTableCell(int tableIndex, int row, int col, string text);
public bool WriteValueByLabel(int tableIndex, string label, string expectedOldText, string newText);
CLI:
OpenHwp.Automation.Cli.exe dump-table input.hwp 0
OpenHwp.Automation.Cli.exe table-get input.hwp 0 4 5
OpenHwp.Automation.Cli.exe table-set input.hwp 0 4 5 "차라투 주식회사" out.hwpx
OpenHwp.Automation.Cli.exe table-set-by-label input.hwp 0 "기관명" "차라투 주식회사" out.hwpx
검증 전략¶
표 편집은 반드시 3단계 검증이 필요하다.
- 수정 전 셀 읽기
- 수정 실행
- 수정 후 같은 셀 다시 읽기
추가 검증:
- 문서 전체
read-text - 핵심 문자열 출현 횟수 확인
- 필요 시 visible 모드에서 육안 확인
개발 시 주의점¶
- 병합 셀에서는
row/col이 시각적 그리드와 다를 수 있다. - 선택 후
Copy+ 클립보드 읽기는 현재 가장 실용적인 관측 수단이다. - 전역 치환은 표 수정의 보조 수단으로만 써야 한다.
replace-after-marker는 본문 내용 적용에는 쓸 수 있지만, 서식 보존형 편집으로 간주하면 안 된다.- 표 관련 기능은 항상
tableIndex를 명시적으로 받는 쪽이 낫다.
결론¶
현재 문제는 "편집 기능 부족"보다 "정확한 표 관측 모델 부재"에 가깝다.
다음 개발은 반드시 이 순서로 가야 한다.
- 표 덤프
- 라벨 기반 셀 탐색
- 셀 단위 안전 쓰기
- fixture 전용 매핑 적용
이 순서를 지키면 복잡한 양식 문서도 전역 치환에 덜 의존하고 안정적으로 수정할 수 있다.