Deallo

렌더링 · 성능

이미지 뷰어 — 좌우로 넘길 때 2~5초 빈 화면 잡기

채팅 이미지 뷰어에서 좌우 화살표로 넘기면 2~5초간 빈 화면이 떴다. React가 아니라 브라우저의 Fetch·Decode·Paint를 이해하고서야 잡힌 문제다.

왜 느린가 — 브라우저의 3단계

이미지를 화면에 띄우려면 브라우저는 3단계를 거친다.

1. Fetch (네트워크) — CDN에서 바이트 다운로드
2. Decode (CPU)     — 압축 이미지(JPEG/PNG)를 픽셀로 변환
3. Paint (GPU)      — 픽셀을 화면에 그림

채팅 목록은 324px 썸네일이라 빠르다. 뷰어는 원본 해상도(수천 px)라 Decode 시간이 폭증한다. <img src> 변경은 React의 영역이 아니라 브라우저 네이티브 동작이라, React가 빨라도 소용없다.

시도와 함정

V2 — 스피너 + opacity: 두 가지 함정에 빠졌다.

  • key={url} 함정: key가 바뀌면 React가 DOM을 파괴하고 새로 생성 → 캐시에 있어도 새 <img> 라 다시 decode. → key 제거(같은 요소 재사용, src만 변경)
  • transition-opacity 함정: 캐시된 이미지에도 opacity 전환 150ms가 붙어 느려 보임 → transition 제거

V3 — img.complete 캐시 체크: new Image().completeHTTP 캐시만 알려준다. 디코딩 여부는 모른다 → 썸네일로 본 이미지라도 원본은 다시 decode해야 한다.

해결 — 인접 이미지를 미리 decode

다음/이전 이미지를 미리 img.decode() 디코딩까지 끝내 두고, 현재 이미지는 같은 <img> 요소를 재사용해 교체했다. 넘기는 순간엔 이미 디코딩된 픽셀이라 빈 화면이 사라졌다.

배운 점

  • key prop은 리스트에서만. 단일 요소의 key를 바꾸면 불필요한 DOM 재생성 + 재디코딩이 생긴다
  • 캐시(complete)와 디코딩 완료(decode())는 다르다. "느린 이미지"는 네트워크가 아니라 Decode가 범인인 경우가 많다 — 프레임워크가 아니라 브라우저 파이프라인을 봐야 잡힌다