이미지 파일 여러 개 한 번에 리사이즈·워터마크 처리하기

블로그에 올릴 사진 60장을 하나씩 열어서 크기를 줄이고, 오른쪽 아래에 출처를 적어 넣고, 다시 저장한 적이 있으신가요. 한 장에 30초씩만 잡아도 30분입니다. 상품 이미지를 매주 올리는 쇼핑몰 운영자라면 한 달이면 두 시간이 그냥 사라집니다. 이번 글에서는 이미지 일괄 리사이즈 자동화를 설치 없이 하는 방법부터, 파이썬으로 리사이즈와 워터마크를 한 번에 끝내는 스크립트까지 순서대로 정리합니다.

먼저: 내 작업이 어느 쪽인가

도구를 고르기 전에 작업 성격부터 나눠야 시간을 아낍니다. 기준은 반복 횟수입니다.

  • 이번 한 번만 — GUI 도구가 빠릅니다. 스크립트 짤 시간에 이미 끝납니다.
  • 매주·매달 반복 — 스크립트가 이깁니다. 한 번 만들어두면 다음부터는 실행만 하면 됩니다.
  • 조건이 붙는다 — 가로 사진만, 특정 크기 이상만, 파일명에 따라 워터마크 문구가 다르게. 이건 코드밖에 답이 없습니다.

설치 없이 되는 법: 윈도우 PowerToys

마이크로소프트가 무료로 배포하는 PowerToys를 설치하면 Image Resizer가 함께 들어옵니다. 탐색기에서 사진을 여러 개 선택하고 우클릭하면 바로 크기 조정 메뉴가 나옵니다.

  1. PowerToys 설치 후 좌측 메뉴에서 Image Resizer를 켭니다.
  2. 탐색기에서 사진을 전부 선택하고 우클릭 → 그림 크기 조정.
  3. 원하는 크기를 고르고, “원본 파일 크기 조정”은 체크하지 않습니다. 체크하면 원본이 덮어써집니다.

여기까지가 리사이즈입니다. 다만 PowerToys에는 워터마크 기능이 없습니다. 출처 표기까지 필요하다면 다음 단계로 넘어가야 합니다.

전용 도구 비교

워터마크까지 얹으려면 전용 도구나 스크립트가 필요합니다. 국내에서 많이 쓰는 선택지를 정리하면 이렇습니다.

도구플랫폼비용특징
PowerToys Image Resizer윈도우무료탐색기 우클릭으로 즉시 리사이즈. 워터마크는 없음
IrfanView윈도우개인 사용 무료일괄 변환(Batch Conversion) 기능이 강력. 워터마크 오버레이 지원
XnConvert윈도우 / 맥 / 리눅스개인 사용 무료GUI로 리사이즈·워터마크·포맷 변환을 한 번에
ImageMagick윈도우 / 맥 / 리눅스무료 (오픈소스)명령줄 한 줄. 서버나 배치 파일에 넣기 좋음
Pillow (파이썬)윈도우 / 맥 / 리눅스무료 (오픈소스)조건 분기까지 자유롭게. 반복 작업에 최적

클릭 몇 번으로 끝내고 싶다면 XnConvert, 명령 한 줄이 편하면 ImageMagick, 앞으로 계속 손볼 생각이라면 파이썬을 권합니다.

명령 한 줄로: ImageMagick

ImageMagick을 설치했다면 터미널에서 한 줄이면 됩니다. mogrify는 여러 파일을 한꺼번에 처리하는 명령이고, -path로 결과 폴더를 지정하면 원본을 건드리지 않습니다.

# 현재 폴더의 jpg를 전부 가로세로 1200 이내로 줄여 output 폴더에 저장
magick mogrify -resize 1200x1200 -quality 85 -path output *.jpg

# 오른쪽 아래에 텍스트 워터마크까지 함께
magick mogrify -resize 1200x1200 -quality 85 \
  -gravity southeast -pointsize 40 -fill "rgba(255,255,255,0.55)" \
  -annotate +20+20 "codetipbox.com" -path output *.jpg

-path 옵션을 빼면 mogrify는 원본 파일을 그 자리에서 덮어씁니다. 되돌릴 수 없으니 결과 폴더를 반드시 지정하세요.

파이썬으로 일괄 리사이즈하기

파이썬은 Pillow 하나만 있으면 됩니다. 터미널에서 pip install Pillow로 설치한 뒤, 아래 스크립트를 resize.py로 저장하고 실행하세요.

from pathlib import Path
from PIL import Image, ImageOps

SRC = Path("images")           # 원본 폴더
DST = Path("images_resized")   # 결과 폴더 (원본과 반드시 분리)
MAX_SIZE = (1200, 1200)        # 가로·세로 최대 크기
EXTS = {".jpg", ".jpeg", ".png", ".webp"}

DST.mkdir(exist_ok=True)

for path in sorted(SRC.iterdir()):
    if path.suffix.lower() not in EXTS:
        continue

    img = Image.open(path)
    img = ImageOps.exif_transpose(img)   # 세로 사진이 눕는 문제 방지
    img.thumbnail(MAX_SIZE, Image.LANCZOS)  # 비율 유지하며 축소

    img.save(DST / path.name, quality=85)
    print(path.name, "->", img.size)
Pillow 일괄 리사이즈 실측 결과 493KB에서 49KB로 감소
실제로 돌린 결과입니다. 3장 합계 493 KB → 49 KB, 91% 줄었습니다.

핵심은 thumbnail()입니다. resize()와 달리 비율을 유지한 채 지정한 크기 안에 들어가도록 줄여줍니다. 가로 사진이든 세로 사진이든 찌그러지지 않습니다.

exif_transpose()도 빼먹기 쉬운 부분입니다. 스마트폰으로 세로로 찍은 사진은 실제 픽셀은 가로로 저장되고 “90도 돌려서 보라”는 정보만 EXIF에 들어 있습니다. 이 처리를 건너뛰면 결과물이 옆으로 누워버립니다.

워터마크 얹기

워터마크는 원본 위에 반투명 레이어를 하나 올리는 방식으로 만듭니다. 이렇게 해야 글자 뒤로 사진이 비쳐 보입니다.

from PIL import Image, ImageDraw, ImageFont

FONT_PATH = "C:/Windows/Fonts/malgun.ttf"   # 맥이라면 /Library/Fonts/AppleGothic.ttf

def add_watermark(img, text, margin=20, opacity=140):
    img = img.convert("RGBA")
    layer = Image.new("RGBA", img.size, (255, 255, 255, 0))
    draw = ImageDraw.Draw(layer)

    # 이미지 폭에 비례해 글자 크기를 정해야 큰 사진·작은 사진 모두 자연스럽습니다.
    font = ImageFont.truetype(FONT_PATH, max(16, img.width // 25))

    box = draw.textbbox((0, 0), text, font=font)
    tw, th = box[2] - box[0], box[3] - box[1]
    x = img.width - tw - margin
    y = img.height - th - margin * 2

    draw.text((x, y), text, font=font, fill=(255, 255, 255, opacity))
    return Image.alpha_composite(img, layer)
워터마크 일괄 적용 실행 결과
워터마크를 얹으면 용량이 다시 조금 늘어납니다(15→30 KB). 알파 합성 때문입니다.

opacity 값이 투명도입니다. 0이면 완전히 투명하고 255면 불투명한데, 사진을 가리지 않으면서 눈에는 들어오는 120~160 사이가 무난합니다. 폰트 경로는 환경마다 다르니 자기 PC에 맞게 바꿔야 합니다. 윈도우는 C:/Windows/Fonts 폴더를 열어보면 설치된 폰트 파일명을 확인할 수 있습니다.

둘을 합쳐 완성본 만들기

실무에서는 리사이즈와 워터마크를 따로 돌리지 않습니다. 하위 폴더까지 훑어서 한 번에 처리하고, 결과를 같은 폴더 구조로 저장하는 스크립트가 아래입니다.

from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageOps

SRC = Path("images")
DST = Path("output")
MAX_SIZE = (1200, 1200)
WATERMARK = "codetipbox.com"
FONT_PATH = "C:/Windows/Fonts/malgun.ttf"
EXTS = {".jpg", ".jpeg", ".png", ".webp"}


def add_watermark(img, text, margin=20, opacity=140):
    img = img.convert("RGBA")
    layer = Image.new("RGBA", img.size, (255, 255, 255, 0))
    draw = ImageDraw.Draw(layer)
    font = ImageFont.truetype(FONT_PATH, max(16, img.width // 25))
    box = draw.textbbox((0, 0), text, font=font)
    tw, th = box[2] - box[0], box[3] - box[1]
    draw.text(
        (img.width - tw - margin, img.height - th - margin * 2),
        text, font=font, fill=(255, 255, 255, opacity),
    )
    return Image.alpha_composite(img, layer)


count = 0
for path in sorted(SRC.rglob("*")):        # 하위 폴더까지 전부
    if path.suffix.lower() not in EXTS:
        continue

    out = DST / path.relative_to(SRC).with_suffix(".jpg")
    out.parent.mkdir(parents=True, exist_ok=True)   # 폴더 구조 그대로 복원

    img = ImageOps.exif_transpose(Image.open(path))
    img.thumbnail(MAX_SIZE, Image.LANCZOS)
    img = add_watermark(img, WATERMARK)

    # RGBA 상태로는 JPEG 저장이 안 되므로 RGB로 되돌립니다.
    img.convert("RGB").save(out, "JPEG", quality=85, optimize=True)
    count += 1

print("완료:", count, "장")

rglob()이 하위 폴더까지 전부 뒤지고, relative_to()로 원본의 폴더 구조를 결과 폴더에 그대로 재현합니다. 상품별로 폴더가 나뉘어 있어도 정리된 상태로 나옵니다.

주의할 점

  • 원본은 절대 덮어쓰지 않습니다. 결과 폴더를 따로 두세요. 리사이즈는 되돌릴 수 없습니다. 한 번 줄인 사진을 다시 키우면 화질이 돌아오지 않습니다.
  • 먼저 5장으로 시험하세요. 원본 폴더를 통째로 복사해 몇 장만 넣고 돌려본 뒤, 결과가 마음에 들면 전체에 적용합니다.
  • 투명 PNG를 JPEG로 저장하면 배경이 검게 나옵니다. JPEG는 투명도를 지원하지 않기 때문입니다. 투명 배경을 살려야 한다면 확장자를 .png로 유지하세요.
  • 한글 워터마크가 네모로 나온다면 폰트 문제입니다. 한글이 없는 폰트 파일을 지정한 경우이니 malgun.ttf(맑은 고딕)처럼 한글이 포함된 폰트로 바꾸면 해결됩니다.
  • 품질 값은 85 근처가 적당합니다. 100으로 올려도 눈에 띄는 차이는 거의 없는데 용량만 크게 늘어납니다.

마무리

가장 작은 첫걸음은 사진 5장이 든 연습용 폴더를 하나 만드는 것입니다. 거기에 리사이즈 스크립트만 먼저 돌려보세요. 결과가 나오는 걸 눈으로 확인하고 나면 워터마크를 붙이는 건 어렵지 않습니다. 한 번 만들어둔 스크립트는 다음 달에도, 내년에도 그대로 씁니다.

다음 글에서는 매번 비슷한 것만 지적하다 끝나는 코드 리뷰를 체크리스트로 정리해 시간을 아끼는 방법을 다루겠습니다.

댓글 남기기