[스타트업/기술] WebP 변환을 통한 이미지 로딩 최적화 및 GCS 업로드 자동화 | PNG 대비 용량 89%, 지연 시간 82% 개선

 지난번 Firebase Storage의 오버헤드를 줄이기 위해 GCP Bucket 직접 호출로 전환하며 Latency를 한 차례 개선한 적이 있다. 당시 네트워크 홉(Hop)을 줄임으로써 유의미한 성능 향상을 이뤄냈지만, 프로덕트를 직접 사용해 보며 여전히 아쉬움이 남았다. 이미지 하나를 불러오는 데 여전히 1초 남짓한 시간이 걸리고 있었기 때문이다.

지난번 1차례 개선 결과

페이지 이동과 동시에 매끄럽게 이미지가 렌더링되는 다른 서비스들과 비교했을 때, 우리의 로딩 시간은 분명 사용자 경험을 저해하는 요소였다. 병목이 어디서 발생하는지 다시 한번 근본적인 원인을 찾아야만 했다.


원인은 무손실 압축, 해결책은 WebP

네트워크 탭을 열어 리소스 요청 내역을 분석해 보았다. 문제는 파일의 형식과 크기 그 자체에 있었다. 현재 프로덕트에서 사용 중인 이미지들이 모두 무손실 압축 포맷인 PNG로 저장 및 서빙되고 있었던 것이다(피그마로 제작하거나 AI로 이미지를 생성하면 대부분 PNG 형태로 저장). PNG는 이미지 퀄리티를 완벽하게 보존하는 데는 탁월하지만, 그만큼 파일 크기가 비대해져 제한된 네트워크 대역폭 내에서 빠르게 전송되어야 하는 웹 환경에는 적합하지 않다.

이를 해결하기 위해 웹 이미지 최적화의 표준으로 자리 잡고 있는 WebP 포맷을 도입하기로 했다. WebP는 구글이 웹 페이지 로딩 속도를 높이기 위해 개발한 포맷으로, 예측 코딩(Predictive Coding) 기법을 사용하여 픽셀의 색상을 주변 픽셀로부터 예측하고 그 차이만을 인코딩한다. 이 덕분에 기존 PNG나 JPEG와 비교해 시각적인 품질 저하를 최소화하면서도 파일 크기를 극적으로 줄일 수 있다.


89%의 용량 감소, 82%의 로딩 속도 개선

이론적 배경을 확인한 후, 곧바로 기존 PNG 이미지를 WebP로 변환하여 성능을 측정해 보았다. 결과는 매우 효과적이었다.

  • 파일 용량: 477KB -> 51.7KB (약 89% 감소)

  • 로딩 시간: 1.39s -> 259ms (약 82% 감소)

    WebP 형태로 변환 후

브라우저 렌더링 파이프라인에서 가장 큰 비중을 차지하던 리소스 다운로드 병목이 해소되었다. 용량이 10분의 1 수준으로 줄어드니, 1초대를 맴돌던 로딩 시간이 200ms대로 대폭 단축되며 페이지 진입과 거의 동시에 이미지가 뜨는 것을 체감할 수 있었다!



반복되는 작업의 자동화: 변환 및 GCS 업로드 파이프라인 구축

성능 지표는 매우 만족스러웠지만, 실무 차원에서는 새로운 고민거리가 생겼다. 에셋이 추가될 때마다 수동으로 이미지를 WebP로 변환하고, 이를 다시 GCP Storage(GCS) 콘솔에 접속해 업로드하는 과정은 너무나 비효율적이었다. 나름 스타트업으로 기술의 최선단에 있는 팀으로서 단순 반복 작업은 자동화하여 휴먼 에러를 막고 개발 리소스를 아껴야 한다고 생각했다. 따라서 이 일련의 과정을 스크립트로 자동화하기로 했다.

이를 위해 Shell 스크립트 기반의 자동화 파이프라인을 설계하기로 마음먹었다. 먼저 이미지 변환을 담당하는 convert_webp.sh 스크립트를 Copilot의 도움을 받아 작성했다. 뭐든 만들고 싶은게 생기면 바로 만들어볼 수 있는 세상...크

SUPPORTED_EXTENSIONS=(jpg jpeg png bmp tif tiff gif)

print_usage() {
    cat <<'EOF'
Usage:
    ./CloudStorage/convert_webp.sh <target_path> [options]

Description:
    Recursively converts supported images under <target_path> to .webp.
    For safety, original files are kept by default.

Options:
    --quality <1-100>       WebP quality (default: 82)
    --dry-run               Print actions without changing files
    --overwrite             Overwrite existing .webp outputs
    --delete-original       Delete source file after verified .webp exists or is created
    --help                  Show this help
EOF
}
# ... (이하 생략)

Copilot이 제안해준 실무의 방향성에 따라 이 스크립트는 '안전성'과 '유연성'에 초점을 맞춰 작성되었다. 원본 유실의 위험을 방지하기 위해 기본적으로 원본 파일을 보존하도록 설계했고, --dry-run 옵션을 두어 실제 변환 전 결과를 미리 확인할 수 있게 했다. 작업이 확정된 후에만 --delete-original 플래그를 통해 원본을 삭제하도록 하여 작업의 안정성을 확보했다.


효율적인 업로드 및 캐싱: 배포 자동화

변환된 이미지를 GCS에 올리고, 클라이언트에서 즉시 사용할 수 있도록 만드는 과정 또한 auto_upload.sh 스크립트로 구성했다.

#!/bin/bash
set -e
set -o pipefail

TARGET_BUCKET_NAME="your-gcs-bucket-name"
TARGET_GCS_PREFIX="story_list/"
SOURCE_LOCAL_DIR="./static"
OUTPUT_JSON_FILE="uploaded_file_urls.json"

CACHE_CONTROL_STR="public, max-age=604800"

echo "[Phase 1] Syncing files to Cloud Storage..."
gsutil -m rsync -r "${SOURCE_LOCAL_DIR}" "gs://${TARGET_BUCKET_NAME}/${TARGET_GCS_PREFIX}"

echo "[Phase 2] Applying Cache-Control: ${CACHE_CONTROL_STR} ..."
gsutil -m setmeta -h "Cache-Control:${CACHE_CONTROL_STR}" "gs://${TARGET_BUCKET_NAME}/${TARGET_GCS_PREFIX}**"

echo "[Phase 3] Generating JSON URL map..."
# ... jq를 활용한 json 생성 로직 ...

단순히 업로드(rsync)만 하는 것에 그치지 않고, setmeta 명령어로 Cache-Control 헤더를 일괄 적용했다는 점이 중요하다. 브라우저와 CDN이 일주일(604800초) 동안 이미지를 캐싱하도록 설정함으로써, 불필요한 네트워크 요청을 줄이고 로딩 속도를 한층 더 끌어올렸다.

마지막으로 프론트엔드 환경에서 새로 업로드된 이미지들의 경로를 쉽게 참조할 수 있도록 jq를 이용해 URL 맵핑 JSON 파일을 생성하는 로직을 추가하며 배포 파이프라인을 완성했다.



단순히 로딩 속도라는 엔드 유저의 경험을 개선하는 것을 넘어, 내부 팀의 리소스 관리 측면에서도 운영 효율성을 확보할 수 있는 구조를 만든 것이다!
점점 내부적으로 신경써야 할 일이 많아지면서, 이런 식으로 팀 내부적으로 리소스를 효율적으로 활용할 수 있는 방법에 대해 고민하는 중이다.





hyeon_B

안녕하세요! AI 기술을 이용해 더 나은 세상을 만들어 나가고 싶은 과기원생 Hyeon이라고 합니다. 저는 앞으로 인공지능 시대에는 지식을 '활용'하는 능력이 중요해질 것이라고 생각합니다. 대부분의 일들은 인공지능이 뛰어난 모습을 보이지만, 인공지능은 데이터로 부터 연관관계를 학습하기 때문에 지식들을 새로 통합해서 활용하는 능력이 부족합니다. 인공지능이 뉴턴 전에 만들어졌다면 사과가 떨어지는 이유에 대답하지 못했을 것이고, 아인슈타인 전에 만들어졌다면 중력이 어떻게 생기는지 설명하지 못했을 것입니다. 따라서 앞으로 우리는 '본질'을 탐구하고 그 본질로부터 다른 곳에 적용하며 인공지능을 현명하게 활용해야 할 것입니다. 함께 인공지능 시대를 준비합시다!

댓글 쓰기

다음 이전

POST ADS1

POST ADS 2