AI 해결 노트 · 2026-09-18 · 실측 2026-09-18

한글 양식 빈칸 채우는 세 가지 — 표 칸·누름틀·찾아 바꾸기를 같은 서식으로 대조

한 줄로

표로 된 신청서 더미를 직접 만들어 같은 값을 세 가지로 넣어 봤습니다. 표 칸에 대고 치기, 누름틀에 값 넣기, [성명] 같은 자리표시자를 찾아 바꾸기입니다. 나온 PDF 세 장은 150dpi로 그려 해시를 내 보니 화소 한 점까지 같았습니다. 갈리는 건 결과가 아니라 틀렸을 때입니다. 없는 누름틀 이름을 부르면 오류도 없이 문서가 그대로였고, 없는 자리표시자를 찾으면 False가 돌아왔고, 표 칸에서 커서를 한 칸 잘못 옮기면 값이 라벨 칸에 붙어 버렸습니다.

이런 분께

사내 신청서·확인서 같은 한글 양식에 이름만 바꿔 여러 장을 만들어야 하는 분입니다. 어느 방식으로 양식을 짜 둬야 나중에 덜 고생하는지가 궁금한 경우입니다.

실측 환경

항목
OSWindows 11 Home (10.0.26200)
한글한글 Office 2022, 버전 문자열 12, 0, 0, 4605
PDF 읽기PyMuPDF 1.28.0 (MuPDF 1.29.0), Python 3.12.10
더미 양식6줄 2칸 표. PDF에서 잰 칸 너비는 왼쪽 78.5mm, 오른쪽 78.6mm, 줄 높이 약 11mm
채운 값홍길동 / 010-0000-0000 / 총무팀 / 2026-09-18 / 문서 작성 기초 / 신청 사유 81자
만든 곳C:\Users\User\AppData\Local\Temp\office_KO3

양식과 값은 전부 지어낸 것입니다. 회사 서식이나 남의 신청서는 쓰지 않았습니다.

아래 숫자는 한글에서 같은 기능을 자동화(한글 COM)로 불러 얻은 것입니다. 메뉴를 눌러 보고 찍은 화면은 이 글에 없습니다.

더미 양식

빈칸이 오른쪽에 있는 흔한 꼴로 만들었습니다.

여섯 줄짜리 신청서 표. 왼쪽에 성명·연락처·소속·신청일·과정명·신청 사유, 오른쪽은 모두 비어 있다.
여섯 줄짜리 신청서 표. 왼쪽에 성명·연락처·소속·신청일·과정명·신청 사유, 오른쪽은 모두 비어 있다.

같은 표를 세 벌 만들었습니다. 오른쪽 칸을 그냥 비워 둔 것(A), 누름틀을 하나씩 심어 둔 것(B), [성명] 같은 자리표시자를 적어 둔 것(C)입니다. 파일 크기가 셋 다 다릅니다.

SaveAs KO3_55_form_A.hwp -> True (18944 바이트)   ← 빈칸
SaveAs KO3_55_form_B.hwp -> True (22016 바이트)   ← 누름틀 6개
SaveAs KO3_55_form_C.hwp -> True (19968 바이트)   ← [성명] 자리표시자
누름틀 목록 = '성명\x02연락처\x02소속\x02신청일\x02과정명\x02신청사유'

누름틀을 심은 B가 빈칸 양식 A보다 3,072바이트 큽니다. 무엇이 그만큼을 차지하는지는 뜯어보지 않았습니다.

세 가지로 채운 결과

같은 값을 세 방식으로 넣고 각각 PDF로 저장했습니다.

세 방법으로 채운 신청서. 왼쪽부터 표 칸 직접 입력, 누름틀, 찾아 바꾸기 — 셋이 똑같아 보인다.
세 방법으로 채운 신청서. 왼쪽부터 표 칸 직접 입력, 누름틀, 찾아 바꾸기 — 셋이 똑같아 보인다.

눈으로는 구별이 안 됩니다. 그래서 세 PDF를 150dpi로 그려 화소 덩어리째 해시를 냈습니다.

① 표 칸 직접 입력   1240x1753 화소  sha256[:16]=11066f40de6a401c
② 누름틀에 값 넣기   1240x1753 화소  sha256[:16]=11066f40de6a401c
③ 찾아 바꾸기      1240x1753 화소  sha256[:16]=11066f40de6a401c
세 해시가 모두 같은가 = True

글꼴도 셋 다 INPILL+HCRBatang 한 벌, 쪽 수도 1쪽, PDF에서 뽑히는 글자도 186자로 같습니다. 파일 크기만 22,947 / 22,949 / 22,947바이트로 두 바이트 차이가 났습니다.

받는 사람이 보는 결과물만 놓고 보면 세 방식은 구별할 게 없습니다.

안 채운 채로 넘기면 — 여기서 갈린다

값을 하나도 넣지 않은 채 세 양식을 그대로 PDF로 저장해 봤습니다.

안 채운 세 양식의 PDF. 빈칸 양식과 누름틀 양식은 오른쪽이 비어 있고, 자리표시자 양식만 대괄호 글자가 그대로 찍혀 있다.
안 채운 세 양식의 PDF. 빈칸 양식과 누름틀 양식은 오른쪽이 비어 있고, 자리표시자 양식만 대괄호 글자가 그대로 찍혀 있다.

누름틀 양식은 문서 글자를 읽어 보면 "성명 입력" 같은 안내문이 나옵니다.

B_누름틀  본문글자='… / 성명 / 성명 입력 / 연락처 / 연락처 입력 / 소속 / 소속 입력 / …'

그런데 그 상태로 PDF를 만들면 안내문은 따라가지 않습니다.

A_빈칸       PDF에서 뽑은 글자: … 성명 / 연락처 / 소속 / 신청일 / 과정명 / 신청 사유
B_누름틀      PDF에서 뽑은 글자: … 성명 / 연락처 / 소속 / 신청일 / 과정명 / 신청 사유
C_자리표시자    PDF에서 뽑은 글자: … 성명 / [성명] / 연락처 / [연락처] / 소속 / [소속] / …

빈 양식 PDF의 크기도 A와 B가 16,198바이트로 같았고, C만 16,423바이트였습니다. 누름틀 안내문은 이번 PDF에서 뽑은 글자에 없었고, 자리표시자는 그냥 글자라서 빠뜨리면 [연락처]가 적힌 채로 상대에게 갑니다.

이게 두 방식의 진짜 차이입니다. 자리표시자 방식은 안 채운 칸이 눈에 확 띄고, 누름틀 방식은 안 채운 칸이 PDF에서 그냥 빈칸이 됩니다. 종이에 인쇄했을 때도 같은지는 확인하지 않았습니다.

이름만 바꿔 세 장

같은 양식을 열어 여섯 칸을 채워 세 장씩 뽑았습니다. 시간은 양식 파일을 여는 호출부터 PDF 저장이 끝날 때까지입니다.

방식1장2장3장합계세 장 모두 값이 제자리에
① 표 칸에 직접 입력1.42초0.50초0.51초2.42초들어감
② 누름틀에 값 넣기0.27초0.33초0.38초0.99초들어감
③ 찾아 바꾸기0.60초0.40초0.46초1.46초들어감

①의 첫 장만 1.42초로, 둘째·셋째의 0.50초·0.51초보다 오래 걸렸습니다. 왜 첫 장만 그런지는 재지 않았습니다. 이건 자동화로 돌렸을 때의 시간이고, 사람이 손으로 칠 때의 시간은 아닙니다.

세 방식 모두 세 장이 다 1쪽으로 나왔고, 이름과 소속이 PDF에서 빠짐없이 검색됐습니다.

① 1장:O/O 쪽수1  2장:O/O 쪽수1  3장:O/O 쪽수1
② 1장:O/O 쪽수1  2장:O/O 쪽수1  3장:O/O 쪽수1
③ 1장:O/O 쪽수1  2장:O/O 쪽수1  3장:O/O 쪽수1

틀렸을 때 무엇이 남는가

세 방식에 흔한 실수를 하나씩 넣어 봤습니다.

누름틀 이름을 잘못 부르면 — 실제 이름은 성명인데 이름으로 값을 넣어 봤습니다.

PutFieldText('이름') 반환 = None | 예외 없음
넣은 뒤 본문 = '… / 성명 / 성명 입력 / 연락처 / 연락처 입력 / …'
실제 누름틀 목록 = '성명\x02연락처\x02소속\x02신청일\x02과정명\x02신청사유'

오류도 없고 되돌려 주는 값도 없습니다. 문서는 그대로입니다. 넣었다고 생각하고 그냥 저장하면 빈칸인 채로 나갑니다.

찾아 바꾸기 이름을 틀리면 — 없는 [이름]을 찾게 하면 False가 돌아옵니다.

AllReplace('[이름]'->'홍길동') 반환 = False
AllReplace('[성명]'->'홍길동') 반환 = True

적어도 못 찾았다는 사실은 알려 줍니다. 그리고 안 바뀐 칸은 [연락처]처럼 종이에 그대로 남으니 눈으로도 잡힙니다.

표 칸에 직접 칠 때 커서가 한 칸 밀리면 — 값이 라벨 칸으로 들어갑니다.

왼쪽은 값이 전부 라벨 칸에 들어가 오른쪽이 비어 버린 양식, 오른쪽은 성명만 채워지고 나머지는 대괄호가 남은 양식.
왼쪽은 값이 전부 라벨 칸에 들어가 오른쪽이 비어 버린 양식, 오른쪽은 성명만 채워지고 나머지는 대괄호가 남은 양식.
본문 = '… / 홍길동성명 /  / 010-0000-0000연락처 /  / 총무팀소속 /  / …'

홍길동성명처럼 라벨에 값이 붙고 오른쪽 칸은 비었습니다. 이건 한눈에 보이니 오히려 나은 편입니다.

긴 값을 넣으면 칸이 어떻게 되나

누름틀 양식의 신청 사유 칸에 81자와 567자를 각각 넣어 봤습니다.

왼쪽은 사유 81자로 사유 칸만 조금 커진 양식, 오른쪽은 567자로 사유 칸이 쪽 절반을 먹은 양식.
왼쪽은 사유 81자로 사유 칸만 조금 커진 양식, 오른쪽은 567자로 사유 칸이 쪽 절반을 먹은 양식.

PDF에서 가로줄 사이를 재 보면 이렇습니다.

81자  : 칸 높이(pt) = [31.1, 31.2, 31.0, 31.2, 31.2, 60.7]   표 아래끝 348.9pt
567자 : 칸 높이(pt) = [31.1, 31.2, 31.0, 31.2, 31.2, 364.4]  표 아래끝 652.6pt

앞 다섯 줄은 31pt(약 11mm)로 그대로인데 마지막 줄만 60.7pt(약 21mm), 567자에서는 364.4pt(약 129mm)까지 늘었습니다. 표 아래끝도 348.9pt에서 652.6pt로 내려갔고, "위 내용이 사실임을 확인합니다."라는 문장은 두 경우 모두 표 아래쪽에 있었습니다.

81자를 넣은 결과에서는 칸을 넘친 글자가 한 조각도 없었습니다. 글자 조각마다 자리를 재 표 테두리와 견줘 봤습니다.

가로줄 y = [132.5, 163.6, 194.8, 225.8, 257.0, 288.2, 348.9]
세로줄 x = [86.5, 309.1, 531.8]
칸을 넘친 글자 조각 수 = 0

이번 양식에서는 사유가 길어지자 칸이 커지고 표 아래끝이 내려갔습니다. 567자 쪽에서도 같은 넘침 검사를 돌리지는 않았습니다. 한 장에 딱 맞춰 만든 서식이라면 사유란에 긴 글을 넣는 순간 아래가 밀려 나가니, 값을 넣은 뒤 쪽 수를 한 번 세 보시는 게 안전합니다.

결과

보는 눈① 표 칸 직접 입력② 누름틀③ 찾아 바꾸기
채운 뒤 PDF화소까지 같음화소까지 같음화소까지 같음
양식 파일 크기18,944바이트22,016바이트19,968바이트
세 장 뽑는 데2.42초0.99초1.46초
이번에 넣어 본 실수커서 한 칸 밀림 → 라벨 칸에 값이 붙음없는 이름 호출 → 예외 없이 문서 그대로없는 자리표시자 → False 반환
안 채우고 보내면빈칸빈칸(안내문은 안 뽑힘)[연락처] 가 찍힘
사유 81→567자시험 안 함마지막 칸 높이 60.7 → 364.4pt시험 안 함

실패한 것

ps.AllReplace = 1 -> AttributeError: … object has no attribute 'AllReplace'
있는 항목 = ['AllWordForms', 'AutoSpell', … 'ReplaceMode', 'ReplaceString', … 'WholeWordOnly']

확인하지 않은 것

함께 보기