본문은 SKT DEVOCEAN Tech 세미나와, 개인적으로 학습하고 경험했던 프론트엔드 성능 최적화 지식을 바탕으로 작성되었습니다.
피드백 정말 완전 대 환영입니다!
최근 프론트엔드 생태계에서 성능 최적화와 관련된 이슈가 핫토픽이었다.
이었다기보단 여전히 중요하고, 앞으로도 프론트엔드 개발자에게 가장 필요한 덕목 중 하나로 이어질 것 같다.
나 역시 개인적으로 해당 주제에 대해 관심을 갖고 있었기 때문에 프론트엔드 성능 최적화 가이드 Cheat Sheet 스터디에 참여하기도 했고, 이번 Next.js 한글화 번역에서도 useReportWebVitals
번역의 컨트리뷰팅을 담당하기도 했다.
그러던 중 이번 SKT DEVOCEAN Tech 세미나
주제가 프론트엔드 성능 최적화라는 것을 알게 되었고, 당장 신청하여 들어보았다.
실제로 이전까지 들어왔던 훌륭한 세미나들보다 훨씬 많은 것을 배울 수 있었다.
두루뭉술하게 가이드라인을 잡아주는 느낌이 아니라, 정말 웹 성능을 측정하는 지표에 대한 구체적인 설명과, 이것이 필요한 이유, 실제 프로덕트(Ifland)에서 적용하여 개선한 사례까지...
그동안 성능 최적화에 대한 칼럼을 읽으면서도 직접적으로 와닿지 않는다고 느껴졌던 부분들이 명쾌하게 해결된 세미나였다.
2시간이라는 시간이 진짜 후딱 지나갔고, 헷갈리는 부분은 몇 번씩 돌려보면서 체화하려고 노력했다.
당장 나의 프로젝트에서도 적용하고 싶어지는 느낌이었다 진심 🐶🍯
이전까지의 나처럼, 채용 공고에서 성능 최적화 경험이 있으신 분 이라는 말을 보면 도대체 뭘 원하는 건지 이해하지 못했고, 여러 블로그 글이나 칼럼을 읽어도 직접적으로 느끼지 못했던 분들에게는 최고의 세미나가 될 것 같다.
하지만 2시간이라는 시간이 압박이 될 수 있고, 나 또한 다시 돌려보지 않고 글로 빠르게 보기 위해 이렇게 정리를 결심하게 되었다.
최대한 세미나 내용 + 그 동안의 성능 최적화 경험 (Cheat Sheet 스터디 + 프로젝트 경험 + Next.js 번역 등...)을 살려서 정리해보았다.
많은 분들에게 이 글이 도움이 되기를...!!
이미지들이 세미나 중간 중간 캡쳐한 것들이라 화질이 좋지 않을 수 있습니다.
이미지에 대해 필요한 설명은 글로 정리해두었으니, 양해 부탁드립니다!
사실 PM, 디자이너, 백엔드 개발자들에게 프론트엔드 성능 최적화는 이해할 수 없는 부분으로 다가올 수 있다.
실제로 겨우 0초대의 로딩 시간을 줄여보기 위해서 리소스를 투자한다는 것에 고개를 젓는 프론트엔드 개발자도 많다.
chatGPT 4.0도 인정한 성능 최적화 역량
하지만 프론트엔드 개발자에게 성능 최적화 역량은 중요한 역할 중 하나라는 점!
그리고 지금같이 생태계가 미친듯이 변화하고 기술이 범람하는 시대에서, 다양한 기술에 눈이 멀어 UX를 잊는 것은 본질을 잊는다는 것과 같다.
단순히 프론트엔드 개발자의 역량 향상에 국한되는 토픽은 아니랍니다.
위 이미지는 사이트 로딩에 따른 이탈율이다.
애초에 풀이 그렇게 넓지도 않지만, 겨우 1-2초 차이일뿐인데 이탈율의 갭이 어마어마하다는 것을 알 수 있다.
사실 이 정도는 대부분이 알고있는 사실이다.
하지만 진짜 놀라운 건, 사용자 경험을 조사했을 때 "내가 찾는 정보가 해당 페이지에 있는가" 보다 "해당 페이지에 있는 정보가 얼마나 빨리 나타나는가" 가 사용자 경험에 더 큰 영향을 미치고 있다는 것이다.
심지어는 겨우 0.1초의 성능 개선이 Conversion Rate(제품이 실제로 판매될 확률)
에 큰 영향을 미치고 있었다.
실제로 Tokopedia에서 성능 최적화 전 3.7초대의 로딩 시간과, 성능 최적화 후 1.7초 대의 로딩 시간에서 사용자를 트래킹했다.
그 결과, 로딩 시간을 포함했음에도 성능 최적화 후의 사용자가 더 오랜 시간동안 사이트에 머물러있었다는 것을 알 수 있었다 한다.
자, 이제 성능 최적화를 위해 리소스를 투자하는 것이 얼마나 중요한지 느껴지는가?
3년 전 Google IO에서 CWV
, Core Web Vitals를 발표하였다.
격변하는 생태계에서 무려 이 지표가 뉴노멀이 된 이유는 아래에서 설명한다.
물론 이전에도 웹 성능 측정을 위한 다양한 지표들이 사용되었다.
그럼에도 CWV
가 각광받은 이유는, 개발자가 아닌 사용자 관점에서 정의된 지표라는 점이다.
해당 지표들을 Next.js의 공식문서 속 useReportWebVitals를 번역하며 공부한 적 있는데, 구체적인 의미와 역할에 대해서 명료하게 알게 되었다.
아래에서 함께 살펴보자!
화면이 처음 그려질 때, 가장 크게 보여지는 영역의 콘텐츠가 얼마나 빨리 로드되는가?
이미지
이다.이미지
가 얼마나 빨리 로드되는가?사용자가 최초 인풋을 행했을 때, 얼마나 빠르게 반응하는가?
최초 인터랙션
에 대해 얼마나 빨리 반응하는가에 대한 지표이다.비주얼이 얼마나 긍정적인가?
얼마나 예쁘게 만들었는가에 대한 지표가 아님
사용자 중심에서 속도를 정의했다는 점이,
CWV
롱런의 비결
개발자 중심의 지표에서 벗어나, 사용자 중심의 지표를 제안했다는 점이 CWV
의 롱런 비결이다.
그럼에도 지난 5월 진행된 2023 Google IO
에서 새로운 인사이트에 대한 발표가 있었다.
기존의
CWV
는 너무 최초에 국한된다.
INP will replace FID
LCD
와 FID
를 보면 알 수 있듯, 기존의 CWV
는 최초에 국한되는 경향이 있었다.
특히 FID
의 경우에는 이름에서부터 알 수 있듯 최초 인풋과 그 응답 시간에 대한 지표이다.
때문에 구글에서는 FID
가 아닌 INP
지표의 사용을 공식적으로 예고하였다.
FID
는 최초 인풋에 대한 응답INP
는 웹에 존재하는 모든 인풋을 비교하여 평균낸 지표정답은, 아니다!
프론트엔드 성능 개선은 흔히 알고 있는 SEO 최적화
와도 밀접한 연관이 있다.
왜냐하면, CWV
는 SEO 점수에도 포함되기 때문이다.
검색엔진은 세상에 존재하는 여러 페이지에 점수와 랭크를 부여하는데, 이 랭크는 검색엔진에 얼마나 노출되느냐와 직접적인 연관이 있다.
실제로 최상단에 노출되는 페이지들은 검색엔진에서 높은 점수를 받은 것이다.
결국 프론트엔드에서의 성능 개선은 SEO의 한 부분으로 자리 잡았고, 컨텐츠 내부의 디스크립션도 중요하지만 속도 역시 중요한 덕목이 되었음을 알 수 있었다.
CWV
를 기반으로 성능을 측정할 수 있는 여러 도구들이 존재한다.
그 중에서도 Lab Data
기반의 측정 도구와 Rum Data
기반의 측정 도구를 분류하여 확인해보자.
실제로 속도에 관심이 있고, 계속 개선하려는 의지가 있다면 아래의 서드파티 툴을 도입해보는 것도 좋을 것이다.
Rum Data
와Lab Data
모두를 기반으로 측정할 수 있는 도구들은Rum Data
측정 도구 하위에 리스트업하였다.
말 그대로 실험 데이터를 기반으로 성능을 측정할 수 있는 도구를 말한다.
Lab Data
를 기반으로 성능을 측정하는 실험실이다.Performance Insight
Lighthouse
탭에서 성능을 측정할 수 있다.실시간 유저 데이터를 Rum Data
라고 한다.
캐싱으로 인한 성능 혜택까지 포함된 데이터로, 실제 사용자가 느끼는 것은 Rum Data
라고 할 수 있다.
대신 해당 페이지에 통계를 낼만한 유저가 방문했을 때에만 확인이 가능하다.
CWV
값을 측정한다.Page-Level
측정 도구이다.PageSpeed Insights
와 다르게 Origin-Level
측정 도구이다.Page-Level
측정 도구이다.Origin
하위에 CWV
가 좋은 페이지와 그렇지 못한 페이지를 분류하여 통계치를 보여준다.강연자님 말씀에 따르면, 가장 많이 사용하는 툴이라고 한다.
흔히 ROI가 나온다고 말 하는,
즉 투자 대비 성능 개선 여지가 높은 것을 위주로.
가장 크게 보여지는 콘텐츠의 로딩 속도를 의미하는 LCP
를 최적화하는 방법이다.
하기에 언급되는 LCP 자원
을 가장 크게 보여지는 콘텐츠라고 이해하면 된다.
주로 이미지가 되는 LCP 자원
을 빨리 발견될 수 있도록 HTML에 추가한다.
CSS에서 이미지를 로드하는 방식은, CSS가 로드되기 전까지 모른다는 뜻이기 때문에, HTML에서 찾아질 수 있도록하는 것이다.
빠르게 발견되는 것도 중요하지만, 해당 리소스가 우선적으로 다운로드 되도록 설정하는 것도 중요하다.
LCP 자원
을 먼저 다운로드 하라고 브라우저에게 명시한다.
이전 포스팅에서 AWS CloudFront를 이야기할 때 언급했던 개념이다.
사용자로부터 물리적으로 가까운 서버에서 데이터를 전해주는 CDN을 사용하는 것도 LCP 최적화
의 한 방법이다.
<link>
에 preload
속성 값을 부여해서, 브라우저에게 먼저 로드해야한다는 것을 알릴 수 있다.LCP 자원
인 경우, HTML 헤더에 preload 시그널을 부여하면 좋다.LCP 자원
에 height
를 부여하면 LCP 자원
이 priority 되도록 설정할 수 있다.LCP 자원
이 아닌 이미지의 경우에는 Lazy-Loading을 적용할 수 있다.최 하단의 그래프의 경우에는 녹색 LCP 자원
을 최대한 앞으로 땡겨와서 다운 받는다는 것을 의미한다.
여기서 만약 속도를 더 개선하고 싶다면, 그래프의 노란 부분(스크립트)를 defer
하거나 async
할 수 있다.
지금은 스크립트 다운로드가 끝나고 LCP 자원
이 그려지고 있는데, defer
나 async
의 경우에는 해당 스크립트가 지연로딩 되어도 괜찮은지 여부를 확인해야하기 때문에 복잡하다.
다른 최적화보다는 쉬운편이다.
비주얼 요소의 평가 지표를 의미하는 CLS
를 최적화하는 방법이다.
콘텐츠 사이즈를 정확하게 명시해서 개선할 수 있다.
애초에 CLS
가 평가하는 비주얼의 긍정적인 영향 중 큰 부분이 레이아웃이 밀려남에 따라 사용자의 잘못된 인터랙션을 유발하는 것이기 때문이다.
때문에 이미지가 들어올 자리를 미리 확보해 놓는다면, 이미지가 들어오기 전에는 아무것도 안보이는 문제가 발생할 수 있겠지만 의도치 않게 사용자가 잘못 인터랙션 하는 경우는 사라진다.
(아무것도 안보이는 문제를 해결하기 위해 스켈레톤UI를 도입할 수 있다.)
BF Cache
란 브라우저에서 뒤로 가거나 뒤로 갔다가 다시 예전에 브라우징 했던 페이지로 돌아왔을 때,
해당 정보를 저장해놓고 다시 네트워킹을 하지 않는 최신 기술을 뜻한다.
BF Cache
가 동작하지 않을 수 있다.CLS
개선에 상당한 도움이 된다.콘텐츠 사이즈를 명시하라고 하지만, 반응형 UI가 기본이 된 현 시점에서... 이게 쉽지 않다.
때문에 일반적인 width
height
보단, aspect-ratio
속성을 활용하는 것이 좋다고 한다.
aspect-ratio
속성을 이용하면 특정 이미지가 제대로 로드되기 전에 브라우저가 해당 이미지의 높이를 알 수 있고, 다른 영역에 미치는 영향을 미리 정해놓을 수 있기 때문에 CLS
개선에 좋다고 한다.
기본적으로 min-height
속성을 부여하는 것도 CLS
개선에 도움이 된다.
세가지 지표의 최적화 중, 가장 어려운 편이다.
특히 최근에 등장한 INP
와 맞닿아있기 때문에, 더 신경쓸 것이 많다.
자바스크립트는 싱글 스레드 언어임을 기억해야한다.
즉, 어떤 리소스를 다운 받는 중에는 다른 작업을 할 수 없다.
스크립트를 다운받는 긴 시간 동안 렌더링되지 않는 문제를 해결하기 위해서
가장 필요한 자바스크립트를 먼저 다운받고, 메인스레드가 동작할 수 있게 양도할 수 있다.
실제 다운로드는 패러렐하게 여러개가 진행되지만,
메인 스레드가 어떤 일을 하고 있더라면 그것과 병렬로는 다른 일을 할 수 없기 때문이다.
때문에 태스크를 잘게 쪼개면, 사이사이에 브라우저가 유저에게 반응할 수 있게 되어 FID
를 최적화하는데에 도움이 된다.
최근 유행하는 SPA 프레임워크에선 꽤나 많은 부분을 기본적으로 제공하고 있다.
코드 스플리팅 툴이 궁금하다면, 이 링크를 참고해보면 을 것이다.
번들링 툴로도 큰 스크립트나 큰 스타일 시트를 하나의 Chunk로 만들 수 있다.
yield
라는 Scheduled Task API도 등장함크롬 데브툴에 있는 Coverage 툴로 불필요한 자바스크립트를 확인할 수 있다.
GA
같은 분석툴이 정말 많다.
해당 툴 덕분에 사용자가 어디에 방문하고 어떤 활동을 하는지 트래킹할 수 있지만, 이 트래킹이 많아질 수록 페이지가 무거워진다.
때문에 상시적으로 모니터링하는 부분이 아닌, 이제는 불필요해진 트래킹 태그는 덜어낼 수 있어야한다.
window.requestAnimationFrame()
메서드는 브라우저에게 수행하기를 원하는 애니메이션을 알리고 다음 리페인트 바로 전에 브라우저가 애니메이션을 업데이트할 지정된 함수를 호출하도록 요청합니다.
해당 메서드를 활용하면 큰 렌더링 업데이트가 발생한다.
마찬가지로 최대한 지양해야한다.
DOM 사이즈 측정값을 최대한 작게 줄여서 로드하고, 돔트리가 그려질 수 있도록 한다.
만약 DOM 사이즈가 너무 크면 최초 인터랙션에 반응할 수 없기 때문이다.
결국 페이지 내부 요소들을 조금이라도 컴팩트하게 가져가는 것이 중요한 키포인트라고 할 수 있다.
말은 쉽지만, 어느 것이 불필요한지 알기가 어렵다.
크롬 데브툴에서 어느 라인이 사용되지 않고 있는지 간단하게 확인할 수 있다.
한 번에 빠르게 로드가 되고, 부분 부분 업데이트가 되어야 한다는 뜻이다.
가령 인피니티 스크롤을 예시로 들어보겠다.
만약 해당 스크롤의 로직이,
페이지 접근 > 뷰포트 최하단 접근 감지 > 데이터 로드 > 컨텐츠 영역 렌더링
라면, 최초에 페이지가 렌더링 될때 인피니티 스크롤이 동시에 한 번 더 로딩 될 수 있다.
이 경우에 컨텐츠가 전체적으로 다시 그려지기 때문에 큰 부분의 렌더링 업데이트가 일어난다.
세미나에서는 실제 Ifland 개발자이신 신정민님께서 적용한 성능 최적화 사례를 말씀해주셨다.
위의 프로세스를 기반으로 성능을 개선하였다고 한다.
<script async>
<script defer>
Ifland의 경우에는 현재 사용하지 않는 스크립트는 제거하고, 사용하는 스크립트의 경우에는 렌더 블라킹을 유발하는 요소들을 <script defer>
로 변환했다고 한다.
jpeg
png
보다는 webP
및 AVIF
의 이미지를 추천.Ifland의 경우에는 webP
포맷을 선택하여 적용하였다고 한다.
데스크탑과 모바일에서 동일한 이미지를 사용하는 상황
위와 같이 해상도에 맞지 안흔 이미지를 사용하는 경우가 존재했다.
위의 예시는 약간 극단적이지만, 큰 이미지를 작게 사용하는 경우에 최적화 여지가 존재한다.
가령 데스크탑과 모바일을 같은 이미지를 사용하는 경우, 각각 이미지를 분리하는 것을 권장한다고 한다.
(모바일용 이미지의 경우에는 확대를 위해 두배정도 큰 이미지로 설정한다.)
위의 이야기는 개념적 설명이었고, 실제 마크업 관점에서 바라보았을 때 해결 방법은 아래와 같다.
<picture>
태그와 <source>
태그를 사용한다.실제로 위 솔루션을 적용하여 이미지 용량을 6-80% 가량 크게 감소시켰다고 한다.
핀터레스트
와 같이 이미지가 많은 경우를 생각하면 쉽다!loading="lazy"
를 적용preload
속성을 적용함이미지 파일을 로드하는 형식이 아닌,
svg
를 HTML의 인라인에 삽입 + preload
설정을 통해 로드 속도를 개선하였다고 한다.
svg
를 인라인에 추가하면 서버 요청하는 횟수도 없어지기 때문에 네트워크 양 감소로 이어진다고 한다.
코드 스플리팅 외에, 용량 자체를 감소할 수 있는 여지가 존재했다.
파일 자체를 압축하여 (gZip) 서버 네트워크 용량을 줄일 수 있었다고 한다.
impact fraction
distance fraction
Request header
옵션 추가를 통해 백엔드 브라우저 캐싱을 적용실제 수치적으로 많이 개선되었다는 것을 알 수 있었다!
defer
나 async
)WebP
나 AVIF
이미지 포맷을 적용해본다Image Lazy Loading
적용preload
옵션 적용preload
적용CWV
개선은 지속적인 노력이 요구되는 작업임개인적으로 사용자 중심으로 성능을 개선하는 문화를 만들 필요가 있다는 말이 와닿는다.
사실 어떤 프로젝트에서든 "성능 개선을 위해서 며칠정도..." 라는 이야기를 꺼내는 것을 상상하기 어렵다.
나조차도 성능 개선보다 당장 더 많은 프로덕트를 만들어내는 것이 중요하게 느껴질 때가 있는데, 프론트엔드 개발자가 아닌 사람의 입장에서는 더 심하게 느낄 것 같기 때문이다.
물론 상황 바이 상황으로 무엇이 중요한지 판단할 필요가 있겠지만, 처음 코드를 작성할 때부터 성능을 고려하며 작성하는 것이 가장 베스트가 아닐까하는 생각이 들었다.
역대급 길이의 포스팅이었지만, 개발자로 생활하면서 가장 깊게 고민하고 흥미롭게 생각하면서 작성한 글이 아닌가 싶다. 나 역시 이 글을 여러번 읽으면서 체화할 듯 하다.
세미나 전에 들어온 사전 질문에 대한 답변 시간이 있었다.
그 중 도움이 될만한 이야기들을 추려서 요약해보았다.
LCP
외에 뷰포트 박의 영역이 느려졌다는 것을 인지하고 개선할 수 있는가INP
가 등장Performance Insight
탭)Thread-holding
을 해서 일부러 느리게 만든 다음, 어떤 요소들이 늦게 뜨는지 직접 확인defer
할 수 없음defer
하는게 좋을 것 같음CDN 서비스
를 많이 활용함CDN 서버
에 분리하였다고 함)