엑셀 매크로(VBA) 보안 경고 끝! 파이썬으로 똑같은 작업 대체하기

이미지
엑셀 매크로(VBA) 보안 경고 끝! 파이썬으로 똑같은 작업 대체하기 (완성 코드 공개) "매크로가 포함된 파일을 다른 사람에게 보내면 보안 경고 때문에 매크로 사용 설정을 매번 따로 해야 한다", "VBA 코드가 회사 PC마다 다르게 작동해서 디버깅이 어렵다", "매크로를 쓰는 동료가 퇴사하면 그 파일을 아무도 수정하지 못한다"… VBA 매크로는 엑셀 안에서 바로 실행되는 장점이 있지만, 보안 정책으로 차단되거나, 회사 PC마다 설정이 달라 오작동하거나, 코드 관리가 어려운 한계가 있습니다. 이 글에서는 이 블로그에서 지금까지 소개한 파이썬 코드들이 VBA의 어떤 기능을 대체할 수 있는지 비교하고, 대표적인 매크로 작업을 파이썬으로 구현하는 방법을 소개합니다. VBA 매크로 vs 파이썬 비교 VBA 매크로 파이썬 실행 환경 엑셀 안에서만 동작 독립적으로 어디서나 동작 보안 경고 매번 발생, 차단 가능 해당 없음 배포 파일에 코드 포함 스크립트 파일만 공유 디버깅 엑셀 VBA 편집기 사용 다양한 코드 에디터 사용 가능 다른 프로그램 연동 제한적 (Win API 필요) 매우 풍부함 (이메일, 웹, DB 등) 버전 관리(깃 등) 거의 불가능 일반적인 코드처럼 관리 가능 배우는 난이도 엑셀에 특화된 문법 범용 프로그래밍 언어, 활용도 넓음 💡 언제 VBA가 더 나을까: 엑셀을 열자마자 버튼 클릭으로 즉시 실행하고 싶거나, 엑셀 전용 UI(사용자 정의 양식)가 필요한 경우는 VBA가 더 간단할 수 있습니다. 하지만 안정성, 보안, 확장성을 고려하면 대부분의 반복 작업은 파이썬이 더 유리합니다. 대표 매크로 작업 1 — 여러 시트 합치기 VBA로 흔히 작성하는 "모...

PDF 용량 때문에 이메일 전송 실패! 파이썬으로 자동 압축하기

PDF 용량 때문에 이메일 전송 실패! 파이썬으로 자동 압축하기 (완성 코드 공개)

파이썬으로 PDF 파일의 용량을 자동으로 압축하는 모습


"스캔한 계약서 PDF가 50MB라서 이메일 첨부 용량 제한에 걸린다", "사진이 많이 포함된 보고서 PDF를 클라우드에 올리려니 너무 오래 걸린다", "여러 개의 PDF를 한꺼번에 압축해야 한다"…

PDF 용량이 큰 이유는 대부분 안에 포함된 고해상도 이미지 때문입니다. 텍스트 자체는 용량을 거의 차지하지 않습니다. 파이썬을 한 번만 세팅해두면 PDF 안의 이미지 해상도와 압축률을 조정해 파일 용량을 대폭 줄이면서도 읽는 데는 문제없는 수준으로 만들 수 있습니다.


1단계: 준비물 설치

파이썬이 설치되어 있어야 합니다. 없다면 python.org에서 최신 버전을 받아 설치하세요. 설치 시 반드시 "Add Python to PATH"에 체크해야 합니다.

PDF 압축에 필요한 라이브러리를 설치합니다. 터미널(윈도우: CMD 또는 파워셸)을 열고 아래 명령어를 실행하세요:

pip install PyMuPDF

PyMuPDF(설치명은 PyMuPDF이지만 코드에서는 fitz로 불러옵니다)는 PDF를 분석하고 안의 이미지를 추출·재압축해서 다시 삽입할 수 있는 강력한 라이브러리입니다. 텍스트와 레이아웃은 그대로 유지하면서 이미지만 압축합니다.

💡 이 코드로 할 수 있는 것: PDF 안의 모든 이미지를 지정한 품질로 재압축, 이미지 해상도 다운스케일링, 전체 PDF 객체 스트림 최적화를 통해 원본 대비 50~90%까지 용량을 줄입니다. 폴더 단위 일괄 처리도 지원합니다.

2단계: 완성 코드

아래 코드를 그대로 복사해서 메모장에 붙여넣고, 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}")

3단계: 실행 방법

  1. 코드 ① 설정의 INPUT_PATH에 압축할 PDF 경로를, OUTPUT_PATH에 저장 경로를 입력합니다.
  2. IMAGE_QUALITY로 압축 강도를 조절합니다. 이메일 첨부용이면 50~60, 화질을 더 유지하려면 75~85를 권장합니다.
  3. 터미널에서 아래 명령어로 실행합니다:
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

4단계: 자주 발생하는 오류와 해결법

오류 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를 한꺼번에 압축하기

여러 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를 바로 첨부해서 발송할 수 있습니다.

핵심 요약

  • 준비물: Python + PyMuPDF 설치 (pip install PyMuPDF)
  • 설정: INPUT_PATH, OUTPUT_PATH, IMAGE_QUALITY(50~85 권장), MAX_DIMENSION 수정
  • 실행: 터미널에서 python compress_pdf.py 입력
  • 결과: 이미지 재압축으로 용량 50~90% 감소, 텍스트·레이아웃은 그대로 유지

이 코드를 응용하면 페이지별로 다른 압축률을 적용하거나, 압축 전후 용량을 비교하는 보고서를 자동 생성하는 것도 가능합니다.

이 블로그의 인기 게시물

느려진 구글 크롬 속도 2배 빨라지는 3가지 설정 방법 (2026년 최신 가이드)

카카오톡 용량 줄이기! 대화방 파일 삭제로 스마트폰 용량 확보법 (1분 해결)

업무 속도 3배 빨라지는 윈도우 기본 캡처 도구 단축키 및 200% 활용 꿀팁