[트러블 슈팅] 웹 폰트 깜박임( FOUT) 해결

seonghui Moon·2024년 6월 20일
0

트러블 슈팅

목록 보기
1/7
post-thumbnail

들어가며

웹 폰트란 로컬의 폰트 설치 상황에 상관없이 웹에서 항상 원하는 타이포그래피를 사용할 수 있게 하는 기술이다.

웹 폰트의 사용법은 css의 @font-face 규칙을 사용한다.

문제 인식

페이지에 진입했을 때 폰트의 적용까지 시간이 걸려 폰트가 깜박이는 현상이 지속적으로 발생했다.

단순 서버가 느려서 인 줄 알았으나 이에 대해 찾아보니  FOUT(Flash Of Unstyled Font) 현상임을 알게 되었다.

📌 FOUT(Flash Of Unstyled Font)
:
폴백 폰트를 먼저 표시하고, 웹폰트 로딩이 완료되면 해당 폰트를 적용하는 것

문제 원인

우리가 기존의 폰트를 불러오던 방법은 createGlobalstyle로 글로벌 스타일 내에 @font-face로 폰트를 설정했었다.

CSS-in-CSSCSS-in-JS
스타일 시트가 별도로 로드되며, 이는 브라우저가 CSSOM을 생성하게 한다. CSSOM이 한 번 생성되고 나면, 다시 렌더링 될 때는 해당 스타일 시트를 다시 파싱할 필요가 없으므로 렌더링 성능이 향상될 수 있다.컴포넌트가 렌더링 될 때마다 새로운 스타일을 동적으로 생성한다. 따라서, 상태 변경이나 prop 변경에 따라 스타일이 자주 변경되는 컴포넌트의 경우 CSS-in-JS가 더 효율적일 수 있지만, 매우 빈번한 렌더링이 발생하는 경우에는 성능에 부담을 줄 수 있다.
ex) Sass, Less, Stylusex) Styled-component, Emotion

styled-component는 스타일이 렌더링 될 때 마다 head 태그의 style 태그를 변경한다. 즉, 새로운 스타일이 등장할 때마다 폰트를 재요청 이 진행중이었다.

실제로 캐러셀에 의해 스타일이 랜더링 될 때 마다 폰트를 재요청 하고 있음을 알 수 있다.

📌 브라우저 동작
1. 브라우저가 html 문서 요청
2. DOM 구성 & css, js 및 기타 리소스 요청
3. CSSOM 구성 & 렌더 트리 구축, 폰트 리소스 요청
4. reflow, repaint → 폰트를 사용할 수 없는 단계면 브라우저는 글자를 렌더링 하지 않을 수 있음

위의 동작처럼 CSSOM을 생성하는 과정에서 폰트 리소스를 다운로드 하기 시작한다.
하지만 paint 단계에서 폰트 파일의 다운이 완료되지 않으면 브라우저는 해당 자원을 사용하는 컨텐츠의 렌더링을 차단하게 된다.

브라우저에서 웹 폰트 렌더링을 차단하는 방법은 2가지이다.

  1. FOIT: 웹폰트가 적용된 텍스트 영역을 여백으로 처리하고, 웹폰트 로딩이 완료되면 해당 폰트를 적용한다. 단, 3초의 제한 시간이 있어 로딩이 3초를 넘어가면 폴백 폰트로 렌더링 한다.
  2. FOUT: 폴백 폰트를 먼저 표시하고, 웹폰트 로딩이 완료되면 해당 폰트를 적용한다.

따라서 우리의 프로젝트에서 폰트 깜박임 현상이 나타난 것은 웹 폰트가 로딩되기전에 paint가 되어 FOUT이 발생한 것이다.

문제 해결

@font-face 를 globalStyle에 넣지 않고 따로 css로 관리하는 것이다.

그리고 해당 파일을 main.tsx에 import 해주었다.

/* globalFonts.css */

@font-face {
  font-family: 'YouandiModernTR';
  src:
    url('@/assets/fonts/YouandiModernTR.woff2') format('woff2'),
    url('@/assets/fonts/YouandiModernTR.ttf') format('truetype');
  font-display: swap;
}

@font-face {
  font-family: 'SpoqaHanSansNeoRegular';
  src:
    url('@/assets/fonts/SpoqaHanSansNeoRegular.woff2') format('woff2'),
    url('@/assets/fonts/SpoqaHanSansNeoRegular.ttf') format('truetype');
  font-display: swap;
}

@font-face {
  font-family: 'SpoqaHanSansNeoMedium';
  src:
    url('@/assets/fonts/SpoqaHanSansNeoMedium.woff2') format('woff2'),
    url('@/assets/fonts/SpoqaHanSansNeoMedium.ttf') format('truetype');
  font-display: swap;
}

@font-face {
  font-family: 'SpoqaHanSansNeoBold';
  src:
    url('@/assets/fonts/SpoqaHanSansNeoBold.woff2') format('woff2'),
    url('@/assets/fonts/SpoqaHanSansNeoBold.ttf') format('truetype');
  font-display: swap;
}

추가적인 웹 폰트 최적화 - 폰트 파일의 용량 줄이기

웹 폰트는 네트워크를 통해 다운로드하는 자원이기 때문에 파일의 크기가 크면 웹 폰트가 적용된 글자가 화면에 표시될 때까지 시간이 지연되는 문제가 발생하고 이러한 문제는 폰트 파일의 용량을 최적화해 완화할 수 있다.

폰트 형식에서 WOFF(Web Open Font Format) 형식과 WOFF 2.0 형식은 압축된 폰트 형식이다

우리 프로젝트는 기존의 ttf의 폰트를 사용중이었기에 woff2의 폰트도 추가하고 브라우저에 지원범위가 폰트마다 다르기에 woff2 다음으로 ttf형식의 폰트를 폴백 폰트로 사용할 수 있게 해주었다.

성과

폰트 깜박임이 사라지고 폰트 재요청 역시 사라진 것을 볼 수 있다.

마치며

웹폰트를 깜박임 해결을 찾아보며 웹 폰트 최적화에 굉장히 많은 방법들이 존재함을 알게되었다. 폰트를 적용함에 있어서 그 과정에 대해서 크게 생각을 해보지 않았는데 이에 대해 생각을 해볼 수 있던 뜻 깊은 경험이었다.

또한 CSS-in-JS 방식의 라이브러리는 스타일 속성이 런타임에 동적으로 생성된다는 사실을 알게 되었고 styled-component를 생성할 때마다 각 컴포넌트 인스턴스에 대해 고유한 클래스 이름을 생성하고, 해당 클래스에 적용될 CSS 규칙을 계산해야 하기에 이러한 동작은 런타임 성능에 부정적인 영향을 미칠 수 있다는 새로운 사실에 대해서 알게 되었다.

폰트의 경우에는 상태에 따라 변경될 일이 없으니 css-in-js 방식의 스타일 라이브러를 사용할 때는 css파일로 따로 관리해야 한다는 점에 대해서 배우게 되었다.

참고 링크

https://armadillo-dev.github.io/html/css/webfont-optimize/

https://d2.naver.com/helloworld/4969726

0개의 댓글