소개
법무 부서가 여러 계약 PDF를 받을 때, 각 검토자는 종종 기밀 조항을 보호하기 위해 자체 비밀번호를 추가합니다. 비밀번호가 서로 달라 파일을 직접 병합할 수 없기 때문에 이러한 파일을 하나의 바인더로 통합하는 일은 악몽이 됩니다. 수동으로 해제하는 작업은 시간이 많이 걸리고 오류가 발생하기 쉬우며, 특히 수십 개의 PDF를 다룰 때 더욱 그렇습니다.
Password‑merge는 Java용 GroupDocs.Merger 워크플로우로, 이질적인 PDF를 해제하고 병합된 결과를 단일 자격 증명으로 다시 보호합니다. 이 튜토리얼에서는 보호된 PDF를 감지하고, 해제하고, 내용을 병합하며, 필요에 따라 통합 비밀번호를 회전하는 과정을 단계별로 안내합니다.
이 과정을 통해 Merger API를 구성하고, 바이트 스트림을 처리하며, 30줄 미만의 Java 코드로 안전한 결합 PDF를 생성하는 방법을 배울 수 있습니다.
언제 비밀번호로 보호된 PDF를 병합해야 할까요?
비밀번호로 보호된 PDF를 병합하는 것은 보안 정책을 유지하면서 여러 비밀번호를 관리하는 번거로움을 없애고, 검색 가능한 단일 아카이브가 필요할 때 의미가 있습니다. 일반적인 시나리오로는 분기별 재무 보고서 번들, 감사용 계약 바인더, 각 기여자가 서로 다른 비밀번호를 적용한 법률 사건 파일 등이 있습니다. 각 파일을 프로그래밍 방식으로 해제하고 통합 비밀번호를 적용하면 아카이브를 안전하게 유지하면서 후속 검토 프로세스를 단순화할 수 있습니다. 전체 작업을 CI 파이프라인에서 자동화하면 수작업 시간을 크게 절감할 수 있습니다.
사전 요구 사항
- Java 11 이상
- GroupDocs.Merger for Java 24.6+ (temporary license)
- 각 PDF 파일과 선택적으로 해당 비밀번호가 포함된 세트
Maven을 통해 라이브러리를 설치합니다:
mvn dependency:copy -Dartifact=com.groupdocs:groupdocs-merger:24.6
단계 1 – PDF가 비밀번호로 보호되었는지 감지하기
파일을 해제하려 시도하기 전에 실제로 비밀번호가 설정되어 있는지 확인합니다. 이렇게 하면 불필요한 처리를 방지하고 어떤 파일에 자격 증명이 필요한지 로그에 남길 수 있습니다.
// Returns true if the PDF at `path` has an owner or user password
public boolean isDocumentProtected(String path, String password) {
Merger merger;
if (password == null || password.isEmpty()) {
merger = new Merger(path);
} else {
merger = new Merger(path, new LoadOptions(password));
}
try {
return merger.isPasswordSet();
} finally {
merger.dispose();
}
}
핵심 포인트:
LoadOptions는 알려진 비밀번호를 전달합니다; 비밀번호가 없으면 파일을 일반적으로 엽니다.isPasswordSet()은 소유자 비밀번호와 사용자 비밀번호 모두에 대해true를 반환합니다.- 네이티브 리소스를 해제하기 위해
Merger인스턴스를 항상dispose()해야 합니다.
단계 2 – 각 PDF를 해제하고 원시 바이트 수집하기
키가 파일 경로이고 값이 비밀번호(없을 경우 null)인 맵을 순회합니다. 이 메서드는 해제된 PDF를 나타내는 바이트 배열 리스트를 반환합니다.
public List<byte[]> unlockAll(Map<String, String> sources) throws IOException {
List<byte[]> unlocked = new ArrayList<>();
for (Map.Entry<String, String> e : sources.entrySet()) {
String path = e.getKey();
String password = e.getValue();
System.out.println("Unlocking (credentials=" +
(password != null ? "yes" : "no") + "): " + path);
if (password == null || password.isEmpty()) {
unlocked.add(Files.readAllBytes(Paths.get(path)));
} else {
LoadOptions opts = new LoadOptions(password);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
Merger m = new Merger(path, opts);
try {
m.removePassword();
m.save(buf);
} finally {
m.dispose();
}
unlocked.add(buf.toByteArray());
}
}
return unlocked;
}
핵심 포인트:
removePassword()는 기존 보호를 제거합니다.- 복호화된 내용은 메모리 내 처리를 위해
ByteArrayOutputStream에 기록됩니다. - 비밀번호가 없는 파일은 흐름을 단순화하기 위해 직접 읽어들입니다.
단계 3 – 해제된 PDF를 병합하고 통합 비밀번호 적용하기
첫 번째 PDF 스트림으로 Merger를 생성한 뒤, 나머지 스트림을 차례로 연결합니다. 마지막으로 AddPasswordOptions를 사용해 통합 비밀번호를 설정합니다.
public void mergeAndProtect(List<byte[]> unlockedPdfs,
String unifiedPassword,
String outputPath) {
InputStream first = new ByteArrayInputStream(unlockedPdfs.get(0));
Merger merger = new Merger(first);
try {
for (int i = 1; i < unlockedPdfs.size(); i++) {
merger.join(new ByteArrayInputStream(unlockedPdfs.get(i)));
}
merger.addPassword(new AddPasswordOptions(unifiedPassword));
merger.save(outputPath);
} finally {
merger.dispose();
}
System.out.println("Merged output: " + new File(outputPath).getAbsolutePath());
System.out.println("Unified password: " + unifiedPassword);
}
핵심 포인트:
addPassword는 제공된 자격 증명으로 최종 PDF를 암호화합니다.- 모든 작업이 메모리 내에서 이루어지며, 최종 파일만 디스크에 기록됩니다.
- 네이티브 핸들을 해제하기 위해
Merger를 반드시dispose()해야 합니다.
단계 4 – 이미 보호된 PDF의 비밀번호 회전 (선택 사항)
조직에서 정기적인 비밀번호 교체를 요구하는 경우, 원본 파일을 다시 병합하지 않고도 자격 증명을 업데이트할 수 있습니다.
public void rotateUnifiedPassword(String path,
String oldPassword,
String newPassword,
String outputPath) {
Merger merger = new Merger(path, new LoadOptions(oldPassword));
try {
merger.updatePassword(new UpdatePasswordOptions(newPassword));
merger.save(outputPath);
} finally {
merger.dispose();
}
System.out.println("Rotated output: " + new File(outputPath).getAbsolutePath());
System.out.println("New password: " + newPassword);
}
핵심 포인트:
- 현재 비밀번호로 보호된 PDF를 로드합니다.
updatePassword가 새 자격 증명으로 교체합니다.- PDF 내용을 다시 처리하지 않기 때문에 매우 빠르게 수행됩니다.
실제 적용 사례
다섯 개의 투자자 계약서 각각에 서로 다른 검토자 비밀번호가 적용된 상황을 마주했습니다. 위 단계들을 따라 모든 파일을 해제하고 하나의 바인더로 병합한 뒤, 우리 기업 정책에 맞는 단일 비밀번호를 적용했습니다. 전체 과정은 일반 노트북에서 2분 이내에 완료되었습니다.
모범 사례
- 비밀번호를 조기에 검증:
isDocumentProtected를 사용해 수동 검토가 필요한 파일을 미리 표시합니다. - 메모리 사용량 제한: 대용량 PDF의 경우 모든 바이트 배열을 메모리에 보관하지 말고 디스크에 스트리밍합니다.
- 객체를 즉시 해제:
Merger클래스는 네이티브 리소스를 보유하므로finally블록에서 반드시dispose()를 호출합니다. - 임시 라이선스는 개발용으로만 사용하고, 배포 전에는 정식 라이선스를 획득합니다.
결론
GroupDocs.Merger for Java는 PDF 컬렉션을 해제, 병합 및 재보호하는 깔끔한 API를 제공합니다. 보호 감지, 해제, 통합 비밀번호 적용, 필요 시 비밀번호 회전이라는 네 단계만 따르면 수동 개입 없이 안전한 PDF 바인더를 자동으로 생성할 수 있습니다.
다음 단계:
- 병합 후 PDF 메타데이터 설정과 같은 추가 옵션을 살펴보세요 (문서).
- 북마크를 유지하면서 PDF를 병합하는 방법을 배워보세요 (API 참조).
- 실행 가능한 구현 예제가 포함된 전체 샘플 프로젝트를 GitHub에서 확인하세요.