웹에서 이미지는 페이지 용량의 상당 부분을 차지합니다. WebP 포맷은 동일한 화질에서 PNG 대비 약 26%, JPG 대비 약 25~34% 더 작은 용량을 제공하는데, 이미지가 많은 사이트일수록 변환 효과가 뚜렷하죠.
저는 보통 Google의 Squoosh를 사용했었는데, 이 사이트에서는 GUI + 여러 고급 옵션들을 제공하지만 여러 파일을 한 번에 변환하는 기능이 없습니다. 구글링을 하면 수없이 나오는 변환 사이트는 여러 파일들을 한 번에 변환하는 기능을 제공하지만 무료에서는 개수 제한이 있는 등 제한이 있었죠.
그래서 직접 만드는 방법을 알아보게 되었습니다.
이 글에서는 이런 내용을 다뤄보았습니다.
- Mac 환경에서
cwebpCLI 도구를 사용해 이미지를 WebP로 변환하는 방법 - 단일 파일 수동 변환부터, 하위 디렉토리 전체를 일괄 변환하는 쉘 스크립트 구성까지
개인 Notion에만 정리해뒀던 내용이 혹시 주변에 도움이 될까 싶어 블로그로 옮겨봅니다.
Mac 환경에서 동작하는 내용으로, 다른 OS에서는 정상 동작이 검증되지 않았습니다.
macOS에서 cwebp를 설치하려면 먼저 패키지 매니저인 Homebrew가 필요합니다. 이미 설치되어 있다면 이 단계는 건너뛰세요.
터미널(⌘ + Space → "터미널" 검색)을 열고 아래 명령어를 실행합니다.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
설치 후 brew 명령어가 인식되지 않으면 셸 환경 설정을 추가해야 합니다.
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile eval "$(/opt/homebrew/bin/brew shellenv)"
Homebrew가 준비되었으면 webp 패키지를 설치합니다. Google에서 제공하는 이 패키지에 cwebp CLI 도구가 포함되어 있어요.
brew install webp
가장 기본적인 사용법은 입력 파일과 출력 파일을 지정하는 것입니다.
cwebp input.jpg -o output.webp
PNG 파일도 동일하게 변환할 수 있어요.
cwebp input.png -o output.webp
-q 옵션으로 변환 품질(1~100)을 지정합니다. 값이 높을수록 화질이 좋지만 용량도 커지는데, 90 정도면 원본과 거의 차이가 없으면서도 용량이 크게 줄어들죠.
cwebp -q 90 input.jpg -o output.webp
파일명에 띄어쓰기가 포함되어 있다면 반드시 따옴표로 감싸야 합니다.
cwebp -q 90 "this is for test.png" -o "this is for test.webp"
현재 디렉토리에 파일이 없으면 전체 경로를 지정하거나, 이미지가 있는 디렉토리로 먼저 이동합니다.
# 전체 경로 지정 cwebp -q 90 "/Users/username/Downloads/image.png" -o "/Users/username/Downloads/image.webp" # 또는 폴더로 이동 후 변환 cd ~/Downloads cwebp -q 90 "image.png" -o "image.webp"
for 루프를 사용하면 현재 디렉토리의 모든 JPG 또는 PNG 파일을 한 번에 변환할 수 있습니다.
# 모든 JPG 변환 for file in *.jpg; do cwebp "$file" -o "${file%.jpg}.webp" -q 90; done # 모든 PNG 변환 for file in *.png; do cwebp "$file" -o "${file%.png}.webp" -q 90; done
여기까지가 cwebp의 기본 사용법이에요. 파일 몇 개만 변환할 때는 이 정도로 충분합니다. 하지만 하위 디렉토리까지 포함해서 수십~수백 개의 이미지를 관리해야 한다면, 자동화 스크립트가 필요해지죠.
블로그의 public/imgs/ 디렉토리에는 하위 디렉토리가 여러 개 있고, 이미지가 계속 추가됩니다. 단순한 for 루프로는 하위 디렉토리를 재귀적으로 탐색하기 어렵고, 이미 변환한 파일을 다시 처리하는 문제도 있어요.
스크립트에 필요한 기능을 정리했습니다.
- 지정한 디렉토리 하위의 모든 PNG, JPG 파일을 재귀적으로 탐색
- 동일 파일명의 WebP가 이미 있으면 스킵
- 변환 품질은 실행 시 입력받되, 미입력 시 기본값 90 적용
- 변환 후 원본 대비 크기 비율 표시
- 완료 시 변환/스킵/실패 건수 요약
스크립트 시작 시 cwebp가 설치되어 있는지 먼저 확인합니다. 설치되지 않았으면 안내 메시지와 함께 종료해요.
if ! command -v cwebp &> /dev/null; then echo "cwebp가 설치되어 있지 않습니다." echo " 다음 명령어로 설치해주세요: brew install webp" exit 1 fi
read로 사용자 입력을 받고, 입력하지 않으면 기본값 90이 적용됩니다. 1~100 범위 밖의 값은 거부하도록 검증도 추가했어요.
read -p "WebP 압축 품질을 입력하세요 (1-100, 기본값: 90): " QUALITY QUALITY=${QUALITY:-90} if ! [[ "$QUALITY" =~ ^[0-9]+$ ]] || [ "$QUALITY" -lt 1 ] || [ "$QUALITY" -gt 100 ]; then echo "유효하지 않은 값입니다. 1~100 사이의 숫자를 입력해주세요." exit 1 fi
1.png를 변환하기 전에 1.webp가 이미 존재하는지 확인합니다. 이미 변환된 이미지를 다시 처리하지 않기 때문에, 이미지가 추가될 때마다 부담 없이 재실행할 수 있어요.
output="$dir/$name.webp" if [ -f "$output" ]; then echo "스킵 (이미 존재): $output" SKIPPED=$((SKIPPED + 1)) continue fi
cwebp로 변환한 뒤 stat으로 원본과 변환된 파일의 크기를 비교해서 압축률을 표시합니다.
if cwebp -q "$QUALITY" "$file" -o "$output" -quiet 2>/dev/null; then ORIG_SIZE=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null) NEW_SIZE=$(stat -f%z "$output" 2>/dev/null || stat -c%s "$output" 2>/dev/null) RATIO=$(echo "scale=1; $NEW_SIZE * 100 / $ORIG_SIZE" | bc 2>/dev/null || echo "?") echo "변환 완료 (${RATIO}% of original)" CONVERTED=$((CONVERTED + 1)) fi
stat -f%z는 macOS용, stat -c%s는 Linux용 옵션입니다. 둘 다 시도해서 현재 OS에 맞는 쪽이 동작하도록 했어요.
find로 대상 디렉토리 하위의 모든 PNG, JPG, JPEG 파일을 재귀적으로 찾습니다. -print0과 read -d ''를 사용해서 파일명에 공백이 포함되어 있어도 안전하게 처리해요.
while IFS= read -r -d '' file; do # 변환 로직 done < <(find "$TARGET_DIR" -type f \( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" \) -print0)
위 내용을 모두 합친 전체 스크립트입니다. TARGET_DIR만 본인 프로젝트의 이미지 디렉토리로 변경하면 어떤 프로젝트에서든 바로 사용할 수 있어요.
#!/bin/bash set -e TARGET_DIR="./public/imgs" # 본인의 이미지 디렉토리로 변경 # cwebp 설치 확인 if ! command -v cwebp &> /dev/null; then echo "cwebp가 설치되어 있지 않습니다." echo " 다음 명령어로 설치해주세요: brew install webp" exit 1 fi # 대상 디렉토리 확인 if [ ! -d "$TARGET_DIR" ]; then echo "대상 디렉토리가 존재하지 않습니다: $TARGET_DIR" exit 1 fi # 압축률 입력 (기본값: 90) read -p "WebP 압축 품질을 입력하세요 (1-100, 기본값: 90): " QUALITY QUALITY=${QUALITY:-90} # 숫자 검증 if ! [[ "$QUALITY" =~ ^[0-9]+$ ]] || [ "$QUALITY" -lt 1 ] || [ "$QUALITY" -gt 100 ]; then echo "유효하지 않은 값입니다. 1~100 사이의 숫자를 입력해주세요." exit 1 fi echo "" echo "대상 디렉토리: $TARGET_DIR" echo "압축 품질: $QUALITY" echo "-------------------------------------------" CONVERTED=0 SKIPPED=0 FAILED=0 while IFS= read -r -d '' file; do dir=$(dirname "$file") filename=$(basename "$file") name="${filename%.*}" output="$dir/$name.webp" if [ -f "$output" ]; then echo "스킵 (이미 존재): $output" SKIPPED=$((SKIPPED + 1)) continue fi echo -n "변환 중: $file → $output ... " if cwebp -q "$QUALITY" "$file" -o "$output" -quiet 2>/dev/null; then ORIG_SIZE=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null) NEW_SIZE=$(stat -f%z "$output" 2>/dev/null || stat -c%s "$output" 2>/dev/null) RATIO=$(echo "scale=1; $NEW_SIZE * 100 / $ORIG_SIZE" | bc 2>/dev/null || echo "?") echo "완료 (${RATIO}% of original)" CONVERTED=$((CONVERTED + 1)) else echo "실패" FAILED=$((FAILED + 1)) fi done < <(find "$TARGET_DIR" -type f \( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" \) -print0) echo "" echo "===========================================" echo "변환 완료" echo " 변환: ${CONVERTED}개" echo " 스킵: ${SKIPPED}개" echo " 실패: ${FAILED}개" echo "==========================================="
스크립트 파일을 생성한 후 실행 권한을 부여합니다.
chmod +x convert-to-webp.sh ./convert-to-webp.sh
Node.js 프로젝트라면 package.json에 등록해서 더 편하게 실행할 수도 있어요.
{ "scripts": { "convert:webp": "./scripts/convert-to-webp.sh" } }
스크립트를 실행하면 아래와 같은 흐름으로 진행됩니다.
WebP 압축 품질을 입력하세요 (1-100, 기본값: 90): 대상 디렉토리: ./public/imgs 압축 품질: 90 ------------------------------------------- 변환 중: .../image1.png → .../image1.webp ... 완료 (45.2% of original) 변환 중: .../photo.jpg → .../photo.webp ... 완료 (62.1% of original) 스킵 (이미 존재): .../logo.webp =========================================== 변환 완료 변환: 2개 스킵: 1개 실패: 0개 ===========================================
