원문: https://jakearchibald.com/2025/present-and-future-of-progressive-image-rendering/

프로그레시브 이미지 포맷을 쓰면 이미지 리소스의 일부만 받은 상태에서도 디코더가 부분 렌더링을 만들어낼 수 있습니다. 이미지의 일부만 표시되기도 하고, 저품질·저해상도 버전으로 표시되기도 합니다. 최근 이 주제를 깊이 파고들다가 흔한 오해 몇 가지를 발견했고, AVIF에서 좀 더 실용적인 해결책이 나오면 좋겠다는 생각을 하게 됐습니다.
지금 브라우저가 기본으로 지원하는 프로그레시브 렌더링이 어떤 것들인지부터 살펴보겠습니다.
브라우저의 프로그레시브 이미지 렌더링 현황
비교에 쓸 이미지를 소개합니다. 이 글을 다 읽을 때쯤이면 이 이미지가 눈에 아른거릴 겁니다.

해상도가 높고(1598x1753) 디테일이 선명한 탓에 AVIF로 압축해도 155 kB나 됩니다.
렌더링이 시작되는 시점과 숲 속 여우임을 알아볼 수 있는 시점을 비교하려고, 파일 크기가 비슷한 다른 포맷 버전들을 만들었습니다.
시작해 봅시다!
JPEG
믿음직스러운 구형 JPEG는 프로그레시브 방식으로 인코딩할 수 있고, Squoosh에서는 이게 기본값입니다.
이미지의 프로그레시브 렌더링을 테스트하는 작은 웹 앱을 만들었습니다. 여우 이미지의 JPEG 버전을 바로 로드한 상태로도 확인할 수 있습니다.
약 1.2 kB부터 위에서 아래로 저해상도 렌더링이 시작되고, 약 33 kB(파일 크기의 21%)에서 첫 번째 패스가 끝납니다. 흥미롭게도 Firefox와 크로미엄 기반 브라우저에서는 Safari와 다르게 보입니다.

두 경우 모두 이미지가 무엇인지는 알아볼 수 있지만, Safari는 블록 형태로 표시되는 반면 Firefox와 크로미엄은 매끄럽게 표시됩니다. 저는 Firefox/크로미엄의 렌더링 방식이 훨씬 좋습니다.
이후로는 여러 패스를 거치면서 해상도가 점점 좋아집니다.
JPEG에서 프로그레시브 렌더링은 품질·파일 크기로 보면 사실상 공짜지만, 디코딩 시간은 그렇지 않습니다. 브라우저의 이미지 디코딩을 벤치마크하는 또 다른 작은 웹 앱도 만들었습니다. 프로그레시브 JPEG와 비프로그레시브 JPEG를 비교하면 프로그레시브 JPEG가 디코딩에 약 40% 더 걸리지만, M4 Pro 기준으로는 약 1.3ms에 불과해서 충분히 합리적인 트레이드오프라고 봅니다.
PNG
PNG는 보통 위에서 아래로 렌더링됩니다. 인터레이스드(interlaced) 모드가 있긴 하지만 파일 크기가 크게 늘어나서 권하지 않습니다.
그래도 완성도를 위해 데모를 붙여둡니다. 파일 크기를 봐줄 만한 수준으로 낮추려고 이미지를... 음... 약간 손봐야 했습니다.
WebP
Firefox와 Chrome에서 WebP는 위에서 아래로 렌더링됩니다. 그리고 약 43 kB(28%)는 받아야 렌더링이 시작됩니다. 파일 앞부분에 색상 데이터가 있어서, 최종 루마(luma) 채널이 들어오기 시작할 때부터 프로그레시브 렌더링이 되는 것 같습니다.
아쉽게도 Safari는 WebP의 프로그레시브 렌더링을 아예 하지 않습니다. 파일을 전부 받을 때까지 아무것도 그리지 않습니다.
데모를 확인해보세요.
AVIF
일반 AVIF는 프로그레시브 렌더링을 아예 지원하지 않습니다. 아무것도 안 보이다가 이미지 전체가 한 번에 나타납니다(데모).
그런데 AVIF에는 잘 알려지지 않은 프로그레시브 렌더링 기능이 있습니다! --progressive 플래그로는 좋은 결과를 못 얻었지만, 하위 레벨의 --layered 플래그로는 흥미로운 결과가 나왔습니다. 이 기능은 실험적이라, 쓰려면 libavif를 직접 컴파일해야 했습니다. 제가 쓴 명령어는 다음과 같습니다.
avifenc -s 0 -y 420 --layered \
--scaling-mode:u 1/8 -q:u 0 fox.png \
--scaling-mode:u 1 -q:u 16 fox.png \
fox-progressive.avif
이렇게 하면 레이어가 두 개 생깁니다. 하나는 1/8 해상도에 '최소' 품질, 다른 하나는 전체 해상도에 좋은 품질입니다. 이 레이어들은 동영상의 프레임과 비슷하게 동작해서, 두 번째 레이어는 공간을 아끼려고 첫 번째 레이어의 데이터를 가져다 씁니다(동영상 용어로는 P-프레임에 해당합니다).
데모를 확인해보세요. 크로미엄 브라우저에서만 프로그레시브 렌더링이 됩니다. 렌더링은 두 가지로 나옵니다. 모든 데이터를 받으면 전체 이미지가 표시되고, 그보다 이른 약 5.8 kB(데이터의 4%) 시점에서는 초기 렌더링이 표시됩니다.
원문에는 5.8 kB / 전체 이미지(151 kB)를 직접 비교하는 인터랙티브 탭이 있습니다.
보기 좋지는 않지만 무엇인지는 알아볼 수 있고, 약 5.8 kB치고는 꽤 괜찮습니다.
단점은 --layered 모드에서 품질 옵션이 이상하게 동작한다는 겁니다. 일반 AVIF 인코딩과 같은 수치를 나타내지 않습니다.
파일 크기에도 오버헤드가 있는 것 같은데, 측정하기가 어렵습니다. MS-SSIM으로 테스트해보니 5~6 kB 정도, 그러니까 초기 패스의 크기만큼인 것 같습니다.
제가 쓴 레이어 구성은 하나의 예시일 뿐인데도 꽤 제약이 많습니다. 특정 스케일링 값만 허용하고 1/8이 최솟값입니다. 레이어를 더 넣어서 프로그레시브 렌더링 단계를 늘릴 수 있다고는 하지만, 막상 해보면 인코더 오류가 나거나 최저 품질에서도 파일 크기가 ~400 kB로 폭증했습니다. 그래서 '실험적'이라고 표시해둔 거겠죠.
디코딩 시간으로 프로그레시브 AVIF와 일반 AVIF를 비교하면, Firefox와 Safari에서는 프로그레시브 버전이 약 1~2% 더 걸립니다. Chrome에서는 약 40% 더 걸리는데, M4 Pro 기준 3.5ms입니다. Chrome은 첫 번째 레이어를 건너뛰어도 되는 경우를 위한 빠른 처리 경로를 아직 안 갖춘 것 같습니다.
Firefox에서 AVIF 프로그레시브 렌더링을 추적하는 이슈도 참고하세요.
JPEG XL
웹 개발자들에게 JPEG XL을 원하는 이유를 물어보면(블루스카이와 마스토돈에서 물어봤습니다), 거의 다 프로그레시브 렌더링을 꼽습니다. 그래서 JPEG XL을 지원하는 유일한 브라우저인 Safari가 어떤 프로그레시브 렌더링도 하지 않는다는 사실이 놀라웠습니다. 데모와 버그 리포트를 확인해보세요.
Safari에서 JPEG XL이 150%(즉 2.5배) 더 오래 걸려 디코딩된다는 점도 놀라웠습니다. 같은 AVIF와 비교하면 M4 Pro 기준으로 17ms나 더 걸리는 겁니다. Apple 하드웨어가 고성능이긴 해도 이 차이는 여전히 유의미합니다. 프로그레시브 렌더링과는 상관없이, 그냥 디코더가 느린 것입니다. Apple의 구현이 단일 코어에서 돌고 있다는 의견도 있어서, 개선의 여지가 있을지도 모릅니다.
Safari의 JPEG XL 지원은 사실 브라우저가 아니라 기반 OS에서 옵니다. Apple이 iPhone 사진 저장 형식으로 HEIC 대신 JPEG XL 도입을 검토 중이고, 브라우저의 JPEG XL 지원은 그 덕에 따라온 게 아닐까 싶습니다. 순전히 제 추측이지만요.
크로미엄에서 플래그로 켰던 구현은 프로그레시브 렌더링을 어느 정도 실제로 지원했지만, ~60 kB(파일의 39%)가 될 때까지 아무것도 그리지 않았습니다. 렌더링 결과는 앞서 말한 초기 JPEG 렌더링과 비슷한데, 이미지 데이터는 훨씬 더 많이 필요합니다. 포맷 자체의 한계가 아니라 디코더의 한계입니다. JPEG XL이 어디까지 해낼 수 있는지는 잠시 후에 자세히 살펴보겠습니다.
크로미엄에서 플래그로 켰던 JPEG XL 디코더 성능도 테스트해봤는데, AVIF보다 500% 이상(6배) 느렸습니다. Firefox에서 플래그로 켰던 JPEG XL 디코더는 Safari 디코더와 속도가 비슷했습니다. 아직 출시되지도 않은 실험적인 것의 성능을 따지는 게 공정하진 않지만, 이 중 하나라도 Safari 구현이 이상값임을 보여주길 기대했습니다.
AVIF 대비 JPEG XL의 장점 중 하나가 "빠른 디코딩"이라고 생각했는데, 이제는 확신이 안 섭니다.
Firefox에서 Rust로 JPEG XL을 구현하는 작업이 진행 중이지만, 실제로 쓰려면 성능이 훨씬 더 좋아져야 합니다.
프로그레시브 렌더링이 정말 도움이 될까?

최신 이미지 포맷(여기서는 AVIF) 덕분에 위 이미지는 크기가 56.4 kB입니다. HD를 넘는 이미지치고는 놀라운 수치입니다. 여우 이미지는 디테일이 선명해서 훨씬 크지만, 대부분의 이미지는 그렇지 않습니다. 그러니 많은 이미지에서는 프로그레시브 렌더링의 이점이 크지 않습니다.
하지만 155 kB짜리 여우 이미지를 두고 Kornel이 좋은 지적을 했습니다.
혼잡과 버퍼블로트(bufferbloat)로 인해 데이터는 서서히 도착하는 것이 아니라 끊기는 방식으로 들어옵니다. 신호가 매우 약할 때도 끊기는 경향이 있습니다.
이미지를 받는 데 5초가 걸리더라도, 그 5초 동안 155 kB가 서서히 들어올 가능성은 낮습니다. 지금 저는 비행기 WiFi를 쓰고 있는데, 맞아요... 느린 시간의 대부분은 아무것도 안 들어오는 시간입니다.
그러니 프로그레시브 렌더링을 실제로 보려면 적당히 좋고 나쁜 운이 맞아떨어져야 합니다.
- 나쁜 운: 연결 상태가 안 좋아서 이미지 전체를 가져오는 데 시간이 걸림.
- 좋은 운: 이미지의 일부가 들어옴.
이 (달갑지 않은) 조건이 맞으면 프로그레시브 렌더링은 좋은 경험을 주지만, 느린 연결에서도 못 볼 수 있습니다. 이미지 크기에 신경 쓰는 것을 대신하기는 어렵습니다.
그래서 저는, 프로그레시브 렌더링을 지원하도록 이미지를 인코딩할 때는 파일 크기 오버헤드가 최소여야 하고, 프로그레시브 렌더링이 필요 없는 경우에는 디코딩 오버헤드도 최소이거나 없어야 한다고 봅니다.
설득력 있는 '이야기'가 아니라는 건 압니다. 이 글을 쓰기 시작할 때만 해도 프로그레시브 렌더링에 대한 강력한 논거를 펴려고 했습니다. 그런데 깊이 파고들수록 확신이 흔들렸습니다.
반응형 이미지 대신 프로그레시브 렌더링은 어떨까?
JPEG XL에 관심을 두는 이유를 물었을 때, 일부는 최대 해상도의 단일 JPEG XL 파일로 반응형 이미지를 대체할 수 있겠다고 했습니다. 아이디어는 이렇습니다.
- 브라우저가 이미지 데이터 일부를 받습니다.
- 브라우저가 이미지 데이터를 부분 디코딩합니다.
- 브라우저가 픽셀 밀도가 더는 필요 없다는 걸 인식합니다.
- 브라우저가 서버에 "이 리소스 전송을 중단해"라고 지시합니다.
- 서버가 충실히 리소스 전송을 중단합니다.
나쁜 소식을 전하게 돼 유감이지만, 이 방법은 작동하지 않을 것이 거의 확실합니다.
각 단계 사이에 지연이 상당해서, 응답이 제대로 취소되기 전에 이미지마다 필요 없는 데이터를 메가바이트 단위로 받을 가능성이 높다는 게 문제입니다.
범위를 미리 알 수 있는 새 브라우저 기능이 함께라면 가능할 수도 있습니다. 그러면 브라우저가 필요한 부분만 요청할 수 있습니다.
<!-- 가상의 예시 -->
<img
srcset="
img.jxl#range=0-10000 200w,
img.jxl#range=0-30000 400w,
img.jxl#range=0-70000 800w,
img.jxl#range=0-150000 1600w
"
sizes="…as usual…"
/>
그런데 이 #range= 기능이 도입되더라도(CORS 관련 큰 문제들이 있어서 무산될 수도 있지만), 복잡성은 지금의 반응형 이미지와 비슷하고, 반응형 이미지는 쓸 수 있는 해상도를 더 잘 제어하게 해줍니다.
'range' 방식의 장점은, 나중에 더 높은 해상도가 필요하다고 판단될 때(예를 들어 섬네일에서 전체 화면으로 전환) 이미지 다운로드를 '재개'할 수 있다는 겁니다. 하지만 그래도 지금의 반응형 이미지가 더 다양하게 쓸 수 있는 것 같습니다.
JPEG XL의 잠재적 프로그레시브 렌더링
브라우저 디코더 말고도 JPEG XL의 프로그레시브 렌더링에는 유망한 작업이 있습니다. 다만 이 중 얼마나 브라우저에 적용될지는 불분명하고, 인코더 설정도 좀 복잡합니다.
현재 버전의 cjxl(0.11.1)에는 --progressive 플래그가 있지만, 이렇게 만든 파일은 데이터가 상당히 들어올 때까지 렌더링되지 않습니다(여우 이미지는 파일의 39%).
더 일찍 렌더링하려면 --progressive_dc 1을 써야 하는데, 이 옵션은 cjxl --help에는 안 나옵니다. cjxl --help -v -v처럼 verbose 플래그를 두 번 줘야만 보입니다. 코드베이스에서는 --progressive가 이제 progressive_dc 1을 포함하도록 바뀐 것 같은데, 아직 릴리스에는 안 들어갔습니다.
progressive_dc가 파일 크기에 영향을 주는 것 같지는 않습니다. 있다 해도 미미한 수준입니다. 디코딩 시간에서도 차이를 못 찾았지만(Safari에서 테스트), JPEG XL 디코딩 자체가 느리다 보니 큰 이점이 되지는 않습니다.
djxl은 --progressive_dc가 주는 조기 렌더링을 지원하지 않지만, Rust 기반 디코더인 jxl-oxide는 조기 렌더링을 처리할 수 있습니다.
jxl-oxide는 브라우저에 쓰기에는 빠르지 않지만, 가능성을 엿볼 수 있는 흥미로운 사례입니다. 부분 렌더링을 직접 확인할 수 있는 깔끔한 웹어셈블리(wasm) 버전도 있습니다. 직접 해보고 싶다면 여우 이미지의 프로그레시브 버전을 써보세요. 그렇지 않다면 대략적인 가이드를 소개합니다.
원문에는 2 kB / 3 kB / 4.25 kB / 6 kB / 10 kB / 17 kB / 27 kB / 46 kB / 60 kB / 68 kB 단계별 렌더링을 직접 비교하는 인터랙티브 탭이 있습니다.
6 kB 시점이면 이미지가 어떤 건지 어느 정도 감이 오고, 46 kB쯤이면 아주 선명해집니다. Firefox/Chrome에서 봤던 JPEG 프로그레시브 렌더링처럼 스무딩이 적용되면 더 좋을 것 같습니다.
60 kB 이후에는 전체 디테일이 사각형 블록 형태로 나타납니다. JPEG XL의 좋은 점은 이 블록들이 어떤 순서로든 나타날 수 있다는 겁니다. 68 kB 렌더링에서는 다른 데보다 여우 얼굴 주변이 먼저 선명해집니다. cjxl은 "중심점"으로 쓸 지점을 지정할 수 있고, 더 똑똑한 인코더라면 얼굴 같은 걸 감지해서 우선 처리할 수도 있습니다.
주관적이지만, 앞서 나온 5.8 kB AVIF 프로그레시브 렌더링은 디테일 면에서 17~27 kB의 JPEG XL 렌더링 사이 어딘가라고 봅니다. 직접 판단해보세요.
원문에는 JXL 6 kB / JXL 10 kB / JXL 17 kB / AVIF 5.8 kB / JXL 27 kB / JXL 46 kB를 직접 비교하는 인터랙티브 탭이 있습니다.
브라우저가 6 kB를 받다가 멈춘다면 AVIF가 더 나은 결과를 낼 수 있습니다. 하지만 AVIF 렌더링은 2패스뿐이라, 예를 들어 브라우저가 50 kB를 갖고 있다면 JPEG XL 렌더링이 훨씬 낫습니다.
브라우저에 쓸 만큼 빨라지기 전까지는 다 '이론'일 뿐입니다.
Firefox를 위해 개발 중인 또 다른 JPEG XL Rust 디코더 jxl-rs도 있습니다. 이 팀도 프로그레시브 렌더링이 어떻게 동작할 수 있는지 탐색하고 있습니다.
AVIF의 더 나은 '프로그레시브' 기능은?
생각보다 글이 길어졌는데, 마지막으로 한 가지만 더 이야기하겠습니다...
제 경험상 AVIF는 제가 생각하는 '웹 품질' 기준에서 JPEG XL보다 파일 크기가 작습니다(특히 고밀도 화면에 최적화할 때). 게다가 현재 구현 기준으로 AVIF는 JPEG XL보다 훨씬 빠르게 디코딩됩니다. 파일이 작고 디코딩이 빠른 건 큰 장점이지만, AVIF에 더 나은 '프로그레시브' 기능이 있으면 좋겠습니다. 초기 패스의 품질과 스케일을 더 세밀하게 제어하고, 이상적으로는 압축 아티팩트(artifact)를 숨기는 설정 가능한 후처리 블러 효과까지 지원한다면 좋을 것 같습니다.
'레이어' 방식을 개선할 수도 있을 것 같습니다. 아니면 과한 복잡성이 될 수도 있고요. 예를 들면 이렇습니다.
원문에는 블러 적용 / 블러 미적용 / 최종 이미지를 비교하는 인터랙티브 탭이 있습니다.
위 이미지는 1.97 kB입니다. 축소하고, 최소 품질로 AVIF 압축한 다음, 후처리 블러를 입혔습니다.
블러를 적용하면 미리보기로 훌륭하게 동작합니다. 그렇다면... 이게 전체 이미지 파일의 앞부분에 그냥 들어가면 안 될까요?
이렇게 하면 '미리보기'가 파일 크기로 보면 100% 오버헤드가 됩니다. 하지만 웹 개발자가 허용 가능한 오버헤드를 정해서 이 '미리보기'의 품질을 자유롭게 설정할 수 있다는 뜻이기도 합니다. 1.97 kB만 쓰는 건 너무 보수적인 것 같은데, 예를 들어 10 kB를 쓰면 어떨까요?
원문에는 블러 적용 / 블러 미적용 / 최종 이미지(10 kB 버전)를 비교하는 인터랙티브 탭이 있습니다.
또 완전히 별개의 이미지이다 보니, 브라우저가 미리보기를 보여줄 이점이 없다고 판단하면 디코딩 오버헤드는 0입니다. 즉 브라우저가 파일을 전부 갖고 있으면 미리보기를 그냥 건너뛰면 됩니다.
후처리 블러는 CSS 같은 데 맡길 게 아니라 포맷의 일부가 되어야 합니다. 그래야 포맷이 저렴한 블러 필터를 써서 높은 렌더링 비용을 피할 수 있습니다.
이 아이디어는 2020년에 '레이어' 방식이 탐색되기 전에 처음 제안했습니다. 하지만 이제 다시 들여다볼 때가 된 것 같습니다. 기대해봅니다!
'Web Frontend Developer' 카테고리의 다른 글
| [번역] Fetch 스트림은 훌륭하지만, 업로드/다운로드 진행 상황 측정에는 적합하지 않습니다 (0) | 2026.08.09 |
|---|---|
| [번역] 컴포넌트가 WCAG를 준수할 수 있을까요? (0) | 2026.06.05 |
| [번역] 타입스크립트 성능 문제 해결하기 — 사례 연구 (0) | 2026.06.05 |
| [번역] 리액트는 기본값으로 승리했습니다 – 그리고 프런트엔드 혁신을 죽이고 있습니다 (0) | 2026.05.08 |
| [번역] 자바스크립트 Date 계산은 얼마나 잘못될 수 있을까요? (0) | 2026.05.08 |