Streams API 부록 2. 왜 이미지는 위에서 아래로 나타날까

작성일:2026.03.19|수정일:2026.07.07|조회수:13

Streams API 부록 2. 왜 이미지는 위에서 아래로 나타날까

느린 네트워크에서 큰 이미지를 열면 가끔 화면이 위에서 아래로 채워진다. 마치 누군가 아주 성실하게 롤러로 이미지를 칠하는 것처럼 보인다. 물론 브라우저 안에 그런 직원은 없다. 있다면 우리보다 야근을 더 많이 하고 있을 것이다.

이 현상은 단순한 시각 효과가 아니라 네트워크 전송, 브라우저 버퍼링, 이미지 디코딩이 맞물린 결과다. 이미지는 완성된 파일이 모두 도착한 뒤에만 그려지는 것이 아니라, 형식과 인코딩 방식에 따라 도착한 데이터부터 점진적으로 해석될 수 있다. 이 부록에서는 이미지 로딩을 stream 관점에서 바라보며, 왜 어떤 이미지는 조금씩 보이고 어떤 이미지는 한 번에 나타나는지 정리한다.

데이터는 처음부터 완성된 파일이 아니다

HTTP로 이미지를 다운로드할 때 브라우저는 완성된 파일을 한 번에 받지 않는다. 네트워크는 데이터를 여러 조각으로 나누어 전달하고, 브라우저는 이 데이터를 스트림 형태로 받는다. fetch()Response.bodyReadableStream<Uint8Array> 타입인 것도 바로 이 때문이다. 즉 이미지 다운로드도 내부적으로는 다음과 같은 흐름을 가진다.

TXT
network
↓
byte stream
↓
browser buffer

여기까지는 일반적인 HTTP 응답과 크게 다르지 않다. 텍스트든 JSON이든 이미지든, 네트워크를 타고 들어오는 순간에는 일단 바이트 조각의 흐름이다. 하지만 이미지의 경우 이 다음 단계가 조금 다르다. 브라우저는 단순히 바이트를 다 모아 두었다가 “끝났으니 이제 그리자”라고만 하지 않는다. 가능한 경우에는 도착한 데이터부터 이미지로 해석하려고 한다.

브라우저는 이미지 데이터를 점진적으로 해석할 수 있다

이미지 파일은 단순한 바이트 덩어리가 아니다. 내부에는 픽셀 데이터를 어떻게 해석해야 하는지에 대한 구조가 존재한다. 브라우저의 이미지 디코더는 이 구조를 이용해, 파일의 일부만 도착한 상태에서도 이미지를 해석할 수 있는 경우가 있다.

많은 이미지 포맷은 **스캔라인(scanline)**이라는 구조를 가진다. 이미지는 내부적으로 다음과 같은 방식으로 저장되는 경우가 많다.

TXT
row 1
row 2
row 3
row 4
...

즉 이미지 데이터가 위에서 아래로 이어지는 행(row) 단위로 저장된다. 브라우저는 네트워크에서 데이터가 도착하는 즉시 이 행 단위 데이터를 해석할 수 있고, 해석된 부분을 화면에 그릴 수 있다. 그래서 다운로드가 진행되는 동안 화면에서는 다음과 같은 모습이 나타난다.

TXT
████████
████████
████████
--------
--------
--------

이미지의 윗부분은 이미 렌더링되고, 아랫부분은 아직 도착하지 않은 상태다. 데이터가 더 도착하면 브라우저는 다음 행을 해석하고 다시 화면을 업데이트한다. 우리가 보기에는 이미지가 위에서 아래로 “그려지는” 것처럼 보이지만, 실제로는 도착한 바이트를 디코더가 해석할 수 있는 단위까지 읽고, 그 결과를 화면에 반영하는 과정에 가깝다.

이미지가 항상 이렇게 나타나는 것은 아니다

하지만 모든 이미지가 항상 위에서 아래로 나타나는 것은 아니다. 어떤 이미지는 다운로드가 끝날 때까지 아무것도 보이지 않다가 갑자기 한 번에 나타나기도 한다. 이 차이는 이미지 포맷과 브라우저 디코딩 방식에 따라 달라진다. JPEG에는 대표적으로 두 가지 저장 방식이 있다.

baseline JPEG에서는 이미지가 비교적 단순한 순서로 디코딩된다. 일부 데이터가 도착하면 부분적으로 이미지를 그릴 수 있지만, 브라우저가 충분한 데이터가 쌓일 때까지 렌더링을 미루는 경우도 있다. 반면 progressive JPEG에서는 이미지가 여러 단계의 패스로 나누어 저장된다. 처음에는 전체 이미지의 대략적인 형태가 나타나고, 이후 데이터가 더 도착하면서 점점 더 선명해지는 방식이다. 그래서 progressive JPEG에서는 이미지가 흐릿하게 먼저 나타났다가 점점 선명해지는 장면을 볼 수도 있다.

이 차이 때문에 “이미지는 원래 위에서 아래로 그려진다”라고 말하면 조금 부정확하다. 더 정확히는, 브라우저가 받은 바이트를 이미지 포맷의 구조에 맞춰 점진적으로 디코딩할 수 있고, 그 결과를 중간중간 화면에 반영할 수 있다는 쪽에 가깝다. 위에서 아래로 채워지는 모습은 그중 하나의 흔한 결과다.

Streams API 관점에서 보면

Streams API 관점에서 보면 이 현상은 꽤 자연스럽다. 브라우저는 네트워크에서 바이트 스트림을 받고, 이 데이터를 내부 버퍼에 쌓는다. 이미지 디코더는 이 버퍼를 읽어 해석할 수 있는 부분이 있는지 확인하고, 해석 가능한 데이터가 생기면 화면에 반영한다. 이 구조를 단순화하면 다음과 같다.

TXT
network stream
↓
browser buffer
↓
image decoder
↓
partial rendering

즉 이미지 렌더링은 완성된 파일을 조용히 기다리기만 하는 과정이 아니라, 스트림 위에서 점진적으로 이루어지는 해석 과정이다. 물론 모든 포맷과 모든 브라우저가 같은 방식으로 중간 결과를 보여 주는 것은 아니다. 하지만 큰 흐름은 같다. 네트워크는 바이트를 조금씩 보내고, 브라우저는 그 바이트를 버퍼에 쌓고, 디코더는 해석 가능한 만큼을 화면으로 끌어올린다.

결국 위에서 아래로 채워지는 이미지는 Streams API를 설명할 때 좋은 예가 된다. 스트림은 추상적인 API 이름처럼 보이지만, 실제로는 우리가 오래전부터 봐 온 화면의 움직임 속에 이미 들어 있다. 파일이 한 덩어리로 “도착”하는 것이 아니라, 도착하는 중에도 해석되고, 때로는 그 중간 상태가 화면에 드러나는 것이다.

댓글

댓글을 불러오는 중...