DocPilot 문서 코멘트 기능의 설계와 사용 방법

DocPilot 개발을 위해 빈 영역을 드래그해 다는 영역 코멘트(쪽 위치와 화면 이미지 전달)와 긴 인용의 중략 전달을 설계·구현하고, Claude·Codex 실제 전송으로 확인한 결과와 사용 방법을 정리합니다.

DocPilot 은 HWP·HWPX·DOCX·PPTX·XLSX 문서를 열어 편집하고, 오른쪽 패널의 AI 에이전트(Claude·Codex)에 수정을 요청하는 데스크톱 앱입니다. 문서의 수정할 위치에 코멘트를 달아 두었다가 에이전트에 한꺼번에 요청하는 기능이 있었지만, 코멘트는 글을 선택한 곳에만 달 수 있었습니다. 또한 긴 인용은 뒷부분이 잘린 채 전달되었습니다. 그림·도형·빈 여백에 대한 요청을 표현할 방법과, 긴 글에서 위치 단서를 잃지 않는 전달 방식을 정해야 했습니다.

이 글은 DocPilot 개발을 위해 설계하고 구현한 두 기능, 빈 영역을 드래그해 다는 영역 코멘트와 긴 인용의 중략 전달을 다룹니다. 기능의 구조와 사용 방법, 실제 앱에서 Claude·Codex 로 전송해 확인한 결과를 차례로 정리합니다.

시험 범위. DOCX 시료 1건, 화면 배율 100% 에서 확인했습니다. 시험하지 않은 형식과 배율은 마지막 절의 한계에 적었습니다.

글 코멘트와 영역 코멘트의 구분

코멘트는 두 종류입니다. 어느 쪽이든 패널 목록에 쌓였다가 '한꺼번에 요청' 한 번으로 같은 요청 글에 들어갑니다.

구분글 코멘트영역 코멘트
만드는 방법글을 선택하고 '코멘트' 단추, 리본 '코멘트', 또는 Ctrl+Alt+M문단 밖 빈 곳(쪽 여백·문단 사이·그림 위)에서 드래그, 글 위에서는 Alt+드래그
대상선택한 글, 선택이 없으면 커서가 있는 문단드래그한 사각형. 두 쪽에 걸쳐도 됨
문서 표시노란 강조와 문단 옆 번호쪽마다 점선 사각형, 번호는 첫 쪽 사각형에
에이전트에 전달하는 정보문단 id, 쪽 표시, 대상 글(긴 글은 중략), 요청쪽 표시, 쪽마다 가로·세로 % 위치, 영역 이미지(PNG), 겹치는 문단 id(최대 12개), 요청
사용 조건편집할 수 있는 문서가 열려 있을 때PDF·읽기 전용 문서와 변경안 비교 화면에서는 쓸 수 없음

코멘트 작성과 변경안 반영 절차

  1. 글 코멘트 달기. 고칠 글을 선택하면 0.15초 뒤 선택 영역 아래에 '코멘트' 단추가 나타납니다. 누르고 요청을 적은 뒤 Enter 로 추가합니다. Shift+Enter 는 줄바꿈, Esc 는 취소입니다.
  2. 영역 코멘트 달기. 쪽 여백이나 그림 위처럼 글이 없는 곳에서 눌러 끌면 점선 사각형이 그려집니다. 놓으면 그 영역의 화면 이미지가 입력 창에 썸네일로 나오고 아래에 쪽 표시와 위치가 표시됩니다. 글 위에서 영역을 고르려면 Alt 를 누른 채 끕니다.
  3. 목록 확인. 추가한 코멘트는 에이전트 패널 입력창 위 '수정 요청 N건' 목록에 쌓입니다. 영역 항목은 썸네일과 "영역 · 1~2쪽" 같은 쪽 표시로 구분됩니다. 항목을 누르면 문서의 해당 위치로 이동하고, 문서의 번호를 누르면 목록의 항목이 깜박입니다.
  4. 한꺼번에 요청. Claude 또는 Codex 탭에서 '한꺼번에 요청'을 누르면 편집 모드로 바뀌고 요청이 전송됩니다. 입력창에 적어 둔 글은 "추가 요청"으로 붙고, 사용자 말풍선에는 '편집'과 '영역 이미지 N장' 표시가 붙습니다.
  5. 변경안 검토. 응답이 끝나면 원본·수정본 비교 화면이 열립니다. 항목별로 적용·제외를 고른 뒤 '수정 확인'을 눌러야 원본 파일에 반영됩니다.
그래프 위를 드래그해 연 코멘트 입력 창에 영역 이미지 썸네일과 쪽 위치가 표시된 DocPilot 화면
그래프 위를 드래그한 직후의 입력 창입니다. 영역 이미지 썸네일과 쪽 위치가 함께 표시됩니다(아래쪽 노란 강조는 먼저 단 글 코멘트입니다).
1쪽과 2쪽에 걸친 점선 사각형과 패널의 수정 요청 3건 목록이 보이는 DocPilot 화면
글 코멘트 1건과 영역 코멘트 2건(세 번째는 1~2쪽에 걸침)을 단 상태입니다. 영역 항목은 목록에서 썸네일로 구분됩니다.

에이전트에 전달되는 요청 글

'한꺼번에 요청'을 누르면 영역 이미지를 PNG 파일로 저장한 뒤 항목을 번호 목록 글로 합칩니다. 앱은 이 글 앞에 현재 문서 맥락 블록(문서 경로·형식·보는 위치·커서 문단·모드)을 붙이고, 이미지 파일 경로 목록을 엔진별 방식으로 함께 넘깁니다. 아래는 영역 코멘트 2건을 보냈을 때의 요청 글입니다. 이미지 경로의 앞부분은 줄였습니다.

문서에 단 코멘트 2건을 반영해 고쳐 줘. 항목마다 문단 id 와 대상 글을 기준으로 고치고, 요청하지 않은 곳은 바꾸지 마. 영역 항목은 이미지 경로의 그림을 먼저 확인하고, 쪽 위치와 겹치는 문단 id 를 기준으로 고쳐.

1. 영역 · 1쪽 (쪽 안 가로 4~82%, 세로 42~70%)
   이미지: <앱 자료 폴더>\regions\<대화 ID>\rmvs6ckfo1.png
   겹치는 문단 id: 4 "그림 1. 분기별 실적"
   요청: 이 그래프 아래에 단위(억 원)를 밝히는 문장을 추가
2. 영역 · 1~2쪽 (1쪽 가로 4~59%, 세로 86~100% / 2쪽 가로 4~59%, 세로 0~13%)
   이미지: <앱 자료 폴더>\regions\<대화 ID>\rmvs6ckfw2.png
   겹치는 문단 id: 9 "5. 세부 추진 항목 설명 문단입니다 (중략 11자) , 예산 집행 계획을 함께 적습니다.", 10 "6. …", 11 "7. …", 12 "8. …"
   요청: 쪽이 나뉜 항목 번호가 이어지는지 확인하고 문장 끝을 존댓말로 통일
항목요청 글에서의 모습만드는 곳
글 코멘트 위치문단 id 41 (3쪽) · 대상 글: "…", 선택이 없으면 대상 문단: "…"comments.js prompt()
영역 쪽 표시1쪽, 걸치면 1~2쪽, 슬라이드 n, 시트 "이름"comments.js regionLabel()
영역 위치한 쪽이면 쪽 안 가로 a~b%, 세로 c~d%, 여러 쪽이면 쪽마다 / 로 구분doc-editor.js regionInfo()
영역 이미지이미지: <PNG 경로>, 캡처에 실패하면 없음(화면 캡처 실패)agent_save_image
겹치는 문단사각형과 겹치는 글 있는 문단 최대 12개, 글은 40자 중략doc-editor.js regionInfo()
추가 요청패널 입력창에 글이 있을 때만 추가 요청: …comments.js prompt()

긴 인용의 중략 전달

인용한 글이 한도를 넘으면 앞부분과 뒷부분을 반씩 남기고 가운데를 (중략 N자) 로 표시합니다. 이전에는 뒷부분을 버리고 … 만 붙였기 때문에 문장 끝의 단서가 전달되지 않았습니다. 전달하는 글자 수는 같습니다.

방식612자 글을 한도 300자로 보낼 때 남는 부분빠지는 부분
이전(후략)앞 300자뒤 312자(표시 없음)
현재(중략)앞 150자와 뒤 150자가운데 312자((중략 312자) 로 표시)

자르는 지점은 화면과 백엔드에 나뉘어 있습니다. 글을 모으는 단계에서 먼저 앞부분만 남기면 뒤 단계에서 중략할 수 없으므로, 모으는 단계는 상한(2만자)만 두고 실제 줄이기는 글을 쓰는 단계 한 곳에서 합니다. 시스템 지시에는 이 표시의 뜻과, 전체가 필요하면 get_paragraph 로 문단을 읽으라는 문장을 넣었습니다.

위치대상한도
comments.js prompt()대상 글 · 대상 문단 · 겹치는 문단300자 · 120자 · 40자, 중략
agents/mod.rs preamble()맥락 블록의 커서 문단 · 선택한 글200자 · 1500자, 중략
doc-editor.js selectionInfo() · contextInfo()선택한 글 · 문단 글(모으는 단계)상한 2만자, 줄이지 않음
/** 긴 글은 앞뒤를 반씩 남기고 가운데를 "(중략 N자)" 로 줄임(글자 단위 = 코드 포인트, 백엔드 clip_mid 와 같은 규칙) */
export const clipMid = (s, max) => {
  const cs = Array.from(s ?? '');
  if (cs.length <= max) return s ?? '';
  const head = Math.ceil(max / 2), tail = max - head;
  return `${cs.slice(0, head).join('')} (중략 ${(cs.length - max).toLocaleString('ko-KR')}자) ${cs.slice(cs.length - tail).join('')}`;
};

Rust 백엔드에서는 agents/mod.rs 의 clip_mid 가 같은 규칙(천 단위 쉼표 포함)으로 맥락 블록을 줄입니다. 기존 clip 은 오류 메시지와 도구 결과 요약에도 쓰는 공용 함수라 그대로 두었습니다.

영역 이미지 캡처 구조

문서는 shadow DOM 안의 HTML 로, PDF 는 canvas 로 그려지므로 영역 이미지는 화면에 보이는 모습을 그대로 잘라 옵니다. Tauri 2.12.5 에는 화면 캡처 API 가 없어 WebView2 의 CapturePreview 를 직접 호출하는 명령 capture_view 를 추가했습니다. webview2-com 0.39 는 wry 가 이미 사용하는 버전이라 새로운 하위 의존성은 추가되지 않았습니다.

  1. 드래그를 놓으면 화면이 표시 상자 층과 노란 강조를 숨기고 두 프레임을 기다립니다. 캡처 이미지에 다른 코멘트 표시가 섞이지 않게 하기 위해서입니다.
  2. capture_view 가 UI 스레드에서 CapturePreview 를 호출해 웹뷰 전체를 PNG 로 메모리 스트림에 받고, 완료 알림에서 바이트를 읽어 돌려줍니다(10초 제한).
  3. 화면이 이미지 폭과 창 폭의 비율로 화면 배율을 구해 사각형만 잘라 내고, 긴 변이 1600px 를 넘으면 줄여 PNG 로 보관합니다.
  4. '한꺼번에 요청' 때 agent_save_image 가 PNG 를 대화별 폴더에 저장하고 경로를 돌려줍니다. 이름은 영문·숫자만 허용하고 PNG 서명을 확인합니다.
let core = w.controller().CoreWebView2()?;
let stream = SHCreateMemStream(None).ok_or_else(|| windows::core::Error::from(E_FAIL))?;
let out = stream.clone();
let handler = CapturePreviewCompletedHandler::create(Box::new(move |res| {
    let _ = tx.send(res.and_then(|_| read_all(&out)).map_err(|e| e.to_string()));
    Ok(())
}));
core.CapturePreview(COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT_PNG, &stream, &handler)

입력 창은 높이를 재서 화면 아래로 넘치면 사각형 위에 엽니다. 처음 구현에서는 썸네일이 렌더링되기 전에 높이를 계산하여 화면 아래쪽 영역에서 입력 창이 잘리는 문제가 있었습니다. 이후 썸네일 디코딩이 끝난 뒤 입력 창을 여는 방식으로 수정했습니다.

에이전트까지의 전달 경로

요청 글과 이미지 경로는 화면에서 앱 백엔드를 거쳐 엔진으로 갑니다. 엔진은 이미지를 확인한 뒤 문서 MCP 서버의 도구로만 문서를 조회하고 수정합니다. 편집된 내용은 원본 파일 대신 변경안 파일에 누적됩니다. 원본은 사용자가 비교 화면에서 승인한 항목만 바뀝니다.

빈 곳 드래그와 코멘트 입력화면 캡처 후 영역만 잘라 보관한꺼번에 요청PNG 저장과 요청 글 작성맥락 블록을 붙여 엔진 실행Claude: Read 도구로 PNG 열기Codex: localImage 로 첨부문서 MCP 도구로 문단 조회와 수정변경안 파일에 기록비교 화면에서 항목별 적용승인한 항목만 원본에 저장
영역 코멘트가 원본 반영까지 거치는 단계입니다. 엔진에 따라 이미지를 받는 방법만 다릅니다.
엔진요청 글영역 이미지시스템 지시
Claude응답마다 claude -p --output-format stream-json 을 실행해 표준 입력으로 전달이미지 폴더를 --add-dir 로 추가하고, 에이전트가 요청 글의 경로를 Read 로 엶--append-system-prompt-file
Codex앱 내장 codex app-server 의 turn/start input 텍스트같은 input 배열에 localImage 항목으로 첨부developerInstructions
Gemini · Copilot읽기 전용이라 보낼 수 없음해당 없음해당 없음

영역 이미지 파일의 보관과 정리

  • 추가할 때는 메모리에만 둡니다. 코멘트를 지우거나 문서를 다시 열면 디스크에 아무것도 남지 않습니다.
  • 보낼 때 파일로 저장합니다. 앱 자료 폴더 아래 대화별 하위 폴더에 저장합니다.
  • 대화가 이어지는 동안은 남깁니다. 같은 대화의 다음 요청에서 에이전트가 이전 이미지를 다시 열 수 있습니다.
  • 새 대화를 시작하면 그 대화 폴더를 지웁니다. 앱을 시작할 때는 7일 넘게 바뀌지 않은 대화 폴더를 지웁니다.

Claude 와 Codex 로 확인한 결과

배포 빌드를 원격 디버깅 포트로 실행하고 Playwright 로 조작했습니다. 시료는 그래프 그림과 20개 문단이 있는 두 쪽짜리 DOCX 입니다. 두 번째 영역 요청은 Codex 시험에서 "이 영역에 보이는 항목 번호를 그대로 적어"로 변경했습니다. 이를 통해 에이전트가 이미지를 실제로 확인했는지 검증했습니다.

시험결과
한 쪽 영역(그림 위 드래그)입력 창에 799x374 썸네일, "영역 · 1쪽 · 가로 4~82%, 세로 42~70%" 표시. 겹치는 문단은 캡션 1개
두 쪽 영역(1쪽 아래~2쪽 위)"1쪽 가로 4~59%, 세로 86~100% / 2쪽 가로 4~59%, 세로 0~13%", 점선 사각형 2개(번호는 첫 쪽에만), 목록 표시 "영역 · 1~2쪽"
캡처 이미지 내용저장된 PNG 에 문서 화면만 담기고 점선 사각형·번호·입력 창은 들어가지 않음
중략 함수105자 글을 한도 20자로 줄이면 가가가가가가가가가가 (중략 85자) 가가가가가끝부분단서
Claude(Sonnet, 추론 낮음)read_paragraphs → Read → set_paragraph_text → save_document. 그림 아래 단위 문장을 넣는 변경안 1건을 만들고, 두 번째 영역은 고칠 것이 없다고 보고. 이미지 2장에 Read 는 1회
Codex(GPT-6.1-Sol, 추론 낮음)19초 만에 응답. Read 없이 첨부 이미지를 보고 "보이는 항목 번호는 5, 6, 7, 8번"이라고 답함. 변경안 2건(그래프 단위 문장, 6번 문단 끝 "(상반기)")
Claude 전송 뒤 원본·수정본 비교 화면과 Read 도구 호출이 보이는 에이전트 패널
Claude 전송 뒤 비교 화면입니다. 패널에 Read 도구 호출과 변경 요약이 보입니다.
Codex 전송 뒤 수정 목록 2건과 영역 안 항목 번호를 읽은 답변이 보이는 DocPilot 화면
Codex 전송 뒤 비교 화면입니다. 영역 이미지에서 읽은 항목 번호와 변경안 2건이 보입니다.

한계와 남은 과제

  • Claude 가 모든 이미지를 여는지는 보장되지 않습니다. 시스템 지시와 요청 글로 열도록 안내할 뿐 강제하지 않으며, 시험에서는 이미지 2장에 Read 호출이 1번이었습니다.
  • Codex 는 문서 도구 외에 셸 도구를 1번 호출했습니다. 사용자 환경에 설치된 문서 관련 스킬을 확인하려는 호출이었고, 문서 수정은 MCP 도구로만 이루어졌습니다.
  • 화면 배율 100%·DOCX 한 건에서만 확인했습니다. 125%·150% 배율, HWP·PPTX·XLSX, 다크 테마는 시험하지 않았습니다.
  • 드래그 중 화면 스크롤은 지원하지 않습니다. 영역은 화면에 보이는 범위 안에서만 고를 수 있습니다.
  • 영역 위치는 쪽 크기 대비 비율입니다. 편집으로 쪽 구성이 바뀌면 같은 곳을 가리키지 않을 수 있어, 문서를 다시 열면 영역 항목도 비웁니다.
  • 문서 내용이 이미지로도 엔진에 전달됩니다. 텍스트와 마찬가지로 폴더 신뢰 확인을 거치지만, 이미지 내 텍스트와 개인 정보도 함께 전송됩니다.
  • 사용자 말풍선에 내부 문단 id 와 이미지 경로가 보입니다. 요청 글을 그대로 기록하기 때문이며, 표시 방식은 별도 개선 대상입니다.

정리

DocPilot 의 코멘트는 이제 글 선택뿐 아니라 화면의 사각형 영역에도 달 수 있고, 영역마다 쪽 위치와 화면 이미지를 에이전트에 함께 전달합니다. 긴 인용은 앞뒤를 남기고 가운데만 줄여 끝부분의 단서가 사라지지 않습니다. 두 기능 모두 기존 전달 경로(요청 글, 엔진, 문서 MCP 도구, 변경안, 비교 화면)의 구조를 바꾸지 않고 정보만 추가했습니다. 또한 Claude 와 Codex 모두 영역 이미지를 받아 변경안을 생성하는 것을 확인했습니다.