FE 성능 최적화 (로딩) - woowacourse-teams/2021-gpu-is-mine GitHub Wiki
webpagetest.org
- LCP 3.x로 길다: why?
- fonts.googleapi.com - css2 접속이 끝나야 document completed 가 된다 : fonts.googleapi.com 이 render-blocking resource 인 것 같다
- NotoSans Font 도입의 목적이 다르다. 디자인의 일관성을 위해서가 아니라, 브라우저에서 한국어가 지원되지 않은 경우에 대비해서 보내주는 것.
- Waterfall View 의 단어의 의미를 명확하게 이해(Start Render, Document Complete 등)
lighthouse
개선방안
- cloudfront
origin sheild
설정 =>TTFB
의 시간 줄이기 - non-blocking render 리소스 형태로 폰트 다운 변환
-
main.bundle.js
의 번들 크기 줄이기 -
main.bundle.js
의cahce-control
헤더 추가를 통한 캐싱 활용
Origin Shield란 additional cache layer입니다.
Without Origin Shield | With Origin Shield |
---|---|
![]() |
![]() |
=> 눈에 띌만큼 LCP가 줄어들지는 아니함
Origin Shiled 적용 후 WebPageTest Detail
-
main.bundle.js
의cache-control
헤더 추가를 통한 캐싱 활용
- main.bundle.js:
cache-control: public, max-age=31536000, immutable
main.bundle.js
의 내용이 변경될 경우, html에서 최신의 main.bundle.js 를 불러오게 하기 위하여,
빌드시에 main.bundle.js 파일명에 [fullhash]
를 추가하였습니다.
-
index.html
에cache-control
헤더 추가를 통한 캐싱 활용
- index.html:
cache-control: public, s-maxage=31536000, max-age=0
- https://toss.tech/article/smart-web-service-cache
- Cache-control 헤더 추가 한 뒤의 webpagetest.org 결과
- LCP의 큰 차이가 없다: cache-control 설정을 하나도 안해두어도, 브라우져가 자체적으로 캐싱을 하기 때문에, 속도면에서는 큰 차이가 없는 것으로 생각된다. 다만, lighthouse로 검사시에는 정적 자산에 대한 max-age 설정을 보기 때문에 의미가 있다고 생각됨
- Repeat View 의 LCP 가 증가하였다: index.html 은 브라우져에서 캐시를 하지 않고 항상 캐시 서버(CloudFront)에서 검증을 하게 하였기 때문이다.
https://www.webpagetest.org/result/210907_AiDc2V_eca9d8e32ec8d8f5125558f3ba40dc01/
- 기본적으로 CSS는 render blocking resource이다. By default, CSS is treated as a render blocking resource.
- head 태그 내의 inline style 태그도 당연히 render-blocking resource이다.
- inline style 태그내에 @import 가 있다면 파일 다운로드는 비동기적으로 수행되지만, 이를 다 다운받을 때까지 render는 blocking 된다.
Q1. Link 태그라서 render blocking 인가? stylesheet 라서 render blocking 인가? -> stylesheet인거 같은데..? 근거 필요함 근거:
- https://web.dev/render-blocking-resources/?utm_source=lighthouse&utm_medium=devtools#which-urls-get-flagged-as-render-blocking-resources Q2. DOM, CSSOM 이 다 완성되고 나서 Render Tree가 완성되어야지만 Layout, Paint, Composite 을 수행하는건가? 네트워크를 통해 DOM, CSSOM이 변경되는 경우에는 언제 render가 되는것인가? 브라우져는 render를 점진적으로 한다?
- https://d2.naver.com/helloworld/59361
Q3. link rel="preload" as="style" href="...css"
는 왜 non render blocking 이 되는것인가?
preload는 그냥 link 보다 더 우선순위가 높아서 반드시 가져와야 한다. 그런데 비동기로 가져오는 것인가????
- lighthouse 돌려보면 에러 하나 줄어드는 것이 확실히 보인다
- https://web.dev/render-blocking-resources/?utm_source=lighthouse&utm_medium=devtools#which-urls-get-flagged-as-render-blocking-resources
- https://web.dev/defer-non-critical-css/
- https://web.dev/preload-critical-assets/
- https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
- https://beomy.github.io/tech/browser/preload-preconnect-prefetch/
- https://beomy.github.io/tech/browser/critical-rendering-path/
- webpageTest 결과 LCP 0.8s 감소
https://www.webpagetest.org/result/210907_BiDc7Y_91406eb153b07e7b75a97c6bc809e488/
- chart.js 가 차지하는 번들 사이즈가 크다 -> chart.js 를 별도의 청크로 분리하고, 이를 동적으로 import 한다.

- tsconfig.json 설정 (https://thesoftwaresimpleton.com/blog/2019/03/15/ts-code-splitting)
- package.json 에서 sideEffects: true 설정 https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free
- JobDetailGraphChart Component 에서 Chart 를 lazy loading (https://reactjs.org/docs/code-splitting.html) (/* webpackPrefetch */ 관련 reference: https://medium.com/webpack/link-rel-prefetch-preload-in-webpack-51a52358f84c)
=> Chart 컴포넌트를 별도의 청크로 분리한 후 React.lazy, React.Suspense 를 이용하여 동적으로 import 한다

-
History.js
가 차지하는 번들 사이즈가 크다. (28.05Kb)
- 현재 History.js 는 테스트 파일에서만 사용하고 있는데, 번들에도 포함되고 있다. react-router-dom 에서 사용하기 때문에 번들에 포함되는지 확인이 필요하다.
-> History.js 를 삭제한 후 번들링을 해본 결과, Type 때문에 제대로 되지 않는다. [email protected] 내부에서 history.js 에 대한 의존성을 가지고 있어서, history.js 를 devDependencies 에 넣는 것과 무관하게 번들에 포함되게 되어있다. https://bundlephobia.com/package/[email protected]
[email protected] 에서는 history.js 에 대한 의존성이 없어 번들 사이즈가 줄어들 수 있다. https://bundlephobia.com/package/[email protected]
https://reacttraining.com/blog/react-router-v6-pre/
하지만 history.js가 전체 번들에서 차지하는 사이즈가 28.05Kb로 유의미하게 크지 않으므로 일단 PASS)
- 외부 패키지를 별도 청크로 분리한다. react, react-dom, styled-components 등의 외부 패키지를 별도 청크로 분리한다. webpack의 optimization.splitChunks로 대응한다. default 옵션을 사용하되, chunk대상만 all로 수정한다 https://webpack.js.org/plugins/split-chunks-plugin/#optimizationsplitchunks

=> 최종 결과 LCP 1.78s 로 감소

https://www.webpagetest.org/result/210908_BiDc9E_4950f7342431ea5450af9e02e9854b48/1/details/
preload 는 단순히 network 요청을 빨리 하는 것이고, load되었을 때 할 작업을 명시해줘야 한다. 따라서 stylesheet 로 속성을 변경해주지 않으면 css로서의 파싱, 실행이 되지 않는다.
-
main.bundle.js
요청 헤더에no-cache
옵션이 존재하는데, http 요청없이 브라우저 캐시(memory/disk)로 부터 데이터를 가져옴(TTFB = 0) - 크롬 개발자 도구에
Disable cache
옵션을 줄 경우 모든 요청 헤더에no-cache
가 포함됨. - http 요청 타겟을
js
가 아닌index.html
에 주목을 했어야함. (in chrome)- Disable cache:
ON
=> 캐싱 X- 요청 헤더
cache-control: no-cache
- 최초 요청에 대한 html 응답 200 / js 응답 200
- 재요청에 대한 응답 200 / js 응답 200
- 요청 헤더
- Disable cache:
OFF
=> 캐싱 O- 요청 헤더
cache-control: max-age=0
- 최초 요청에 대한 html 응답 200 / js 응답 200
- 재요청에 대한 html 응답 304(network) / js 응답 200 (in memory)
- 요청 헤더
- Disable cache:
-
브라우저 캐시
로 부터 받은 응답은 실제 요청에 대한 실제 응답인가?chrome
vsfirefox
-
chrome + Disable cache X
:js
의 응답 date값은 항상 동일 => 기존 응답 -
firefox + Disable cache X
:js
의 응답 date가 항상 다른 값 => 실제 응답
-
- 참고자료
- chrome에서는 html 이 바뀌지 않았다면, 안에 포함된 css, js 는 바뀌지 않은 것으로 간주하여 캐시된 것을 사용한다
- firefox에서는 response header에 cache-control: immutable이 포함되어 있어야만 chrome과 동일하게 행동한다.
- https://stackoverflow.com/questions/45829055/why-doesnt-chrome-re-validate-in-memory-cache-when-doing-a-normal-reload?noredirect=1&lq=1
- react-is 가 왜 dependencies 에 포함되어 있는가? yarn berry 설치시 이슈가 있었다.
- react-is 는 styled-components의 peerDependencies 이고, peerDependencies는 설치시에 부모(사용하는 측)에서 dependencies를 주입해줘야 한다.(설치해야한다) yarn classic을 쓸 때는 별다른 일 없이 잘 사용하고 있었다. (아마도 유령 hoisiting 에 의하여 사용가능했던 것이 아닐까...) 그러나 yarn berry의 엄격한 의존성 정책으로 인해 명시적으로 dependencies에 추가해주어야 했다.
- https://github.com/yarnpkg/berry/issues/966
-
Start Render
: 화면에 그려진 픽셀을 실제로 볼 수 있는 시점 -
Document Complete
:onload
이벤트 + 정적 이미지 로딩 완료
- 크롬 개발자 도구
Disable cache
ON -> OFF :main.bundle.js
파일에 항상 request 헤더에 no-cache 옵션이 남아있음.
https://rattle-king-c48.notion.site/GPU-IS-MINE-089cf77a60764695972a6644f1fb1194
https://nooshu.com/blog/2019/10/02/how-to-read-a-wpt-waterfall-chart/