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

작성일:2026.03.19|조회수:12

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

웹 페이지에서 이미지를 로드할 때 흥미로운 장면을 종종 볼 수 있다. 이미지가 한 번에 완전히 나타나는 것이 아니라, 위에서 아래로 조금씩 채워지면서 나타나는 경우가 있기 때문이다. 특히 네트워크가 느리거나 이미지가 큰 경우 이런 현상이 더 분명하게 보인다. 마치 이미지가 위쪽부터 스캔되듯이 점진적으로 채워지는 것처럼 보인다.

이 현상은 단순한 시각 효과가 아니라, 네트워크 전송 방식과 브라우저의 이미지 디코딩 방식이 결합된 결과다. 즉 이미지는 완성된 파일이 모두 도착한 뒤에 한 번에 그려지는 것이 아니라, 데이터가 도착하는 흐름에 맞춰 점진적으로 해석되고 렌더링될 수 있다.

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

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

TXT
network
↓
byte stream
↓
browser buffer

여기까지는 일반적인 HTTP 응답과 크게 다르지 않다. 하지만 이미지의 경우 이 다음 단계가 조금 다르다.

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

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

많은 이미지 포맷은 스캔라인(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

즉 이미지 렌더링은 완성된 파일을 기다리는 과정이 아니라, 스트림 위에서 점진적으로 이루어지는 해석 과정이다.

더 읽어보기

  • 2026.03.13

    Streams API 2. 상태와 백프레셔의 의미론

    스트림을 처음 배울 때는 읽고 쓰는 예제가 꽤 단순해 보인다. getReader()로 읽고, getWriter()로 쓰고, pipeTo()로 연결하면 끝나는 것처럼 느껴진다. 실제로 짧은 데모는 이 정도만 알아도 돌아간다. 그러나 실무에서 스트림 코드를 망가뜨리는 원인은 거의 언제나 더…

  • 2026.03.13

    Streams API 1. 읽기와 쓰기의 출발점

    자바스크립트에서 비동기를 배울 때 우리는 대개 Promise와 async, await부터 익힌다. 이 조합은 한 번의 결과를 기다리는 문제에는 매우 강력하다. 그러나 데이터가 한 번에 끝나지 않고 계속 흘러들어오는 상황에서는 이야기가 달라진다. 네트워크 응답이 길게 이어지거나, 큰 파일…

  • 2026.02.22

    View Transition API

    웹 애플리케이션에서 전환 품질은 기능 완성도와 동등한 수준으로 중요하다. 사용자가 목록에서 항목을 선택해 상세 화면으로 이동할 때, 화면이 자연스럽게 이어지면 서비스는 빠르고 안정적으로 느껴진다. 반대로 동일한 기능이라도 전환이 끊기면 체감 성능과 신뢰도는 동시에 하락한다. 전환은 부가…

  • 2026.04.11

    Trie 자료구조

    문자열 데이터를 다룰 때 단순히 “이 단어가 있나?”만으로는 부족한 순간이 있다. 자동 완성처럼 특정 접두사로 시작하는 후보를 모아야 할 때도 있고, 어떤 키에 값을 두고 빠르게 찾고 싶을 때도 있다. 이럴 때 가장 자연스럽게 떠올릴 수 있는 자료구조가 바로 Trie이다.Trie는 무엇…

  • 2026.03.13

    Streams API 3. 바이트 스트림과 실전 파이프라인

    앞선 두 글에서 우리는 Web Streams API의 표면과 의미론을 정리했다. 이제 남은 질문은 하나이다. 이 지식을 실제 어디에 써먹을 것인가. 이 질문에 답하는 순간 스트림 학습은 비로소 완성된다. 표준을 읽고 메서드를 아는 것만으로는 충분하지 않다. fetch() 응답 본문을 어…

댓글

댓글을 불러오는 중...