본문으로 건너뛰기

03 이미지 최적화

2013년 한 해 동안 웹 페이지의 콘텐츠 요청 횟수는 그리 늘지 않았는데 이에비해 이미지가 웹 페이지에서 차지하는 비중은 30% 이상 늘어났다. 일반적인 사이트에서는 이미지의 파일 크기와 개수가 차지하는 비중이 크기 때문에 이미지 최적화는 사이트 내의 페이지 로딩 시간을 개선할 때 가장 쉬우면서도 큰 효과를 낼 수 있는 방법이다.

다음 작업을 통해 이미지를 상당 부분 개선할 수 있다.

  • 각 이미지 파일의 크기와 품질 사이의 균형점 찾기
  • 사이트의 이미지 요청 횟수를 줄일 방법 찾기
  • 사이트의 이미지 생성 워크플로를 최적화하여 성능 개선하기

이미지 포맷 고르기

웹에서 사용하는 가장 일반적인 이미지 파일 포맷은 JPEG, GIF, PNG다.

이미지 포맷모범 사용 예최적화 방법
JPEG사진, 많은 색이 사용된 이미지해상도 줄이기, 프로그레시브 JPEG로 내보내기, 이미지 노이즈 줄이기
GIF애니메이션디더링 줄이기, 색상 수 줄이기, 수평 패턴 사용, 수직 노이드 줄이기
PNG-8적은 색이 사용된 이미지디더링 줄이기, 색상 수 줄이기, 수평 및 수직 패턴 사용
PNG-24일부 투명한 이미지노이즈 줄이기, 색상 수 줄이기

JPEG

JPEG 파일을 아주 많은 색상이 사용된 이미지 / 사진에 이상적인 포맷이다. JPEG 파일은 높은 화질의 이미지를 사람의 눈으로 보기에는 차이가 없을 정도까지만 이미지를 손실시킨 후 압축하도록 설계되었다.

다만 JPEG가 손실 압축 방식이므로 낮은 품질의 이미지를 저장하는 데 JPEG 파일을 사용하면 사용자는 이미지가 인위적으로 변경되었다거나 색이 뭉개지고 선명하지 못하다는 느낌을 받을 수 있다.

JPEG 파일은 정보가 많은 이미지라도 다른 포맷보다 상대적으로 크기를 작게 만들 수 있어 웹의 이미지 대부분은 JPEG 파일을 사용한다. 이런 특징은 우리가 웹 페이지 로딩 속도를 개선하고자 하는 목표에 적합하다.

이미지의 색상이 뚜렷할수록 JPEG의 알고리즘이 압축하거나 색을 섞을 곳을 결정하기 어렵기 때문에 JPEG 파일의 크기는 커진다.

어떤 JPEG 포맷을 선택하는지도 사이트 로딩 속도의 체감성능에 영향을 미칠 수 있다. 일반적으로 웹에서 많이 볼 수 있는 기본 JPEG 파일은 최고 품질 상태로 브라우저가 위에서 아래 방향으로 읽는다. 반면 프로그레시브 JPEG 파일은 여러번에 걸쳐 품질을 향상시키면서 이미지 파일을 반복적으로 스캔한다.

기본 JPEG 파일은 브라우저가 위에서 아래로 읽었기 때문에 이미지의 조각도 천천히 위에서 아래 방향으로 화면에 나타난다. 반면 프로그레시브 JPEG은 낮은 품질로 한꺼번에 화면에 나타난 후 이어서 점진적으로 조금 더 높은 품질의 버전으로 대체된다.

프로그레시브 렌더링을 지원하지 않는 일부 브라우저는 프로그레시브 JPEG 파일을 모두 다운받은 후 화면에 이미지를 보여주기 때문에 다운받은 대로 표현하는 기본 JPEG보다 화면에 더 느리게 나타난다.

이미지를 표시하기 위해 여러 번 스캔하는 프로그레시브 JPEG는 각각의 스캔은 기본 JPEG 파일 하나를 페이지에 렌더링하기 위해 스캔하는 것과 같은 수준의 CPU 파워를 소모한다. 모바일 기기에서는 이 부분이 문제가 될 수 있다. 사파리 모바일 브라우저는 현재 프로그레시브 JPEG를 점진적으로 스캔하지 않는다. 반대로 안드로이드의 크롬 브라우저나 다른 모바일 브라우저는 프로그레시브 JPEG 파일을 점진적으로 렌더링한다.

GIF

GIF는 웹에서 가장 오래된 그래픽 파일 포맷 중 하나다. GIF 파일 포맷은 원래 하나의 파일에 복수의 비트맵 이미지를 저장하기 위해 1987년에 만들어졌다. GIF는 투명성뿐 아니라 애니메이션도 지원하지만, 프레임 안에 256색까지만 포함할 수 있다는 제약이 있다. 그리고 JPEG 파일과 달리 GIF는 무손실 파일 포맷이다.

디더링(dithering)은 색상 간 전환이 더 부드럽게 보이도록 도와주는 기능이다. 디더링을 적용하면 색상이 다른 인접한 픽셀들을 검사하여 부드러우면서도 색상들이 조화롭게 보이도록 두 색 사이에 새로운 색을 골라 넣는다.

스피너나 로딩 표시 등의 간단한 애니메이션을 GIF로 저장했다면 이를 CSS3 애니메이션으로 교체할 수 있을지 고려해보자. CSS3 애니메이션은 GIF보다 크기가 작아 가벼우면서도 대체로 성능이 더 좋다.

PNG

GIF 파일 포맷을 개선하고자 설계된 무손실 이미지 포맷이 PNG다. 이미지가 투명해야 한다면 PNG 포맷이 최고의 선택이 될 것이다. GIF 포맷도 투명성을 지원하지만 파일 크기가 대체로 PNG 파일보다 훨씬 더 크다. PNG 파일은 GIF 파일처럼 수평 패턴을 인식하고 압축할 뿐 아니라 수직 패턴도 찾아 압축하기 때문에 GIF 포맷보다 PNG 포맷을 사용할 때 추가적인 압축 효과를 얻을 수 있다.

이미지의 색상 수가 적은 경우라면 PNG-8 파일 포맷이 최선의 선택이 될 수도 있다. PNG-8 파일은 256색만 사용할 수 있는 제한이 있지만 일반적으로 파일의 크기가 작은 편이다.

PNG-24 파일 포맷에는 PNG-8과 같은 팔레트 색상 제한이 없다. 사이트에 추가할 사진을 PNG 파일로 저장할 경우 무손실 압축 파일이기 때문에 파일 크기가 JPEG 파일보다 다섯 배에서 열 배까지 커질 것이다. 그래서 PNG 이미지 파일도 다른 이미지 파일처럼 크기를 줄이려면 노이즈와 색상 수를 줄이는 것이 좋다.

이미지 요청 대체하기

이미지 요청을 줄일 수 있는 대표적인 방법은 크게 두 가지다.

  • 스프라이트로 이미지를 합치기
  • 이미지 파일을 CSS3, 데이터 URI, SVG 버전으로 교체하기

스프라이트

웹 성능을 이야기할 때 "가장 빠른 요청은 처음부터 요청하지 않는 것이다"라는 말을 자주 한다. 페이지의 이미지 요청 수를 감소시키는 대표적인 방법은 낱개의 이미지들을 하나의 스프라이트 이미지로 합치는 것이다. 스프라이트를 이용하면 큰 이미지 하나가 생기고 이미지의 위치와 그래픽을 보여주기 위한 CSS 파일이 추가되기 때문에 용량이 약간 늘어날 수 있다. 하지만 페이지 속도 면에서는 유리하다.

스프라이트로 합치기에 적합한 이미지는 크기가 작고 사이트에서 반복하여 사용되는 것이다.

배경 위치는 여러 종류의 X축과 Y축 값의 쌍을 매개변수로 받을 수 있는데 예를 들면 다음과 같다.

  • 50% 25%
  • 50px 200px
  • left top

스프라이트의 이미지 위치를 css에 적어줘야 한다.

.fave:before {
background: transparent no-repeat;
background-image: url(star.png);
background-position: -216px -15px;
}

스프라이트는 이미지 요청 횟수를 줄여 페이지 로딩 시간을 크게 줄이는데 도움이 된다. 물론 스프라이트 이미지 파일 자체의 크기뿐 아니라 스프라이트 이미지를 사용하기 위해 추가적인 스타일을 포함한 CSS의 크기도 증가했으므로 총 페이지 크기가 커졌다고 느낄 수 있다. 하지만 기존에 여러 번 이미지를 요청하던 것과 달리 한번만 HTTP 요청을 하면 되므로 스프라이트를 사용하는 것이 페이지 로딩 속도에는 훨씬 더 낫다.

스프라이트도 성능을 저하시킬 가능성은 있다. 스프라이트 이미지에 포함된 여러 이미지 중에서 하나만 변경하더라도 스프라이트 파일 전체의 캐시가 중단되기 때문이다. 동시에 사용자가 사이트를 둘러보는 동안 한번도 보지 않는 아이콘이 스프라이트에 포함되어 있다면 사용자는 아무 이유 없이 큰 파일을 받아 디코딩한 셈이다.

우리 팀은 26개의 썸네일 이미지중 25개의 썸네일 이미지를 하나의 스프라이트로 합쳤는데, 그 결과 다음과 같은 변화가 있었다.

  • 스프라이트로 다시 만들면서 추가된 CSS, 자바스크립트, 새로운 이미지로 인해 총 페이지 크기 60KB 증가
  • 요청 횟수 21% 감소
  • 페이지 로딩 시간 35% 감소

CSS3

이미지 요청 횟수를 감소시키는 또 다른 대표적인 방법은 이미지를 CSS로 대체하는 것이다. CSS를 사용하면 도형, 그레이디언트, 애니메이션을 만들 수 있다. 특히 CSS3 그레이디언트는 다음과 같은 장점이 있다.

  • 투명성을 제어할 수 있다.
  • 배경 색상에 오버레이할 수 있다.
  • 이미지 요청 횟수를 줄일 수 있다.
  • 변경이 아주 쉽다.

CSS는 이미지를 대체하기에 적합하고 동시에 성능에도 도움이 되는 방법이다. 사이트에 gzip을 사용하면 CSS 코드는 알아서 최적화된다. 더 많은 CSS를 로딩한다 치더라도 이미지 파일 요청보다 CSS 코드를 요청하는 것이 성능 면에서는 더 낫다.

데이터 URI와 베이스64로 인코딩한 이미지

매우 작고 단순한 이미지라면 URI로 바꾸는 것도 웹 페이지에서 요청 횟수를 줄이는 방법 중 하나다. 이를 위해 Base64 인코딩이라는 방법으로 이미지를 URI로 변환한다.

삼각형 이미지를 Base64로 인코딩한 결과를 CSS 요소의 background-image에 지정하는 코드는 다음과 같다.

background-image: url(data:image/png;base64,iVBORw0...)

이 방법은 이미지를 보여주기 위해 HTTP 요청 결과를 기다리지 않고 즉시 보여줄 수 있다는 장점도 있다. 하지만 이미지를 코드 안에 넣으면 파일 캐싱을 할 수 없고 CSS 파일의 크기도 증가한다. 더불어 데이터 URI의 길이에 따라 때로는 파일 크기가 눈에 띄게 커지기도 한다.

SVG

단일 색상이나 그레이디언트 이미지, 투명한 이미지, 혹은 일부만 상세히 표현된 그래픽이라면 SVG로 내보내는 것을 고려해보자.

SVG의 주된 장점은 디스플레이의 장치의 해상도에 관계없이 같은 품질로 이미지를 보여줄 수 있다는 점이다. 고해상도 디스플레이를 지원하기 위해 같은 이미지를 고해상도로 또 만드는 대신 SVG로 교체하면 된다. SVG는 비트맵 이미지와 달리 확대/축소가 가능한 벡터이기 때문에 화면에 표시되는 크기와 무관하다. 또한 이미지 파일을 인라인 SVG로 대체하면 브라우저가 서버에서 파일을 다운받기 위한 HTTP 요청을 줄일 수도 있다.

SVG를 추가적으로 최적화하고 싶다면 SVG 최적화처럼 SVG 안의 숫자를 단순화하고 불필요한 문자를 제거하는 도구를 사용하자.