엑셀 매크로(VBA) 보안 경고 끝! 파이썬으로 똑같은 작업 대체하기
"스캔한 계약서 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를 바로 첨부해서 발송할 수 있습니다.
이 코드를 응용하면 페이지별로 다른 압축률을 적용하거나, 압축 전후 용량을 비교하는 보고서를 자동 생성하는 것도 가능합니다.