SameOS ~/tools/pillow-bulk-thumbnails.md

Python Pillow로 수천 장 썸네일 일괄 생성하기

이미지 갤러리를 운영하면 원본을 그대로 목록에 띄울 수 없어서 썸네일이 필요해집니다. 몇천 장이 쌓인 뒤에 썸네일을 만들기 시작하면 일괄 생성 스크립트가 필수인데, 단순해 보여도 실제로 돌려 보면 챙길 규칙이 꽤 있습니다. 실제 운영에서 수천 장을 처리해 본 스크립트 기준으로 정리했습니다.

먼저 정할 규칙

첫째, 썸네일은 원본과 같은 폴더가 아니라 하위 thumbs/ 폴더에 저장해 원본 목록을 오염시키지 않습니다. 둘째, 이미 썸네일이 있으면 건너뛰어 스크립트를 언제든 다시 돌려도 안전하게(멱등하게) 만듭니다. 셋째, thumbs/ 폴더 자체는 탐색에서 제외해 썸네일의 썸네일이 생기는 사고를 막습니다.

변환 시 주의할 점

PNG의 투명 채널이나 팔레트 모드(색을 번호표로 저장하는 방식) 이미지를 JPEG로 저장하려면 먼저 RGB로 변환해야 합니다. 움직이는 GIF는 첫 프레임만 사용하면 충분합니다. 리샘플링(줄인 크기에 맞춰 픽셀을 다시 계산하는 과정)은 LANCZOS가 축소 품질이 가장 좋고, thumbnail() 메서드는 비율을 유지하면서 지정 크기 안으로만 줄여 주기 때문에 가로세로를 따로 계산할 필요가 없습니다.

아래 스크립트는 지정 폴더를 재귀 탐색하며 400px 기준 썸네일을 만듭니다. 품질 82는 목록 표시용으로 충분하면서 원본의 3~8% 수준 용량이 나옵니다.

import os
from PIL import Image

ROOT = "/srv/photos"
MAX_SIZE = 400
QUALITY = 82
EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}

def make_thumb(path: str) -> bool:
    parent = os.path.dirname(path)
    if os.path.basename(parent) == "thumbs":
        return False                      # 썸네일의 썸네일 방지
    thumbs = os.path.join(parent, "thumbs")
    os.makedirs(thumbs, exist_ok=True)
    base = os.path.splitext(os.path.basename(path))[0]
    out = os.path.join(thumbs, base + ".jpg")
    if os.path.exists(out):
        return False                      # 이미 있으면 스킵 → 재실행 안전
    with Image.open(path) as img:
        rgb = img.convert("RGB")          # PNG 투명/팔레트 대응
        rgb.thumbnail((MAX_SIZE, MAX_SIZE), Image.LANCZOS)
        rgb.save(out, "JPEG", quality=QUALITY, optimize=True)
    return True

count = 0
for root, dirs, files in os.walk(ROOT):
    dirs[:] = [d for d in dirs if d != "thumbs"]   # thumbs 탐색 제외
    for name in files:
        if os.path.splitext(name)[1].lower() in EXTS:
            if make_thumb(os.path.join(root, name)):
                count += 1
print(f"created {count} thumbnails")

운영 팁

수천 장이면 장당 0.5~1초씩 잡아 수십 분 걸릴 수 있으니 nohup이나 tmux(터미널을 닫아도 작업이 계속 돌게 해 주는 도구)로 백그라운드 실행하세요. 스크립트가 멱등하므로 중간에 끊겨도 다시 실행하면 이어서 처리됩니다. 새 이미지가 계속 추가되는 폴더라면 같은 스크립트를 cron에 등록해 두면 됩니다. 웹에서 쓸 예상 용량이 궁금하면 썸네일 캐시 계산기로 미리 잡아볼 수 있습니다.

전체 스크립트 받기 (GitHub) 썸네일 캐시 용량 계산기 열기 이미지 WebP 변환 기준 잡기 Python으로 중복 이미지 찾기