데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      OpenLab 2기 - 웹 프론트엔드 성능 최적화 : 4번째 스터디 (3장 홈페이지 최적화 - 3편 : 폰트 최적화, 캐시 최적화, 불필요한 CSS 제거)

      Shweety 24.10.30
      220 5 2
      DEVOTEE 요약
      본 블로그는 웹 프론트엔드 성능 최적화에 대한 내용으로, 폰트 최적화, 캐시 관리, 그리고 불필요한 CSS 제거 방법에 대해 설명합니다. 폰트 최적화는 폰트 파일 크기를 줄이고, 폰트 적용 시점을 조절하여 텍스트가 신속하게 표시되도록 하는 방법을 소개합니다. 또한 캐시 최적화를 통해 리소스의 다운로드 시간을 단축시키고, PurgeCSS 등을 활용하여 사용되지 않는 CSS를 제거하는 기술을 설명하며, 이러한 최적화가 최종적으로 사용자 경험을 크게 향상시킬 수 있음을 강조합니다.
      DEVOTEE 추천 블로그

      본 내용은 <프론트엔드 성능 최적화 가이드> 책을 바탕으로 작성된 글입니다.

      안녕하세요. OpenLab 2기 웹 프론트엔드 성능 최적화 스터디에 참여중인 임수현입니다.


      교재의 [ 3장 홈페이지 최적화 ] 중 폰트 최적화, 캐시 최적화, 불필요한 CSS 제거 부분을 맡아 발표하였습니다.

      발표 내용을 바탕으로 정리한 것을 공유하고자 합니다.

      제 글은 블로그의 [3장 홈페이지 최적화- 1편, 2편]과 연결되는 내용입니다.

      앞 파트를 먼저 읽어보시면 개선할 서비스에 대한 이해도를 높이실 수 있습니다.

      3장 홈페이지 최적화 -1편

      글의 목차

      1. 폰트 최적화

      2. 캐시 최적화

      3. 불필요한 CSS 제거


      1. 폰트 최적화

      문제 상황은 폰트가 처음 부터 적용되지 않고, 기본 폰트의 텍스트가 보인 후 텍스트에 폰트가 나중에 적용되는 것입니다.

      image.png

      폰트 파일의 크기가 커서 다운로드까지 시간이 걸리기에, 폰트가 적용되지 않은 텍스트가 먼저 보이는 것입니다.

      image.png

      폰트 파일이 750KB인 것을 확인할 수 있습니다.


      웹 브라우저의 동작 과정을 이해하면, 위와 같은 현상 발생을 이해할 수 있습니다.

      image.png

      브라우저가 HTML을 요청하면, 요청받은 HTML로 dom을 구성합니다.

      그 후 브라우저는 필요한 CSS를 확인하여 요청하고, 응답받은 CSS로 CSSOM을 구성합니다.

      그 때 랜더링에 필요한 폰트를 요청하는데요,

      현재는 파일이 750kb 로 크기 때문에 다운로드 하는데 시간이 필요하고, 당장 그 폰트를 사용할 수 없기에 폰트가 적용되지 않은 텍스트가 보이는 것입니다.


      이렇게 폰트 변화로 발생하는 현상이 2가지가 있습니다.

      FOUT와 FOIT

      FOUT는 Flash Of Unstyled Text의 약자로 폰트가 다운로드 되지 않았다면 기본 폰트를 보여주는 방식입니다.

      그래서 폰트가 다운로드 되면, 그때 텍스트에 폰트를 적용합니다.

      보통은 텍스트가 빨리 보여져야 하는 중요한 상황에서 쓰이는 방법입니다.

      Edge 브라우저는 이 방식을 기본 값으로 하고 있습니다.


      FOIT는 Flash Of Invisible Text의 약자로 폰트가 다운로드 되지 않았다면 텍스트를 보여주지 않고 폰트가 다운로드 되면 그제서야 폰트가 적용된 텍스트를 보여줍니다.

      chrome, safari, firefox 등의 브라우저에서 이 방식을 기본 값으로 하고 있습니다.


      그러나 chrome이 unstyled text를 보여주는 이유는 3초 타임 아웃을 가진 FOIT이기 때문입니다.

      다운로드가 될 때까지 최대 3초를 기다렸다가, 다운로드가 완료되지 않으면 unstyled text를 보여줍니다.

      최적화 방법

      최적화 방법엔 2가지가 있습니다

      1. 폰트 적용 시점 제어

      2. 폰트 파일 크기 줄이기


      1. 폰트 적용 시점 제어

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

      font-display에 다음과 같은 값을 넣어서 설정할 수 있습니다.

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

      • block: FOIT (timeout 3초)

      • swap: FOUT

      • fallback: FOIT (timeout 0.1초)

        3초 후에도 불러오지 못한 경우 기본 폰트로 유지, 이후 캐시

      • optional: FOIT (timeout 0.1초)

        이후 네트워크 상태에 따라 기본 폰트로 유지할지 결정, 이후 캐시

      현재 서비스는 텍스트가 당장 보여야 하는 것이 아니기에, 폰트가 적용된 텍스트를 보여주도록 하겠습니다.

      image.png

      이렇게 설정해두면, 폰트가 적용된 텍스트가 갑자기 '짠!😶' 하고 나타납니다.

      그러면 사용자가 불편함을 느낄 수 있기 때문에,

      폰트가 다운로드 되면, fade-in 애니메이션을 넣어 자연스럽게 텍스트가 보이는 효과를 적용하겠습니다.


      폰트 다운로드 완료 시점은 fontfaceobserver라는 라이브러리를 통해 알 수 있습니다.

      이를 npm으로 설치한 후 이용해보겠습니다.

      image.png

      위와 같이 인스턴스를 생성하고,

      image.png

      다운로드가 완료 되면, isFontLoaded값을 true로 변경하면

      애니메이션 효과와 함께 폰트가 적용된 텍스트가 나타납니다.


      2. 폰트 파일 크기 줄이기

      현재 폰트의 포맷은 TTF입니다.

      image.png

      TTF와 OTF 포맷은 용량이 큽니다.

      이미 다운로드 되어있는 운영 체제 환경에서는 상관없지만, 매번 리소스를 다운로드 해야 하는 웹 환경에서는 적절하지 않습니다.


      그래서 비교적 더 작은 WOFF와 WOFF2를 이용해보겠습니다.

      WOFF는 Web Open Font Format이라는 약자로, 웹을 위한 폰트입니다. 아래 사이트에서 폰트의 포맷을 변경할 수 있습니다.

      폰트 포맷 변경 사이트


      그러나 WOFF와 WOFF2는 브라우저 호환성 문제가 있습니다. 일부 브라우저가 지원하지 않을 수 있기 때문에 다음과 같이 설정해주겠습니다.

      image.png

      이렇게 적용하면 위에 작성 된 것을 우선 적용하면서 브라우저 호환이 되지 않으면 그 아래의 포맷을 로드하게 됩니다.


      throttling 3G를 적용하여 결과를 확인해보니

      image.png

      용량이 눈에 띄게 줄었고, 다운로드 시간도 3초나 앞당길 수 있었습니다.

      그럼에도 폰트가 아직 큰 것을 확인할 수 있습니다. 폰트의 파일을 더 줄여보겠습니다.


      우리는 "KEEP CALM AND RIDE LONGBOARD" 문구에만 폰트가 필요합니다.

      image.png

      필요한 문자의 폰트만 가져오는 서브넷 폰트를 이용해 보겠습니다.


      폰트를 위의 폰트 포맷 변경 사이트에서 서브넷 폰트로 다운로드 할 수 있습니다. characters에 저 문구를 넣고 폰트를 추출합니다.

      image.png

      용량이 눈에 띄게 줄어든 것을 확인할 수 있습니다.

      image.png

      다운로드 시간도 약 3초나 줄어들었습니다.

      image.png

      여기서 그치지 않고 우리는 Data-URI 형태로 CSS 파일에 포함할 수 있습니다.


      Data-URI는 파일을 문자열 형태로 변환하여 문서에 인라인으로 삽입하는 것입니다.

      Data-URI를 App.css 파일에 넣어두면, App.css 파일에서 바로 폰트를 로드할 수 있습니다.


      보통의경우

      App.css로드 -> 폰트 파일 추가 다운로드-> 사용 가능!


      Data-URI 형태로 만들어서 App.css에 넣어두면

      App.css 로드 -> 바로 사용 가능!


      폰트 포맷 변경 사이트 여기서 Data-URI로 변환할 수 있습니다.

      Base64 encode 속성을 활성화하여 추출합니다.

      image.png

      그러면 style.css 파일이 함께 생기는데

      image.png

      해당 파일을 열면 이렇게 문자열이 대신 들어있는데, 이것이 Data-URI입니다. App.css에 src에 폰트의 파일 경로 대신, Data-URI 문자열을 넣어주면 적용은 끝났습니다.

      image.png

      캐시를 비우고 새로 고침하면 이렇게 폰트를 다운 받지 않는 것을 확인할 수 있습니다

      image.png

      그러면 애초에 Data-URI를 이용하면 되잖아요! 🤔

      라고 생각하실 수 있습니다.


      폰트의 정보가 App.css에 포함 된 것이므로, App.css 다운로드 속도에 영향을 줄 수 있습니다.

      그래서 우리는 subnet-font로 바꾼 후 Data-URI로 추출했습니다.

      폰트의 모든 정보가 있는 파일 Data-URI로 넣게 된다면, App.css 파일이 커져서 또 다른 병목을 만들어 낼 것입니다.


      2. 캐시 최적화

      현재 lighthouse는 99점입니다. 👏👏

      image.png

      그러나 diagonostics 섹션에서 ' Serve satic assets with an efficient cache policy'라는 항목이 존재합니다.

      이는 네트워크를 통해 다운로드하는 리소스에 캐시를 적용하라는 의미입니다.

      image.png

      캐시란?

      자주 사용하는 데이터나 값을 미리 복사해 둔 임시 저장 공간 또는 저장하는 동작

      캐시의 종류는 메모리 캐시와 디스크 캐시가 존재합니다.

      메모리 캐시는 RAM 메모리에 저장하는 방식이고, 디스크 캐시는 파일 형태로 디스크에 저장하는 방식입니다.


      브라우저가 캐시를 하기 위해서 응답 헤더 내에 cache-control헤더가 있어야 합니다. 그런데 현재 우리의 응답 헤더에 Cache-Control이라는 헤더가 없습니다.

      image.png

      cache-control의 종류
      • no-cache : 캐시를 사용하기 전 서버에 검사 후 대응

      • no-store : 캐시 사용 안 함

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

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

      • max-age : 캐시의 유효 시간 (단위 : 초)

      현재 server.js 파일 내의 코드 헤더 설정에는 캐시를 하지 않기 위한 설정들이 담겨 있습니다.

      image.png

      기본 값인 public과 max-age는 10초로 설정해보겠습니다.

      image.png

      처음 로드

      image.png

      새로 고침 3번 했을 때 캐시된 파일을 불러오는 것을 확인할 수 있습니다.

      image.png

      10초가 지난 후에는 이렇게 state가 304로 된 응답이 오는 것을 확인할 수 있습니다.

      image.png

      이는 만료시간이 다 되어서 다시 이 데이터를 다시 사용해도 되는지 확인 요청을 보내고 응답 받은 내용입니다. 그래서 260B라는 작은 용량의 파일을 응답 받은 것입니다.

      image.png

      이때 캐시된 파일을 재사용할 수 있는지 확인하기 위해 Etag를 사용합니다.

      Etag란?

      클라이언트가 이전에 요청했던 데이터와 최신 데이터의 변경 사항 유무를 검증하는 데 사용하는 HTTP 응답 헤더입니다.

      Etag 동작 과정은 다음과 같습니다.

      1. 사용자가 데이터를 요청하면, 서버에서는 Etag값과 데이터를 클라이언트에게 보냅니다.

      2. 캐시된 데이터의 유효성을 확인하기 위해, max-age 시간이 초과된 경우 클라이언트는 If-None-Match 헤더에 Etag 값을 포함하여 서버에 요청합니다.

      3. 서버는 If-None-Match의 Etag 값과 서버의 최신 Etag 값을 비교합니다. 값이 같다면 304 Not Modified를 응답해 캐시된 데이터를 사용하도록 합니다.

      image.png

      그래서 처음 데이터를 요청했을 때, 응답 헤더에는 Etag 값이 들어있습니다. 그러나 If-None-Match헤더가 없는 것을 확인할 수 있습니다.

      image.png


      3. 불필요한 CSS 제거

      lighthouse diagonostics 섹션에 사용되지 않는 CSS를 제거하면 605KiB를 줄일 수 있다고 합니다!

      image.png

      coverage 패널을 이용하여 사용하지 않은 파일의 용량을 확인해 보겠습니다!

      image.png

      자바스크립트 리소스인 2.chunk파일을 보면 대략 53.8%가 사용되지 않고 있습니다.

      자바스크립트의 경우 클릭하고, 페이지를 이동하다 보면 비율이 줄어드는 것을 확인할 수 있습니다.

      이는 분기 되어 있는 코드들이 실행되면서 사용 비율이 줄어드는 것입니다.


      그러나 문제는 css 파일입니다. main.chunk.css파일의 99%가 사용되지 않고 있습니다.

      해당 부분을 클릭하면 아래와 같은 화면을 통해 사용되고, 사용되지 않은 리소스들을 확인할 수 있습니다.

      image.png

      굉장히 많은 css 유틸 클래스가 사용되지 않을 것을 확인할 수 있습니다.

      이는 Tailwind css 라이브러리에서 추가된 것으로 사용되지 않은 스타일도 함께 빌드 되어 파일이 커졌습니다.


      이를 PurgeCSS 라는 툴을 이용하여 제거해보겠습니다!


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

      아래의 그림과 같이 잘라서 이용합니다.

      image.png

      먼저 Purgecss를 설치해줍니다.

      > npm install –save-dev purgecss

      PurgeCSS는 devDependency로 설치되었으므로, package.json에 넣어주겠습니다.

      "script":{ ...
              "purge": "purgecss --css ./build/static/css/*.css --output ./build/static/css/ --content ./build/index.html ./build/static/js/*.js"
       },

      PurgeCSS를 실행 하고 나면 unused 비율이 달라진 것을 확인할 수 있습니다.😊

      image.png

      그러나,, 적용 후에 스타일이 깨지는 것을 확인할 수 있습니다. 😢

      image.png


      요소 검사를 통해 해당 요소를 보면, lg:m-8, lg:ml-32 클래스의 스타일이 제대로 정의되어 있지 않은 것을 확인할 수 있었습니다.

      md : text-lg도 없는 것을 확인할 수 있었습니다.

      image.png

      이는 : 을 하나의 키워드로 인식하지 못하고 잘라서 생긴 문제입니다.

      이 문제는 PurgeCSS의 defaultExtractor 옵션을 통해 해결할 수 있습니다.

      //purgecss.config.js
      
      module.exports={
          defaultExtractor: (content) => content.match(/[\w\:\-]+/g) ||[]
      }

      purgecss.config.js라는 이름으로 프로젝트 최상단 경로에 생성합니다.


      defaultExtractor 옵션에는 함수가 들어가는데, 인자로 대상 파일의 전체 코드를 넘겨받고 문자열 배열을 반환합니다.


      인자로 받은 전체 코드는 match 메서드를 통해 정규식에 만족하는 키워드를 배열 형태로 추출합니다.

      위에서 사용한 정규식은 영문자와 숫자, _를 의미하는 \w와 : 그리고 - 으로 이루어진 키워드를 추출하는 정규식입니다.


      이제 스크립트에서 위의 설정 파일의 경로를 지정합니다. --config ./purgecss.config.js를 추가 해줍니다.

      "script":{ ...
             "purge": "purgecss --css ./build/static/css/*.css --content ./build/index.html ./build/static/js/*.js --output ./ --config ./purgecss.config.js"
       },

      다시 purgecss를 실행하면 스타일이 잘 적용된 것을 확인할 수 있습니다. 😊


      image.png


      느낀 점

      OpenLab 스터디에서 배운 프론트엔드 성능 최적화 지식을 실제로 적용해볼 수 있었습니다.

      개인 프로젝트에서 겪고 있던 폰트 로딩 문제를 해결하면서, 작은 코드 수정이 사용자 경험에 얼마나 큰 변화를 가져올 수 있는지 깨달았습니다.

      제 프로젝트에서 폰트 포맷을 woff2로 바꾸어보았는데요, 이 변경만으로도 폰트 용량이 30% 줄어들었고 페이지 로딩도 한결 가벼워졌습니다.

      lighthouse 성능 점수도 5점 높아져서 제가 의도한 개선이 실제로 효과가 있었다는 것을 확인할 수 있었습니다.


      매 스터디 시간마다 발표가 끝나면 스터디원들과 다양한 주제에 대해서 대화를 나누는 시간을 가지는데,

      이론적인 내용을 넘어 실무 이야기와 다양한 개발 팁들을 배울 수 있어서 매 스터디 시간이 더욱 알차게 느껴집니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      Shweety 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기