중복 파일 삭제 스크립트를 돌렸다가 식은땀 흘린 날
"스캔한 계약서 PDF가 50MB라서 이메일 첨부 용량 제한에 걸린다", "사진이 많이 포함된 보고서 PDF를 클라우드에 올리려니 너무 오래 걸린다", "여러 개의 PDF를 한꺼번에 압축해야 한다"…
PDF 용량이 큰 이유는 대부분 안에 포함된 고해상도 이미지 때문입니다. 텍스트 자체는 용량을 거의 차지하지 않습니다. 파이썬을 한 번만 세팅해두면 PDF 안의 이미지 해상도와 압축률을 조정해 파일 용량을 대폭 줄이면서도 읽는 데는 문제없는 수준으로 만들 수 있습니다.
파이썬이 설치되어 있어야 합니다. 없다면 python.org에서 최신 버전을 받아 설치하세요. 설치 시 반드시 "Add Python to PATH"에 체크해야 합니다.
PDF 압축에 필요한 라이브러리를 설치합니다. 터미널(윈도우: CMD 또는 파워셸)을 열고 아래 명령어를 실행하세요:
pip install PyMuPDF
PyMuPDF(설치명은 PyMuPDF이지만 코드에서는 fitz로 불러옵니다)는 PDF를 분석하고 안의 이미지를 추출·재압축해서 다시 삽입할 수 있는 강력한 라이브러리입니다. 텍스트와 레이아웃은 그대로 유지하면서 이미지만 압축합니다.
💡 이 코드로 할 수 있는 것: PDF 안의 모든 이미지를 지정한 품질로 재압축, 이미지 해상도 다운스케일링, 전체 PDF 객체 스트림 최적화를 통해 원본 대비 50~90%까지 용량을 줄입니다. 폴더 단위 일괄 처리도 지원합니다.
아래 코드를 그대로 복사해서 메모장에 붙여넣고, compress_pdf.py로 저장하세요. 저장 시 파일 형식은 "모든 파일", 인코딩은 UTF-8로 설정합니다.
import fitz # PyMuPDF
from pathlib import Path
from io import BytesIO
from PIL import Image
# ① 설정
INPUT_PATH = r"C:\Users\내이름\Desktop\계약서_스캔본.pdf" # ← 압축할 PDF 경로
OUTPUT_PATH = r"C:\Users\내이름\Desktop\계약서_압축본.pdf" # ← 저장 경로
IMAGE_QUALITY = 60 # ← 이미지 압축 품질 (1~95, 낮을수록 용량 작고 화질 저하)
MAX_DIMENSION = 1600 # ← 이미지 최대 가로/세로 픽셀 (이보다 크면 축소)
# ② PDF 열기
doc = fitz.open(INPUT_PATH)
original_size = Path(INPUT_PATH).stat().st_size / 1024 / 1024
print(f" ✔ PDF 로드 완료: {len(doc)}페이지 / 원본 {original_size:.2f}MB")
compressed_count = 0
# ③ 각 페이지에서 이미지 추출 및 재압축
for page_num in range(len(doc)):
page = doc[page_num]
image_list = page.get_images(full=True)
for img_index, img in enumerate(image_list):
xref = img[0]
try:
base_image = doc.extract_image(xref)
image_bytes = base_image["image"]
# PIL로 이미지 열기
pil_image = Image.open(BytesIO(image_bytes))
# 해상도가 너무 크면 축소
if max(pil_image.size) > MAX_DIMENSION:
ratio = MAX_DIMENSION / max(pil_image.size)
new_size = (int(pil_image.width * ratio), int(pil_image.height * ratio))
pil_image = pil_image.resize(new_size, Image.LANCZOS)
# RGBA는 JPEG로 저장 불가하므로 RGB 변환
if pil_image.mode in ("RGBA", "P"):
pil_image = pil_image.convert("RGB")
# JPEG로 재압축
buffer = BytesIO()
pil_image.save(buffer, format="JPEG", quality=IMAGE_QUALITY, optimize=True)
new_image_bytes = buffer.getvalue()
# 압축된 이미지로 교체
doc._updateStream(xref, new_image_bytes)
compressed_count += 1
except Exception as e:
print(f" ⚠ {page_num+1}페이지 이미지 처리 건너뜀: {e}")
print(f" ✔ {page_num+1}/{len(doc)} 페이지 처리 완료")
# ④ 압축 저장 (가비지 컬렉션 + 디플레이트 압축)
doc.save(OUTPUT_PATH, garbage=4, deflate=True, clean=True)
doc.close()
# ⑤ 결과 출력
new_size = Path(OUTPUT_PATH).stat().st_size / 1024 / 1024
reduction = (1 - new_size / original_size) * 100
print(f"\n ✔ 이미지 {compressed_count}개 재압축 완료")
print(f" ✔ 원본: {original_size:.2f}MB → 압축: {new_size:.2f}MB")
print(f" ✔ 용량 감소율: {reduction:.1f}%")
print(f"\n✅ 완료! 압축 저장 → {OUTPUT_PATH}")
INPUT_PATH에 압축할 PDF 경로를, OUTPUT_PATH에 저장 경로를 입력합니다.IMAGE_QUALITY로 압축 강도를 조절합니다. 이메일 첨부용이면 50~60, 화질을 더 유지하려면 75~85를 권장합니다.python compress_pdf.py
정상 실행 시 터미널에 이렇게 출력됩니다:
✔ PDF 로드 완료: 12페이지 / 원본 48.32MB ✔ 1/12 페이지 처리 완료 ✔ 2/12 페이지 처리 완료 ... ✔ 12/12 페이지 처리 완료 ✔ 이미지 12개 재압축 완료 ✔ 원본: 48.32MB → 압축: 6.18MB ✔ 용량 감소율: 87.2% ✅ 완료! 압축 저장 → C:\Users\내이름\Desktop\계약서_압축본.pdf
오류 1: 용량이 거의 줄지 않음
PDF가 이미지 기반이 아니라 순수 텍스트로만 구성된 경우입니다. 텍스트 PDF는 원래도 용량이 작기 때문에 압축 효과가 거의 없습니다. doc.get_images() 결과가 비어 있다면 이미지가 없는 PDF입니다.
오류 2: 압축 후 화질이 너무 나빠짐
IMAGE_QUALITY 값을 너무 낮게 설정한 경우입니다. 70~80 사이로 조정하면 화질과 용량의 균형이 좋습니다. 문서 내용이 작은 글씨로 가득한 스캔본이라면 MAX_DIMENSION을 2000 이상으로 유지하는 것을 권장합니다.
오류 3: ModuleNotFoundError: No module named 'fitz'
설치 시 패키지명은 PyMuPDF이지만 import할 때는 fitz로 불러와야 합니다. 설치가 안 됐다면 다시 시도하세요:
python -m pip install PyMuPDF
여러 PDF를 동시에 압축할 때 유용합니다. 코드 전체를 함수로 감싸고 폴더 반복문으로 호출하면 됩니다.
def compress_pdf(input_path, output_path, quality=60, max_dim=1600):
doc = fitz.open(input_path)
for page in doc:
for img in page.get_images(full=True):
xref = img[0]
try:
base = doc.extract_image(xref)
pil_img = Image.open(BytesIO(base["image"]))
if max(pil_img.size) > max_dim:
ratio = max_dim / max(pil_img.size)
pil_img = pil_img.resize(
(int(pil_img.width*ratio), int(pil_img.height*ratio)), Image.LANCZOS)
if pil_img.mode in ("RGBA", "P"):
pil_img = pil_img.convert("RGB")
buf = BytesIO()
pil_img.save(buf, format="JPEG", quality=quality, optimize=True)
doc._updateStream(xref, buf.getvalue())
except Exception:
pass
doc.save(output_path, garbage=4, deflate=True, clean=True)
doc.close()
# 폴더 일괄 처리
FOLDER_PATH = r"C:\Users\내이름\Desktop\PDF모음"
OUTPUT_DIR = r"C:\Users\내이름\Desktop\압축결과"
from pathlib import Path
output_dir = Path(OUTPUT_DIR)
output_dir.mkdir(exist_ok=True)
for pdf_file in Path(FOLDER_PATH).glob("*.pdf"):
out_path = output_dir / pdf_file.name
compress_pdf(str(pdf_file), str(out_path))
print(f" ✔ {pdf_file.name} 압축 완료")
💡 이전 게시글과 결합하면: PDF 자동 병합 편으로 합친 PDF를 이 코드로 압축하면 용량 걱정 없이 하나의 파일로 정리할 수 있고, 이메일 자동 발송 편과 결합하면 압축된 PDF를 바로 첨부해서 발송할 수 있습니다.
이 코드를 응용하면 페이지별로 다른 압축률을 적용하거나, 압축 전후 용량을 비교하는 보고서를 자동 생성하는 것도 가능합니다.