웹페이지 성능 개선하기: 이미지와 그라데이션

bluestragglr·2020년 12월 2일
13
post-thumbnail

🥑 무엇에 대해 다루나요?

그라데이션과 복잡한 이미지, 애니메이션이 포함된 페이지에서 발생했던 성능 이슈(스크롤시 화면 깜빡임, 느린 로딩)와, 개선 방법에 대한 이슈들을 다룹니다. 페이지가 시각적으로 복잡해서 성능 이슈가 발생하는 경우에 한번 해결한 방법들을 확인해 보시길 권장합니다.

🎄 잡설 (스킵해도 무방)

정신을 차리니 벌써 12월이 되었습니다. 코로나19때문에 2020년에 옴짝달싹 못했는데 벌써 한해가 갔다니 아쉬운 마음이 많이 드네요.

갑자기 연말 이야기로 시작하는 이유는 본 포스트의 주제인 이미지와 그라데이션이 포함된 페이지를 만든 것이 연말 프로모션 페이지 제작에서 나왔기 때문입니다. 코딩교육 스타트업인 코드잇은 교육회사 답게 연말 이벤트를 준비했고, 상당히 복잡하고 화려한 디자인이 나왔습니다. 구현하고 보니 로딩 속도부터 스크롤 인터랙션까지 신형 맥북에서도 버벅임이 발생할 정도로 문제가 심각해졌고, 이를 해결하기 위해서 아래의 작업들을 진행하였습니다.

이미지 사이즈 줄이기

가장 쉬우면서 로딩 속도 개선에 굉장한 도움이 되는 방법입니다. 웹 인터페이스가 지원되는 이미지 압축기가 많아서 손쉽게 사이즈를 줄일 수 있습니다. 두 가지 정도의 사이트를 추천합니다.

  1. https://compresspng.com/

    링크된 사이트의 다른 탭을 통해서 png 뿐만 아니라 gif 등 다양한 형식의 이미지를 압축할 수 있습니다. 제플린에서 뽑아낸 이미지를 기준으로 보통 70~80% 정도의 용량 절감 효과를 기대할 수 있습니다.
    무손실 압축 알고리즘을 사용하는 것으로 보이긴 하는데 간혹 그라데이션 등 높은 색상 정밀도가 필요한 이미지가 깨지는 경우를 발견했습니다. 압축 후 한번씩 확인할 필요가 있습니다.

*간혹 일정 갯수 이상의 이미지를 처리하면 일시적으로 IP밴되는 경우가 있습니다. 하루 정도 지나면 풀리는 것으로 보입니다.

  1. https://kraken.io/web-interface

    훨씬 깔끔하고 다양한 기능들을 제공하긴 하지만 용량 제한이 있고 유료 플랜이 제공됩니다. 유료 버전을 사용하는 경우에는 이미지 크롭, 리사이징 등도 제공된다고 합니다. 손실 압축이나 무손실 압축 등 여러 옵션이 사용가능하며, API화도 가능한 것으로 보입니다. 1번 페이지에서 압축한 이미지를 한번 더 손실압축 하여 크기를 줄이기도 하였습니다.

아쉬웠던 점

당연하게도 이미지는 사이즈에 용량이 비례합니다. 그렇기 때문에 화면에 표시되는 이미지의 크기가 100px * 100px일 때, 400px * 400px 사이즈의 이미지를 사용하면 용량 측면에서 손해를 보게 됩니다. 하지만 위의 이미지 압축기들은 이미지 압축 형식을 변경하여(e.g. 손실 압축 사용) 용량을 최적화할 뿐, 이미지의 물리적 크기에는 영향을 주지 못했습니다. 충분하다고 판단되어 크기까지 줄이지는 않았지만 크기를 최적화했다면 좀 더 개선되지 않았을까 싶습니다.

mix-blend-mode: screen 없애기

다소 생소한 CSS 속성인 mix-blend-mode 는 이미지 레이어들을 어떻게 병합할지에 대한 옵션입니다. 일반적으로 HTML에서 이미지들은 레이어처럼 쌓여 앞의 이미지가 뒤의 이미지를 가립니다. 이는 mix-blend-mode 가 기본값인 normal 로 설정되었을 때의 동작입니다.

본 작업에서는 mix-blend-modescreen 값을 주었습니다. 네온사인의 효과를 주기 위해서 사용한 에셋이 검은 배경을 가지고 있었기 때문이죠.

즉, 아래와 같은 에셋을 다음과 같이 표현하기 위해서 mix-blend-mode: screen 이 사용되었습니다.

왜 문제가 되었나요?

위 이미지의 텍스트에서 설명하듯, mix-blend-mode: normal 의 경우에는 단순히 위의 이미지의 픽셀을 사용하도록 하면 됩니다. 반면, mix-blend-mode: screen 의 경우에는 스크린 표현을 위해 훨씬 많은 연산량이 요구됩니다.

이러한 연산량에 부하가 걸리는 속성을 gif 뿐만 아니라 네온사인 효과를 내기 위한 이미지에 남용하였으며, 개중에는 화면 전체를 덮는 배경 이미지들까지 포함되어 있었습니다. 안그래도 다채로운 색상의 이미지들이 많이 포함되어 있는데(즉, 렌더링 비용이 많이 드는 에셋들이 많은데), 에셋을 단순히 나타내는 것도 아니고 혼합 연산까지 해야 하니 2020년형 16인치 맥북에서마저 스크롤시 렌더 이슈가 발생하였습니다. (아래 영상은 17년형 15인치 기준 당시 상황입니다.)

CLAIM: 배경을 제거하고 이미지를 만들면 안되나요?

PNG 에셋들은 배경을 자연스럽게 제거할 수 있었지만, GIF는 불가능했습니다. 에셋의 특수성때문에 발생한 문제이기도 한데, GIF는 반투명한 색상을 표현할 수 없습니다. 즉, 완전한 투명색 픽셀은 존재할 수 있지만 네온 색상의 glow 효과를 주는 gausian blur같은 효과는 gif에 담을 수 없었습니다. 그러다 보니 디자이너도 검은 배경에 혼합 옵션을 주는 방법밖에는 찾지 못했죠.

어떻게 없앴나요?

GIF 이미지를 따로따로 분리한 뒤, keyframe 을 이용해서 애니메이션을 만들었습니다. 시계가 자연스럽게 돌아가는 애니메이션을 구현하기 위해서 다음과 같은 keyframe 을 작성하였습니다. 반복되는 구문을 줄이기 위해서 scss가 지원하는 for문을 이용하였습니다.

...
.clock-background {
	animation: clock-background-rotate 1.8s infinite;
	animation-timing-function: step-start;
}

...
@keyframes clock-background-rotate {
  @for $i from 12 through 1 {
    #{8.3333% * $i} {
      transform: rotate(calc(30deg * #{$i}));
    }
  }
}

의도된 것은 빛만 움직이고 시계는 가만히 있는 애니메이션이었기에 step-start옵션을 이용하여 정해진 각도만큼만 정확히 회전하도록 하였습니다.

반짝이가 회전하는 애니메이션은 간단하게 transform: rotate(360deg)로 구현하였습니다.

linear-gradient 없애기

이외에도 브라우저에서의 연산량을 조금 더 줄일 수 있는 방법이 있을까 하다가 잔뜩 사용된 linear-gradient를 제거하기로 했습니다. 구현한 페이지에는 아래와 같은 그라데이션 배경을 갖는 요소들이 아주 많았고, 모두 linear-gradient를 사용하고 있었습니다.

그라데이션을 브라우저의 렌더러가 수행하는 과정에서 리소스가 소모될 것이라는 생각이 들어, 그라데이션을 모두 이미지로 대체하였습니다. 즉, 아래와 같이 그라데이션 이미지를 만든 뒤:

아래와 같이 CSS를 변경하였습니다:

/* BEFORE */
.gradient-background {
  background-image: linear-gradient(...)
}

/* AFTER */
.gradient-background {
  background-image: url("...")
}

추가) 그라데이션 테두리와 border-radius 함께 사용하기

그라데이션을 border-color 옵션을 이용해 직접 입힐 수는 없습니다. border-image-source: linear-gradient(...) 형태로 CSS를 적용하는 방법을 주로 사용하게 되는데, 이 때 border-radius를 함께 사용하면 정상적으로 표시되지 않습니다. 이를 해결하기 위해서 background-imageborder-image-source 모두에 border-radius가 적용된 이미지 소스를 포함시킵니다. 즉 아래와 같은 방식을 사용합니다.

border-style: solid;
    border-width: .1rem;
    border-image-source: url("/static/.../float-border.png");
    border-image-slice: 1;
    background-image: url("/static/.../gradient-background.png"),
										  url("/static/.../float-border.png");
    background-size: cover;
    background-origin: border-box;
    background-clip: content-box, border-box;

이 때, 이미지 소스의 테두리도 실제로 둥글게 깎아 transparent 영역을 주어야 정상적으로 모서리가 둥근 그라데이션 테두리를 만들 수 있습니다.

+이 때, 정확히 border를 깎은 이미지를 만들어 테두리를 만드는 일은 굉장히 까다롭고 손이 많이 가는 작업입니다. 같이 작업하는 팀원에게 기술적 비용에 대해서 잘 이야기 하고 작업 시간을 넉넉히 잡으시길 권장합니다.

++물론, 사실 좀 더 좋은 트릭이 있습니다. 배경색을 깔고, 그 뒤에 :after 등의 psuedo-selector를 border 크기 만큼 큰 사이즈로 만들어 그라데이션을 깔고 요소 바로 뒤에 위치하도록 할 수 있습니다. 하지만 모종의 이유로 인해서 이 방법을 사용할 수 없는 경우에 위의 방법을 시도해 보면 되겠습니다 😉

더 알아보기

https://developer.mozilla.org/ko/docs/Web/CSS/blend-mode
https://ezgif.com/help/gif-transparency

profile
디자인하는 프론트엔드 개발자입니다. 코드잇에서 열심히 개발하고 있습니다.

2개의 댓글

comment-user-thumbnail
2020년 12월 15일

Thanks a lot for sharing!JOKER123

1개의 답글