Published on

웹 성능 최적화를 진행 해

Authors
  • avatar
    Name
    sulmo
    Twitter

성능 최적화를 진행하는 이유?

사용자가 서비스를 이용하는데 불편함을 느끼기 때문..

  • 서비스 페이지에 접근할 때 오래걸려서 불편. ⇒ 비어있는 페이지가 지속된다.. 로딩이 오래걸린다 등..
  • 기능을 사용하는데 불편. ⇒ 사용자 이벤트에 반응하지 않거나. 버벅거림 등..

어떠한 자원이든 우리 서비스를 이용 시, 불편함을 느끼지 않도록 개선해야 유저가 우리를 긍정적으로 바라볼 것

어떻게 개선할 건가?

일단 크게 두 부분으로 나눠 정리 했습니다.

  1. 초기 로딩 속도 개선 방안

    • 필요없는 요청 제거 혹은 lazy

      • 필요없는 JS를 번들링에서 분리하고 필요시 받아오기 ( CodeSplitting → Dynamic Import )

        우리는 웹팩을 통해 애플리케이션을 한 파일로 압축하여(번들링) 요청시간을 줄였다. 하지만 페이지가 많아질 수록 한 페이지 접속을 위해 필요하지 않은 나머지 페이지를 함께가져와 압축의 의미가 없어지는 경우가 있다.

        이를 위해 웹팩에서는 코드 분리를 위해 Dynamic Import를 지원한다. 이를 통해 원하는 시점에 필요한 번들을 가져오도록 하자. (페이지단위, 컴포넌트단위 등등)

      • 뷰 포트 바깥에 있는 이미지는 필요해질 때 요청 후 받아오기 (virtual scroll)

    • Request 수 줄이기

      • CSS Sprite: 필요한 여러 이미지(아이콘)들을 한 파일로 묶어서 한 요청에 가져오기(과거에 많이 했다고..)
      • Data URI: 이미지가 로딩 되기전 사용되는 기본 이미지와 같은 파일들은 HTML 요청에 포함시켜서 요청 하나 줄이기. (좀 드럽..) 사용법
    • Request 요청 별 용량 줄이기

      • 용량이 많거나 네트워크가 느린 경우이다. 용량을 줄이거나 나눠서 가져오거나, 난독화 병합 등등 (웹팩), 이미지는 디코드 비용 줄이기 (아래 이미지에서 pdfmake가 2.1MB 정도 함)

        Bundle
    • inital connection(초기 커넥션)

      커넥션 방식
      Bundle
      1. 단기 커넥션

        • HTTP 1.0의 기본 모델
        • 하나의 요청이 응답을 받고 나서야 다음 요청을 할 수 있음

        하나의 커넥션은 하나의 요청 후 서버와 연결이 끊어지기 때문에 매 요청마다 서버와 핸드쉐이크 단계부터 다시 진행한다.

      2. 영속 커넥션(keep-alive 커넥션)

        • HTTP 1.1 기본 모델
        • 같은 서버에 대한 요청이 여러개일 때, 한번 서버와 연결된 커넥션을 끊지 않고 유지하여 커넥션을 연결하는데 쓰이는 오버헤드를 줄인다. (단기 커넥션 단점 보완)
      3. 파이프라이닝 커넥션

        • HTTP 파이프라이닝은 모던 브라우저에서 기본적으로 활성되어있지 않음 (원인 : Head-Of-Line Blocking 문제로 여러 파이프라인 중 어떤 요청에 병목이 생겨서 전체적인 레이턴시가 늘어난다는 맥락으로 일단 이해 함)

        • 동시에 여러개의 요청을 처리하는 방법은 여러개의 영속 커넥션을 연결하는듯..?

        • 브라우저에서 제한 커넥션 수를 제한하는 이유

          커넥션 수가 제한이 없으면, 서버는 각 사용자(N)별로 연결된 커넥션의 수(M)만큼인 M*N의 커넥션을 유지해야 함.

          VersionMaximum connections
          Internet Explorer® 7.02
          Internet Explorer 8.0 and 9.06
          Internet Explorer 10.08
          Internet Explorer 11.013
          Firefox®6
          Chrome™6
          Safari®6
          Opera®6
          iOS®6
          Android™6
      4. HTTP 2 커넥션

        • 영속 커넥션과 파이프라이닝의 개선 버전
        • 멀티플렉싱: 멀티플렉스 알고리즘 사용으로 커넥션 수는 1개이지만 내부에 스트림에 제한이 없어 동시에 여러 데이터를 받아올 수 있음

      도메인 샤딩: 여러 서브 도메인으로부터 모듈을 다운로드 받는 방법이 있다. 도메인당 최대 커넥션 수가 제한되어있는 경우 사용하는 방식이다.

    • 블락 리소스 정리 : 브라우저 렌더링 과정에의해 몇몇 화면이 그려지기 전에 추가적인 데이터를 가져오는데 이런 요소 줄이기

      • script를 body 아래로 내려 일단 dom부터 그리기(또는 태그에서 defer , async 사용하기)
        • 기본 스크립트 : html에 파싱 중 스크립트를 만나면, 스크립트를 받아오고 실행하고 다시 DOM파싱을 시작함
        • defer: DOM과의 의존성이 없는 기능 요소들 가지고 올 때 사용한다, 스크립트를 만나면 요청을 날리고 모든 DOM이 렌더링 되고나서 스크립트를 실행한다.
        • async: 스크립트를 만나는 시점에 요청이 병렬로 시작되며, 응답이 올때까지 DOM을 파싱하고 이후 응답이 완료되면 스크립트를 실행한다. 스크립트를 실행하는동안은 DOM파싱을 멈춤
      Bundle
    • HTTP2 Server Push(서버측에서)

      이전까지는 클라이언트에서 최적화를 진행하는 부분이었고, html을 받은 이후 (html)내부에 js나 css를 확인시 그때 다시 요청을 진행했었다.

      반면 Server Push는 html 요청할때 나머지 css, js, 이미지를 같이 push 해줄 수 있다.

    • HTTP3

      이번에 초기로딩을 위해 HTTP에대한 버전별 사양들을 처음 알아보게 되었다. 2022년인 지금은 HTTP 1점대는 거의 보이지 않으며, 대부분이 2를 사용 중인 것 같다. 그중 구글은 HTTP3를 사용 중이였고 차이가 궁굼해 잠깐 특징만 얕게 알아봤다.

      Bundle

      QUIC라는 프로토콜 기반으로 동작하는 HTTP이다. 기본적으로 HTTP를 통한 통신시 속도 개선을 위해 프로토콜 영역 까지 변경되어 3버전의 HTTP를 사용시 성능향상이 가능하다.

      • 기존의 TCP기반의 통신이 아닌 UDP 통신으로 변경되었다. (TCP/UDP)

        • 변경 원인

          TCP/UDP 의 차이를 아주 간단하게 정리하면, TCP는 신뢰성이 높고 느리다, UDP는 신뢰성이 낮고 빠르다 정도로 정리되는데, HTTP의 개선사항 중 무게를 둔건 속도 였다. TCP는 워낙 오래된 프로토콜이기도 하고 커널까지 내려가는 로우 레벨에서 정의되어 있기 때문에 이걸 뜯어고치는 것도 만만치 않은 대작업이라 UDP를 선택한 것이다. (UDP는 헤더가 널널해 커스텀하기 좋은 프로토콜이기도 하다.)

        → 핸드쉐이크 과정이 사라지며

      • UDP기반의 QUIC 프로토콜을 구글이 만들었다.

      • 2022년 6월 6일 RFC 9114로 표준화 되었다. 표존화된지 얼마 안되어 정형화된 사용방식은 잘 안보였으나, 표준화 전부터 CloudFlare를 통해 사용이 가능했다.

      • 패킷 손실 대처

        기존 TCP는 송신 측이 패킷을 보낸 후 타이머를 사용하여 시간을 재고, 일정 시간이 경과해도 수신 측이 적절한 답변을 주지 않는다면 패킷이 손실된 것으로 판단하고 해당 패킷을 다시 보내는 방식을 사용

        QUIC는 헤더에 별도의 패킷 번호 공간을 부여, 이 패킷 번호는 패킷의 전송 순서 자체만을 나타내며, 재전송시 동일한 번호가 전송되는 시퀀스 번호와는 다르게 매 전송마다 모노토닉하게 패킷 번호가 증가하기 때문에, 패킷의 전송 순서를 명확하게 파악할 수 있다.

    • 웹폰트 확장자는 woff → woff2 : 용량이 줄음

  2. 인터렉션 속도 개선 ⇒ 메인 쓰레드를 괴롭히지 말자

    JS > Style > Layout(reflow) > Paint(repaint) > Composite
    

    위 이미지는 렌더링 파이프라인이다.

    Style : 최종 스타일을 계산하는 과정

    Layout : DOM의 배치와 크기 계산 (reflow)

    Paint : 화면에 그리기 (repaint)

    Composite: 레이어 조합

    • reflow, repaint를 발생시키는 CSS Modification 줄이기 (Layout, Paint 과정 발생 안하도록) csstriggers에서 css속성별 작동하는 영역을 확인해보자

      • element를 옮기거나 모양을 바꿀때 : height, width, left, right, top, bottom은 reflow를 발생시키지만 transform은 reflow repaint를 모두 발생시키지 않으므로(GPU) 뭔가 옮기거나 늘리거나 줄이거나 할때 가능하면 transform을 사용하는게 좋다
      • 투명도 바꿀때 : opacity 역시 두 과정 다 일어나지 않으므로 visibility나 display보다는 opacity 사용하는게 더 좋다
      • transform과 opacity를 제외하고 위치 옮기거나 크기 바꾸는건 대부분 reflow를 발생시키고, 색깔이나 굵기같은 겉모습을 바꾸는 것은 대부분 repaint를 발생시키는 것을 알고있으면 좋당.. 그리고 reflow가 일어나면 repaint가 반드시 일어남
      • visiblitliy: invisible 은 레이아웃 공간을 차지하기때문에 reflow의 대상이 되지만 display:none은 렌더트리에서 제외되기 때문에 후자를 사용하는게 더 좋다
    • Composite : GPU를 이용해 레이어들의 위상을 변경해 겹쳐주는 단계이다.

      GPU가 해당 작업을 할때 각 레이어 층의 변경만 이뤄지는 작업이기 때문에 비용이 크지않다. Composite 단계를 활용할 수 있다면 적절하게 레이어를 구성하여 사용하는 것이 좋다.

      • 브라우저가 규칙에 따라 레이어를 구성 fixed와 같은 포지션관련 범위는 레이어로 구성됨
      • 명시적으로 레이어를 구성 translate3d, scale3d, matrix3d, will-chage…와 같은 css 속성이 존재함(fe-conf처럼 재밌는 페이지 만들기 좋겠음..)
    • Canvas vs Svg

      CanvasSVG
      픽셀(pixel) 기반모양(shape) 기반
      단일 HTML 요소DOM의 일부분이 되는 다중 그래픽 요소
      스크립트(script)를 통해서만 수정할 수 있음.스크립트(script) 및 CSS를 통해서도 수정할 수 있음.
      그래픽이 주작업인 게임에 적합함.렌더링 영역이 넓은 응용 프로그램(application)에 적합함.
    • svg 요소의 성능은 화면이 크거나, 픽셀 수가 적을 경우(<1만)에 좋다.

    • canvas 요소의 성능은 화면이 작거나, 픽셀 수가 많을 경우(>1만)에 좋다.


Web Vital 구간

Bundle
DCL 이전상태 Bundle

DOM Content Loaded (WebApi Event)

CSS, 이미지 및 서브 프레임이로드를 완료 할 때까지 페이지의 초기 HTML 문서가 완전히로드되고 구문 분석 된 시점을 표시합니다.

Bundle

개선 방법

  • block resource를 처리가 되어야 html이 파싱됨

SPA인 console은 어차피 lottie 이 외에 크게

First Content Paint

페이지가 로드되는 시간부터 내용의 일부가 렌더링 될 때까지 시간을 측정합니다.

Bundle

Largest Contentful Paint

로딩 성능을 측정합니다. 우수한 사용자 경험을 제공하려면 페이지가 처음으로 로딩된 후 2.5초 이내에 LCP가 발생해야 합니다.

Bundle

페이지의 기본 콘텐츠가로드되었을 때 페이지로드 타임 라인의 점을 표시합니다.

필요할까

requestIdleCallback

webworkers

Using Web Workers - Web APIs | MDN

참고

성능 최적화 개선 작업 프로세스

  1. 측정
  2. 분석
  3. 최적화

반복

그럼 언제까지? → 사용자가 만족할 때 까지 == 초기 로딩 속도가 1~3초 일때까지

FE 앱 성능 개선의 영역

성능 개선을 위한 영역이 크게 2가지가 있습니다.

  1. 초기 로딩 속도
    • 서비스를 요청시 얼마나 빨리 페이지를 볼 수 있는가?
    • 우리의 경우는, sitie initializing 과정에 진행되는 일들이 많으므로 이 또한 2가지로 나눌 수 있다.
      1. 사이트 로더를 얼마나 빨리 만날 수 있는가(중요도가 높음). 최적화됨.
      2. 진입하려는 페이지를 얼마나 빨리 만날 수 있는가(중요도가 낮음)
        1. 첫 페이지에 필요한 자원들만 가져오도록 함
  2. 인터렉션 속도
    • 서비스를 사용할 때 버벅거림, 애니메이션이 매끄러운가 등
    • 화면에서 유저의 입력에 반응을 얼마나 빨리 하는가

CloudForet의 1차 개선 목적

“처음 대시보드(메인) 페이지 로딩 시, GNB가 렌더링 되었으나 클릭시 화면이동이 이뤄지지 않음 → 메인페이지의 컨텐츠가 그려지지 않아도 GNB의 화면 이동이 작동하길 원함”

  1. GNB 로딩을 최우선 적으로(webpack과 서버단에서 처리 필요할지도) ⇒ 초기 로딩 속도 개선 방안
    1. gnb 와 app 영역 분리
    2. js로 라우팅이 동작하기때문에 gnb가 재빠르게 반응하지 않음. a 태그라면 바로 반응할텐데… vue router를 먼저 가져온다거나, vue router 설정을 한다거나? 등의 방식으로 개선할 수 있을지도.
  2. 불가능하면 대시보드 페이지 우선순위로 렌더링(대시보드 페이지 로딩속도를 최적화) ⇒ 인터렉션 속도 개선 방안
    1. 스크롤 영역에 있는 위젯들만 먼저 로딩
    2. 유저가 마우스를 클릭한 영역에 대한 js를 먼저 가져와 입혀서 실행
      1. 스레드를 강제로 홀드할 수 있는 방법이 있을까?
    3. 차트 자체를 가져오는 시점을 늦춘다 (import 자체를 늦춘다)
  3. 모든 화면이 정상 작동하기 전까지 splash 화면을 띄운다
  4. 암차트를 조진다?

웹 사이트 성능 최적화 - 사람인

실제로 적용해본 성능 최적화 기본 정리

브라우저 리플로우 최적화

기술 부채가 쌓여있는 상황에서 서비스 성능 임팩트있게 개선하기

FE 체크리스트