솔미는 성장중
Lighthouse 점수 어떻게 올리는 건데~! (feat. 성능 최적화) 본문
야놀자 기업연계 프로젝트로 숙박 예약 사이트를 만들었었고 이를 마치며 리팩토링 기간을 가졌다!
전체적으로 type을 분리하고, 가독성을 높이도록 코드를 개선했을 뿐 아니라 성능 개선 또한 도전해보았다.
lighthouse를 이용해 dev서버 성능을 측정해보았다.
여러모로 성능을 저하시키는 요인이 많다.
사용하지 않는 자바스크립트 줄이기
vite에서 build를 하면 기본적으로 webpack과 비슷하게 코드 스플리팅을 해준다.
아래 사진에서도 사용하지 않는 자바스크립트 줄이기 항목에서 아주 많이 절감된 것을 볼 수 있다!
폰트 최적화
다음으로는 폰트 최적화를 시도해보았다.
@font-face {
font-family: "CWDangamAsac-Bold";
src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2108@1.1/CWDangamAsac-Bold.woff")
format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
우선, font-display:swap;을 이용해 FOUT와 동일하게 작동하도록 적용해주었다.
폴백 폰트로 글자를 렌더링하고, 웹 폰트 로딩이 완료되면 웹 폰트를 적용한다. 웹 폰트 로딩 여부와 관계없이 항상 텍스트가 보인다. 이는 UX적으로 향상된 효과를 보일 수 있다.
FOIT(flash of invisible text) : 폰트를 다운로드 전에는 텍스트를 노출 X
FOUT(flash of unstyled text) : 폰트가 다운되기 전에는 기본 폰트를 노출 다운로드 이후 폰트 교체
그리고 메인 폰트로 pretendard를 사용하고 있었는데, 이는 원래 woff2확장자로 다운받아서 사용되고 있었다.
하지만 이는 가장 최적화시키는 방식인 ' 가변 다이나믹 서브셋'을 적용할 수 없는 방법이기에 수정을 해주었다.
가변 다이나믹 서브셋이란?
: 자동적으로 해당 폰트가 적용된 글자만큼만 폰트를 로딩하는 방식
그냥 subset(잘 사용되지 않는 글자는 제외해서 용량을 줄인 버전)을 사용하는 것보다 효과적이다!
참고 링크 : https://github.com/orioncactus/pretendard/discussions/148
그리고 preload도 적용해주었다.
preload 란?
: <link>태그의 rel 속성에 preload 옵션을 사용하면 해당 리소스를 다른 리소스보다 빨리 로딩한다. 주로 폰트 파일, 이미지 파일, 스크립트 파일, 비디오 파일 등 페이지에서 중요도가 높은 자원을 의도적으로 먼저 로딩할 때 preload 옵션을 사용한다.
단, preload 옵션으로 다운로드하는 리소스가 많아질수록 처음 렌더링 시간이 늦어진다는 문제가 있다. 그렇기 때문에 현재 페이지에서 중요도가 높고 먼저 렌더링되어야 하는 리소스만 preload 옵션으로 지정하는 것이 좋다.
출처: https://d2.naver.com/helloworld/4969726
// index.html
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin="anonymous">
<link rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.8/dist/web/variable/pretendardvariable-dynamic-subset.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.8/dist/web/variable/pretendardvariable-dynamic-subset.css" />
결과적으로 성능이 어마무시하게 개선되었다..!
폰트가 무겁다는 얘기를 익히 들었지만 이렇게까지 큰 성능 개선에 영향을 줄지는 몰랐다.
성능, 접근성, 권장사항이 100%로 채워지지 않은 것은 카카오맵 api때문이다.
png로 지도를 보여주는 것, document.write()으로 dom에 직접 접근하는 방식 등에서 마이너스가 되었다.
windowing을 적용해보면 어떨까?
결론은 NO ❌
react-window은 대규모 목록이나 그리드와 같이 많은 아이템이 있는 리스트를 최적화하고 성능을 향상시키는 데 사용되는 라이브러리이다!가상화 기술을 통해 현재 화면에 보이는 일부 아이템만 렌더링하여 효율적으로 관리해준다.
react-window를 사용하는 상황은 대규모의 아이템이 동적으로 로드되는 리스트에서 성능 향상이 필요한 경우이다. 하지만 내가 작성한 숙소 상세 페이지에선 목록이나 그리드와 같은 대규모 리스트가 아닌, 각 섹션에 속하는 여러 개의 컴포넌트들을 렌더링하고 있기 때문에 windowing을 사용하는 것은 적절하지 않다. 여러 섹션을 보여주는 현재의 코드에서는 일반적인 리액트 컴포넌트를 사용하는 것이 적절하다는 판단을 했다.
나중에 windowing 적용 시 참고해보면 좋을 것 같은 링크
https://brunch.co.kr/@devapril/48
https://velog.io/@pandati0710/React-Windowing
이미지 최적화를 해볼까했지만..
성능이 많이 개선되었지만 여전히 메인 이미지에서 최적화가 필요하다고 진단을 받았다.
현재 불러온 이미지의 height가 100%가 아니라 550px로 잘려있기 때문에 적절하지 않은 크기의 이미지를 사용해서, 그리고 jpg확장자이기에 성능 저하가 일어났던 것이다.
이미지를 최적화하는 방법은 다양하다.
1. 브라우저 사이즈에 맞춰 적절한 이미지 제공
: 브라우저 사이즈에 맞게 브레이크 포인트를 걸어 과도한 리소스를 사용하지 못하게 해주어 보다 빠른 렌더링이 가능하도록하는 방법
- 미디어 쿼리 활용
// 예시 코드
@media (min-width: 401px) {
.image-box {
width: 700px;
height: 700px;
background-image: url(./midium.png);
}
}
@media (min-width: 701px) {
.image-box {
width: 1000px;
height: 1000px;
background-image: url(./large.png);
}
}
- img 태그 srcset 속성 사용
<img
srcset="./small.png 400w,
./medium.png 700w,
./large.png 1000w"
sizes="(max-width: 401px) 400px,
(max-width: 700px) 7000px,
1000px"
src="./large.png"
/>
- picture 태그 사용
: <picture>태그는 <img> 태그의 단점을 보완하는 방법.
: picture는 특정 브라우저에서 특정 이미지를 사용할 수 있도록 강제할 수 있으며, 조건에 맞지 않는 이미지는 다운로드 하지 않는다.
<picture>
<source media="(min-width: 700px)" srcset="/examples/images/people_960.jpg">
<source media="(min-width: 400px)" srcset="/examples/images/people_575.jpg">
<img src="/examples/images/people_200.jpg" alt="People">
</picture>
2. 이미지 lazy loading
img 속성을 이용해서 반영했다. (js를 이용하는 방법도 있다.)
<img loading="lazy">
3. 이미지 cdn 사용
Image CDN은 이미지 변환, 최적화 및 전송을 담당하는 서버로, 이미지 CDN을 사용하면 이미지 다운로드 속도를 40%~80%로 줄일 수 있습니다. 또한 Image CDN에서 로드된 이미지의 경우 이미지 URL에 따라 다양한 이미지를 요청할 수 있습니다. 게다가 CDN을 사용하게 되면 HTTP 캐시를 이용하여 이미지 캐싱이 가능해지게 됩니다. 다만 이러한 CDN을 활용한 캐시는 삭제하기 쉽지 않기 때문에 적절한 Cache-Control과 max-age를 통하여 제어해야 합니다
이 방식은
4. CSS Image Spirte
아이콘이나 버튼 같은 이미지들을 하나로 합쳐 배경 이미지로 만들어 놓고 position 값으로 각각의 이미지들을 잘라서 사용하는 방식을 카카오맵 카테고리 아이콘을 표시할 때 사용했다.
5. 확장자를 webp로 변환
하지만 현재 리팩토링 기간에 백엔드 분들께 S3에 저장된 이미지를 수정 또는 추가해달라고 요청하기 어려운 상황이었다.
그래서 프론트딴에서 할 수 있는 방법으로만 이미지 최적화를 진행했다.
=> 이미지 lazy loading, CSS image sprite 사용하기
이미지를 build할 때 압축하는 방법이 있는지 알아보았지만 찾지 못했다.
만약 next.js를 사용했다면 아래 방식으로 이미지 최적화가 가능했을 것 같다.
https://vercel.com/docs/image-optimization/quickstart
Image Optimization Quickstart
The following examples show how you can leverage Vercel Image Optimization with the Next.js and Nuxt.js image components.
vercel.com
import Image from 'next/image';
이렇게 Image를 가져와서 기존에 사용하던 img 태그를 대체해주면 된다.
<Image
src="https://images.unsplash.com/photo-1627843240167-b1f9d28f732e"
alt="Picture of a triangle"
width={500}
height={500}
/>
대신, 이미지를 cloud에 업로드하는 상황이라면 이용하기 좋은 라이브러리를 찾았다!
https://www.npmjs.com/package/browser-image-compression
browser-image-compression
Compress images in the browser. Latest version: 2.0.2, last published: 9 months ago. Start using browser-image-compression in your project by running `npm i browser-image-compression`. There are 129 other projects in the npm registry using browser-image-co
www.npmjs.com
이미지를 압축하는 함수를 작성해서 이미지 input에 업로드되면 이미지를 압축하게 만들 수 있다.
남은 문제들을 모두 카카오맵 api 관련된 것이 대부분이었다.
카카오맵 api는 js기반으로 작성되었기 때문에 react에서 사용할 때 수정해야되는 부분이 조금 있는 것 같다.
https://react-kakao-maps-sdk.jaeseokim.dev/
Hello from react-kakao-maps-sdk docs | react-kakao-maps-sdk docs
Description will go into a meta tag in <head />
react-kakao-maps-sdk.jaeseokim.dev
리액트용으로 사용할 수 있게 만들어진 것이 있는데 kakao에서 공식적으로 지원하는 카카오맵에 비해 기능이 제한적이라고 들었다. (기능을 추가할라면 할 수 있다지만 복잡하다고..)
그래서 나중에 아예 ReactKakaoMapSDK로 수정해야 해결될 문제들이 대부분인 듯했다.
따라서 성능 최적화는 이번 1차 리팩토링에서는 여기서 마무리 지을 생각이다!
'프로젝트' 카테고리의 다른 글
초기세팅 (0) | 2024.01.02 |
---|---|
04_ (패스트캠퍼스X야놀자 프론트엔드 개발 부트캠프) 숙박 예약 사이트 *기업연계* (4) | 2023.12.19 |
proxy 서버 띄워 작업하기 (feat. vite) (1) | 2023.12.04 |
백엔드와 협업할 때 API를 못 받았다면? MSW mocking서버를 이용해보자! (feat. React & TypeScript) (0) | 2023.11.30 |
02_ (패스트캠퍼스X야놀자 프론트엔드 개발 부트캠프) 직원 관리 사이트 (0) | 2023.08.29 |