안녕하세요. 비브로스 웹프론트엔드팀 양희창입니다. 😎
이번 시간에는 웹앱 성능을 최적화 할 수 있는 기법에는 무엇이 있는지 알아보고 실제 저희팀이 서비스 하고 있는 웹앱의 일부 코드에서 최적화 기법을 활용해 보는 시간을 가져보려고 합니다.
대상은 병원어드민으로 불리는 서비스로 환자가 사용하는 똑닥앱 편리한 사용을 위해(?) 병원에서 사용하는 어드민 웹앱입니다. 배민 사장님 사이트 라고 이해하면 편합니다 🙈
📃 웹 성능 최적화의 종류
우선 웹앱의 성능을 개선(최적화)할 수 있는 부분은 크게 2가지로 나눌 수 있습니다.
- 브라우저가 화면에 그려주는 성능을 높이는 방법 (렌더링 성능 최적화)
- 브라우저가 리소스를 불러오는 성능을 높이는 방법 (로딩 성능 최적화)
여기서 좀더 깊이 들어가보면 각 최적화의 기법은 다시 아래와 같이 대략 나눌 수 있습니다.
🎨 렌더링 성능 최적화
애니메이션 최적화 (Reflow, Repaint) ✅
⏱ 로딩 성능 최적화
컴포넌트 Lazy Loading (Code Splitting)
한 페이지 안에서 컴포넌트 단위로 Lazy Loading 하는 방법
컴포넌트 Preloading
컴포넌트를 미리 불러오는 최적화 기법
이미지 Preloading
이미지를 미리 불러와서 화면에 빠르게 띄울 수 있는 기법
위에 나열된 여러가지 기법 중 이번 글에서 소개할 성능 최적화 주제는 렌더링 성능 최적화(애니메이션 최적화) 입니다.
😱 오늘도 우리가 안된다고 하는 이유 (feat.디자이너)
안돼요, 아 아무튼 그거 안돼요
디자이너 A씨: 여기 이부분 애니메이ㅅ...
개발자 B씨: 안됩니다.
프론트엔드는 서비스의 최전선에서 고객이 직접 사용하는 웹 또는 앱을 만드는 직무 이므로 디자이너와 소통할 일이 굉장히 많은 편입니다. 또한 디자이너분들의 수준 높은 미적 감각으로 완성된 화려한 결과물을 구현해야 하는 경우가 생기죠.
하지만 우리는 안된다고 말합니다. (말하고 싶습니다)
왜 우리는 디자이너에게 안된다고 하는 걸까요? 그냥 단순히 귀찮고 싫은 걸까요? 아니면 대부분의 개발자들이 UI/UX 에 관심이 없는 성향이기 때문일까요?
저는 오히려 역설적이게도 개발자들이 평소에 가장 중요하게 생각하는 성능개선 측면에 있어서 디자인 요구사항이 웹앱의 성능에 큰 영향을 미치기 때문이라고 생각합니다. (적어도 웹은 더 치명적으로요)
알고리즘이나 코드 자체의 완성도, 논리적으로 설명 가능한 부분을 수정하는 것이 더 쉬운 개발자들에게 느낌적인 느낌(?)인 렌더링 성능 개선은 차라리 외면 하고 싶은 주제일지도 모릅니다. 🤯
하지만 우리는 언제나 답을 찾을 겁니다. (늘 찾는 건 아니…)
렌더링 성능 개선을 위해 무엇을 할 수 있는 지 소개합니다.
렌더링 성능 개선에 앞서 알아야 할 것
Reflow, Repaint 의 개념
Reflow
브라우저가 문서 내 요소의 위치나 도형을 다시 계산하는 작업
Repaint
문서 내 레이아웃에 영향을 주지 않는 스타일 속성이 변경 되었을 때 발생
브라우저가 화면을 그리는 과정(Reflow, Repaint)은 아래와 같은 단계로 진행됩니다.
💡 Repaint 에서는 Layout 과정이 생략됩니다.
DOM + CSS Object Model
HTML, CSS를 가공해서 DOM, CSSOM 으로 만듬
위 두가지를 조합해서 Render Tree 라는 구조를 만듬
Layout 이라는 단계에서 위치나 요소에 대한 크기를 실제로 계산을 합니다.
어느 위치에 어느정도의 사이즈로 있어야 하는지 브라우저가 계산 하는 과정입니다.
Paint 에서는 브라우저가 색을 채워 넣음
Composite
위 단계를 거쳐 쪼개진 각 레이어를 합성하는 과정을 거침
위 전체의 과정을 Critical Rendering Path 또는 Pixel Pipeline 이라고 부르기도 합니다.
만약 사용자 액션이나 여러 이유로 인해 애니메이션에 변화가 생기면 브라우저는 위 과정을 처음부터 반복하게 되는거죠.
따라서 불필요하고 과도한 반복이 생기게 되면 우리의 소중한(?) 웹앱의 렌더링 성능에 악영향을 미치게 됩니다.
그렇다면 여기서 잠깐 애니메이션의 원리에 대해서 간단하게 알고 넘어가야 할 것 같습니다.
애니메이션 원리
애니메이션은 여러장의 이미지를 연속적으로 보여주는 일종의 눈속임입니다.
우리가 지금 보고 있는 모니터도 1초에 60번 깜빡이는 일종의 애니메이션과 같다.
그렇다면 애니메이션 렌더링 성능이 떨어진다는건 무슨 의미 일까요?
그건 바로 애니메이션의 초당 프레임수가 60이 안되어 사용자가 보기에 부드럽지 않고 뚝뚝 끊기는 것 처럼 보이는 현상을 말합니다.
이러한 현상을 쟁크(Jank) 현상이라고 하며 브라우저가 애니메이션을 초당 60 Frame 으로 그려주지 못하는 문제입니다.
여담으로 최근 120Hz 고주사율 지원으로 급나누기를 한 아이폰14 의 경우도 비슷한 경우라 할 수 있겠네요.
어떻게 개선할 것인가?
애니메이션 성능은 브라우저에게 부담을 덜 주는 방식으로 개선 할 수 있습니다.
위 과정에서 Layout, Paint 과정을 건너 뛸 수 있기 때문이죠.
CSS 속성중에는 GPU의 도움을 받을 수 있는 속성이 있는데 그중 대표적인 속성이 transform
과 opacity
입니다.
따라서 transform
및 opacity
로 커버할 수 있는 다른 속성이 있다면 적극적으로 사용해 주는게 애니메이션 성능에 좋습니다.
렌더링 성능을 측정하는 방법
우선 성능을 개선하려면 성능을 측정할 수 있는 도구가 필요합니다.
분석 툴
로딩 성능 분석을 위한 툴
- 크롬 Network 탭
- Webpack-bundle-analyzer
렌더링 성능 분석을 위한 툴
- 크롬 Performance 탭 ✅
웹앱의 성능을 개선하기 위해서는 다양한 툴을 사용할 수 있지만 여기서는 크롬 브라우저의 Performance 탭을 이용합니다.
렌더링 성능 개선 테스트
테스트 환경
애니메이션 성능 개선 샘플로 사용될 병원어드민 내 기능은 바로 화상진료 환경 테스트
기능입니다!
화상진료 환경 테스트 모달안에는 마이크의 볼륨을 측정할 수 있는 SoundMeter 컴포넌트가 구현되어 있습니다. 연결된 마이크의 볼륨값을 실시간으로 노란막대로 표시해주는 기능이죠.
우선 테스트를 위해 시인성도 개선할 겸 총 2가지 다른 CSS 속성값과 <meter>
태그로 구현된 사운드 미터를 만들었습니다.
<p>by Width</p>
<div class="sound-meter-width">
<div class="gage" [ngStyle]="{ 'width': gageMeterWidth }"></div>
</div>
<p>by Transform</p>
<div class="sound-meter-transform">
<div class="gage" [ngStyle]="{ 'transform': gageMeterScaleX }"></div>
</div>
<p>by Meter Tag</p>
<meter
class="sound-meter"
max="100"
[value]="soundMeterValue"
></meter>
by Width
width
속성 값으로 변화by transform
transform
속성의scaleX
값으로 변화by
<meter>
taghtml
<meter>
태그 사용
🎥 영상 테스트
아니 그런데...??
육안으로 보기에는 3가지 구현 방법 모두 성능에 전혀 차이가 없어 보입니다만 크롬 Performance 탭에서 앞서 말한 기대효과가 있는지 확인해 봅시다. 🤦🏻
Performance Tab Recording 테스트
3초간의 Recording 후 Event Log 에서 브라우저에서 어떤 이벤트가 발생했는지 살펴보면
Width
속성으로 구현된 경우Transform
속성으로 구현된 경우<meter>
태그로 구현된 경우
테스트 결과
조금 재밌는 결과가 나왔습니다. 현재 병원어드민에 사용되는 사운드미터 컴포넌트는 <meter>
태그로 구현되어 있는데 측정결과 제일 성능이 낮은 걸로 확인이 됐습니다. (반성합니다) 🤦♂️
GPU의 도움을 받는 Transform 속성 사용의 경우 Layout은 이벤트 로그 자체가 없지만 Paint의 경우 이벤트는 존재하되 0ms 인 부분이 흥미롭습니다.
영상 테스트시 차이를 느끼지 못했던 원인을 알아낸것 같네요. 사실 0.1ms~0.2ms 는 사용자가 체감할 수 있는 범위의 지연시간은 아니어서 그랬던 것 같습니다.
결론
렌더링 성능 개선에 대해서 간단하게 알아보면서 그동안 별 생각없이 사용해 왔던 태그 및 스타일 속성에 대해서 다시 한번 생각해보게 되는 기회가 된 것 같습니다. 사운드 미터 구현 당시 뚝뚝 끊기는 느낌이 찜찜해 렌더링 성능 개선이란 주제의 먹잇감으로 삼았는데 정말 잘한 결정인 것 같았다는 생각이 듭니다!