
파이썬 PDF 합치기는 결과가 바로 보여서 만족감이 큽니다. 여러 파일을 하나로 묶기만 해도 폴더가 훨씬 덜 어수선해지고, 스캔 문서나 부록 자료를 정리할 때도 바로 써먹을 수 있기 때문입니다.
이번 글에서는 pypdf로 가장 짧은 병합 예제부터 시작해서, 실제로 더 자주 막히는 파일 순서, 암호 PDF, 메타데이터 주의점까지 한 번에 정리하겠습니다. 처음 따라 할 때는 가볍고, 나중에 다시 열어 볼 때는 기준이 남도록 구성했습니다.
파이썬 PDF 합치기 최소 코드
먼저 가장 짧은 버전부터 보겠습니다. 파일 순서만 분명하면 이 정도 코드로도 바로 결과를 만들 수 있습니다.
from pypdf import PdfWriter
writer = PdfWriter()
for pdf in ["01-cover.pdf", "02-body.pdf", "03-appendix.pdf"]:
writer.append(pdf)
writer.write("merged.pdf")핵심은 세 줄입니다. PdfWriter()를 만들고, append()로 순서대로 넣고, 마지막에 write()로 저장합니다. pypdf 공식 문서의 기본 병합 예제도 같은 흐름입니다.
여기서 제일 중요한 건 append에 넣는 순서가 곧 결과 PDF 순서라는 점입니다. 기능보다 먼저 순서를 통제해야 결과가 덜 꼬입니다.
설치
기본 설치는 아래처럼 시작하면 됩니다.
pip install pypdf암호가 걸린 PDF까지 자주 다룰 계획이라면 아래처럼 extra까지 같이 설치해 두는 편이 편합니다. pypdf 문서도 AES 계열 암호화나 복호화를 다룰 때는 추가 의존성이 필요할 수 있다고 안내합니다.
pip install "pypdf[crypto]"공식 문서는 pypdf 병합 문서, 메타데이터 문서, 암호화·복호화 문서를 기준으로 참고했습니다.
순서
입문자가 가장 많이 놓치는 건 코드보다 파일 순서입니다. 병합은 성공했는데 페이지 흐름이 이상해지는 이유는 대개 여기서 나옵니다.
- 파일명 앞에 01, 02, 03처럼 번호를 붙인다
- 코드에서 명시적으로 리스트 순서를 적는다
- 폴더 전체를 읽더라도 결과 순서를 한 번 눈으로 확인한다
예를 들어 report1.pdf, report2.pdf, report10.pdf처럼 이름이 섞여 있으면 문자열 정렬만으로는 report10.pdf가 앞에 올 수 있습니다. 초보자에게는 영리한 정렬보다 파일명 앞에 번호를 붙이는 습관이 훨씬 안전합니다.
PDF를 잘 합치는 사람은 pypdf 문법보다 먼저 파일 순서를 정리합니다.
폴더의 PDF를 한 번에 읽고 싶다면 아래처럼 시작할 수 있습니다. 다만 이 방식도 결국 파일명 규칙이 깔끔해야 덜 헤맵니다.
from pathlib import Path
from pypdf import PdfWriter
input_dir = Path("pdfs")
pdf_files = sorted(input_dir.glob("*.pdf"))
writer = PdfWriter()
for path in pdf_files:
writer.append(str(path))
writer.write("merged.pdf")실전용 스크립트
이제 조금 더 실제 작업용에 가깝게 바꿔보겠습니다. 아래 스크립트는 파일 여러 개를 인자로 받고, 결과 파일명도 바꿀 수 있으며, 암호 PDF가 섞여 있으면 이유를 보여줍니다.
from __future__ import annotations
import argparse
from pathlib import Path
from pypdf import PdfReader, PdfWriter
def merge_pdfs(input_files: list[str], output_file: str, password: str | None = None, title: str | None = None) -> None:
writer = PdfWriter()
for file_name in input_files:
path = Path(file_name)
reader = PdfReader(str(path))
if reader.is_encrypted:
if not password:
raise ValueError(f"암호가 걸린 파일입니다: {path.name}")
result = reader.decrypt(password)
if result == 0:
raise ValueError(f"암호가 맞지 않습니다: {path.name}")
writer.append(reader)
if title:
writer.add_metadata({"/Title": title})
writer.write(output_file)
def main() -> None:
parser = argparse.ArgumentParser(description="PDF 여러 개를 하나로 합칩니다.")
parser.add_argument("files", nargs="+", help="합칠 PDF 파일들")
parser.add_argument("-o", "--output", default="merged.pdf", help="출력 파일명")
parser.add_argument("--password", help="입력 PDF가 같은 비밀번호를 쓸 때 사용")
parser.add_argument("--title", help="결과 PDF 제목 메타데이터")
args = parser.parse_args()
merge_pdfs(args.files, args.output, password=args.password, title=args.title)
print(f"저장 완료: {args.output}")
if __name__ == "__main__":
main()실행은 아래처럼 하면 됩니다. 입문자에게는 이 한 줄이 가장 실용적인 출발점입니다.
python merge_pdfs.py 01-cover.pdf 02-body.pdf 03-appendix.pdf -o report.pdf암호가 걸린 파일이라면 이렇게 쓸 수 있습니다.
python merge_pdfs.py a.pdf b.pdf -o merged.pdf --password mypass이 버전이 좋은 이유는 분명합니다. 파일 여러 개를 명령줄 인자로 받을 수 있고, 출력 파일명을 바꿀 수 있고, 암호 PDF가 섞였을 때 미리 실패 이유를 보여줄 수 있기 때문입니다.
암호 PDF 주의점
암호가 걸린 PDF는 reader.is_encrypted로 먼저 확인하고, 필요하면 decrypt()를 거쳐야 합니다. 여기서 많이 막히는 지점이 세 가지 있습니다.
- 파일마다 비밀번호가 다르면 한 개의 –password 옵션만으로는 부족하다
- AES 계열 PDF는 추가 의존성이 필요할 수 있다
- 합친 뒤 다시 암호를 거는 기능까지 한 번에 넣으면 초반 디버깅이 어려워진다
특히 두 번째는 자주 놓칩니다. 설치는 됐는데 어떤 파일은 열리고 어떤 파일은 안 열릴 때, 코드보다 먼저 의존성 조건을 확인하는 편이 빠를 때가 많습니다.
참고로 pypdf 문서는 새 PDF를 암호화할 때 알고리즘을 생략하면 호환성 때문에 RC4가 기본값이 될 수 있고, RC4는 안전하지 않으므로 AES 계열 사용을 권장합니다. 이 글의 핵심은 병합이지만, 나중에 보호 기능까지 붙일 계획이라면 이 차이는 미리 알아두는 편이 좋습니다.
메타데이터
PDF는 페이지 내용만 있는 파일이 아닙니다. 제목, 작성자, 생성 도구 같은 메타데이터가 들어갈 수 있고, pypdf 문서를 보면 일반 메타데이터와 XMP 메타데이터가 동시에 존재할 수도 있습니다.
여기서 실무 감각 하나만 기억하면 됩니다. 합친 결과물의 메타데이터는 자동으로 깔끔하게 정리될 거라고 기대하지 않는 편이 낫다는 점입니다. 어떤 값을 남길지 애매하면 필요한 값만 새로 넣는 편이 덜 헷갈립니다.
from pypdf import PdfReader, PdfWriter
reader = PdfReader("source.pdf")
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
if reader.metadata is not None:
writer.add_metadata(reader.metadata)
writer.write("copied-meta.pdf")여러 파일을 합칠 때는 특히 더 그렇습니다. 첫 번째 파일의 제목을 따를지, 새 제목을 줄지, 작성자 정보를 비워야 할지 상황마다 다르기 때문입니다. 그래서 병합 도구를 만들 때는 --title처럼 필요한 값만 명시적으로 받는 방식이 실용적입니다.
자주 하는 실수
- 파일 순서를 코드보다 나중에 확인한다
- glob 결과가 원하는 순서일 거라고 그냥 믿는다
- 암호 PDF인데 decrypt 없이 바로 합치려 한다
- 출력 파일명을 입력 파일 중 하나와 같게 둔다
- 메타데이터가 자동으로 정리될 거라고 기대한다
이 다섯 가지 중에서 가장 흔한 것은 첫 번째와 두 번째입니다. 병합 자체는 성공했는데 페이지가 뒤섞이면 라이브러리가 이상해 보이기 쉽지만, 실제 원인은 파일명 규칙인 경우가 많습니다.
마무리
파이썬 PDF 합치기의 출발점은 어렵지 않습니다. PdfWriter, append, write 이 세 가지면 이미 꽤 쓸 만한 도구를 만들 수 있습니다.
다만 실제로 덜 헤매려면 문법보다 먼저 파일 순서를 정리하고, 암호 PDF와 메타데이터는 자동으로 잘 되겠지라고 넘기지 않는 습관이 중요합니다. 처음에는 가장 짧은 버전으로 성공하고, 그다음에 명령줄 인자와 암호 처리, 제목 메타데이터 정도만 붙여도 충분히 실용적인 미니 도구가 됩니다.
같은 흐름의 작은 파이썬 도구를 더 보고 싶다면 파이썬 터미널 로딩 스피너 만들기나 파이썬 QR 코드 생성기 만들기도 같이 읽어보면 좋습니다. 입력을 받고 결과 파일이나 화면 출력을 만드는 감각이 자연스럽게 이어집니다.