리액트 네이티브 웹뷰로 제작한 프로덕트에 'Pretendard' 폰트를 적용하게 되면서, FOIT + FOUT 이슈를 겪게 되었다. 🥲
폰트 파일은 CDN으로 연결되어있고,
font-family: optional;
으로 설정이 되어있는 상태였다. 이 옵션은 네트워크의 연결 상태가 안 좋으면 웹 폰트의 다운로드가 완료되어도 캐시에 저장만 하고 적용은 되지 않는다고 한다. 이후에 해당 페이지를 다시 접속하면 캐싱되어있으니 폰트가 적용되긴 했지만 모든 페이지 각각이 web-view 컴포넌트로 쌓여있는 구조인 우리 프로젝트는 모든 페이지마다 브라우저를 새로 띄워 폰트를 다운로드하는 상황과도 같기 때문에 네트워크 상태가 안좋을 경우 모든 페이지마다 첫 진입 시 폰트가 적용되지 않는 현상이 발생했다.
프로덕트 정비 기간에 해결해야할 이슈 중 하나였는데, 내가 맡게되었고 아래와 같이 이슈를 해결하는 과정을 기록해보았다.
그러나!!! 폰트 파일 최적화를 통한 로딩 속도 개선을 통해 해당 현상들을 최소화할 수는 있다.
웹 폰트는 네트워크를 통해 다운로드 하는 자원이므로 파일 용량의 최적화를 통해 웹폰트 적용 지연 문제를 완화할 수 있다.
→ 한글의 경우 모든 경우의 수를 조합하면 총 11,172자가 나오는데, 여기서 보통 사용되지 않는 불필요한 글자를 제거하면 총 2,350자로 줄일 수 있다. (기준은 국가 표준(KS)인 정보 교환용 부호계(한글 및 한자) 참조)
프로젝트의 디자인 시스템에 사용되는 굵기 파일만 추가 (light, thin, extraLight 등은 제외)
서브셋 폰트로 변환된 폰트 파일을 WOFF2, WOFF 형식으로 변환
TTF, OTF와 동일하게 작동하지만 압축을 통해 더 작은 크기를 가지는 WOFF2, WOFF 형식이다.
WOFF2는 웹 폰트를 압축하고 최적하하기 위한 형식인 WOFF와 다른 압축 알고리즘을 사용하여 더 작은 용량으로 폰트 데이터를 압축한다. WOFF2가 WOFF 대비 30-50% 가량 더 적은 용량을 가질 수 있지만, WOFF보다 낮은 호환성을 가지고 있어서 WOFF2의 폴백폰트로 사용된다.
서브셋 적용 전 용량
서브셋 적용 후 용량
기존에는 태그 안에 폰트 페이스를 지정한 pretendard.css 파일을 로드하고있는데, 이 경우
html 로딩 순서에 따라 html 로드 → pretendard.css 파일 로드 → 그 안의 font url로 폰트 요청 → 폰트 다운로드 후 적용
위와 같은 순서로 실행되기 때문에 폰트 로딩은 비교적으로 느릴 수 밖에 없었음.
이보다 더 빠른 폰트 로딩을 위하여 태그 안에 preload link를 추가 적용
(화면에 주로 보이는 자주 사용되는 폰트만 프리로드 하려고 했는데, 각 view마다 메인으로 보여지는 폰트가 조금씩 달라서 그냥 전부다 추가했습니다 🥲)
<link
rel="preload"
as="font"
type="font/woff2"
crossOrigin=""
href="/fonts/Pretendard-Regular.woff2"
/>
<link
rel="preload"
as="font"
type="font/woff2"
crossOrigin=""
href="/fonts/Pretendard-Bold.woff2"
/>
<link
rel="preload"
as="font"
type="font/woff2"
crossOrigin=""
href="/fonts/Pretendard-SemiBold.woff2"
/>
<link
rel="preload"
as="font"
type="font/woff2"
crossOrigin=""
href="/fonts/Pretendard-ExtraBold.woff2"
/>
<link
rel="preload"
as="font"
type="font/woff2"
crossOrigin=""
href="/fonts/Pretendard-Medium.woff2"
/>
기존 Network waterfall
pretendard.css 로드와 pretendard 폰트 파일 요청 시점에 차이가 있다. → 폰트 적용 느림
변경 후 Network waterfall
pretendard 폰트 파일 요청이 pretendard.css 보다 먼저 이루어진다. → 바로 폰트 적용됨
완료
완료
완료
(network type을 변경하면 너기존에도 서브셋으로 이루어진 WOFF2, WOFF 형식의 폰트 파일을 CDN 링크를 통해 import 하고 있었는데, 해당 링크의 경우 서브셋이 여러개의 파일로 나뉘어져 있어서 regular 스타일의 폰트를 preload 하려면 5개의 파일을 링크를 걸어야하는 불편함이 있어서 직접 public 폴더에 업로드 하는 방식으로 변경하였습니다.
따라서, 기존에도 서브셋 + WOFF2 조합은 반영되어 있었기에 기존 이슈가 용량 이슈는 아니었던 것 같고, 이번 이슈 해소에 가장 큰 영향을 미친 것은 preload link인 것으로 판단하고 있습니다.
참고 링크
🔗 https://yozm.wishket.com/magazine/detail/2107/
🔗 https://blog.banksalad.com/tech/font-preload-on-safari/
🔗 https://d2.naver.com/helloworld/4969726
🔗 참고 링크
RN에도 Typography 하나의 컴포넌트를 사용하고 variant 값으로 스타일을 넘기도록 Webview와 통일하는 작업을 진행하였는데, Typography에 font-family: ‘Pretendard’;
를 적용했을 때 iOS는 정상 반영, Android는 Emulator에서만 정상 반영되고 실제 디바이스에서는 폰트가 반영되지 않는 이슈가 있었다.
검색해보니 RN에 웹폰트를 적용할 때에는 font-family를 ‘Pretendard-Bold’ 처럼 font-weight까지 적용된 폰트명을 작성해야 한다. (아마도 굵기별로 font-face를 지정하지 않아서인 듯…)
그래서 RN에 적용하던 스타일을 아래와 같이 font-weight값을 없애고 font-family를 굵기별로 지정해 해결하였다.
as-is
extraTitle: {
weight: '800',
size: 36,
lineHeight: 36,
letterSpacing: '-1%',
},
bigTitleB: {
weight: '700',
size: 32,
lineHeight: 36,
},
// weight값은 해당 스타일을 Typography 컴포넌트에 맵핑할 때 fontWeight으로 프라퍼티명 변환.. weight 오타 아님 주의...
to-be
extraTitle: {
fontFamily: 'Pretendard-ExtraBold',
size: 36,
lineHeight: 36,
},
bigTitleB: {
fontFamily: 'Pretendard-Bold',
size: 32,
lineHeight: 36,
},
참고했던 사이트에서 제안한 해소방안 3가지
font-weight
. Try removing that.assets/fonts
folder and replace them with the new files with the names in snake_case
like ogbrother_medium
and nutinosans_bold
. Try linking the files again after thisfont-weight:"bold"
. Try to replace this with a font value like font-weight:"700"
.이 중에 첫 번째 방법으로 바로 해소가 되다 🙌