JavaScript로 유형별 PDF 양식 필드 값 읽기

2026-09-28 08:33:53 Allen Yang
AI Summarize:
ChatGPT
ChatGPT ✓
Claude ✓
Grok ✓
Perplexity ✓
Quick
Quick
Concise overview
Highlights
Key takeaways
Detailed
Structured explanation
Brief
One sentence summary
Summarize |

The values collected by walking every form field

누군가 PDF 양식을 작성하고 저장하면, 입력된 값은 문서의 필드 구조 안에 저장됩니다. 일괄적으로 검색하거나 복사할 수 있는 일반 텍스트가 아닙니다. 서른 개나 마흔 개의 필드가 있는 양식이라면 손으로 옮겨 적는 작업이 병목이 됩니다. 더 근본적인 문제는 각 필드 유형이 값을 저장하는 방식이 서로 다르다는 점입니다. 텍스트 상자는 문자열을 노출하고, 체크 박스는 불리언을 보고하며, 콤보 상자는 옵션과 선택 항목을 분리하고, 라디오 버튼은 선택된 항목을 저장합니다. 하나의 통일된 "값을 달라"는 호출은 존재하지 않습니다.

이 글에서는 Spire.PDF for JavaScript를 사용하여 PDF에서 양식 필드 값을 추출하는 방법을 살펴봅니다. 이 라이브러리는 브라우저에서 WebAssembly로 실행되므로, 문서가 가상 파일 시스템을 통해 로컬에서 구문 분석되며 서버 왕복이 필요하지 않습니다. 필드 컬렉션을 순회하고, 각 필드의 유형에 따라 분기하고, 텍스트 상자, 목록 상자, 콤보 상자, 라디오 버튼, 체크 박스에 대해 올바른 속성을 읽는 방법을 확인할 수 있습니다.

설정 및 프로젝트 구성에 대해서는 React 프로젝트에 Spire.PDF for JavaScript 통합하기를 참조하세요. 아래 코드는 Spire.PDF가 설치되어 있고 WASM 모듈이 초기화되었다고 가정합니다.


필드 유형 한눈에 보기

구현을 살펴보기 전에 각 필드 유형이 값을 어떻게 노출하는지 정리해 두면 도움이 됩니다. Spire.PDF for JavaScript는 양식 필드를 위젯 클래스로 표현하며, 현재 값을 담고 있는 속성은 유형마다 다릅니다.

필드 유형 위젯 클래스 읽을 속성 비고
텍스트 상자 PdfTextBoxFieldWidget Text 입력된 문자열을 그대로 반환합니다.
목록 상자 PdfListBoxWidgetFieldWidget SelectedValue Values는 전체 옵션 목록이며, 사용자가 선택한 항목이 아닙니다.
콤보 상자 PdfComboBoxWidgetFieldWidget SelectedValue 목록 상자와 동일한 이중 속성 모델을 사용합니다.
라디오 버튼 PdfRadioButtonListFieldWidget Value 선택된 항목 문자열을 한 번에 제공합니다.
체크 박스 PdfCheckBoxWidgetFieldWidget Checked 불리언 상태입니다. Value는 undefined이므로 사용하지 마세요.

패턴은 분명합니다. 모든 경우에 통용되는 단일 속성은 없습니다. 추출 로직은 각 필드의 유형을 확인하고 그에 맞는 속성을 읽어야 하며, 다음 섹션에서 바로 그것을 구현합니다.


필드 반복 및 유형별 값 읽기

핵심 워크플로는 세 단계로 이루어집니다. PDF를 로드하고, 양식을 PdfFormWidget으로 가져온 다음, FieldsWidget 컬렉션을 순회하면서 instanceof로 각 필드의 클래스에 따라 분기합니다. 각 분기에서 유형별 속성을 읽고 결과를 보고서 문자열에 추가합니다. 이 분기 처리가 지원되는 모든 유형을 다루므로, 문서에 어떤 필드가 들어 있는지 미리 알 필요가 없습니다. 인식되지 않는 필드는 단순히 기본 레이블로 처리됩니다.

function App() {
  const getAllFieldValues = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check that the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the PDF file to be read into the VFS
    const inputFileName = 'ApplicationForm.pdf';
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);

    // Create a PdfDocument object and load the PDF document
    const doc = new pdfModule.PdfDocument();
    doc.LoadFromFile(inputFileName);

    // Build a PdfFormWidget from the document's form handle; FieldsWidget is its field collection
    const formWidget = new pdfModule.PdfFormWidget(doc.Form.H);
    const fields = formWidget.FieldsWidget;

    let report = '';

    // Walk the field collection, check each type, and read the matching value
    for (let i = 0; i < fields.Count; i++) {
      const field = fields.get_Item({ index: i });

      // Both the type name and the value are filled in by the type dispatch
      let type = 'Unknown';
      let value = '(Unrecognized field type)';

      if (field instanceof pdfModule.PdfTextBoxFieldWidget) {
        // Text box field: read Text directly
        type = 'TextBox';
        value = field.Text;
      } else if (field instanceof pdfModule.PdfListBoxWidgetFieldWidget) {
        // List box field: Values holds every option, SelectedValue is the current one
        const options = [];
        for (let j = 0; j < field.Values.Count; j++) {
          options.push(field.Values.get_Item(j).Value);
        }
        type = 'ListBox';
        value = `Selected ${field.SelectedValue}, options ${options.join(', ')}`;
      } else if (field instanceof pdfModule.PdfComboBoxWidgetFieldWidget) {
        // Combo box field: like a list box, it has an option collection and a selected value
        const options = [];
        for (let j = 0; j < field.Values.Count; j++) {
          options.push(field.Values.get_Item(j).Value);
        }
        type = 'ComboBox';
        value = `Selected ${field.SelectedValue}, options ${options.join(', ')}`;
      } else if (field instanceof pdfModule.PdfRadioButtonListFieldWidget) {
        // Radio button field: Value is the selected item
        type = 'RadioButton';
        value = `Selected ${field.Value}`;
      } else if (field instanceof pdfModule.PdfCheckBoxWidgetFieldWidget) {
        // Check box field: Checked gives the state, not Value
        type = 'CheckBox';
        value = field.Checked ? 'Checked' : 'Not checked';
      }

      report += `Field "${field.Name}" (${type}): ${value}\n`;
    }

    const outputFileName = 'AllFieldValues.txt';
    window.dotnetRuntime.Module.FS.writeFile(outputFileName, report);
    doc.Close();

    // Read the generated file from the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Extract Form Field Values</h1>
      <button onClick={getAllFieldValues}>
        Extract values
      </button>
    </div>
  );
}

export default App;

루프가 끝나면 보고서 문자열에는 각 필드의 이름, 유형, 현재 값이 한 줄씩 담깁니다. 이 파일은 가상 파일 시스템에 기록된 다음 텍스트 파일로 다운로드됩니다.

The values collected by walking every form field

instanceof 분기 체인이 이 접근 방식의 핵심입니다. 각 분기는 읽어야 할 속성을 정확히 알고 있으므로, 문서에 얼마나 많은 필드 유형이 섞여 있든 출력은 올바릅니다. 다음 세 섹션에서는 필드의 값 속성이 예상과 다를 때 발생하는 함정을 다룹니다.


체크 박스: Checked와 Value의 차이

체크 박스 필드를 읽을 때 흔히 하는 실수는 Value 속성을 사용하려는 것입니다. 체크 박스 위젯인 PdfCheckBoxWidgetFieldWidget은 Value를 전혀 노출하지 않으며, 이를 읽으려 하면 undefined가 반환됩니다. 내부적으로 체크 박스는 내보내기 값(export value)을 통해 상태를 추적합니다. 선택되지 않았을 때는 Off이고, 선택되었을 때는 Yes 또는 사용자 지정 내보내기 문자열입니다. 원시 문자열만으로는 상자가 선택되었는지 확실히 알 수 없으므로, API는 의도적으로 Value를 생략하고 대신 Checked를 제공합니다.

해결 방법은 간단합니다. 항상 불리언인 Checked 속성을 사용하세요.

// Check the state with Checked, not Value
const checked = field.Checked;

이 속성은 상자가 선택되어 있으면 true, 그렇지 않으면 false를 반환하므로, 문자열 구문 분석 없이 후속 로직에 사용할 깔끔한 불리언 값을 얻을 수 있습니다.


목록 상자와 콤보 상자: SelectedValue와 Values의 차이

목록 상자와 콤보 상자는 많은 개발자를 헷갈리게 하는 두 부분으로 된 데이터 모델을 공유합니다. PdfListBoxWidgetFieldWidget과 PdfComboBoxWidgetFieldWidget 모두 Values 컬렉션과 SelectedValue 문자열을 노출하는데, Values에 사용자의 입력이 들어 있다고 가정하기 쉽습니다. 그렇지 않습니다.

Values는 사용 가능한 모든 옵션의 집합입니다. 컬렉션의 각 요소는 PdfListWidgetItem 객체이므로, 옵션 텍스트를 얻으려면 .Value로 풀어내야 합니다. Values를 순회하면 사용자가 선택할 수 있었던 항목을 알 수 있을 뿐, 실제로 선택한 항목은 아닙니다. 사용자의 실제 선택은 일반 문자열인 SelectedValue에 있습니다.

현재 값에는 SelectedValue를 사용하고, 사용 가능한 선택지를 열거해야 할 때만 Values를 순회하세요.

// The text of the currently selected item
const selected = field.SelectedValue;

// Every available option
const options = [];
for (let j = 0; j < field.Values.Count; j++) {
  options.push(field.Values.get_Item(j).Value);
}

이 두 속성을 정확히 구분하는 것이 중요합니다. Values를 답으로 취급하면 채워진 결과 대신 옵션 목록을 얻게 되며, 이 둘은 길이가 같은 경우가 거의 없습니다.


암호화된 PDF 처리

양식 추출은 문서를 여는 것에서 시작합니다. PDF가 암호로 보호되어 있다면 파일 이름만으로 LoadFromFile을 호출할 때 오류가 발생합니다. — "Can not open an encrypted document. The password is invalid." — 그리고 문서 객체가 반환되지 않습니다. 양식 필드에는 아예 도달하지 못합니다.

해결 방법은 열기 암호를 두 번째 인수로 전달하는 것입니다.

doc.LoadFromFile(inputFileName, 'spire123');

문서가 성공적으로 열리면 나머지 추출 흐름 — PdfFormWidget 생성, 필드 순회, 유형별 분기 처리 — 은 암호화되지 않은 파일과 완전히 동일하게 작동합니다. 암호는 최초 로드만 제한할 뿐, 필드 값을 읽는 방식에는 영향을 주지 않습니다.


관련 항목


결과 문서에서 평가 메시지를 제거하거나 기능 제한을 없애려면, 30일 동안 유효한 임시 라이선스를 위해 영업팀에 문의하세요.