프론트엔드 성능 측정 및 개선

kim yeeun·2023년 7월 14일
1

코드 최적화

목록 보기
1/1

브라우저 렌더링

  1. HTML, CSS, JS, image, font 파일 등 렌더링에 필요한 리소스를 요청하고 서버로부터 응답받는다.
  2. 브라우저의 렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 DOM과 CSSOM을 생성하고 이들을 결합하여 Render Tree를 생성한다.
  3. 브라우저의 자바스크립트 엔진은 서버로부터 응답된 자바스크립트를 파싱하여 AST (Abstract Syntax Tree)를 생성하고, 바이트코드로 변환하여 실행한다. 이때 자바스크립트는 DOM과 CSSOM을 변경할 수 있다. 변경된 DOM은 다시 Render Tree로 결합된다.
  4. Render Tree를 기반으로 HTML 요소의 Layout을 계산하고 브라우저 화면에 HTML 요소를 Painting 한다
    • 요약 : Javascript ⇒ Style ⇒ Layout ⇒ Paint ⇒ Composite
      • Javascript : 페이지에 DOM 요소 추가 등 시각적 변화를 일으키는 작업을 처리
      • Style: CSS를 어떤 DOM 요소에 적용해야할 지 계산
      • Layout: 각 요소의 너비나 위치를 계산해 화면 상에 배치
      • Paint: 각 요소에 배경색, 글자 색, 그림자 등과 같이 픽셀을 채움
      • Composite: 이전 과정에서 생선된 레이어를 병합

렌더링 차단

HTML 마크업을 파싱하여 DOM을 빌드하는 동안 parser에서는 스크립트를 발견 할 때마다 파싱을 중지하고 스크립트를 실행해야 HTML을 계속 파싱 할 수 있다.

  • 최적화 방법
    • body 아래에 script를 놓는다
    • defer 속성사용: 스크립트 다운로드는 비동기적으로 실행되지만, JS 파싱과 실행은 HTML parcing이 완료된 직후 (DOMContentLoaded)에 진행된다.
    • async 속성 사용: 스크립트 다운로드가 끝나면 바로 실행된다. HTML parcing 중간에 실행될 수 있다.
    • window.onload 사용

CSS 성능 향상

  • Reflow : 사용자에 의해 발생하는 이벤트로 인해서 HTML요소가 추가되거나, 기존 요소의 스타일이 바뀌게 된다. 이련 변경을 통해 영향을 받게되는 모든 노드에 대해서 렌더 트리 생성과 레이아웃 과정을 다시 수행하게 된다.
    • 대표적인 속성: position, width, height, margin, padding, border, border-width, font-size, font-weight, line-height, text-align, overflow
  • Repaint : 리플로우는 렌더 트리를 생성하고 레이아웃 과정을 다시 수행하는 것이고, 실제 이 결과를 화면에 그려지기 위해서는 다시 페인팅 단계를 거친다. 레이아웃에 영향이 미치지 않는 단순한 생상 변경 같은 변경사항은 리플로우 수행 없이 리페인트만 수행하게 된다.
    • 대표적인 속성: background, color, text-decoration, border-style, border-radius
  • Reflow가 일어나면 브라우저가 전체 픽셀을 다시 계산해야 하기때문에 되도록 Repaint 속성을 사용해 스타일을 작성하는 것이 좋다.
  • CSS 압축하기.
  • 사용하지 않는 CSS 제거. CSS-in-JS를 사용하면 CSS 관리가 수월해진다.
  • @import는 CSS파일의 렌더링 속도를 느리게 하므로 사용을 자제한다.
  • Reflow 대상인 height, width, top, right, bottom, left 대신 transform을 활용한다.
  • CSS 셀렉터를 구조화하는 방법에 따라서 브라우저가 CSS를 매칭하는데 필요한 속도가 달라진다. 브라우저는 셀렉터를 오른쪽에서 왼쪽으로 읽기 때문에 자식에서 부모로 거쳐서 올라가게 된다. 따라서 원하는 요소에 class나 id를 지정하여 선택하도록 한다.
  • 인라인 스타일은 웹페이지가 그려지면서 레이아웃에 영향을 미치면서 추가로 리플로우를 발생시킨다. 따라서 사용을 지양한다.

성능측정

  1. 성능측정의 의의

    • 서비스의 지연 속도가 늘어날수록 이탈률이 급격하게 증가, 구매 전환률 또한 감소한다.
    • 사용선 개선을 통한 이익 증대를 이끌어 낼 수 있다.
    • 측정할 성능 : 로딩 속도, 렌더링 시간, 메모리 누수 확인
  2. 메모리 누수

    • 할당 자원이 제때 해제되지 않고 계속해서 남아있는 현상
    • 대부분은 GC에 의해 해제가 되지만, 아래의 이유로 인해 해제가 되지 않는 경우가 있음
      • 전역변수
      • 해제되지 않은 Timer, Callback
      • DOM 외부에서의 참조
      • Closer
  3. 로딩 속도 측정 지표

    • TTFB (Time To First Byte) : 페이지를 요청했을 때 서버에서 데이터의 첫 번째 바이트가 도착하는 시점. 주로 서버의 성능과 직결된다.

      • 최적화 방법
        • 서버 애플리케이션 로직 최적화
        • DB 쿼리 최적화 또는 더 빠른 DB로 마이그레이션
        • 더 많은 RAM 또는 CPU를 갖도록 서버 하드웨어 업그레이드
    • FP (First Paint) : 첫 픽셀이 그려지는 시점

    • DCL(DOMContentLoaded Event) : DOM이 준비되고 그 시점에 자바스크립트 실행을 차단하는 스타일시트가 없는 시접을 표시. 즉, 이제 잠재적으로 렌더 트리 생성이 가능하다. DCL이 빠를수록 다음 로직이 더 빠르게 실행된다.

    • FCP (First Contentful Paint) : 페이지가 로드되기 시작하고 컨텐츠의 일부 (텍스트, 이미지, svg, canvas 등)가 화면에 보이지 시작할 시점

      Untitled

      • 최적화 방법
        • 렌더링을 block 하는 리소스 제거
        • CSS minify
        • 사용하지 않는 CSS 제거
        • 요청할 origin에 preconnect (외부 도메인의 리소스를 참고하는 것을 브라우저에게 미리 알리는 것)
        • 리소스 preload (현제 페이지에서 사용될 것이 확실한 리소스들을 빠르게 가져오기)
        • 서버 응답 시간 (TTFB) 줄이기
        • 여러 페이지 리다이렉트 줄이기
        • 지나치게 큰 네트워크 payload 피하기
        • 효율적인 캐시 정책으로 정적 자산 제공
        • 과도한 DOM size 피하기
        • 중요한 요청의 depth를 최소화
        • 웹 폰트 로드 중에 텍스트가 계속 보이도록 함
        • 요청 수를 줄이고 전송 크기를 작게 하기
    • FMP (First Meaningful Paint) : 브라우저가 페이지의 주요 컨텐츠들을 렌더링하기 시작하는 순간. FMP는 페이지로드의 작은 차이에 지나치게 민감하므로 인관성이 떨어짐. 따라서 LCP를 측정하는 것을 권장

    • LCP (Largest Contentful Paint) : 페이지에서 가장 용량이 큰 컨첸츠가 표시되는 시점. FCP의 경우 페이지에 스플래시 화면이 표시되거나 로딩중 아이콘이 표시되는 경우도 포함하므로 사용자와 크게 관련이 없다.

      Untitled

      Untitled

      • 최적화 방법
        • 느린 서버 응답 시간 해결
          • 서버 최적화
          • 가까운 CDN으로 라우팅
          • asset 캐싱
          • cache-first HTML 페이지 서빙
          • 서드파티 origin 리소스 preconnect
        • 렌더링을 block 하는 JS 및 CSS 해결
          • CSS block 시간 단축
            • CSS minify
            • 중요하지 않은 CSS는 defer로 load
            • 중요 CSS는 inline 으로 load
          • Javascript block 시간 단축
            • JS 파일 minify 및 압축
        • 느린 리소스 (img, svg, video, ...) 로드 시간 해결
          • 이미지 최적화 및 압축
          • 중요한 리소스 preload
          • 텍스트 파일 압축
          • 적응형 리소스 서빙
            • 예: 네트워크가 느린 경우 video 대신 image 서빙
          • 서비스 워커를 통한 자산 캐싱
        • 클라이언트 사이드 렌더링의 경우
          • 중요한 자바스크립트 최소화
            • 자바스크립트 minify
            • 사용하지 않는 자바스크립트 defer로 load
            • 사용하지 않는 polyfill 최소화
          • 서버사이드 렌더링 사용
            • TTFB 증가, TTI 증가를 고려해야 함
          • pre-rendering 사용
            • TTI는 증가할 수 있으나 TTFB는 SSR보다 나음
    • CLS (Cumulative Layout Shift) : 페이지가 로드되기 시작하는 시점과 lifecycle 상태가 숨김으로 변경되는 시점 사이에 발생하는 모든 예기치 않은 레이아웃 이동의 누적 점수를 측정. 일반적으로 리소스가 비동기적으로 로드되거나 DOM 요소가 기존 컨텐츠가 있는 페이지에 동적으로 추가될 때 발생

      Untitled

      • 최적화 방법
        • 이미지나 동영상 요소에 크기를 미리 지정하거나 임시 박스같은 것으로 필요한 공간을 확보
        • 사용자 interactive 응답 외에 기존 컨텐츠 위에 컨텐츠를 넣지 않기
        • layout 변경을 트리거하는 css property보다 transform 애니메이션을 사용
    • TTI (Time to Interactive) : 웹페이지가 완전히 상호작용이 가능(interactive)하게 되는 시점을 나타낸다. 컨텐츠를 볼 수 있지만 스크롤 할 수 없거나 항목을 클릭해도 효과가 없으면 interactive 하지 않은 것이다.

      • 최적화 방법
        • JS minify
        • 요청할 origin에 preconnect
        • 리소스 preload
        • 타사 코드의 영향 범위 감소
        • 중요한 요청의 depth를 최소화
        • JS 실행시간 단축
        • 메인 스레드 작업 최소화
        • 요청 수를 줄이고 전송 크기를 작게 유지
    • FID (First Depth Delay) : 사용자가 페이지와 처음 상호작용한 (클릭 또는 키 입력 등) 시간부터 브라우저가 실제로 이벤트 핸들러 처리를 시작할 수 있는 시간까지의 시간을 측정.

      Untitled

      Untitled

      일반적으로 입력 지연은 브라우저의 기본 스레드가 다른 작업 (예: 대용량 js 파일 파싱)을 실행 중일때 발생한다. 위와 같이 메인 스레드에서 작업중인데 user input이 발생하면 브라우저가 이에 대응하는데 FID만큼의 시간이 소요된다.

      • 최적화 방법
        • 긴 Tasks 분할하기
          • 메인 스레드를 50ms 이상 차단하는 긴 Task를 분할하기 (TBT 개선)
          • 코드 스플리팅
        • 상호작용 준비를 위한 페이지 최적화
          • JS 코드 및 기능을 점진적으로 load
          • SSR의 경우 논리를 서버측으로 이동하고 코드 스플리팅을 고려해야 함
          • cascading data fetch를 최소화
          • 서드파티 코드 로딩시간 고려해야 함
        • web worker 사용하기
          • 백그라운드 스레드에서 자바스크립트를 실행할 수 있음
        • JS 실행시간 단축
          • 사용하지 않는 자바스크립트 defer로 load
            • 특별한 이유가 없는 한 서드파티 스크립트는 defer 또는 async로 로드되어야 함
          • 사용하지 않는 polyfill 최소화
            • module / nomodule 패턴을 활용하여 개별 번들 제공
    • TBT (Total Blocking Time) : TBT는 주 스레드가 input 응답을 막을 정도로 오래 차단 되었을 FCP와 TTI 사이의 총 시간을 나타낸다. 긴 작업의 차단 시간은 50ms를 초과하는 시간으로 계산한다. 즉 task의 작업 시간이 250ms이면 TBT는 200ms이다.

      Untitled

      • 최적화 방법
        • 타사 코드의 영향 범위 감소
        • JS 실행시간 단축
        • 메인 스레드 작업 최소화
        • 요청 수를 줄이고 전송 크기를 작게 유
    • L (onload Event) : 각 페이지 로드의 최종 단계로, 브라우저가 추가 애플리케이션 로직을 트리거할 수 있는 onlaod 이벤트를 발생시킴

    SSR과 CSR에서의 성능 메트릭

    Untitled

    1. SSR : SSR은 일반적으로 빠른 FP와 FCP를 가진다. 또한 많은 JS를 서버단에서 실행하므로 TTI
      도 빠르게 수행할 수 있다. 다만 서버에서 페이지를 생성하는데 시간이 걸리기 때문에 TTFB가 느려질 수 있다.
    2. SSR with hydration : 유려한 페이지 전환과 인터랙션을 위해 첫 페이지 렌더는 SSR, 이후에는 CSR
      로 이루어지는 SSR with hydration이라는 기법도 존재한다. Next.js를 활용하면 쉽게 구현할 수 있다.
    3. CSR : CSR에서는 리소스를 가져오기만 하면 되기 때문에 빠른 TTFB를 가진다. 하지만 FCP가 느리고 자연히 TTI는 그보다 더 느리다는 단점이 있다.

성능 측정 도구

  • Lighthouse: 크롬 개발자 도구에서 제공
    • 구글이 제시한 WebVitals를 이용하여 성능을 측정하고 결과를 제공
  • 개발자 도구의 Performance 탭
    • 원하는 구간에 녹하하여 네트워크, 렌더링, 메모리 전반에 관한 사항을 확인
  • 개발자 도구의 Memory 탭
    • 현재 메모리의 사용을 확인
    • 각 스냅샷의 차이를 알 수 있다.
    • 누수가 발생하는 항목을 찾을 수 있다.
  • 개발자 도구의 Network 탭
    • 네트워크 요청이 처리되는데 얼마나 시간이 걸리는지 확인
    • 옵션을 통해 모바일 환경과 유사한 네트워크를 설정해 시뮬레이션도 가능
  • 리엑트 프로파일러
    • 컴포넌트별 렌더링 시간 파악 가능
    • 사용자의 인터렉션에 대한 변화를 추적
  • 모니터링 도구
    • 실시간 서비스 중에 성능을 측정
    • sentry

참고: https://ui.toast.com/fe-guide/ko_PERFORMANCE#강제-동기-레이아웃-최적화

참고: https://velog.io/@yrnana/웹사이트-성능-메트릭#주요-metrics지표-항목

profile
안녕하세요 프론트엔드 엔지니어 김예은입니다.

0개의 댓글

관련 채용 정보