프론트엔드 성능 최적화가이드-3
3장 홈페이지 최적화
본 글은 “웹 개발 스킬을 한 단계 높여주는 프론트엔드 성능 최적화 가이드” 책의 공부내용으로 기억력이 좋지 않은 미래의 나를 위한 정리글입니다.
3.1 이미지 지연로딩
- 문제 상황 : 처음 페이지에 들어오면 비디오가 표현되는데, 스크롤을 내려야 표현되는 이미지정보가 먼저 로드되고 있어서 비디오정보를 받아오는데 지연이 발생하고있다.
- 목표 : 이미지는 조금 더 늦게 받아오고, 우선순위가 높은 비디오를 먼저 받아오자
그럼.. 이미지는 언제 받아오지??
→ 이미지가 필요하기 직전, 즉 스크롤을 내리다가 이미지가 보이는 시점에 이미지를 로드하자!
이미지 파일은 사용자가 스크롤을 내려 해당 이미지가 뷰포트에 들어오는 시점에 로드하게하면 페이지가 로드되었을 때 비디오 파일을 먼저 로드할 수 있고, 사용자 경험을 향상시킬 수 있다
자 그럼 이 3개의 이미지를 늦게 받아오도록 처리해봅시다
3.1.1 throttle 기법
웹 페이지에서 스크롤 이벤트는 매우 빈번하게 발생한다.
(실제로 스크롤을 감지하고 콘솔에 로그를 출력해보면, 한 틱당 거의 하나씩 많은 이벤트가 발생함)
이런 상황에서 Throttle 기법을 고려해본다면??
[throttle]
- 이벤트가 과도하게 발생할 경우, 그 처리를 제한하는 기법
- 일정 시간 (예: 200ms) 동안 이벤트를 한 번만 처리하도록 제한할 수 있다.
[문제점]
- 정확성 문제: Throttle을 사용하면, 이미지가 뷰포트에 들어왔을 때 로딩되어야 하는 시점을 놓칠 수 있다. 예를 들어, 200ms 동안 이벤트를 무시한다면, 사용자가 빠르게 스크롤을 움직여 이미지가 뷰포트에 짧은 시간 동안만 노출되었을 때, 로딩이 이루어지지 않을 수 있다는것…
책에서는 이에 대한 대한으로 Intersection Observer 방법을 소개한다.
3.1.2 Intersection Observer
[Intersection Observer]
Intersection Observer는 웹 페이지의 특정 요소와 뷰포트가 교차하는 시점을 감지하는 데 사용되는 브라우저 API
즉, 어떤 요소가 화면에 보이는지 혹은 사라지는지를 실시간으로 판단할 수 있는데 이를 활용하면, 이미지나 다른 요소들이 실제로 사용자에게 보일 필요가 있을 때만 로딩을 시작하도록 할 수 있다.
브라우저 API가 뭐지??
브라우저 API는 별도의 설치나 다운로드 없이 웹 브라우저에서 기본적으로 제공하는 기능의 모음 이를 통해 웹 페이지에 다양한 기능(예: 위치 정보 얻기, 오디오 재생, 웹 페이지 요소 변경 등)을 JavaScript로 쉽게 추가할 수 있다)
자 그럼 바로 코드를 확인해보자 이미지 로딩은 img태그에 src가 할당되는 순간에 발생한다
→ 즉 적절한 시점에 src를 할당하여 이미지를 로딩하게해야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
useEffect(() => {
// 하나의 인스턴스만 생성하도록 useEffect를 사용
const options = {
root: null
// 뷰포트 요소 설정( null 이면 기본 브라우저)
threshhold: 1,
// 0~1 사이의 값으로, 이미지 노출 비율을 의미한다. 1이면 전부 보여야 실행
rootMargin: 0,
// 이미지가 미리 로딩되도록 여유분을 준다
};
// 이때 이미지의 크기를 미리 지정하지않으면, threshold를 1로 하더라도, 이미지 높이가 0이라서 이미지 전체가 보이지 않아도 로딩된다.
const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) { // true라면 로딩 시작
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target); // observe해제
}
});
};
const observer = new IntersectionObserver(callback, options);
observer.observe(imgRef.current);
return () => observer.disconnect(); // clean up 함수를 사용해 리소스 낭비를 줄인다
}, []);
return (
<div className="Card text-center">
<img data-src={props.image} ref={imgRef} />
//data-src에 임시로 할당 후 특정 시점에 src로 값을 할당한다
<div className="p-5 font-semibold text-gray-700 text-xl md:text-lg lg:text-xl keep-all">
{props.children}
</div>
</div>
);
이제 3개의 이미지(main1,2,3…jpg)는 스크롤을 내리다가 보이는 시점에 다운받게 되었다
Q. data-src라는 명칭은 예약어인가?? 암묵적인 표현?
HTML5에서는 사용자가 임의로 데이터를 요소에 저장할 수 있도록 data-*
형식의 속성을 도입했다.
즉 아무 이름이나 사용 가능
data-*
속성의 이름은 data-
접두사를 제거하고, 나머지 부분은 카멜케이스(camelCase)로 변환하여 사용하므로 entry.target.dataset.src 형태로 값에 접근한것
Q. 혹시 다른 기법의 이미지 지연로딩도 있나?
3.1.3 Native Lazy Loading
[Native Lazy Loading]
Native Lazy Loading은 웹 브라우저에서 내장된, 즉 “네이티브”로 제공하는 이미지 지연 로딩 기능.
이 기능을 활용하면 별도의 JavaScript 코드 없이도 이미지나 iframe을 지연 로딩할 수 있다
1
2
3
4
5
6
7
8
<img src="image.jpg" alt="Description" loading="lazy">
// loading 속성
// - auto : 브라우저가 결정
// - lazy : 요소가 뷰포트에 가까워질때까지 지연
// - eager : 지연없이 바로 로딩
[주의점]
-
상세한 시점 제어 불가 세부적인 지연 로딩의 시점을 지정할 수 없다. 즉, 뷰포트가 얼마나 가까워져야 이미지를 로드할 것인지 같은 상세한 설정이 불가능.
-
브라우저 지원성 모든 웹 브라우저에서 이 기능을 지원하지는 않아 호환성을 고려해야 한다.
-
SEO 관련 주의점 Native Lazy Loading을 사용하면, 일부 검색 엔진이 지연 로딩된 콘텐츠를 정확하게 크롤링하지 못할 수 있어 이는 웹 페이지의 검색 엔진 순위에 영향을 미칠 수 있다. (특히, 중요한 이미지나 콘텐츠에 대해서 더욱 주의필요)
3.2 이미지 최적화
Squoosh 웹사이트를 통한 이미지 변환 (구글 제공) -> 불필요하게 큰 이미지를 WebP포멧으로 압축하자!
기존 이미지 vs WebP확장자로 압축한 이미지 크기 비교
단, 호환성 문제를 위해 picture태그를 사용해야한다.
- picture태그는 다양한 타입의 이미지 렌더링에 사용되는 컨테이너로 지원되는 타입의 이미지를 위에서부터 확인 후 렌더링한다.
- picture의 경우 sreset에 이미지 주소가 들어가면 이미지가 로딩된다 (img의 경우 src)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
useEffect(() => {
const options = {
const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const target = entry.target;
const previousSibling = target.previousSibling;
// previousSibling 은 DOM에서 이전 형제 요소를 참조하는 속성
// ref자체가 img태그를 가리키기 때문에 상위의 source에 접근하기 위해 사용한다
target.src = target.dataset.src;
previousSibling.srcset = previousSibling.dataset.srcset;
observer.unobserve(entry.target);
}
});
};
return (
<div className="Card text-center">
<picture>
<source data-srcset={props.webp} type="image/webp" />
<img data-src={props.image} ref={imgRef} />
</picture>
<div className="p-5 font-semibold text-gray-700 text-xl md:text-lg lg:text-xl keep-all">
{props.children}
</div>
</div>
);
- 위 코드에서 가장 상위의 WebP이미지를 우선으로 로드한다
- 지원하지 않으면 다음 sourece , 그리고 마지막에는 Img를 로드한다
Q. previousSibling을 통해 상위 source태그에 접근했는데 만약 이미지가 여러개라면? ⇒ 아래처럼 previousSibling을 여러 번 사용하면된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<picture>
<source data-srcset={main_styles_webp} type="image/webp" />
<source data-srcset={main_styles_png} type="image/png" />
<img data-src={main_styles} ref={imgEl3} />,
</picture>;
const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const pngSourceEl = entry.target.previousSibling; // for png
const webpSourceEl = pngSourceEl.previousSibling; // for webp
webpSourceEl.srcset = webpSourceEl.dataset.srcset;
pngSourceEl.srcset = pngSourceEl.dataset.srcset;
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
});
};
또는 아래처럼 클래스를 할당해서 접근하면 DOM구조의 변경에 보다 안정적이고 명확하게 특정 요소를 참조할 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
const webpSourceEl = entry.target.parentElement.querySelector('.webp-source');
const pngSourceEl = entry.target.parentElement.querySelector('.png-source');
{..}
<picture>
<source class="webp-source" data-srcset={main_styles_webp} type="image/webp" />
<source class="png-source" data-srcset={main_styles_png} type="image/png" />
<img data-src={main_styles} ref={imgEl3} />,
</picture>
3.3 동영상 최적화
일단 동영상의 경우 필요한 앞 부분을 먼저 다운로드한 뒤 순차적으로 나머지 내용을 다운로드하기때문에 Network패널에서 나누어서 다운받는다.
(라우저나 스트리밍 서비스가 알아서 처리해줌)
단 압축 자체가 화질을 떨어뜨리기때문에 영상의 화질이 중요한 서비스라면 압축에 대해 주의해야한다.
-> 동영상 최적화도 이미지와 마찬가지로 WebM확장자를 사용해 압축을 처리하자.
-> 이때도 마찬가지로 호환성을 고려해 video태그안에 source를 사용해야한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 전
<video
src={video}
className="absolute translateX--1/2 h-screen max-w-none min-w-screen -z-1 bg-black min-w-full min-h-screen"
autoPlay
loop
muted
/>
//후
<video
className="absolute translateX--1/2 h-screen max-w-none min-w-screen -z-1 bg-black min-w-full min-h-screen"
autoPlay
loop
muted
>
<source src={video_webm} type="video/webm" />
<source src={video} type="video/mp4" />
</video>
압축으로 인한 화질 저하 문제를 완전히 해결할 수는 없지만, 사용자 경험을 손상시키지 않으면서 로딩 속도를 개선하는 데 아래의 방법이 활용되기도한다.
-
블러 처리 : css의 Blur필터를 사용해 화질저하를 눈에 띄지 않도록 하기
-
패턴 적용 : 영상위에 패턴을 씌우는 방법
원본 , blur , 패턴을 적용한 각각의 이미지들
눈속임(?) 같지만 화질이 낮다는 인식을 줄여주는 것 같다는 생각이 든다
1
2
3
4
5
6
7
8
9
10
11
// 패턴의 경우 아래 css를 사용
// blur의 경우 filter 옵션을 사용
.pattern-overlay {
background-image: url('path/to/pattern.png');
opacity: 0.5;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
3.3 폰트 최적화
- 문제 상황 : 비디오에 표현되는 폰트가 늦게 적용되면서, 폰트가 변하는게 사용자 눈에 바로 들어온다.
- 목표 : 사용자에게 시각적으로 부정적인 영향을 줄이기 위해 폰트를 최적화해보자
3.3.1 FOUT vs FOIT
[FOUT (Flash of Unstyled Text)]
-
폰트의 다운로드 여부와 상관없이 먼저 텍스트를 보여줌 (위 예시 영상에서 사용중인 방식)
- 장점: 페이지 내용을 바로 볼 수 있다.
- 단점: 텍스트 스타일이 바뀌는 것이 사용자에게 어색하게 느껴질 수 있다.
[FOIT]
-
폰트가 로드되어야 텍스트를 표현
- 장점: 시각적으로 어색한 변화가 없다.
- 단점: 사용자가 페이지 내용을 확인하는 데 시간이 더 걸림.
“늦게 보여줘도 되는 텍스트는 차라리 FOIT으로 표현하자!”
3.3.2 시점제어
폰트 적용의 시점을 제어해서 최적화를 수행
-> ‘CSS의 font-display 속성을 활용하자!”
font-display
속성의 주요 값
- auto: 브라우저의 기본 로딩 동작 (대부분의 경우 FOIT)
- block: FOIT
- swap: FOUT
- fallback: 3초동안 FOIT 후 FOUT
- optional: FOIT 후 브라우저가 네트워크 상태를 기준으로 폰트 로딩을 포기
여기서는 CSS의 애니메이션 효과를 통해 사용자 경험을 개선시키자. (이때, fontfaceobserver 라이브러리를 활용)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import FontFaceObserver from "fontfaceobserver";
const font = new FontFaceObserver("BMYEONSUNG");
function BannerVideo() {
const [isFontLoaded, setIsFontLoaded] = useState(false);
useEffect(() => {
// 20초 타임아웃시 Promise에서 에러 발생
font.load(null, 20000).then(() => {
console.log("폰트 로드");
setIsFontLoaded(true);
});
}, []);
return (
{ ... }
<div
className="w-full h-full flex justify-center items-center"
style=
>
{ ..content }
</div>
);
}
export default BannerVideo;
//1. fontfaceobserver라이브러리를 통해 폰트 다운시점을 확인
//2. 로드 되는 시점에 fade-in 애니메이션을 적용시키자
기존과 다르게 폰트가 로드되는 시점에 CSS효과를 넣어 자연스럽게 폰트가 표현된다
3.3.3 포멧 변경
웹 페이지에서 폰트를 사용할 때, 파일 포맷의 선택은 성능과 호환성에 큰 영향을 미치며 주로 TTF, OTF, WOFF, WOFF2 등의 다양한 포맷이 있음
-
TTF (TrueType Font) & OTF (OpenType Font)
- 특징: 벡터 기반으로 만들어져 확대/축소가 자유로움.
- 웹 최적화: 본래 데스크톱 환경을 위해 설계되었기 때문에 파일 크기가 상대적으로 크다.
- WOFF (Web Open Font Format)
- 특징: OTF와 TTF를 기반으로 하되, 웹 전송을 위해 압축되어 있다.
- 웹 최적화: 파일 크기가 작아 로딩 시간이 빠르지만, 모든 브라우저가 지원하지 않을 수 있음.
- WOFF2
- 특징: WOFF1보다 약 30% 더 효율적으로 압축되어 있다.
- 웹 최적화: 필요한 글리프만 포함하여 서브셋을 생성할 수 있다.
- 예를들어 “Hello”라는 문자만 사용한다면 해당 문자열에 대한 폰트정보만 가져오는것
https://transfonter.org/ 사이트 이용해 폰트의 확장자를 줄여보자
특히 서브셋 폰트를 사용하면 필요한 일부 문자정보만 가져올 수 있다.
(서브셋폰트와 기존폰트의 크기차이 비교)
그리고 마찬가지로 호환성문제를 고려해 아래와 같이 폰트를 적용시키자.
1
2
3
4
5
6
7
8
@font-face {
font-family: BMYEONSUNG;
src: url("./assets/fonts/subset-BMYEONSUNG.ttf") format("woff2"),
url("./assets/fonts/subset-BMYEONSUNG.woff") format("woff"),
url("./assets/fonts/subset-BMYEONSUNG.ttf") format("truetype");
font-display: block;
/* 위에서부터 우선적으로 폰트를 다운로드 */
}
3.3.4 DATA-URL
Data-URL은 웹 리소스를 URL 문자열로 직접 내장하는 방식을 의미.
이를 통해 폰트 파일을 CSS 내부에 직접 삽입할 수 있으며 별도의 네트워크 없이 css파일에서 폰트 사용이 가능하다.
- 장점
- HTTP 요청 감소: 별도의 파일 요청이 없기 때문에 네트워크 부하를 줄일 수 있다.
- 로딩 속도 향상: 작은 이미지나 폰트의 경우, 별도의 네트워크 지연 없이 즉시 로딩이 가능.
- 호환성 보장: URL 형식은 모든 웹 브라우저에서 지원되므로 호환성 문제가 없다.
- 단점
- 파일 크기 증가: Base64 인코딩은 원래의 파일 크기를 약 33% 증가.
- 캐싱 문제: Data-URL은 브라우저 캐시에 저장되지 않아, 재방문 시 다시 로드.
- 유지 보수 어려움: 원본 파일의 수정이 필요할 경우, 다시 인코딩해야 하는 작업 복잡.
즉 폰트와 같이 작은 파일의 경우 data-url을 사용하면 리소스를 바로 로딩해 사용자에게 보여줄 수 있다!!
물론 그만큼 처음 로딩시간이 길어진다
파일이 길어지고 수정할때마다 다시 인코딩을 해야하는데 작은 파일에 대해 사용한다면 사용자 눈에 로딩이 직접적으로 드러날 것 같지는 않다
네트워크 속도가 빨라진 요즘에는 잘 안쓰일 것 같다고 생각🤔
1
2
3
4
5
6
7
@font-face {
/* dataURL방식의 base64인코딩된 정보를 넣으면 번들파일내에서 한꺼번에 데이터를 받아오기때문에 별도의 HTTP요청 반복 횟수를 줄일 수 있다.
(기존에는 번들파일 외적으로 따로 각 이미지나 폰트정보를 HTTP로 받아와야함 */
font-family: BMYEONSUNG;
src: url("data:font/woff2;charset=utf-8;base64......format("woff2")
}
실제 네트워크 트래픽으로 인식할 뿐 별도의 다운로드 시간이 걸리지 않는다
3.4 캐시 최적화
- 문제 상황 : lighthouse를 통해 분석한 결과, 캐시 적용이 필요하다는 경고가 발생하고있다.
- 목표 : 캐시의 개념을 이해하고, 적절한 캐시 설정을 적용하기 (노드서버에서)
[캐시]
- 메모리 캐시
- RAM (랜덤 액세스 메모리)에 저장하는 캐시
- RAM은 빠른 속도로 데이터에 접근할 수 있기 때문에, 자주 사용되는 자원들을 메모리 캐시에 저장
- 브라우저를 종료하면 사라진다
-
디스크 캐시
- 웹 페이지의 자원을 하드 드라이브나 SSD와 같은 영구 저장 매체에 저장하는 캐시
- 브라우저를 종료해도 저장된 데이터는 유지
캐시는 브라우저가 알고리즘에 의해 자동으로 처리하며 캐시 적용 여부는 Cache-Control 응답 헤더를 통해 확인할 수 있다
[cache-control 헤더의 주요 속성]
max-age
- 캐시의 유효시간, 단위는 초
no-cache
- 캐시를 사용전에 서버에게 한번 검증을 받아야 함
no-store
- 캐시를 사용하지 않음 (민감한 정보 및 개인 데이터를 다룰때 쓰인다)
private
- 응답이 개별 사용자에게 특화되었음 (개인 프로필 정보 등등..)
- “이 응답은 개인적”이므로, 오직 사용자의 브라우저에만 응답을 캐시해라!
- “CDN과 같은 중간 서버들에서 캐시를 하지 말아라 (= 다른 사용자가 동일한 URL에 접근했을때 정보가 보여지지 않도록)
- 응답이 개별 사용자에게 특화되었음 (개인 프로필 정보 등등..)
-
public
- 응답이 공용 캐시에 저장될 수 있음, 즉 모든 환경에서 캐시 O
위 내용을 기반으로 노드 서버에서 적절한 캐시 헤더를 설정해보자!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 기존코드
const header = {
setHeaders: (res, path) => {
res.setHeader(
"Cache-Control",
"private, no-cache, no-store, must-revalidate"
);
res.setHeader("Expires", "-1");
res.setHeader("Pragma", "no-cache");
},
};
// 수정코드
const header = {
setHeaders: (res, path) => {
if (path.endsWith(".html")) {
res.setHeader("Cache-Control", "no-cache");
} else if (
path.endsWith(".js") ||
path.endsWith(".css") ||
path.endsWith(".webp")
) {
res.setHeader("Cache-Control", "public, max-age=31536000");
} else {
res.setHeader("Cache-Control", "no-store");
}
},
};
[적절한 캐시 유효 시간]
- HTML 파일:
- HTML 파일은 애플리케이션의 동적 내용을 반영하는 중심적인 자원이기때문에 자주 변경될 가능성이 높다. (게시물 내용, 이미지…등등 동적으로 가져오는 경우가 많으니까)
no-cache
를 사용하면 브라우저는 항상 원래 서버에 해당 리소스의 유효성을 확인하고, 변경되었을 경우 최진 버전을 제공해주어야한다
- JS, CSS, 이미지 파일:
- 이러한 정적 자원들은 자주 변경되지 않을 가능성이 높다.
public, max-age=31536000
는 리소스가 공용 캐시에 저장될 수 있음을 나타낸다
- 기타 파일:
no-store
는 캐시에 어떠한 것도 저장되지 않도록 하는데 이는 매번 해당 자원을 서버에서 새로 가져와야 함을 의미한다.- 민감한 데이터나 자주 변경되는 데이터에 적합
캐시 적용 전/후 이미지
그런데 캐시설정은 사실상 서버쪽에서 다루는 듯 하다. 프론트쪽에서 일방적으로 설정하는건 어려운듯.. 개념을 이해하고있자!
3.5 CSS 최적화
- 문제 상황 : Lighthouse의 분석 결과, 불필요한 main.chunk.css 리소스에 의해 경고가 표시되고 있다.
- 목표 : 사용하지 않는 CSS를 제거하여 성능을 개선하자.
Q. 왜 해당 경고는 배포서버에서만 표현될까?
→ 로컬에서는 모든 클래스(사용 여부와 상관없이)가 포함된 상태로 CSS가 빌드되므로 “사용하지 않는 CSS” 경고가 나타나지 않는다. 그러나 배포 환경에서는 그러한 경고가 나타날 수 있다.
(보통 실시간으로 코드 변경을 반영하기 위한 설정이 활성화되어 있고, 이 때문에 모든 가능한 CSS가 로드)
3.5.1 PurgeCSS
[PurgeCSS] PurgeCSS는 사용되지 않는 CSS를 자동으로 제거하여 최종 CSS 파일의 크기를 줄이는 도구이다
[작동 원리]
- HTML, JS, 템플릿 파일 분석: PurgeCSS는 프로젝트 내의 파일들을 스캔하여 사용된 CSS 클래스와 선택자를 파악
- CSS 파일과 비교: 그 다음, 스타일시트에 정의된 모든 클래스와 선택자를 검토하여 실제로 파일에서 사용되지 않는 클래스와 선택자를 확인
- 불필요한 CSS 제거: 실제로 사용되지 않는 클래스와 선택자를 CSS 파일에서 제거
1
2
3
4
5
6
7
npm install --save-dev purgecss // 설치
purgecss --css ./build/static/css/*.css --output ./build/static/css/ --content ./build/index.html ./build/static/js/*.js --config ./purgecss.config.js
// 명령어 실행 시 빌드된 HTML 및 JS 텍스트 키워드를 추출하여, 최적화를 하게 된다.
여기서 lg:m-8과 같이 Tailwind CSS나 일부 다른 CSS 프레임워크를 사용하고 있다면, 콜론(:) 문자를 올바르게 처리하기 위한 별도의 설정이 필요하다.
1
2
3
4
5
module.exports = {
defaultExtractor: (content) => content.match(/[\w\:\-]+/g) || [],
};
// 콜론 문자를 하나의 키워드로 인식하기 위한 정규식
전/후 이미지 비교
눈에 띄게 크기가 줄어들었다
Q. Tailwind, Bootstrap 같은 CSS 프레임워크를 사용한다면 사용하지 않는 CSS를 줄이기 위해 PurgeCSS를 사용하는게 매우 의미있다고 생각한다. 그럼 보통 그 외에는 안쓰는편일까?
mui를 활용한 프로젝트에 대해 purgeCSS를 써본 결과의 전/후 이미지 비교 (차이가 별로 없는데?)
생각해보면 실수로 지우지 않은 불필요한 CSS를 제거하는 데 유용할 듯 하다
build 명령어 자체에 넣어버려서 자동화하는것도 괜찮지 않을까?
참고자료
https://bongbongdang.tistory.com/142
https://scarlett-dev.gitbook.io/all/it/lazy-loading
댓글남기기