원티드 7월 챌린지 디스코드에 올라온 최적화 영상을 보고 정리해보았다.
프런트엔드 성능 = 로딩 최적화 + 렌더링 최적화
유저가 겪는 성능 문제
공통점: '느리다'
- 내용이 길면 느려져요
- 스크롤이 툭툭 끊어져요
- 로딩이 느려요
- 때때로 화면이 멈춰요
- 페이지 뜨는 데 몇 분씩 걸려요 ㅠㅠ
빠른 로딩 속도는 사업 지표와도 연결
상세 페이지 띄우는 데 20초, 결제 로딩하는 데 20초, ... => 이러면 X
성능 최적화
= 빠른 로딩 속도 → 사용성 개선 → 사업 지표 향상
예: Pinterest
- 2019년 기준으로 월 이용자 수 2억 명의 큰 서비스였음
- 이미지 페이지 띄우는 데 처음에 23초 걸림
⇒ 3개월에 걸쳐 성능 최적화
- 결과
- 사이트에 5분 이상 머무르는 사용자 수 40% 증가
- 매출 44% 증가
- 광고 수익 50% 증가 등등 사업 지표 향상
데모 예시: 개선 전 vs 개선 후
개선 전
- 4초 가량 흰 화면 뜨고
- 로딩 UI와 배경 화면 뜨고
- 약 8초 만에 메인 컨텐츠(애니메이션) 등장
- 애니메이션 렌더링 중에도 계속 툭툭 끊김
개선 후
- 약 1초 만에 흰 화면이 떴다가 사라지고
- 로딩 UI 등장
- 전체 컨텐츠는 개선 전과 마찬가지로 8초 만에 뜨지만
- 이후 끊김 없는 애니메이션 재생
로딩 최적화 🎿
1. 브라우저 기준 최적화
Navigation Timing
- 브라우저에서 뜰 때의 과정
- 앞쪽의 TCP/IP 부분(노란 박스)은 프론트가 최적화할 부분이 크게 없음
- 뒤쪽 Processing과 Load 부분이 중요!
Processing
- HTML, CSS, JS을 파싱하는 과정
- Processing이 끝나면 DomContentLoaded 이벤트 발생
- 브라우저가 어떤 것을 그릴 준비가 되었다는 것을 의미
Load
- 렌더링에 필요한 파일(HTML에 포함된 이미지 등)을 로딩하는 과정
- 로드가 끝나면 load 이벤트 발생
=> 브라우저 최적화란 이 두 이벤트의 시점을 최대한 앞당겨 빨리 일어나게 하는 것
데모 예시
- 0초 ~ 4초
- Processing 단계 (HTML, CSS, JS을 파싱하는 중)
- 사용자에게는 흰 화면이 보임
- 4초 ~ 8초
- 이미지를 비롯한 모든 컨텐츠 로드(load)
- 8초 이후(load 종료) 모든 컨텐츠가 화면에 나타남
흰 화면의 원인: 블록 리소스 (CSS, JS)
- 블록 리소스
- HTML 파싱을 멈추는 리소스 2가지: CSS, JS
- CSS는 CSSOM 트리를 만들기 위함
- JS는 DOM(HTML)에 접근하고 CSS를 변경할 수 있기 때문에
- 브라우저의 입장에서는 JS가 접근하고 변경하는 과정에서 최악의 상황을 대비해 HTML 파싱을 멈추고 지금까지 만들어진 DOM과 CSSOM 트리를 준비해야 함
- 이러한 블록 리소스들이 처리가 되기까지 계속 흰 화면 유지 (렌더링 X)
⇒ 브라우저에서의 최적화란 이 두 블록 리소스를 최적화하는 것
JS 로드 시점 최적화하기
= script 태그의 위치
- 아무 장치 없이 head 태그 내부 (x)
- body 태그 내부 하단 (o)
- HTML이 모두 파싱되고 나서 JS가 실행될 수 있도록
- async, defer와 함께 head 태그 내부 (o)
- 파싱을 하다가 JS를 만났을 때 async가 있으면 블록 리소스인 JS를 파싱하지 않고 계속 DOM 파싱을 진행 (= 렌더링이 막히지 않음)
= async를 써서 ‘블록’되지 않게 할 수 있음
CSS 로드 시점 최적화하기
- Waiting 시간 = TTFB (Time To First Byte)
- 브라우저가 다운로드 요청 뒤 첫 byte를 받아서 처리하기까지 걸리는 시간
- 최적화하는 법?
- link 태그에서
.css
파일로 외부에서 불러오기 (X)
- HTML 내부의
style
태그로 인라인 스타일로 적용
- 다운로드 시간이 줄어들어 DomContentLoaded를 앞당김
- DomContentLoaded를 앞당겨 흰 화면이 뜨는 시간과 로딩 UI 바 띄우는 시간을 2배 빠르게 함
(개인적인 생각: 유지 보수 측면에서 좋은 방법은 아닌 듯 하다..)
브라우저 로딩 최적화의 문제점
- 로딩 UI를 띄우는 것'만' 빨라짐
= DomContentLoaded'만' 빨라짐
- 흰 화면이 뜨는 시간이 줄어들 뿐 4초 대에 유의미한 컨텐츠가 나타나는 현상은 똑같다. (로드 완료도 마찬가지 => 8초 대로 동일)
사용자 기준 최적화 🥇
중요!
- 이 최적화가 브라우저 최적화보다 더 중요함
- 사용자가 중요하다고 느끼는 컨텐츠를 먼저 빠르게 보여줘야 한다.
이미지의 각 단계(노란색 글씨)별 구분
Is it happening?
- First Paint
- 흰 화면에서 처음으로 뭔가가 그려지기 시작하는 순간
- First Contentful Paint
- 화면에 처음으로 텍스트나 이미지가 그려지기 시작하는 순간
Is it useful?
- First Meaningful Paint (FMP)
- 유저에게 의미 있는 컨텐츠를 로드하는 순간
- ** FE 최적화의 궁극적 목표: FMP 시점을 앞당기는 것!
Is it usable?
데모 예시
- 로딩 UI가 뜬 시점 = First Paint
- 브라우저 최적화를 통해 이 시점을 앞당길 수 있음
- 유저에게 유의미한 컨텐츠가 뜬 시점= First Meaningful Paint (FMP)
- 처음으로 배경 등의 컨텐츠 자료가 뜨기 시작한 4초 대
- Time To Interact (TTI)
- 컨텐츠가 완전히 로드되어 이벤트 동작까지 가능한 8초 대
>> 서버 사이드 렌더링 (SSR)
- First Meaningful Paint(FMP)을 앞당기기 위한 방법 중 하나
- 사용자에게 필요한(유의미한) 정보를 빠르게 보여주기 위함
- SPA vs. SSR
- Vue나 React의 SPA 개발
- HTML, CSS 로딩 → 로딩 UI → AJAX로 왔다갔다 하며 필요한 정보 요청(JS) → 화면 로딩
- 흰 화면이 나타나는 시간이 상대적으로 김
- SSR
- SPA에서 FCP 시점을 앞당기기 위해 쓰는 기술
- 브라우저가 HTML을 요청하는 그 시점에 필요한 정보를 모두 로드해서 HTML을 생성해서 응답으로 내려보내고, 응답으로 받은 HTML을 브라우저가 화면에 로딩
- 흰 화면 구간 거의 X
프리 렌더러와 SSR
- 공통점
- 둘 다 FMP를 위해 HTML과 CSS를 생성
- 차이점
- SSR은 서버에 브라우저가 요청하는 런타임 시점에 HTML과 CSS 생성
- 프리 렌더러는 소스 빌드 타임에서 HTML, CSS 생성
PWA 사례
PWA = Web + App
- 웹이 앱과 같은 성능을 가지도록 고안된 패턴
- 신기술은 아니고 기존에 있던 기술들을 집약한 것
PRPL 패턴
Push / Render / Pre-cache / Lazy-load
예: 티켓 선택부터 결제까지 약 30초 안에 끝남
⇒ 로딩 성능은 사용자를 사로잡는 데 아주 중요한 역할
로딩 최적화 총 정리
2. 렌더링 최적화 🛫
레이아웃 스래싱
강제 동기 레이아웃이 빈번하게 발생하는 현상
강제 동기 레이아웃
- 렌더링 시작 후 툭툭 끊기는 원인
- 레이아웃을 변경하는 게 아닌 코드를 읽기만 해도 레이아웃 변경
- 레이아웃 변경의 뜻 = DOM(HTML), CSS, Layout 변경이 모두 일어남
- DOM 변경을 안 하고 '읽기'만 해도 스타일 계산을 새로 하고 레이아웃도 새로 만들어야 함
- 예:
- offset 관련 코드: 매 애니메이션이 발생할 때마다 브라우저가 코드를 계속 읽음
- 매번 강제 동기 레이아웃 발생
- offset 프로퍼티를 읽는 순간 브라우저는 매번 최신 값을 계산하고 리턴하기 위해 레이아웃을 계속 다시 함
=> 렌더링 성능을 급격히 떨어뜨림
해결법
강제 동기 레이아웃이 발생하는 코드는 한 번만 쓰고 캐싱하기
캐싱만 해도 성능이 많이 개선됨 (렌더링 속도 엄청 빨라짐)
개선 전: 약 30fps 안팎 / 개선 후: 60fps (최적화 = fps 수치 높이기)
가상돔
- 가상 돔에서 변경 사항 연산을 끝내고 레이아웃 변경이 일어나는 실제 돔에는 1번만 반영
웹 워커
- 웹팩으로 기존 스레드에서 웹 워커 스레드로 옮기기