관리 메뉴

Dev Blog

3장 홈페이지 최적화 본문

Tech Books/프론트엔드 성능 최적화 가이드

3장 홈페이지 최적화

Nomad Kim 2023. 3. 20. 23:46

Repository

학습할 최적화 기법

  • 이미지 지연 로딩
  • 이미지 사이즈 최적화
    • 서버에 저장되어 있는 정적 이미지 최적화
  • 폰트 최적화
    • 커스텀 폰트 적용 최적화
  • 캐시 최적화
  • 불필요한 CSS 제거
    • 사용하지 않는 CSS 코드 제거

이미지 지연 로딩

네트워크를 확인할 때는 명확한 흐름을 파악할 수 있도록 네트워크에 throttling을 적용합니다.

 

 

banner-video는 페이지에서 가장 처음으로 사용자에게 보이는 콘텐츠인데 가장 나중에 로드되면, 사용자가 첫 화면에서 아무것도 보지 못한 채로 오랫동안 머물게 되므로 사용자 경험에 좋지 않을 겁니다.

동영상의 다운로드를 방해하는, 당장 사용되지 않는 이미지를 나중에 다운로드되도록 하여 동영상이 먼저 다운로드되게 하는 것입니다.

즉, 이미지를 지연 로드하는 것이죠.

 

뷰포트에 이미지가 표시될 위치까지 스크롤되었을 때 이미지를 로드할지 말지 판단할 수 있습니다. 그림으로 보면, 아직 스크롤이 이미지 위치까지 도달하지 않았을 때는 이미지를 로드하지 않고 있다가(그림 3-11 왼쪽), 이미지 위치에 도달하면 그때 이미지를 로드(그림 3-11 오른쪽)하는 것입니다.

 

 

하지만 스크롤이 이동하는 중에 이 이벤트가 계속 발생합니다. 여기에 조금이라도 무거운 로직이 들어가기라도 한다면 브라우저의 메인 스레드에 무리가 갑니다. 성능을 향상시키려다가 오히려 악화시키게 되는 꼴인 거죠.

 

Intersection Observer

Intersection Observer는 브라우저에서 제공하는 API인데요. 이를 통해 웹 페이지의 특정 요소를 관찰(observe)하면 페이지 스크롤 시, 해당 요소가 화면에 들어왔는지 아닌지 알려 줍니다. 즉, 스크롤 이벤트처럼 스크롤할 때마다 함수를 호출하는 것이 아니라 요소가 화면에 들어왔을 때만 함수를 호출하는 겁니다. 따라서 성능 면에서 scroll 이벤트로 판단하는 것보다 훨씬 효율적입니다.

 

 

options: Intersection Observer 의 옵션

- root: 대상 객체의 가시성을 확인할 때 사용되는 뷰포트 요소입니다. 기본 값은 null이며, null로 설정 시 브라우저의 뷰포트로 설정됩니다.

- rootMargin: root 요소의 여백입니다. 쉽게 얘기해서 root의 가시 범위를 가상으로 확장하거나 축소할 수 있습니다.

- threshold: 가시성 퍼센티지입니다. 대상 요소가 어느 정도로 보일 때 콜백을 실행할지 결정합니다. 1.0으로 설정하면 대상 요소가 모두 보일 때 실행되며, 0으로 설정하면 1px이라도 보이는 경우 콜백이 실행됩니다.

 

callback: 가시성이 변경될 때마다 실행되는 함수입니다.

options와 callback을 정의한 후, IntersectionObserver 객체를 생성하면 인스턴스(observer)가 나오는데 이 인스턴스를 이용하여 원하는 요소를 관찰할 수 있습니다. 이렇게 하면 대상 요소의 가시성이 변할 때마다 콜백이 실행되며 콜백에서는 첫 번째 인자로 가시성이 변한 요소(entries)를 배열 형태로 전달받습니다. 그 이후에는 원하는 로직을 실행하면 되는 것이죠.

 

적용 코드

function Card(props) {
  const imgRef = useRef(null);

  useEffect(() => {
    const options = {};
    const callback = (entries, observer) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          console.log("entry", entry);
          entry.target.src = entry.target.dataset.src;
          observer.unobserve(entry.target);
        }
      });
    };
    const observer = new IntersectionObserver(callback, options);
    observer.observe(imgRef.current);
    return () => observer.disconnect();
  }, []);

  return (
    <div className="Card text-center">
      <picture>
        <source data-secret={props.webp} type="image/not-support" />
      </picture>
      <img data-src={props.image} ref={imgRef} />
      <div className="p-5 font-semibold text-gray-700 text-xl md:text-lg lg:text-xl keep-all">
        {props.children}
      </div>
    </div>
  );
}

중복을 방지하고자 useEffect에서 인스턴스를 생성해야 합니다. 또한 생성된 인스턴스는 정리(Clean-up) 함수에서 observer.disconnect 함수를 호출함으로써 리소스가 낭비되지 않도록 합니다.

 

 

그중에서도 가장 중요한 값은 바로 isIntersecting. 이 값은 해당 요소가 뷰포트 내에 들어왔는지를 나타내는 값입니다.

이 값을 통해 해당 요소가 화면에 보이는 것인지, 화면에서 나가는 것인지 알 수 있습니다.

 

주소를 data-src에 넣어서 나중에 이미지가 뷰포트에 들어왔을 때, data-src에 있는 값을 src로 옮겨 이미지를 로드해줍니다.

 


이미지 사이즈 최적화

이미지 사이즈가 크면 다운로드에 많은 시간이 걸리고 그만큼 다른 작업에 영향을 줍니다.

그래서 이번에 적용할 최적화 기법은 바로 이미지 사이즈 최적화입니다.

 

 

이미지 포맷 종류

PNG는 무손실 압축 방식으로 원본을 훼손 없이 압축하며 알파 채널을 지원하는 이미지 포맷입니다. 알파 채널이란 투명도를 의미합니다. PNG 포맷으로 배경 색을 투명하게 하여 뒤에 있는 요소가 보이는 이미지를 만들 수 있습니다.

 

JPG는 PNG와는 다르게 압축 과정에서 정보 손실이 발생합니다. 하지만 그만큼 이미지를 더 작은 사이즈로 줄일 수 있습니다. 그래서 일반적으로 웹에서 이미지를 사용할 때는 고화질이어야 하거나 투명도 정보가 필요한 게 아니라면 JPG를 사용합니다.

 

WebP는 무손실 압축과 손실 압축을 모두 제공하는 최신 이미지 포맷으로, 기존의 PNG나 JPG에 비해서 대단히 효율적으로 이미지를 압축할 수 있습니다.

Squoosh를 사용하여 이미지 변환

현재 적용된 JPG 또는 PNG 포맷의 이미지를 WebP 포맷으로 변환하여 고화질, 저용량의 이미지로 최적화해 보려고 합니다. 그러려면 이미지를 변환해 주는 컨버터(converter)가 필요한데, 여기서 사용할 컨버터는 바로 Squoosh라는 애플리케이션입니다.

 

 

보이는 이미지의 사이즈의 두 배 정도의 사이즈로 변환합니다.

다음은 Compress 섹션의 압축 방식과 압축률(Quality)을 각각 WebP, 75로 설정합니다(이 값이 너무 작으면 화질이 많이 떨어지고, 너무 크면 용량이 커지므로 70~80 정도를 권장합니다).

그 외 Effort 설정이 있는데, 이 값은 CPU의 리소스를 어느 정도로 사용할지에 대한 설정입니다. 여기서는 기본 값인 4로 두겠습니다.

 

WebP는 효율이 좋지만 호환성 문제가 있습니다. 즉, WebP로만 이미지를 렌더링할 경우 특정 브라우저에서는 제대로 렌더링되지 않을 수도 있다는 뜻입니다. 이런 문제를 해결하려면 단순 img 태그로만 이미지를 렌더링하면 안 되며, picture 태그를 사용해야 합니다.  

picture 태그는 다양한 타입의 이미지를 렌더링하는 컨테이너로 사용됩니다. 예를 들어 아래 코드처럼 브라우저 사이즈에 따라 지정된 이미지를 렌더링하거나 지원되는 타입의 이미지를 찾아 렌더링합니다.

<picture>
  <source data-secret={props.webp} type="image/webp" />
  <img data-src={props.image} ref={imgRef} />
</picture>

img 태그를 picture 태그로 수정한 후, 안에 source 태그와 img 태그를 넣었습니다. 이렇게 하면 가장 상위에 있는 WebP를 우선으로 로드하고, 브라우저가 WebP를 지원하지 않으면 img 태그에 있는 JPG 이미지를 렌더링합니다.

 

최적화 전후 비교

 

 

이미지를 최적화하기 전에는 다운로드에 대략 30초가 소요된 반면 최적화 후에는 대략 100밀리초가 소요되었습니다.

 


동영상 최적화

동영상 콘텐츠 분석

동영상 콘텐츠의 특성상 파일 크기가 크기 때문에 당장 재생이 필요한 앞부분을 먼저 다운로드한 뒤 순차적으로 나머지 내용을 다운로드합니다. 그래서 동영상 콘텐츠의 다운로드 요청이 여러 개로 나뉘어 있는 것입니다. 또한 일정 시간 동안 동영상 콘텐츠가 다운로드되고, 그 이후에야 재생이 되는 것을 볼 수 있습니다.

 

 

동영상 압축

동영상이 메인 콘텐츠가 아닌 서비스에서는 이 최적화 기법을 적용해도 무방합니다.

WebM은 앞에서 배운 WebP와 동일하게 구글에서 개발한 동영상 포맷입니다. 역시나 웹에 최적화된 포맷입니다.

 

Media.io에 동영상 파일을 업로드하면 다음과 같이 몇 가지 설정을 할 수 있습니다.

 

 

다운로드된 동영상을 확인해 보면 용량이 원본의 1/5인 12MB가 됐습니다.

<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
    style={{
      filter: "blur(4px)",
    }}
>
  <source src={video_webm} type="video/webm" />
  <source src={video} type="video/mp4" />
</video>

 

최적화 전후 비교

최적화 후 Performance 패널로 분석해 보면 동영상이 이전과 달리 매우 빠르게 로드되고 재생되는 것을 확인할 수 있습니다

 

동영상을 압축해야 한다면 저하된 화질을 보완할 수 있는 방법이 몇 가지 있습니다. 바로 패턴과 필터를 이용하는 것입니다. 

말 그대로 동영상 위에 패턴을 넣거나 동영상에 필터를 씌우는 방법입니다.

 

 

다양한 필터가 있지만, 그중에 가장 효과적인 것은 blur입니다. video 요소에 CSS 코드로 filter: blur(10px)를 넣어 주면 동영상이 흐려집니다.

 


폰트 최적화

 

이 현상은 텍스트가 보이는 시점에 폰트 다운로드가 완료되지 않아 생기는 현상입니다. Network 패널에서 폰트를 확인해 보면 파일 크기가 750kB이며 다운로드하는 데 4.82초가 걸린 것을 볼 수 있습니다.

폰트가 바뀌면서 깜박이는 모습은 페이지가 느리다는 느낌을 줄 수도 있고 또는 다른 요소를 밀어낼 수도 있습니다.

 

FOUT, FOIT

폰트의 변화로 발생하는 이 현상을 FOUT(Flash of Unstyled Text) 또는 FOIT (Flash of Invisible Text)라고 합니다.

FOUT는 Edge 브라우저에서 폰트를 로드하는 방식으로, 폰트의 다운로드 여부와 상관없이 먼저 텍스트를 보여 준 후 폰트가 다운로드되면 그때 폰트를 적용하는 방식입니다.

FOIT 는 폰트가 완전히 다운로드되기 전까지 텍스트 자체를 보여 주지 않습니다. 그리고 폰트 다운로드가 완료되면 폰트가 적용된 텍스트를 보여 줍니다.

 

 

폰트 최적화 방법

폰트를 최적화하는 방법은 크게 두 가지가 있습니다. 

하나는 폰트 적용 시점을 제어하는 방법이고, 다른 하나는 폰트 사이즈를 줄이는 방법입니다.

 

폰트 적용 시점 제어

CSS의 font-display 속성을 이용하면 폰트가 적용되는 시점을 제어할 수 있습니다. font-display는 @font-face에서 설정합니다.

 

auto: 브라우저 기본 동작 (기본 값)

block: FOIT (timeout = 3s)

swap: FOUT fallback: FOIT (timeout = 0.1s) / 3초 후에도 불러오지 못한 경우 기본 폰트로 유지, 이후 캐시 optional: FOIT (timeout = 0.1s) / 이후 네트워크 상태에 따라 기본 폰트로 유지할지 결정, 이후 캐시

 

문제는 이렇게 block 옵션을 설정하면 안 보이던 폰트가 갑자기 나타나서 조금 어색할 수도 있다는 점인데요. 이 문제를 해결하기 위해 페이드 인(fade-in) 애니메이션을 적용해 보려고 합니다.
폰트에 페이드 인 효과를 적용하려면 CSS가 아닌 자바스크립트의 도움이 필요합니다.

폰트가 다운로드 완료되는 시점을 알아야 합니다. 폰트의 다운로드 시점은 fontfaceobserver라는 라이브러리를 통해 알 수 있습니다

 

여기서는 타임아웃을 20초(20,000밀리초)로 설정했는데요. 만약 이 시간 안에 폰트가 다운로드되지 않으면 Promise에서 에러를 발생시킵니다.

 

 

그리고 폰트가 로드되는 시점을 알았으니 배너 텍스트에 페이드 인 효과를 적용합니다.

텍스트를 단순히 빠르게 띄우는 것도 좋지만, 이런 식으로 사용자에게 보기 편하게 서비스하는 것도 체감 성능을 높이는 데 중요합니다.

폰트 파일 크기 줄이기

두가지 방법이 있습니다. 

하나는 이미지나 비디오와 마찬가지로 압축률이 좋은 폰트 포맷을 사용하는 것이고, 

다른 하나는 필요한 문자의 폰트만 로드하는 것입니다. 

 

폰트 포맷 변경하기

TTF 포맷은 파일 크기가 매우 큽니다. 이미 다운로드되어 있는 운영 체제 환경에서는 상관없지만 매번 리소스를 다운로드해야 하는 웹 환경에서는 적절하지 않습니다. 그래서 나온 것이 WOFF입니다. WOFF는 Web Open Font Format의 약자로, 이름 그대로 웹을 위한 폰트입니다. 이 포맷은 TTF 폰트를 압축하여 웹에서 더욱 빠르게 로드할 수 있도록 만들었습니다. 더 나아가서 WOFF2라는 더욱 향상된 압축 방식을 적용한 포맷도 있습니다.

 

 

폰트를 적용하려면 App.css의 @font-face에 넣으면 되며, src 속성에 적용 우선순위가 높은 것부터 차례로 나열하면 됩니다. 이때 format 정보도 함께 작성합니다.

 


서브넷 폰트 사용(필요한 문자에만 폰트 적용하기)

 

 

모든 문자의 폰트 정보를 가지고 있을 필요 없이 해당 문자의 폰트 정보만 있으면 됩니다. 이렇게 모든 문자가 아닌 일부 문자의 폰트 정보만 가지고 있는 것을 서브셋(subset) 폰트라고 합니다.

설정 중에 Characters에 폰트를 적용할 문자(KEEP CALM AND RIDE LONGBOARD)를 넣으면 해당 문자에 대한 서브셋 폰트가 나옵니다. 이번에는 TTF 포맷의 폰트도 생성합니다.

Network 패널에서 살펴보면 throttling을 적용했는데도 빠르게 로드되는 것을 볼 수 있습니다.

 

폰트를 파일 형태가 아닌 Data-URI 형태로 CSS 파일에 포함할 수도 있습니다.
Data-URI란 data 스킴이 접두어로 붙은 문자열 형태의 데이터인데, 쉽게 말해서 파일을 문자열 형태로 변환하여 문서(HTML, CSS, 자바스크립트 등)에 인라인으로 삽입하는 것입니다.

Data-URI 형태로 추출하기 위해서 Base64 encode 옵션을 On으로 설정해 줍니다.

 

다운받은 stylesheet.css 파일을 텍스트 에디터로 열어 줍니다. 그러면 그림 3-54와 같이 @font-face에 폰트 파일의 경로가 아닌 이상한 문자열이 들어가 있는 것을 볼 수 있습니다. 이 문자열이 바로 WOFF2 파일을 Data-URI로 변환한 것입니다.

 

 

기본적으로 브라우저에서 Data-URI를 네트워크 트래픽으로 인식해서 기록하지만 실제로는 이미 다른 파일 내부에 임베드되어 있어 별도의 다운로드 시간이 필요하지 않습니다. 그래서 시간이 매우 짧은 것이죠.

 

주의사항

App.css의 다운로드 속도 또한 고려해야 합니다. Data-URI가 포함된 만큼 App.css 파일의 다운로드는 느려질 것입니다(App.css는 main.chunk.js에 포함되어 빌드됩니다). 매우 큰 파일을 Data-URI 형태로 포함한다면 포함한 파일 크기가 그만큼 커져 또 다른 병목을 발생시킬 수 있습니다.

 


캐시 최적화

 

Diagnostics 섹션의 Serve static assets with an efficient cache policy라는 항목입니다. 이 항목은 네트워크를 통해 다운로드하는 리소스에 캐시를 적용하라는 의미입니다. 항목을 클릭해서 펼쳐 보면 거의 모든 리소스에 캐시가 적용되어 있지 않다고 나옵니다.

 

실제로 Network 패널에서 위 리소스 중 하나를 확인해 보면 응답 헤더에 캐시에 대한 설정인 Cache-Control이라는 헤더가 
없는 것을 알 수 있습니다. 즉, npm run start로 실행한 서버에는 캐시 설정이 제대로 되어 있지 않은 것입니다.

 

 

캐시란?

캐시는 간단히 말하면 자주 사용하는 데이터나 값을 미리 복사해 둔 임시 저장 공간 또는 저장하는 동작입니다.

 

캐시의 종류

메모리 캐시: 캐시는 간단히 말하면 자주 사용하는 데이터나 값을 미리 복사해 둔 임시 저장 공간 또는 저장하는 동작입니다.
디스크 캐시: 파일 형태로 디스크에 저장하는 방식입니다.

 

구글 홈페이지에서 개발자 도구를 열어 Network 패널을 확인해 보시면 Size 항목에 memory cache 또는 disk cache라고 표시된 것을 볼 수 있습니다. 이 리소스들이 브라우저에 캐시된 리소스입니다.

 

구글에서 단순 새로고침을 한 후 확인했다면 memory cache가 많을 것입니다. 

왜냐하면 이미 구글의 리소스가 메모리에 캐시되었기 때문입니다. 

브라우저를 완전히 종료한 후 구글에 접속하는 첫 네트워크 리소스를 확인하면 disk cache가 많을 것입니다. 

브라우저가 완전히 종료되면 메모리에 있는 내용은 제거하고 다음 접속 때는 파일 형태로 남아 있는 캐시를 활용하기 때문입니다.

 

 

Cache-Control

Cache-Control은 리소스의 응답 헤더에 설정되는 헤더입니다. 브라우저는 서버에서 이 헤더를 통해 캐시를 어떻게, 얼마나 적용해야 하는지 판단합니다. 

 

- no-cache: 캐시를 사용하기 전 서버에 검사 후 사용

- no-store: 캐시 사용 안 함

- public: 모든 환경에서 캐시 사용 가능

- private: 브라우저 환경에서만 캐시 사용, 외부 캐시 서버에서는 사용 불가

- max-age: 캐시의 유효 시간

 

웹 리소스는 브라우저뿐만 아니라 웹 서버와 브라우저 사이를 연결하는 중간 캐시 서버에서도 캐시될 수 있습니다.

 

캐시 적용

캐시는 응답 헤더에서 Cache-Control 헤더를 통해 설정된다고 했습니다. 응답 헤더는 서버에서 설정해 줍니다.

 

 

코드를 수정한 후, 수정된 코드가 제대로 반영될 수 있도록 실행 중인 서버를 종료하고 다시 실행합니다. 그리고 실행된 서비스에서 새로고침을 몇 번 하면 Network 패널에 다음과 같이 리소스들이 캐시되는 것을 볼 수 있습니다.

 

하지만 설정한 시간인 10초가 지난 후 다시 새로고침해 보면 조금 다른 모습으로 기록되는 것을 볼 수 있습니다.

왜냐하면 캐시 유효 시간이 만료되면서 브라우저는 기존에 캐시된 리소스를 그대로 사용해도 될지, 아니면 리소스를 새로 다운로드해야 할지 서버에 확인하기 때문입니다. 

여기서는 서비스의 각 리소스가 변경되지 않아 브라우저에 캐시되어 있는 리소스를 그대로 사용해도 무방하기 때문에, 서버에서는 변경되지 않았다는 304 상태 코드를 응답으로 보냈습니다.

캐시를 그대로 사용했음에도 리소스의 Size가 260B로 기록된 이유는 캐시를 사용해도 되는지 확인하기 위해 네트워크 요청을 보내고 응답을 받았기 때문입니다.

 

 

캐시된 리소스와 서버의 최신 리소스가 같은지 다른지 어떻게 체크할까?

: 캐시 유효 시간이 만료되면 브라우저는 캐시된 리소스를 계속 사용해도 될지 서버에 확인합니다. 이때 서버에서는 캐시된 리소스의 응답 헤더에 있는 Etag 값과 서버에 있는 최신 리소스의 Etag 값을 비교하여 캐시된 리소스가 최신인지 아닌지, 즉, 계속 사용해도 되는지 아닌지 판단합니다. 만약 서버에 있는 리소스가 변했다면 Etag 값이 달라지고, 서버는 새로운 Etag 값과 함께 최신 리소스를 브라우저로 다시 보내 줍니다.

적절한 캐시 유효 시간

일반적으로 HTML 파일에는 no-cache 설정을 적용합니다. 항상 최신 버전의 웹 서비스를 제공하기 위해서입니다.

HTML이 캐시되면 캐시된 HTML에서 이전 버전의 자바스크립트나 CSS를 로드하게 되므로

항상 최신 버전의 리소스를 제공하면서도 변경 사항이 없을 때만 캐시를 사용하는 no-cache 설정을 적용합니다.

 

자바스크립트와 CSS는 다릅니다. 빌드된 자바스크립트와 CSS는 파일명에 해시를 함께 가지고 있습니다(main.bb8aac28.chunk.js). 즉, 코드가 변경되면 해시도 변경되어 완전히 다른 파일이 되어 버립니다. 따라서 캐시를 아무리 오래 적용해도 HTML만 최신 상태라면 자바스크립트나 CSS 파일은 당연히 최신 리소스를 로드할 것입니다. 이미지도 마찬가지입니다.

 

 


불필요한 CSS 제거

 

Coverage 패널은 페이지에서 사용하는 자바스크립트 및 CSS 리소스에서 실제로 실행하는 코드가 얼마나 되는지 알려 주며 그 비율을 표시해 줍니다. 따라서 이 패널을 통해 서비스 코드에 불필요한 코드가 얼마나 있는지 확인할 수 있습니다.

 

 

오른쪽 Unused Bytes(사용하지 않는 바이트)와 Usage Visualization (사용량 시각화) 항목에서 전체 코드 대비 실행된 코드의 양을 비율로 보여 줍니다. 아래 두 리소스는 사이즈 자체가 작아서 크게 의미가 없지만 위쪽 두 리소스에서는 실행되지 않은 코드가 꽤 많습니다. 

페이지를 Coverage 패널의 기록이 진행 중인 상태에서 페이지를 이동하고 기타 동작들을 해 보면 점점 코드 사용 비율이 증가합니다.

왜냐하면 자바스크립트의 경우 if문 같은 조건이 걸려 있어 분기되는 코드가 많아 이런 코드는 경우에 따라 실행이 안 됐다가도 특정 동작에 의해 다시 실행되기도 하기 때문입니다. 그렇다 보니 자바스크립트 코드의 커버리지는 어느 정도 감안해야 합니다.

 

해당 항목을 눌러 보면 Sources 패널에서 해당 코드가 뜨면서 어떤 코드가 실행되었고 어떤 코드가 실행되지 않았는지 자세히 볼 수 있습니다. 코드 왼쪽에 파란 막대는 해당 코드가 실행되어 적용되었다는 의미이고, 빨간 막대는 실행되지 않았다는 의미입니다.

 

 

실행되지 않은 CSS 코드를 살펴보면 상당히 많은 유틸 클래스가 사용되지 않았음을 알 수 있습니다. 이 클래스들은 모두 Tailwind CSS 라이브러리에서 추가된 것으로, 개발할 때는 미리 만들어진 클래스를 통해 쉽고 빠르게 스타일을 적용할 수 있다는 장점이 있었지만 막상 빌드하고 나니 사용하지 않은 스타일도 함께 빌드되어 파일의 사이즈를 크게 만든다는 단점이 있습니다.

PurgeCSS

PurgeCSS는 파일에 들어 있는 모든 키워드를 추출하여 해당 키워드를 이름으로 갖는 CSS 클래스만 보존하고 나머지 매칭되지 않은 클래스는 모두 지우는 방식으로 CSS 파일을 최적화합니다.

 

 

예를 들어 그림 3-75와 같은 텍스트 파일이 있을 때, 키워드를 추출하면 figure, class, bg-slate-100, rounded-xl, p-8, div, pt-6, space-y-4 등이 추출될 것입니다. 그럼 추출된 키워드와 Tailwind CSS에서 제공하는 유틸리티 클래스의 이름을 비교하여 일치하는 클래스만 남기는 방식입니다. 이렇게 하면 코드 내에서 사용하지 않은 클래스는 매칭되지 않으므로 CSS 파일에서 제거될 것입니다.

 

 

-알라딘 eBook <프론트엔드 성능 최적화 가이드> (유동균 지음) 중에서

Comments