💡 전체 작업 예제가 GitHub에 있습니다:
metadata-diff-doc-versions-using-groupdocs-metadata-nodejs

소개

Metadata 비교는 GroupDocs.Metadata 기능으로, 문서 버전 간에 숨겨진 변화를 드러내어 감사자가 신뢰성을 빠르게 확인할 수 있게 합니다. 법무팀이 계약서가 변조되지 않았음을 입증해야 할 때, 저자, 편집자, 수정 타임스탬프와 같은 보이지 않는 속성이 실제 이야기를 전달합니다.

저는 공급업체 계약서 배치를 검토하던 중 이 문제에 직면했습니다. 각 파일은 동일한 생성 날짜를 주장했지만, 숨겨진 저자 필드는 서로 달랐으며, 수동 감사를 수행하면 몇 시간이 걸렸을 것입니다.

숨겨진 메타데이터가 법무팀에 중요한 이유

법률 및 컴플라이언스 전문가들은 불변의 증거에 의존합니다. 눈에 보이는 텍스트가 동일하더라도 Creator 또는 LastPrinted 필드의 변경은 무단 편집을 나타낼 수 있습니다. 이러한 변화를 조기에 감지하면 비용이 많이 드는 분쟁을 예방하고 규제 감사 추적을 충족시킬 수 있습니다.

GroupDocs.Metadata를 활용한 솔루션

GroupDocs.Metadata for Node.js는 모든 지원 속성을 추출하고 두 개의 리비전을 비교하며 구조화된 diff 보고서를 생성하는 단일 API를 제공합니다. SDK는 DOCX, PDF, XLSX 등 많은 형식을 기본적으로 지원하므로 파일 형식마다 별도의 파서를 만들 필요가 없습니다.

Step 1 – 문서에서 모든 메타데이터 가져오기

아래 헬퍼는 파일을 열고 전체 속성 트리를 순회하여 속성 이름 → 값 형태의 평범한 JavaScript 맵을 반환합니다. AnySpecification을 사용해 내장 필드와 사용자 정의 필드 모두를 포착합니다.

/** Extract all metadata from a file */
function extractAllMetadata(documentPath) {
  const result = {};
  const metadata = new groupdocs.Metadata(documentPath);
  try {
    const properties = metadata.findProperties(new groupdocs.AnySpecification());
    for (let i = 0; i < properties.getCount(); i++) {
      const p = properties.get_Item(i);
      const name = p.getName();
      const value = valueToString(p);
      result[name] = value;
    }
  } finally {
    metadata.close();
  }
  return result;
}

Performance note: 5 MB DOCX에서 메타데이터를 추출하는 데 일반적인 2 GHz CPU Docs 기준 200 ms 미만이 소요됩니다.

Step 2 – 두 리비전 간 차이점 찾기

두 버전을 평범한 객체로 표현한 뒤, diff 루틴은 added, removed, changed 세 개의 버킷을 만들고 편리한 totalChanges getter를 제공합니다.

/** Compare two metadata maps */
function compareMetadataSets(pathV1, pathV2) {
  const v1 = extractAllMetadata(pathV1);
  const v2 = extractAllMetadata(pathV2);

  const added = {};
  const removed = {};
  const changed = {};

  for (const k of Object.keys(v2)) {
    if (!(k in v1)) added[k] = v2[k];
    else if (v1[k] !== v2[k]) changed[k] = { from: v1[k], to: v2[k] };
  }
  for (const k of Object.keys(v1)) {
    if (!(k in v2)) removed[k] = v1[k];
  }

  return {
    added,
    removed,
    changed,
    get totalChanges() {
      return Object.keys(this.added).length + Object.keys(this.removed).length + Object.keys(this.changed).length;
    },
  };
}

Step 3 – 소유권 및 리비전 인사이트 (선택 사항)

소유권 변경 감지

법무 감사자는 문서를 누가 만들었고 누가 편집했는지에 주목합니다. 이 헬퍼는 해당 필드를 분리하고 변동이 있으면 표시합니다.

/** Ownership change detection */
function detectOwnershipChanges(pathV1, pathV2) {
  const v1 = readOwnership(pathV1);
  const v2 = readOwnership(pathV2);
  const all = new Set([...Object.keys(v1), ...Object.keys(v2)]);
  const changes = {};
  for (const k of all) {
    const oldV = v1[k] !== undefined ? v1[k] : '<missing>';
    const newV = v2[k] !== undefined ? v2[k] : '<missing>';
    if (oldV !== newV) changes[k] = { from: oldV, to: newV };
  }
  return changes;
}

리비전 히스토리 변경 감지

리비전 번호, 총 편집 시간, 마지막 인쇄 타임스탬프는 숨겨진 활동을 드러냅니다.

/** Revision history change detection */
function detectRevisionHistory(pathV1, pathV2) {
  const v1 = readRevision(pathV1);
  const v2 = readRevision(pathV2);
  const all = new Set([...Object.keys(v1), ...Object.keys(v2)]);
  const changes = {};
  for (const k of all) {
    const oldV = v1[k] !== undefined ? v1[k] : '<missing>';
    const newV = v2[k] !== undefined ? v2[k] : '<missing>';
    if (oldV !== newV) changes[k] = { from: oldV, to: newV };
  }
  return changes;
}

Step 4 – 감사 보고서 내보내기

CSV 내보내기

CSV 형식은 Excel이나 SIEM에 적합합니다. 필드에 쉼표와 개행이 포함될 경우 이스케이프합니다.

/** Export diff to CSV */
function exportDiffToCsv(diff, outputPath) {
  const rows = ['change_type,property,old_value,new_value'];
  for (const [k, v] of Object.entries(diff.added)) rows.push(`added,${esc(k)},,${esc(v)}`);
  for (const [k, v] of Object.entries(diff.removed)) rows.push(`removed,${esc(k)},${esc(v)},`);
  for (const [k, o] of Object.entries(diff.changed)) rows.push(`changed,${esc(k)},${esc(o.from)},${esc(o.to)}`);
  fs.writeFileSync(outputPath, rows.join('\n') + '\n', 'utf-8');
}

JSON 내보내기

JSON은 계층 구조를 유지하여 프로그램에서 바로 사용할 수 있습니다.

/** Export diff to JSON */
function exportDiffToJson(diff, outputPath) {
  const payload = { added: diff.added, removed: diff.removed, changed: diff.changed };
  fs.writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf-8');
}

전체 드라이버 스크립트

스크립트는 모든 작업을 연결하고 간단한 요약을 출력한 뒤 CSV와 JSON 보고서를 모두 작성합니다.

const path = require('path');
const fs = require('fs');
const groupdocs = require('@groupdocs/groupdocs.metadata');

function esc(txt) { return /[",\n]/.test(txt) ? `"${txt.replace(/"/g, '""')}"` : txt; }

const diff = compareMetadataSets('resources/document-v1.docx', 'resources/document-v2.docx');
console.log(`Total metadata changes: ${diff.totalChanges}`);

exportDiffToCsv(diff, path.resolve('metadata-diff.csv'));
exportDiffToJson(diff, path.resolve('metadata-diff.json'));

node index.js를 실행하면 프로젝트 루트에 metadata-diff.csvmetadata-diff.json이 생성됩니다.

두 버전의 문서 메타데이터를 어떻게 비교할 수 있나요?

각 리비전을 Metadata 클래스로 로드하고 extractAllMetadata를 통해 평면 이름 → 값 맵을 추출한 뒤, 두 맵을 compareMetadataSets에 전달하면 됩니다. 이 함수는 추가, 삭제, 변경 세 컬렉션과 totalChanges 카운터를 반환합니다. 이 방식은 GroupDocs.Metadata가 지원하는 모든 형식(DOCX, PDF, XLSX, PPTX 등)에 적용 가능하며 일반적인 오피스 파일에서는 1초 미만에 완료되어 배치 처리나 CI 통합에 적합합니다.

비즈니스 영향

지표 수동 프로세스 GroupDocs.Metadata를 사용한 자동화
문서당 소요 시간 ~45 분 (수동 검토) <30 초 (메타데이터 diff)
오류율 ~3 % 누락된 변경 0 % – 모든 속성을 검사
처리량 리뷰어당 20 문서/일 보통 VM에서 1 000 + 문서/일
감사 추적 손글씨 메모 구조화된 CSV/JSON 로그
비용 $X / 시간 × N 리뷰어 고정 라이선스, 무제한 실행

자동화로 검토 시간이 99 % 이상 단축되고 인간 오류가 사라지며, 컴플라이언스 대시보드와 직접 연동 가능한 기계 판독 가능한 감사 추적을 제공합니다.

환경에서 시작하기

  1. 무료 체험 – 임시 30일 라이선스를 여기에서 받으세요.
  2. SDK 설치npm install @groupdocs/groupdocs.metadata.
  3. 샘플 실행 – GitHub 저장소를 클론하고 node index.js를 실행합니다.
  4. 더 알아보기 – 고급 필터와 사용자 정의 속성 처리를 위한 전체 API 레퍼런스를 확인하세요.

리소스