일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- k for k
- 자바스크립트
- Python
- JS
- kotlin
- javascript
- C++
- react
- nodejs
- CSS
- 프론트엔드
- Android
- 최적화
- 스택
- 코딩테스트
- firebase
- 백준 스택
- 타입스크립트
- NPM
- 백준 스택 시간초과 python
- 리액트
- typescript
- stdin vs input
- HTML
- next Link
- TS
- 알고리즘
- 파이어베이스
- 안드로이드
- 파이썬
- Today
- Total
sooleeandtomas
webfont 최적화로 FCP 줄이기 본문
크롬의 lighthouse에서 확인할 수 있는 최적화에는 LCP, FCP가 있습니다.
FCP: First Contentful Paint 초기 DOM 콘텐츠를 렌더링하기까지 걸리는 시간을 말합니다.
LCP: Largest Contentful Paint 페이지의 주요 내용이 화면에 렌더링되기까지 걸리는 시간을 말합니다.
사용자가 빈 화면만 보고 있다면 페이지가 로드되고 있는지 알 수 없습니다.
하지만, FCP이후, 첫번째 콘텐츠 비트를 렌더링하여 첫 피드백을 사용자에게 제공하기 때문에, 사용자가 페이지가 로딩 되고있음을 느끼게 됩니다. (FCP 이후엔 사용자가 조금더 인내심을 갖고 기다릴 수 있게 되는..)
그래서 저는 최적화의 첫단추로, FCP에 조금 더 초점을 맞춰보려 합니다.
FCP의 속도는 아래와 같이 점수를 매길 수 있습니다.
- Good – between 0 seconds and 1.8 seconds
- Needs Improvement – between 1.8 seconds and 3 seconds
- Poor – over 3 seconds
FPC의 속도를 측정하는 데에는 아래와 같은 사이트, tool이 있습니다.
- PageSpeed Insights
- Chrome User Experience Report
- Search Console (Speed Report)
- Web Vitals JavaScript library
- Lighthouse
- Chrome DevTools
FCP가 되기까지 브라우저는 DOMtree, COMTree를 형성합니다. 이때 필요한 파일을 모두 다운로드한 후에야, 페이지는 렌더링을 시작하게 됩니다. 여기서 필요한 파일이란 <HTML, Javascript, fonts, css file>을 말합니다. 이 파일들을 모두 불러온 후, 이미지, 일반 텍스트, 기타 콘텐츠를 로드합니다. (따라서 Image 사이즈는 FCP크게 영향이 없다는 것을 유추해볼 수 있습니다. 실제로는 FCP개선한다고 이미지 webp, srcset.. dog삽질을 했지만yo..)
FCP를 blocking하고 있는 파일에 어떤 파일이 있는지 찾아보았습니다. 우선, 가장쉽게 찾아볼 수 있는 방법은 Light house에서 fcp 개선점을 보는 것입니다.
LightHouse에서 페이지 검사 후, 아래 FCP탭에서 추천하는 작업을 펼쳐보면, 어느 지점에서 blocking이 많이 일어나는지 찾아볼 수 있습니다. 저 같은 경우에는 css파일에서 google-font를 가져오는 데 많은 시간이 걸리고 있었습니다.
google-font는 NotoSans를 제공하는데 이 폰트를 한꺼번에 가져오는 파일이 css에 import되어 있었기 때문입니다. 이놈의 font가 매우 큰 blocking요소가 되고 있었던 것입니다. 이 폰트들을 가져오는 시간만 약 2300ms이었습니다.
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&family=Roboto:wght@100;300;400;500;700;900&display=swap');
이를 개선하기 위해, webfont의 <subset, font-face, Cloud front>을 활용했습니다.
1. subset
서브셋은 한글에서 잘 사용하지 않는 폰트를 제외하고 잘 사용하는 글자만 적용해 놓는 것을 말합니다. <국가 표준(KS) 정보 교환용 부호계>에서 지정한 2,350자가 있는데, 이것만 폰트로 만들어 놓고, 그 외의 폰트는 제거하는 것입니다. 11,172자에서 2,350자로 거의 5분의 1을 줄일 수 있습니다.
"가각간갇갈갉갊감갑값갓갔강갖갗...."
정보 교환용 부호계만 해도 사용하지 않을 글자들이 대다수 일듯합니다.
subset을 만드는 방법은 두가지가 있습니다.
첫째, 일본어 프로그램 "서브셋 폰트 메이커"를 설치해 사용한다.
둘째, 파이썬 프로그램 "fonttools" 라이브러리를 커멘드 라인으로 사용한다.
저는 첫번째 방법을 사용했습니다. 사용법은 D2 naver <웹 폰트 최적화의 최근 동향>에 나와있습니다.
subset을 다시한번, 변환 프로그램을 활용해 woff2, woff, ttf 각각의 폰트 파일을 제작해줍니다.
woff2, woff를 제작하는 이유는 woff2로 변환하게 되는 경우, 폰트 사이즈를 더욱 최적화 할 수 있기 때문입니다.
이후 나오게 된 결과는 아래 font-face에서 적용해줍니다.
2. font-face
font-face는 각 폰트의 파일이 있는 지 확인 후, 체이닝 방식으로 없으면 그 다음 폰트를 로드해줍니다.
앞서 만든 woff2, woff, ttf subset 파일을 css 에 font-face로 작성해줍니다.
*주의 ttf의 format은 "ttf"가 아니라 "truetype"입니다.
아래처럼 font-face를 적용해주면 폰트가 woff2의 네트워크 실패가 난 경우, woff를 로드하고, woff에서 또 실패한 경우 ttf를 로드합니다.
@font-face {
font-family: 폰트 이름;
src: url(경로) format(포맷),
font-weight: 폰트 웨이트;
...기타 폰트 스타일
}
@font-face {
font-family: 'Noto Sans KR';
src: url('https://.../notoSansKR-light-subset.woff2') format('woff2'),
url('https://.../notoSansKR-light-subset.woff') format('woff'),
url('https://.../notoSansKR-light-subset.ttf') format('truetype');
font-weight: 300;
}
@font-face {
font-family: 'Noto Sans KR';
src: url('https://.../notoSansKR-regular-subset.woff2') format('woff2'),
url('https://.../notoSansKR-regular-subset.woff') format('woff'),
url('https://.../notoSansKR-regular-subset.ttf') format('truetype');
font-weight: 400;
}
3. s3 cloud front
앞서 font-face에 적용한 url은 s3의 url입니다. (subset 폰트를 제작한 후 s3에 올려놓았습니다.) 그런데, 브라우저에서 Webfont를 요청할 때 CORS에러가 나는 경우가 발생했습니다. 그 이유는 바로 "Preflight Request" 때문입니다. 브라우저에서 서버에 webFont를 요청할 때는 일반적인 요청과 다르게, Preflight Request로 요청을 보내는데, Cloudnfront에서 Preflight에 대한 설정이 안되어 있기 때문이었습니다.
Preflight에 관해 간략히 설명하자면 아래와 같습니다.
브라우저에서 서버로 요청을 보낼 때는 일반적으로 Simple Request로 보내는데, Simple Request가 아닌 요청은 브라우저에서 Preflight로 미리 서버에 요청을 보내어 서버의 허락을 받은 후에야 실제 Request를 보냅니다.
Simple Request의 조건은 아래와 같습니다.
- HTTP Method: GET, POST, HEAD
- Content-type: text/plain, application/x-www-form-urlencoded, multipart/form-data
Webfont의 경우 MIME type이라 Simple Request의 Content-type에 해당되지 않습니다. 그렇기 때문에 Webfont의 요청은 Preflight처리가 되는 것입니다. Preflight의 경우 HTTP Method에 OPTION이 허용되어야 합니다. (참고: coding-groot)
따라서, s3관련 Cloudfront에서 [/fonts/*] 경로 패턴에 대한 동작을 추가해주었습니다.
Allowed HTTP methods를 GET,HEAD,OPTIONS가 있는 것으로 선택해줍니다.
이 후, CORS 에러가 나지 않는 것을 확인할 수 있습니다.
이렇게 font를 가져오는 속도를 줄여 FCP를 약 1초 가량 개선할 수 있었습니다! 👏👏👏
[참고]
'코딩 공부 노트 > next js' 카테고리의 다른 글
1.5. next v.12 Routing, 팀버너스리의 a태그의 진화 (0) | 2023.07.12 |
---|---|
2. next v.12 getServerSideProps 스마트하게 사용하기 (0) | 2023.06.30 |
1. next v.12 Routing, 팀버너스리의 a태그의 진화 (0) | 2023.06.28 |