감상문

오늘부터 나도 FE 성능분석가

ihl 2021. 5. 10. 18:52

 


  성능이슈를 맞닥드렸을 때 어떤 생각을 갖고 해결해나가야하는가에 관한 내용이다. 성능개선을 어떻게 시작하고 무엇을 고려해야할지 큰 그림을 어떻게 그려야할지 감을 잡게 해주는 강연이었다.

 

1. 성능 분석가의 관심사

  • 서버 성능 분석가: 서버가 얼마나 많은 요청을 처리할 수 있는가(Transition Per Seconds)
  • 사용자 입력에 얼마나 빠르게 반응할 수 있는가(Loading An Interaction)
    • 초기 로딩 속도: 얼마나 초기 페이지를 빠르게 볼 수 있는가
    • 인터렉션 속도: 애니메이션의 매끄러운 동작, 사용자 입력 시 버벅거림의 정도

2. 성능 개선 과정

  1) 대상 선정하기

    성능 개선을 했을 때 사용자의 만족도가 가장 올라갈 화면을 찾아 개선하는 것이 가장 효율적이다. 따라서 서비스에서 사용자가 가장 많이 보고 이용하는 화면이 무엇일지 알고있어야 한다.

 

  2) 개선 프로세스

    측정 -> 분석 -> 최적화 -> 측정 ... 목표에 도달할 때까지 반복

 

  3) 목표

    네이버: 초기 로딩기간 모바일 1.5s / PC 2s

    구글: 초기 로딩시간 1s / 사용자 입력에 대한 반응 0.1s / 애니메이션 16ms...

 

3. 초기 로딩속도 개선

waterfall chart

  1) 로딩속도 측정 / 분석

    개발자 도구 > NetworkTab에서 볼 수 있는 WaterFall 차트를 개선하는 것이 로딩속도 개선과 일치한다. WaterFall 차트의 높이를 줄이고, 폭을 줄이고, 간격을 당기는 것이 바로 그것이다. 추가적으로 총체적 점검도 필요하다.

 

  2) 높이 줄이기

    높이 줄이기는 Request 수를 줄이는 것과 동일하다. 특히 브라우저는 한 도메인에 동시 연결할 수 있는 갯수가 정해져 있으므로 요청의 갯수가 동시 연결 갯수를 넘지 않도록 하는 것도 중요하다.

 

    Request를 줄이기 위해선 코드, 이미지 등을 하나로 합친다. 대표적으로 CSS Sprite가 있다. 사용할 아이콘들을 하나의 이미지로 만든 후 사용할 때는 이미지의 영역을 잡아 해당 부분만 사용한다.

 

    DATA URI를 사용하는 것도 Request 수 감축에 도움이 된다. DATA URI는 파일을 문자열로 변환한 것으로, HTML에 이미지 대신 DATA URI를 포함시켜 이미지 불러오기 과정을 없애는 것이다. 단, base64 형식으로 변하면서 용량이 커지고, 이미지 캐싱이 안 된다. (작은 아이콘이 아주 많을 경우 유용하다고 한다.)

 

    초기 로딩 시 불필요한 자원을 삭제하거나 Lazy Load하는 것도 방법이다. 초기 로딩 시 필요없는 JS 모듈이나, 당장 뷰포트 안에 없는 이미지는 로딩 후에 별도로 불러오는 것이 좋다.

 

  3) 폭 줄이기

    폭 줄이기는 Request에 걸리는 시간을 줄이는 것이다. 주요 요소로 Initial Connection, TTFB, Content Download 시간이있다.

 

    Initial Connection은 초기 연결(HandShaking + SSL)에 걸리는 시간으로 이를 개선하는 최선의 방법은 HTTP2.0으로 옮기는 것이다.(HTTP2.0은 하나의 연결에 여러 스트림 전송 지원/Multiplexing Stream)

 

    TTFB(Time to First Byte) 브라우저가 HTTP 요청을 했을때 처음 응답(Byte)을 받은 시간으로, TTFB가 오래 걸린다면 서버 비즈니스 로직이 느린 것이다.

 

    Content Download가 오래 걸린다면 개발자는 컨텐츠의 크기를 줄이는 방법을 취할 수 있다. 주로 gzip을 적용한다.특히 이미지의 경우 실제론 200x180 사이즈가 필요한데 1280×780 이미지를 쓰는 등의 경우를 개선해보자. 이 때 요즘에 많이 사용되는 레티나 디스플레이는 같은 공간에 더 많은 픽셀을 그리므로 필요한 크기의 2배인 400*360을 가져오는 것이 더 적절하다. (자체 이미지가 아니라 이미지를 자를 수 없다면 이미지 CDN이나 imgix 로 이미지를 가공해서 가져오도록 하자)

 

  4) 간격 당기기

    간격 당기기는 Request 요청을 최대한 왼쪽으로 당기는 것이다.(최대한 빨리 Request 시작하기) 이를 위해선 HTML 파일의 렌더링 과정을 알아야 한다. HTML 파일의 head태그 내의 자원은 병렬로딩 되며, head가 끝나야 body 그리기가 시작된다. 그리고 JS는 DOM을 조작할 수 있으므로 JS 실행 시 렌더링이 멈춘다. 따라서 head 태그에는 CSS와 필수 JS만 넣어야 하며, JS는 body 태그 마지막에 넣는 것이 좋다. script 태그의 defer, async 속성을 이용하는 것도 좋다.

  • defer: script가 body가 모두 그려진 후에 실행되도록 하는 태그. 
    • DOM 제어와 관련 있는 스크립트
    • 구 버전 브라우저는 지원하지 않을 수 있음
  • async: script가 비동기적으로 실행(브라우저가 페이지 파싱하는 동안에 스크립트 실행)
    • Google Analysistic 코드

    CSS 파일에서 폰트, 이미지 사용 시에는 CSS파일을 부른 후에 폰트/이미지가 로딩 되므로 link 태그의 rel 속성을 preload로 설정하여 폰트나 이미지를 미리 로딩하도록 할 수 있다. 또한 HTTP2의 경우 Server Push를 사용하여 정적 HTML페이지와 함께 JS, CSS, 이미지가 로딩되게 할 수 있다.

 

4. 인터렉션 속도 개선

  인터렉션 속도를 개선하는 것은 브라우저의 메인 스레드의 작업을 최소화하는 것과 동일하다. 이를 위해선 브라우저 Rendering Pipeline을 알고 있어야 한다. 브라우저는 다음과 같은 과정으로 화면을 렌더링한다.

  • DOM(HTML) / CSSOM: DOM요소 CSS 요소를 각각 계층적으로 가공한다.
  • Render Tree: DOM과 CSSOM을 조합한다 ex. p 태그의 폰트는 16px...
  • Layout: 브라우저 상의 각 요소의 위치와 크기를 계산한다.
  • Paint: 색을 넣는다.
  • Composite: 각 레이어를 합성한다.

  그런데 JS로 DOM을 변경하면 메인 스레드에 의해 Rendering Pipeline이 재동작하게 된다. 따라서 이들을 최대한 덜 동작시키는 방향으로 JS 코드를 작성해야 한다. csstriggers.com사이트에 가면 CSS의 어떤 속성이 얼만큼의 렌더링 파이프라인을 재동작 시키는지 알 수 있다. 예를 들어 width이라는 속성을 변경하면 모든 렌더링 파이프(Layout-Paint-Composite)에서 작업이 필요(Reflow)하다. 그러나 transform은 Composite 과정만 필요하므로 width 보다는 transform속성을 쓰는 것이 메인 스레드 작업을 최소화할 수 있다.

 

  렌더링 파이프라인에서 Composite 작업은 이미 그려진 것을 겹치는 작업이므로 비용이 가장 적게 들며, 메인 스레드(CPU)가 아닌 GPU가 역할을 수행한다. 따라서 GPU의 도움을 받기위해 별도의 레이어를 만드는 CSS 속성(translate3d, scale3d, positon: fixed)을 사용하면 성능 개선에 도움이 된다. (그러나 레이어 초기 구성 자체는 CPU가 진행하며 원래 비트맵 정보를 복사하므로 메모리가 2배 필요하다 따라서 꼭 필요한 부분만 레이어로 만들어야 한다.)

 

  대부분의 디스플레이는 초당 60번 새로고침 되므로 브라우저에서는 초당 60프레임으로 페이지를 렌더링한다. 즉, 60FPS가 애니메이션이 가장 부드럽게 보이는 초당 프레임 수인 것이다. 이를 위해 1프레임은 16ms 내에 완료되어야 한다. 애니메이션은 requestAnimationFrame()로 16ms 주기로 작성하고, 코드 실행시간도 16ms 내로 줄이는 것이 좋다.