본문 바로가기

프로그래밍 공부내용/자바스크립트(JS)

기깔나게 웹 성능 최적화하기

웹 성능 최적화?

'웹 성능 최적화...' 들으면 가슴이 벅차오르지만 동시에 쉽지 않은 주제이기도 합니다.

Google / SOASTA Research, 2017

구글 연구 자료입니다. 시간에 따라서 이탈률이 어마어마하게 차이가 나게 됩니다.

 

구글같이 큰 서비스는 로딩시간이 0.1초만 차이나도 엄청난 돈 차이가 날 것 같습니다.

 

웹 성능을 개선하려면 우선 사용자가 웹에 접근하게 됐을 때 무슨 일이 일어났는지 알아야합니다.

 

간단한 그림을 준비했습니다!

 

그림의 각 단계별로 최적화 하는 방법이 있습니다.

 

저는 프론트엔드 개발 담당이기 때문에 Client / Client <-> Front Server 에서 최적화를 진행하였습니다.

(글을 정리하다가 궁금해서 백엔드 크루에게 물어보니 Backend 단에서는 요청을 압축하기나 DB 접근시간을

줄이는 등의 최적화도 가능하다고 합니다)

 

1. Front Server <-> Client

번들사이즈 줄이기

최적화는 로딩 + 렌더의 최적화가 거의 대부분이라고 생각합니다.

 

첫번째로 작업했던 것은 번들사이즈 줄이기 입니다.

 

서버에 요청을 했을 때 '땡쿠'가 열심히 짠 프론트엔드 코드의 빌드 결과물을 client 에게 전달해 줍니다.

 

가벼울수록 전달하는데 유리할 것이기 때문에 로딩 속도를 개선할 수 있습니다.

gzip

빌드파일을 압축해서 용량을 줄이면 전송속도가 더 빨라질 것입니다.

 

gzip과 brotli 같은 양식이 있습니다.

can i use 검색결과
can i use 검색결과

brotli가 더 효과적인 압축방법이라고 합니다. IE에서 지원을 안하는 점만 생각하면 될 것 같습니다.

 

궁금했던 것은 Encoding을 하게 되면 Decoding도 동반됩니다.

 

대부분의 브라우저는 자체적인 Decoder를 내장하고 있는데요,

 

사실상 최적화가 잘 되려면 "Decoding 시간 < 압축으로 인한 전송시간 이득"이어야만 합니다.

 

항상 이게 만족이 되는지 궁금해서 찾아봤습니다. 스택오버플로우에 저와 같은 고민이 있었습니다.

 

결론은 거의 모든 경우에 overhead가 없다고 합니다.

 

gzip은 어떻게 압축하길래 가능한 걸까요? 간단히 말하면 중복을 찾는 deflate라는 알고리즘을 사용하는데요, 알파벳 기준을 잡은 후 (a,b)로 몇 개 만큼 중복이 있는지를 표기합니다. 자세한 설명은 여기 있습니다.

 

webpack

webpack을 이용하는 것 만으로도 내장된 Tree Shaking이 작동합니다.

 

es6에서 import, export 의 정적인 모듈사용을 하고 있기 때문에 사용하지 않는 모듈을 webpack에서 제거해 줘서 번들 사이즈를 줄일 수 있습니다.

 

또한 css Minimizer webpack plugin 등의 플러그인을 prod 환경에서만 적용하여 css용량을 줄일 수 있었습니다.

lazy loading

페이지별로 로딩을 할 수 있게 되면 메인페이지에서 쓰지 않는 페이지까지 bundle에 포함할 필요가 없습니다.

 

[적용 전]

[적용 후]

분할을 해서 번들을 25% 감소시켰습니다.

 

font subset

해당 페이지에서 필요한 font를 한번에 다 사용할 필요가 없기 때문에 subset을 적용했습니다.

(Lazy load와 비슷한 개념이네요.. 프론트에서는 이렇게 필요할 때 불러오는 최적화가 상당히 중요한 요소인 것 같습니다.)

 

subset 적용한 모습

 

pretendard라는 font를 사용했는데 운이 좋게도 subset이 미리 준비 돼 있었습니다.

 

font 양식

pretendard는 woff2 양식을 사용하고 있었습니다.

 

woff 형식과 woff2는 압축된 폰트 형식입니다. 같은 계열이지만 woff2는 더 개선돼서 30~50% 더 압축한다고 합니다.

https://www.w3schools.com/Css/css3_fonts.asp

IE와 Opera를 제외하고는 모든 브라우저에서 작동하기 때문에 적절한 것 같습니다.

 

 

폰트에 관해서는 D2에서 작성한 글을 많이 참조했습니다.

이미지 형식 변경

손실이미지를 사용하면 무손실 이미지보다 용량을 많이 줄일 수 있습니다.

 

하지만 전... 제가 그린 귀여운 캐릭터들을 손실시키고 싶지 않았습니다 :(

귀엽지 않나요..?

실제로 에디터로 그림을 그린 후 export 할 때 jpg나 png보다 svg가 용량이 컸습니다.

 

하지만 무손실 이어도 svg는 text 로 표현이 가능해서 gzip등으로 압축이 가능했습니다.

 

압축을 적용해서 6kb의 원본을 2.4kb까지 줄일 수 있었습니다.

 

2. Client

Css

브라우저 렌더링 과정을 간단히 보면

 

dom tree -> cssom tree -> render tree -> reflow -> repaint -> 를 진행합니다.

 

rflow를 통해서 위치를 잡고 repaint를 통해서 그림을 그립니다.

- reflow, repaint를 일으키지 않는 속성

transform, opacity 등을 사용한다.

- repaint를 일으키지 않는 속성

background, color 등을 사용한다.

- gpu 사용하는 속성

gpu사용하는 animation 속성을 이용(https://wit.nts-corp.com/2017/08/31/48610) 하는 방법도 있다.

Browser 설정

http1.1 vs http2 vs http3 마다 통신할 때 특징이 다르다.

 

각각을 비교해보고 기존 http1.1에서 http2로 변경했다.

 

http1.0은 3way handshake를 통신마다 적용한다

 

http1.1은 연결을 안끊어서 3way handshake를 한번만 해도 된다.

 

http2는 mux를 사용해서 Head of Line Blocking을 막을 수 있다.

 

http3는 udp를 사용한다.

 

http2가 거의 대부분의 브라우저에서 작동하면서 속도가 좋은 것을 볼 수 있다.

can i use 검색결과

 

코드레벨

- 리액트 리렌더링 막기

fcp와 같은 항목에는 해당하지 않지만 key와 memo등을 사용하거나 적절히 state를 사용해서 리렌더링을 막는 것으로 최적화를 해서 사용자 경험을 높일 수 있다.

 

매번 100개의 컴포넌트를 다 보여주게 되면 사용자에게는 로딩이 느껴지게 된다.

 

이미지의 경우에는 계속 다시 그리게 될 수도 있다.

- dom에 직접 접근하기보다 html 속성 사용하기

dom에 직접 접근하는 것보다 html 속성이 존재한다면 그 속성을 사용하는 것이 비용적으로 더 좋다.

 

maxLength같은 속성은 몇몇 스크린리더에서 읽어주기도 해서 접근성 면에서도 더 좋다.

- 리팩토링을 통해서 사용하지 않는 코드라인 제거

사용하지 않는 코드라인, 주석, css속성들을 직접 삭제해 주면 사이즈를 줄일 수 있다.

(현재는 별로 줄일게 없었지만 레거시라면 꽤 많이 줄일수도 있을 것 같다)

성능측정하기

마지막으로 light house 점수를 통해서 얼만큼 개선됐는지를 확인했습니다.

 

 

 

더 할 것은 없었나?

LCP 같은 경우 이미지 때문에 노란불을 받았습니다.

 

무손실 이미지를 사용하자고 합의를 하였기 때문에 이 점수로 했습니다.

 

아이콘 로고들 보다는 쿠폰을 보내는 도메인상 TTI나 FCP가 UX상 더 중요하다고 판단했기 때문에 92점 정도까지 최적화를 진행하고 마무리 하였습니다.