WebP 파일을 헥스 에디터로 열어 본다. 처음 12바이트는 다음과 같다.
52 49 46 46 ?? ?? ?? ?? 57 45 42 50
이것은 RIFF/WAVE 스타일 컨테이너 헤더다. 52 49 46 46은 ASCII로 "RIFF"를 뜻한다. 다음 4바이트는 리틀엔디안 uint32로 된 파일 크기다. 57 45 42 50은 ASCII로 "WEBP"를 뜻한다. WebP 파일은 원시 비트스트림이 아니다. WAV 오디오나 AVI 비디오와 마찬가지로 Microsoft가 1991년에 소개한 동일한 RIFF 규격을 기반으로 한 컨테이너다. 그 컨테이너 안에는 VP8 비디오 프레임이 들어 있으며, 이는 정지 이미지 용도로 재활용된 것이다. 이 설계 선택 — 비디오 코덱을 사진에 사용하는 것 —이 WebP를 이해하는 데 가장 중요한 부분이다.
Google은 2010년 9월 30일 WebP를 발표했다. 제안은 단순했다. JPEG과 동일한 시각적 품질이지만 25-34% 더 작은 파일. PNG에 대해서는 주장이 더 대담했다 — 무손실 이미지의 경우 26% 더 작다. 이미지 바이트가 전체 전송 데이터의 약 절반을 차지하는 웹에서 이 수치는 대규모 도입을 촉발하기에 충분했어야 했다. 그러지 않았다.
WebP를 탄생시킨 인수
WebP는 이미지 연구소에서 나온 것이 아니다. 비디오 코덱에서 나왔다.
2010년 2월, Google은 On2 Technologies를 약 1억 2,400만 달러에 인수했다. On2는 오랜 역사를 가진 비디오 압축 회사였다 — 그들의 코덱은 Flash video, Skype 영상 통화, AOL 스트리밍을 지원했다. 그들의 대표 제품은 VP8으로, 특허 로열티 없이 H.264와 경쟁하도록 설계된 비디오 코덱이었다.
Google은 2010년 5월 VP8을 WebM이라는 이름으로 오픈소스화했다. Vorbis 오디오 코덱과 Matroska 컨테이너를 함께 묶어 배포했다. 목표는 명확했다. 웹 비디오 스트리밍에서 로열티를 요구하기 시작한 MPEG-LA의 H.264 라이선스 풀에 맞설 수 있는 특허 없는 비디오 스택을 구축하는 것이다.
하지만 Google은 두 번째 사용 사례도 염두에 두고 있었다. VP8의 인트라프레임 압축 — 다른 프레임을 참조하지 않고 단일 비디오 프레임을 압축하는 것 —은 본질적으로 정지 이미지 코덱이었다. VP8을 비디오에서 효율적으로 만든 동일한 예측 모드, 변환 코딩, 엔트로피 코딩이 사진에서도 작동했다. Google은 인트라프레임 모드를 추출해 RIFF 컨테이너로 감싸고 WebP라고 불렀다.
이름은 마케팅적 선택이었다. "Web"은 웹을 위해 설계되었다는 의미. "P"는 이미지 형식이 P로 끝나기 때문 — JPEG, PNG, BMP, TIFF. 결과는 기술적으로 비디오 프레임이 사진인 척하는 형식이었다.
굳이 새로운 형식이 필요했던 이유
2010년이 되면 JPEG은 18살, PNG은 14살이었다. 둘 다 자리를 잡은 상태였다. 왜 Google은 새로운 형식이 필요하다고 판단했을까?
JPEG의 한계는 분명했고, 이미 널리 알려져 있었다.
- 투명도가 없다. JPEG 픽셀은 완전히 불투명하거나 별도의 마스크가 필요하다.
- 애니메이션이 없다. 표준으로서의 Animated JPEG은 존재하지 않는다.
- 손실 압축만 가능하다. JPEG의 기본 규격에는 무손실 모드가 없다. (JPEG-LS와 JPEG 2000은 존재하지만 둘 다 웹 호환성이 없다.)
- 채널당 8비트 색심도. 기본 규격에는 넓은 색역이나 HDR 지원이 없다.
- 낮은 품질에서 블로킹 아티팩트. 8 x 8 DCT 그리드는 품질 설정 75 이하에서 눈에 띈다.
PNG의 한계도 마찬가지로 실재했다.
- 손실 압축 모드가 없다. PNG는 항상 무손실이다. 12MP 사진을 PNG로 저장하면 15-25MB가 된다.
- 사진 파일이 크다. PNG의 DEFLATE 압축은 DCT 기반 심리-시각적 손실과 경쟁할 수 없다.
- 기본 규격에 애니메이션이 없다. APNG은 존재하지만 브라우저 지원을 얻는 데 수년이 걸렸다.
Google은 틈새를 발견했다. 손실과 무손실 압축, 투명도, 애니메이션을 모두 제공하고 기존 형식보다 더 작은 파일을 만드는 단일 형식. 이것이 WebP의 제안이었다.
WebP의 실제 작동 방식
WebP에는 근본적으로 다른 두 가지 내 형식이 있다. 손실 WebP (VP8 인트라프레임)과 무손실 WebP (별도의 코덱, 역시 VP8 연구에서 파생됨).
손실 WebP: VP8 인트라
손실 WebP는 RIFF 컨테이너 안에 VP8 비트스트림을 저장한다. 인코딩 파이프라인은 개념적으로 JPEG과 유사하지만 핵심적인 차이가 있다.
| 단계 | JPEG | 손실 WebP |
|---|---|---|
| 변환 | 8 x 8 DCT | 4 x 4 또는 16 x 16 정수 DCT 유사 변환 |
| 예측 | 없음 (인트라만) | 4 x 4 블록당 4개의 인트라 예측 모드 |
| chroma subsampling | 4:2:0 기본값 | 4:2:0 기본값 |
| 엔트로피 코딩 | Huffman | 이진 산술 코딩 |
| 비트 심도 | 8비트 | 8비트 |
인트라 예측이 가장 큰 이점이다. JPEG은 각 8 x 8 블록을 독립적으로 인코딩한다. WebP는 각 4 x 4 블록을 이미 인코딩된 이웃 — 위, 왼쪽, 또는 둘 다 —에서 예측한 뒤 예측 오차만 인코딩한다. 부드러운 그라데이션과 넓은 평탄한 영역에서는 오차가 매우 작아 압축률이 크게 향상된다.
산술 코더 역시 JPEG의 Huffman 코딩보다 효율적이다 — 동일한 품질에서 일반적으로 5-10%의 추가 절감 효과가 있다.
Google의 2010년 자체 벤치마크 주장은 다음과 같다.
| 지표 | WebP 대 JPEG |
|---|---|
| 평균 파일 크기 감소 | 동등한 SSIM에서 25-34% |
| 인코딩 속도 | libjpeg보다 약 8배 느림 |
| 디코딩 속도 | libjpeg과 비슷함 |
인코딩 속도가 숨겨진 비용이었다. WebP 파일을 생성하는 데는 JPEG보다 훨씬 더 많은 CPU가 필요했다. 수백 장의 이미지를 내출하는 사진작가에게 이것은 문제였다.
무손실 WebP
무손실 WebP는 완전히 다른 코덱을 사용한다. VP8이 아니다. 다음을 결합한 커스텀 형식이다.
- 예측 코딩: 픽셀당 14개의 다른 공간 예측 모드.
- 색상 캐시: 최근 본 색상의 해시 테이블로 국소적 반복을 활용한다.
- LZ77 back-references: PNG의 DEFLATE와 유사하지만 2D 공간 인식 매칭을 제공한다.
- Huffman + 산술 하이브리드: 엔트로피 코딩이 국소 통계에 적응한다.
Google은 PNG보다 평균 26% 더 작은 파일을 주장했다. 실제로 절감 효과는 매우 다양했다 — 넓은 평탄한 영역이 있는 단순 그래픽은 큰 이점이 없고, 미세한 질감이 있는 사진은 30-40% 감소를 볼 수 있다.
Extended WebP (VP8X)
VP8X chunk는 WebP에 추가 기능을 확장한다.
- 알파 채널: 8비트 알파가 별도로 인코딩되며, 무손실 WebP의 엔트로피 코더로 압축된다.
- 애니메이션: 타이밍 메타데이터가 있는 다중 프레임으로, 본질적으로 축소된 VP8 비디오다.
- EXIF 메타데이터: 카메라와 지리 위치 데이터.
- XMP 메타데이터: Adobe 스타일 처리 지시사항.
- ICC 색상 프로필: 넓은 색역과 HDR 색상 관리.
VP8X 파일은 VP8X chunk 헤더로 시작하며, 어떤 확장 기능이 존재하는지 나타내는 플래그가 뒤따른다.
파일 형식
WebP는 RIFF 컨테이너다. RIFF를 알면 바이트 레이아웃을 이해하는 것이 간단하다.
RIFF 컨테이너 구조
Bytes 0-3: "RIFF" (0x52 0x49 0x46 0x46)
Bytes 4-7: 파일 크기 - 8 (little-endian uint32)
Bytes 8-11: "WEBP" (0x57 0x45 0x42 0x50)
Bytes 12-15: Chunk FourCC — "VP8 ", "VP8L", 또는 "VP8X"
Bytes 16-19: Chunk 크기 (little-endian uint32)
Bytes 20+: Chunk 데이터
VP8X 확장 헤더
바이트 12-15의 FourCC가 "VP8X" (0x56 0x50 0x38 0x58)인 경우:
Bytes 20-23: Chunk 크기 = 10 (little-endian uint32)
Bytes 24: 플래그 바이트
Bit 0: 애니메이션 존재
Bit 1: XMP 메타데이터 존재
Bit 2: EXIF 메타데이터 존재
Bit 3: Alpha channel 존재
Bit 4: ICC 프로필 존재
Bits 5-7: 예약됨
Bytes 25-27: 캔버스 너비 - 1 (little-endian uint24)
Bytes 28-30: 캔버스 높이 - 1 (little-endian uint24)
캔버스 크기는 width - 1과 height - 1로 저장되므로, 1200 x 675 이미지는 1199와 674를 저장한다. 최대 캔버스 크기는 16,777,215 x 16,777,215 픽셀이다.
Chunk 유형
| FourCC | 내용 | 압축 |
|---|---|---|
VP8 | VP8 비트스트림 (손실) | VP8 인트라 |
VP8L | VP8L 비트스트림 (무손실) | 커스텀 무손실 |
VP8X | 확장 헤더 + 플래그 | 없음 |
ALPH | 알파 채널 데이터 | 무손실 WebP 엔트로피 |
ANMF | 애니메이션 프레임 | 프레임당 VP8/VP8L |
ICCP | ICC 색상 프로필 | 없음 |
EXIF | EXIF 메타데이터 | 없음 |
XMP | XMP 메타데이터 | 없음 |
파일 서명을 읽어 WebP 감지하기
.webp 확장자를 믿지 마라. 처음 16바이트를 읽고 RIFF 헤더를 파싱하라.
간단한 손실 WebP의 정확한 바이트 레이아웃:
Bytes 0-3: "RIFF"
Bytes 4-7: 파일 크기 (little-endian uint32)
Bytes 8-11: "WEBP"
Bytes 12-15: "VP8 " (lossy) 또는 "VP8L" (lossless) 또는 "VP8X" (확장)
브라우저용 TypeScript:
interface WebPInfo {
valid: boolean
type: "lossy" | "lossless" | "extended" | "unknown"
width?: number
height?: number
hasAlpha?: boolean
isAnimated?: boolean
}
async function inspectWebP(file: File): Promise<WebPInfo> {
const buffer = await file.slice(0, 30).arrayBuffer()
const bytes = new Uint8Array(buffer)
if (bytes.length < 12) return { valid: false, type: "unknown" }
const riff = String.fromCharCode(...bytes.slice(0, 4))
const webp = String.fromCharCode(...bytes.slice(8, 12))
if (riff !== "RIFF" || webp !== "WEBP") {
return { valid: false, type: "unknown" }
}
const type = String.fromCharCode(...bytes.slice(12, 16))
if (type === "VP8 ") {
// Lossy: 너비/높이는 바이트 26-29에 있음
const w = bytes[26] | (bytes[27] << 8)
const h = bytes[28] | (bytes[29] << 8)
return { valid: true, type: "lossy", width: w, height: h, hasAlpha: false }
}
if (type === "VP8L") {
// 무손실: 크기는 바이트 21-24의 비트에 패킹됨
const bits =
bytes[21] | (bytes[22] << 8) | (bytes[23] << 16) | (bytes[24] << 24)
const w = (bits & 0x3fff) + 1
const h = ((bits >> 14) & 0x3fff) + 1
const alpha = ((bits >> 28) & 0x01) !== 0
return {
valid: true,
type: "lossless",
width: w,
height: h,
hasAlpha: alpha,
}
}
if (type === "VP8X") {
const flags = bytes[20]
const w = (bytes[24] | (bytes[25] << 8) | (bytes[26] << 16)) + 1
const h = (bytes[27] | (bytes[28] << 8) | (bytes[29] << 16)) + 1
return {
valid: true,
type: "extended",
width: w,
height: h,
hasAlpha: (flags & 0x10) !== 0,
isAnimated: (flags & 0x02) !== 0,
}
}
return { valid: false, type: "unknown" }
}
Python
import struct
from typing import TypedDict
class WebPInfo(TypedDict):
valid: bool
type: str
width: int | None
height: int | None
has_alpha: bool | None
is_animated: bool | None
def inspect_webp(path: str) -> WebPInfo:
with open(path, "rb") as f:
header = f.read(30)
if len(header) < 12:
return {"valid": False, "type": "unknown"}
if header[:4] != b"RIFF" or header[8:12] != b"WEBP":
return {"valid": False, "type": "unknown"}
chunk_type = header[12:16]
if chunk_type == b"VP8 ":
w, h = struct.unpack("<HH", header[26:30])
return {"valid": True, "type": "lossy", "width": w, "height": h,
"has_alpha": False, "is_animated": None}
if chunk_type == b"VP8L":
bits = struct.unpack("<I", header[21:25])[0]
w = (bits & 0x3FFF) + 1
h = ((bits >> 14) & 0x3FFF) + 1
alpha = ((bits >> 28) & 0x01) != 0
return {"valid": True, "type": "lossless", "width": w, "height": h,
"has_alpha": alpha, "is_animated": None}
if chunk_type == b"VP8X":
flags = header[20]
w = struct.unpack("<I", header[24:27] + b"\x00")[0] + 1
h = struct.unpack("<I", header[27:30] + b"\x00")[0] + 1
return {"valid": True, "type": "extended", "width": w, "height": h,
"has_alpha": bool(flags & 0x10), "is_animated": bool(flags & 0x02)}
return {"valid": False, "type": "unknown"}
Pillow를 사용한 고수준 옵션:
from PIL import Image
with Image.open("image.webp") as img:
print(img.format) # WEBP
print(img.mode) # RGB or RGBA
print(img.size) # (width, height)
print(img.is_animated) # True if animated
또는 webp 라이브러리 사용:
import webp
info = webp.WebPInfo("image.webp")
print(info.width, info.height, info.has_alpha)
Go
package main
import (
"encoding/binary"
"fmt"
"os"
)
type WebPInfo struct {
Valid bool
Type string
Width int
Height int
HasAlpha bool
IsAnimated bool
}
func inspectWebP(path string) (WebPInfo, error) {
f, err := os.Open(path)
if err != nil {
return WebPInfo{}, err
}
defer f.Close()
buf := make([]byte, 30)
if _, err := f.Read(buf); err != nil {
return WebPInfo{}, err
}
if string(buf[:4]) != "RIFF" || string(buf[8:12]) != "WEBP" {
return WebPInfo{Valid: false, Type: "unknown"}, nil
}
typeStr := string(buf[12:16])
switch typeStr {
case "VP8 ":
w := int(binary.LittleEndian.Uint16(buf[26:28]))
h := int(binary.LittleEndian.Uint16(buf[28:30]))
return WebPInfo{Valid: true, Type: "lossy", Width: w, Height: h, HasAlpha: false}, nil
case "VP8L":
bits := binary.LittleEndian.Uint32(buf[21:25])
w := int(bits&0x3FFF) + 1
h := int((bits>>14)&0x3FFF) + 1
alpha := ((bits >> 28) & 0x01) != 0
return WebPInfo{Valid: true, Type: "lossless", Width: w, Height: h, HasAlpha: alpha}, nil
case "VP8X":
flags := buf[20]
w := int(binary.LittleEndian.Uint32(append(buf[24:27], 0))) + 1
h := int(binary.LittleEndian.Uint32(append(buf[27:30], 0))) + 1
return WebPInfo{
Valid: true, Type: "extended", Width: w, Height: h,
HasAlpha: (flags & 0x10) != 0, IsAnimated: (flags & 0x02) != 0,
}, nil
}
return WebPInfo{Valid: false, Type: "unknown"}, nil
}
PHP
function inspectWebP(string $path): array {
$header = file_get_contents($path, false, null, 0, 30);
if (strlen($header) < 12) {
return ["valid" => false, "type" => "unknown"];
}
if (substr($header, 0, 4) !== "RIFF" || substr($header, 8, 4) !== "WEBP") {
return ["valid" => false, "type" => "unknown"];
}
$type = substr($header, 12, 4);
if ($type === "VP8 ") {
$w = unpack("v", substr($header, 26, 2))[1];
$h = unpack("v", substr($header, 28, 2))[1];
return ["valid" => true, "type" => "lossy", "width" => $w, "height" => $h, "has_alpha" => false];
}
if ($type === "VP8L") {
$bits = unpack("V", substr($header, 21, 4))[1];
$w = ($bits & 0x3FFF) + 1;
$h = (($bits >> 14) & 0x3FFF) + 1;
$alpha = (($bits >> 28) & 0x01) !== 0;
return ["valid" => true, "type" => "lossless", "width" => $w, "height" => $h, "has_alpha" => $alpha];
}
if ($type === "VP8X") {
$flags = ord($header[20]);
$w = unpack("V", substr($header, 24, 3) . "\x00")[1] + 1;
$h = unpack("V", substr($header, 27, 3) . "\x00")[1] + 1;
return [
"valid" => true, "type" => "extended",
"width" => $w, "height" => $h,
"has_alpha" => (bool)($flags & 0x10),
"is_animated" => (bool)($flags & 0x02),
];
}
return ["valid" => false, "type" => "unknown"];
}
fileinfo 사용:
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file('image.webp');
// image/webp
ImageMagick CLI
magick identify -verbose image.webp | grep "Format:"
# Format: WEBP (WebP Image Format)
전체 메타데이터 추출:
magick identify -verbose image.webp
이 명령은 너비, 높이, 색상 심도, 알파 존재 여부, 압축 유형, ICC 프로필 정보를 출력한다.
또는 간단하게:
file image.webp
# image.webp: RIFF (little-endian) data, Web/P image
강점
WebP는 특정 영역에서 기술적으로 인상적이다.
더 작은 파일: Google의 참조 코퍼스에서 손실 WebP는 동일한 SSIM에서 JPEG보다 평균 25-34% 더 작았다. 무손실 WebP는 PNG보다 평균 26% 더 작았다. 고트래픽 사이트에서는 이 절감 효과가 곧대로 대역폭 비용과 더 빠른 페이지 로드로 이어진다.
기능 통합: 하나의 형식이 대부분의 사용 사례에서 JPEG과 PNG를 모두 대체한다. 사진용 손실 모드, 그래픽용 무손실 모드, 투명도용 알파 채널, 짧은 시퀀스용 애니메이션. 웹 개발자는 세 가지 대신 하나의 형식만 알면 된다.
브라우저 네이티브 디코딩: Chrome, Firefox, Safari, Edge 모두 하드웨어 가속 또는 고도로 최적화된 소프트웨어 WebP 디코더를 탑재하고 있다. 디코딩 속도는 데스크톱에서는 JPEG과 비슷하고 모바일에서는 10-20% 이내다.
프로그레시브 디코딩: WebP는 데이터가 도착함에 따라 점진적 표시를 지원한다. JPEG의 프로그레시브 모드와 유사하다. 느린 연결에서 파일의 약 30%를 수신한 뒤에도 인식 가능한 이미지가 나타난다.
애니메이션: Animated WebP 파일은 동등한 시각적 품질에서 animated GIF보다 일반적으로 60-80% 더 작으며, 프레임당 전체 24비트 색상과 8비트 알파를 제공한다.
약점
WebP의 문제는 기술적이지 않다. 생태계적인 문제다.
인코딩 속도: 2010년 WebP 인코딩은 libjpeg보다 대략 8배 느렸다. 격차는 좁혀졌다 — 2026년의 libwebp는 libjpeg-turbo보다 약 2-3배 느리다 — 하지만 여전히 배치 워크플로우에서 문제가 된다. 1,000장의 이미지를 내출하는 사진작가는 눈에 띄게 더 오래 기다릴 것이다.
16비트나 HDR 지원 없음: WebP는 엄격하게 채널당 8비트다. 넓은 색역 사진, 의료 영상, HDR 콘텐츠에는 WebP를 사용할 수 없다. HEIC, AVIF, JPEG XL 모두 더 높은 비트 심도를 지원한다.
무손실 JPEG 재압축 없음: JPEG XL은 기존 JPEG을 가져와 무손실로 재압축해 약 20% 절감할 수 있다. WebP는 불가능하다. JPEG을 WebP로 변환하려면 전체 재인코딩이 필요하며, 이는 세대 손실을 유발한다.
도구 격차: Photoshop은 2022년까지 WebP를 네이티브로 지원하지 않았다. ImageMagick의 WebP 지원은 libwebp 플러그인 컴파일이 필요했는데, 많은 배포판이 기본적으로 이를 제외했다. 여전히 많은 콘텐츠 관리 시스템이 기본적으로 JPEG/PNG를 생성한다.
VP8 특허 구름: Google은 VP8을 특허 면책 약속과 함께 공개했지만, 이 코덱의 특허 환경은 PNG나 JPEG만큼 깨끗하지 않았다. 일부 조직은 Google의 법적 보호막이 법정에서 버틸 수 있을지 신뢰하지 못해 WebP를 의도적으로 피했다.
"열등한" 형식이 이긴 이유
JPEG은 34살이다. 투명도가 없고, 애니메이션이 없고, 무손실 모드가 없으며, 품질 75에서 가시적인 아티팩트가 있다. WebP는 거의 모든 지표에서 JPEG을 능가한다. 그런데도 2025년 Web Almanac은 JPEG이 전체 웹 이미지의 약 **46%**를, WebP는 **19%**를 차지한다고 한다.
이유는 기술적이지 않다. 네트워크 효과와 전환 비용 때문이다.
JPEG은 이미지 형식의 QWERTY다. 모든 카메라가 기본적으로 JPEG을 저장한다. 모든 휴대폰이 JPEG을 네이티브로 표시한다. 모든 프린터가 JPEG을 수용한다. 모든 소셜 네트워크, CMS, CDN, 이메일 클라이언트가 플러그인, 코덱, 변환 없이 JPEG을 처리한다. 이 형식은 너무 보편적이어서 대부분의 사용자에게 "이미지"와 "JPEG"은 기능적으로 동의어다.
WebP의 채택 곡선이 이야기를 보여준다.
| 연도 | 마일스톤 |
|---|---|
| 2010 | Google, WebP 발표 (Chrome 8) |
| 2012 | Chrome 23, 무손실 및 알파 지원 추가 |
| 2013 | Chrome, animated WebP 추가 |
| 2014 | Android 4.0+, 네이티브 WebP 지원 추가 |
| 2015 | Facebook, 모든 모바일 사진을 WebP로 변환 |
| 2016 | Safari 14, WebP 지원 추가 |
| 2020 | 보편적인 브라우저 지원 달성 |
| 2022 | Photoshop, 네이티브 WebP 출력 추가 |
| 2025 | Web Almanac 기준 웹 이미지의 19% 차지 |
Chrome은 Google이 브라우저와 형식을 모두 통제했기 때문에 WebP를 공격적으로 밀어붙였다. Facebook은 페타바이트 대역폭을 절약했기 때문에 채택했다. 하지만 웹의 롱테일 — WordPress 블로그, 소규모 전자상거래 사이트, 기업 CMS 배포, 이메일 뉴스레터 —는 느리게 움직이거나 전혀 움직이지 않았다.
결정적인 실패 지점은 Apple의 생태계였다. iPhone은 기본적으로 HEIC를 저장했지 WebP가 아니었다. macOS Preview는 macOS 11 Big Sur(2020)까지 WebP를 지원하지 않았다. iOS 공유 시트는 WebP 내출을 제공하지 않았다. Apple 기기에서 주로 작업하는 사진작가, 디자이너, 소셜 미디어 크리에이터에게 WebP는 보이지 않았다.
한편, AVIF는 2019년에 등장해 WebP보다 더 나은 압축과 Alliance for Open Media의 로열티 프리 라이선싱을 제공했다. Chrome, Firefox, Safari 모두 AVIF를 탑재하고 있다. Cloudflare와 Cloudinary는 자동으로 AVIF를 제공한다. WebP는 징검다리가 되었다 — JPEG보다 낫지만, 이미 다음 세대에게 뛰어넘겨지고 있다.
WebP의 현재 위치
WebP는 실패가 아니다. 부분적 성공이다.
2026년에 새 프로젝트를 구축하는 웹 개발자에게 WebP는 투명도나 애니메이션이 필요한 이미지를 위한 실용적인 기본값이다. 무손실 그래픽에서는 PNG보다 작고 사진에서는 JPEG보다 작다. 브라우저 지원은 보편적이다. 인코딩 도구는 성숙했다.
하지만 WebP는 JPEG을 대체하지 못했다. JPEG 옆에 틈새를 개척했을 뿐이다 — PNG가 이미 점유하고 있던 동일한 틈새를, 단지 더 작은 파일로. "모든 이미지를 위한 하나의 형식"이라는 비전은 실현되지 않았다.
2026년의 실제 현실:
| 사용 사례 | 최적의 형식 | 이유 |
|---|---|---|
| 사진 (레거시) | JPEG | 보편적, 빠른 인코딩, 충분히 작음 |
| 사진 (신규) | AVIF | WebP보다 30% 더 작음, 로열티 프리 |
| 사진 (폴이백) | WebP | JPEG보다 25% 더 작음, 보편적 지원 |
| 무손실 그래픽 | WebP 또는 PNG | WebP가 더 작음; PNG는 안전한 폴이백 |
| 투명도 | WebP 또는 PNG | WebP 파일이 더 작음; PNG는 안전한 폴이백 |
| 애니메이션 | WebP 또는 AVIF | 둘 다 GIF보다 60-80% 우수; AVIF는 더 새로움 |
| 넓은 색역 / HDR | AVIF 또는 JPEG XL | 10비트 이상 심도, ICC/ICC v4 지원 |
| 인쇄 워크플로우 | TIFF 또는 JPEG XL | CMYK, 16비트, 무손실 JPEG 재압축 |
WebP의 진짜 유산은 주요 브라우저 벤더가 충분히 밀어붙이면 웹이 새로운 이미지 형식을 채택할 수 있다는 것을 입증한 것이다. AVIF를 위한 길을 닦았다. Apple에게 네이티브로 JPEG/PNG가 아닌 형식을 지원하도록 압박했다. 투명도와 애니메이션이 단일 컨테이너에 속한다는 것을 보여주었다.
하지만 기술적 우월성만으로는 충분하지 않다는 것도 입증했다. 보편성, 관성, 생태계 정렬이 압축률보다 더 중요하다. JPEG은 더 나은 것이 아니라 이미 모든 곳에 있기 때문에 WebP보다 오래 살 것이다.
핵심 요약
WebP는 대역폭 문제를 해결하기 위해 비디오 코덱에서 만들어졌다. Google, Facebook, 이미지 파이프라인을 변환하려는 모든 사이트를 위해 그 문제를 실제로 해결했다. 압축은 실재하고, 기능은 유용하며, 브라우저 지원은 완전하다.
하지만 웹은 백지에서 새 형식이 25% 더 작다고 해서 형식을 바꾸지 않는다. 새 형식이 기존 형식보다 사용하기 쉬워질 때, 또는 기존 형식이 너무 심하게 실패해 이전이 불가피해질 때 바뀐다. JPEG은 충분히 심하게 실패하지 않았다. WebP는 충분히 쉽지 않았다. 그리고 WebP가 쉬워졌을 때쯤, AVIF는 이미 스펙 시트에 더 큰 숫자를 달고 도착했다.
WebP는 이미지 형식의 Betamax다 — 기술적으로 탄탄하고, 지원이 잘 되며, 결국 조금 더 늦게 등장한 더 나은 마케팅과 더 넓은 지지를 가진 무언가에게 추월당했다. 사라지지는 않을 것이다. JPEG, PNG, AVIF, 그리고 앞으로 나올 무엇과 공존할 것이며, 오늘날 PNG가 수행하는 동일한 역할을 할 것이다. 어디서나 작동하는 안전하고 유능한 폴이백.
웹에서 더 작은 발자국이 필요한 PNG 파일이 있다면, PNG to WebP가 브라우저에서 로컬로 변환한다 — 업로드 없이, 서버 처리 없이. 투명도나 애니메이션이 필요한 JPEG이 있다면, JPG to WebP가 품질 제어와 함께 변환을 처리한다. 그리고 보편적인 폴이백이 필요할 때는, WebP to PNG와 WebP to JPG가 WebP 파일을 모든 뷰어에서 열리는 형식으로 되돌린다.



