본문 바로가기

IT/Frontend

프론트엔드 성능이 중요한 이유와 개선 방법

6월 22일(목) Devocean에서 진행한 "웹 프론트엔드 성능 최적화 방법 및 적용 사례"  세미나의 발표 내용을 기반으로 작성된 글입니다.

 

오늘 포스팅에서는 크게 두 목차를 다루겠습니다.

 

  1. 왜 프론트엔드 성능이 중요한가?
  2. 어떻게 프론트엔드 성능을 개선하는가?

왜 프론트엔드 성능이 중요한가?

속도가 느리면 유저는 이탈하기 마련

누구라도 긴 로딩이 꺼려지는 법 입니다. 실제로 한국의 경우 로딩이 2초만 걸려도 사람들이 이탈하죠. 실제로 Tokopedia라는 서비스가 로딩 시간을 3.78s에서 1.72s로 개선하니, 유저 평균 체류 시간이 15%, 세션 유지 시간은 23%나 늘었습니다. 그래서 서비스 속도를 빠르게 하는 것이, 유저 이탈을 막는 지름길이라 말할 수 있습니다.


그렇다면, 속도의 정의가 무엇이지?

앞서, 서비스 속도를 빠르게 하는 것이, 유저 이탈을 막는 지름길이라 말씀 드렸습니다. 그렇다면 과연 서비스 속도가 무엇일까요? 이를 나타내는 지표가 있으면 편할 것 같습니다.

 

이전에는 Page 수, Network Request 등 여러 속도 지표가 있었습니다. 하지만 이 지표들은 개발자의 관점에서 만들어진 표죠. 사용자가 사이트를 방문할 때 개발자 도구를 보고 Network Request 수를 일일히 확인하지는 않죠.

 

CWV(Core Web Vitals)

그래서 Google에서 CWV(Core Web Vitals)라는 재밌는 지표를 정의했고, 현재까지 널리 사용되고 있습니다. 이 지표가 널리 사용되는 이유는 Google이 만들어서일 수도 있지만, 이 지표가 사용자 관점을 잘 대변해주기 때문이예요.

CWV에는 크게 세 가지 지표가 있습니다.

  1. LCP(Largest Contentful Paint)
    사용자가 사이트 화면을 봤을 때, 가장 큰 컨텐츠가 얼마나 빨리 로드되는지?
    (대부분 LCP 컨텐츠는 이미지, 동영상 등의 미디어 컨텐츠입니다)
  2. FID(First Input Delay)
    초기 화면의 첫 번째 Input을 클릭했을 때. 사이트가 얼마나 빨리 반응하는지?
  3. CLS(Cumulative Layout Shift)
    얼마나 사이트 내용이 안정적인지?
    (광고가 사이트 내용을 가린다던지, 갑자기 내용의 위치가 바뀌어 버린다던지 하는 경우)

 

보시면 아시겠지만, 모두 사용자 입장에서 바라본 지표이죠. 이 지표가 높을수록 사용자는 사이트에서 좋은 경험을 얻을 가능성이 매우 높습니다. 그런데 FID는 맨 처음 Interaction만 나타내는 지표라는 지적이 많이 있습니다. 그래서 모든 Input에 대한 평균 응답 시간을 나타내는 INP(Interaction to Next Paint)로 대체될 예정입니다.

 

이처럼 CWV는 오직 사용자를 대변해 사이트를 바라보며, 지속적으로 기준을 수정 및 개선하고 있습니다. 그래서 CWV는 많은 관심과 사람을 받으며, 현재까지도 많이 사용되고 있죠.

 

CWV가 중요한 이유

이제 CWV는 SEO(Search Engine Optimization)에도 큰 영향을 끼칠 것입니다. Google Search의 발표에 따르면, 검색 엔진이 기존 HTTPS, 모바일 접근성 등과 더불어, 이 CWV도 평가에 고려하겠다고 합니다. 즉, 사용자 경험이 좋은 사이트를 우선적으로 보여주겠다는 것이죠.

 

대부분의 웹 서비스들은 SEO가 서비스 유입에 매우 중요한데, 왜 CWV 지표를 개선해야 하는지 분명이 보여주고 있네요.

 


그렇다면, 속도는 어떻게 측정할까?

앞서 CWV에 대해 간단하게 살펴 봤습니다. 그렇다면 어떻게 CWV 지표를 확인할 수 있을까요? 다행히 Google이나 여러 Third-Party 서비스들이 존재합니다. 서비스를 알아보기 전에, 간단하게 LAB Data와 RUM Data에 대해 알아봅시다. 

 

  • LAB Data: 사이트 개발자가 Tool을 활용해 시뮬레이션해서 얻은 데이터
  • RUM Data: Real-User-Monitoring이란 뜻으로, 실제 사용자의 여러 지표를 수집해 측정한 데이터

  • PageSpeed Insights
    RUM Data와 Lab Data를 둘 다 제공합니다.
  • Chorme UX Report
    Chrome 사용자들의 CWV를 집계해 Google DB에 저장한 뒤, 이를 기반으로 RUM Data를 보여줍니다.
    특별한 점은 Origin Level 별로 Data를 보여준다는 점 입니다.
    (map.naver.com, blog.naver.com 등을 모두 naver.com으로 간주합니다.)
  • Lighthouse
    Github 오픈소스로 제공하는 Tool로, LAB 데이터를 보여줍니다.
    특별한 점은 on-premise로 Network 없이 측정할 수 있어요.
  • Web-Vitals Extension
    Chorme Extension 입니다. 켜두면 사이트를 이동할 때마다 해당 사이트의 CWV를 보여줍니다.

추가로, 발표자 분께서 좋은 Tool을 소개해 주셨습니다. Google Engineer의 선택이니 믿음이 가네요 :)

  • WebPageTest.org
    third-party 분석 Tool 입니다. 신뢰도도 높고, 설정할 수 있는 항목이 많습니다. PageSpeed Insights와 병행해 활용하면 좋습니다.
  • Web-Vitals.js
    위 Extenstion과 다르게, 사이트에 설치하는 JS 라이브러리입니다. 대부분의 Tool은 Chrome 사용자만 집계해 RUM Data를 제공하는데, 이것은 여러 Browser 사용자들의 Data를 집계할 수 있어 RUM Data 수집에 좋습니다.

어떻게 프론트엔드 성능을 개선하는가?

LCP 최적화

LCP 자원, 즉 페이지에서 가장 큰 영역을 차지하는 컨텐츠가 HTML 문서에서 빨리 찾아져야 합니다. 아래는 이를 위한 테크닉들입니다.

 

  1. 미디어 Source를 꼭 HTML src 태그에 추가해 사용합시다.
    • 브라우저가 사이트를 렌더링할 때, CSS보다 HTML 파일을 먼저 읽습니다. 그러므로 CSS 속성 보다, HTML 태그에 직접 명시하는 것이 좋습니다.
  2. LCP를 먼저 다운받을 수 있도록 우선 순위를 설정합시다.
    • LCP 컨텐츠에 fetchpriorty="high" 속성을, LCP가 아닌 컨텐츠에는 loading=”lazy” 속성을 추가해 LCP 컨텐츠를 먼저 Web Server에서 가져오도록 합니다.
    • LCP 컨텐츠가 외부 서버에서 제공되는 경우, HTML Head에 <link rel=”preload”>을 추가합니다.
  3. CDN을 사용해 먼 거리에서도 빨리 다운받을 수 있도록 합니다.
    • 외국에서 접속하는 경우, Web Server와의 거리가 멀어 가져오는데 시간이 걸릴 수밖에 없습니다.
    • CDN은 전 세계 곳곳에 Edge Server를 둬 Content를 제공하므로, 외국에서 접속하는 유저의 전송 속도를 비약적으로 올릴 수 있습니다.

대부분의 웹 사이트는 이 방식으로 LCP 지표를 개선합니다. 물론 SSR(Server-Side-Rendering)인 경우, Client에서 할 작업을 Server에서 처리하는 경우도 있고, 비동기 프로그래밍을 활용해 추가로 개선할 수 있습니다.


CLS 최적화

위 사진의 Before 부분을 봐 주세요. Promo Banner와 Image가 로드되면서 내용이 밑으로 밀리고 있습니다. 이것 또한 CLS 감점 요소입니다. 이런 경우가 없도록, After처럼 Content의 영역을 미리 확보해놔야 합니다.

 

  1. 컨텐츠의 높이를 먼저 확보합시다.
    1. 정적 크기를 가진 요소의 경우, 고정 height 값을 설정하면 됩니다.
    2. 반응형 Layout 등, 동적 크기를 가진 요소는 aspect-ratio을 사용합시다. 요소의 가로, 세로 비율을 기반으로 브라우저가 알아서 맞춰 줍니다. 
  2. 과도한 애니메이션을 지양합시다. (특히 Layout이 흔들리거나, Blur 효과를 주는 등)
  3. 캐시를 활용합시다.
    1. 브라우저는 BFCache(Back/Forward Cache)가 있습니다. 사이트 내 페이지를 이동할 떄, Network Request를 보내는 대신, Cache에서 요소를 찾아 보여줍니다.
    2. 그런데 간혹 사이트나 서버에서 BFCahce를 막는 경우가 있으니, 잘 확인할 필요가 있습니다/

복잡한 반응형 Layout을 가진 사이트 등, 아무리 고민해도 방법이 없으면 min-height 속성을 써 봅시다. 요소의 최소 사이즈라도 미리 영역을 확보한다면, 그나마 컨텐츠가 덜 밀리겠죠.


FID 최적화

가장 어렵습니다. 곧 모든 Input의 반응 시간을 반영하는 INP 지표로 대체되기 때문에 모든 Interaction에 대해 깊게 고민해봐야 하죠.

 

가장 중요한 점은 긴 Task를 짧은 단위로 쪼개야 한다는 점 입니다.

위 사진의 Before와 After를 봐 주세요. Before는 다른 Interaction을 수행하려면 긴 Task가 끝나야만 실행 가능합니다. 반면 After는 Task 중간마다 Term이 있어 다른 Interaction이 실행될 수 있죠. 반응 시간이 훨씬 빨라질 것 입니다.

 

또한 Javascript는 대표적인 싱글쓰레드 언어로, 멀티쓰레딩(Multi-Threading)으로 Task를 병렬로도 수행할 수 없습니다. 최선의 방법은 Code Splitting이나 Webpack Bundling을 활용해 긴 Script를 작은 단위로 분할하고, 공통 Logic을 파악하는 등 불필요한 Script의 사용을 줄일 수밖에 없습니다.

 

  1. 지속적으로 분석 Tool을 보면서 개발합시다.
    • Chorme DevTool 등을 보면서, 얼마나 Script를 사용하는지, 메모리 사용량은 몇인지 보면서 개발합시다.
  2. 큰 Rendering Update를 지양합시다.
    • 예를 들어, Infinity Scroll은 전체 페이지 Update를 야기합니다. 보기에는 깔끔하고 좋지만, CWV에 매우 좋지 않습니다.
    • 또 다른 예로, 애니메이션을 위한 reqeustAnimationFrame() 함수가 있겠습니다. 1초에 60번씩 동작하면서, 전체 페이지를 Update해 큰 성능 하락을 야기합니다.
  3. 꼭 필요한 데이터만 수집합시다.
    • 마케팅을 위한 각 데이터들(페이지 이동, 결제 등) 중, 꼭 필요한 데이터만 수집합시다. 데이터를 수집하는 것도 Overhead를 야기하며, 사이트 속도를 느려지게 하는 큰 요소입니다.
  4. DOM Size를 작게 유지합시다.
    • 브라우저는 Dom-Tree를 그리고, HTML 요소를 불러옵니다. DOM Size가 크면 HTML 요소를 가져오는데 시간을 다 뻇겨서, Interaction을 위한 시간이 줄어듭니다.