글자 깜빡임 현상(FOIT,FOUT) 해결하기

ahyes·2024년 3월 18일
1

FOIT, FOUT이란?

Flash Of Invisible Text, Flash Of Unstyled Text로 페이지에 진입했을 때 폰트 적용이 늦게되어 깜빡이거나 갑자기 글자가 나타나는 현상을 발견했던 경험이 있을 것입니다.이는 사용자 경험에 부정적인 영향을 주게됩니다.

이 현상은 왜 일어나는 것일까요?

발생 원인 : 브라우저 동작

브라우저에 진입할 때 동작을 설명하면
1. 브라우저가 HTML 문서를 요청한다.
2. DOM 구성 & CSS,JS 및 기타 리소스 요청
3. CSSOM 구성 & 렌더트리 구축, 폰트 리소스 요청
4. Reflow, Repaint -> 폰트를 사용할 수 없는 단계면 브라우저는 글자를 렌더링하지 않을 수 있음

주목해야할 부분은 CSSOM을 생성하는 과정인데, 이 과정에서 폰트 리소스를 다운로드하기 시작합니다. 하지만 paint 단계에서 폰트 파일의 다운로드가 완료되지 않았으면 해당 자원을 사용하는 콘텐츠의 렌더링을 차단하게 됩니다.

결국 사용자의 네트워크의 속도, 폰트의 용량 등의 원인으로 폰트가 적용된 텍스트가 보이지 않는 문제가 발생하는 것입니다.
사용자의 네트워크 상태는 제어하기 힘들기 때문에 폰트의 용량텍스트가 보이지 않는 문제를 해결해봅시다.

해결 방법

1. 웹폰트 리소스를 미리 로드

폰트 리소스 요청 시점을 좀 더 앞당기도록 하는 방법입니다.
<head>에서 폰트를 요청할 때 rel = "preload"를 추가해주면 됩니다.

<link rel="preload" href="/font.woff2"as="font" type="font/woff2" crossorigin />

2. 텍스트가 항상 보이게하기

FOIT 방식은 UX 관점에서 좋지 않기 때문에 주로 FOUT 방식으로 의도적으로 작동시키는 방식으로 해결해봅시다.

Font Loading Module 사용

CSS Font Loading Module은 웹 폰트의 로딩을 제어하기 위한 표준화된 방법을 제공하는 W3C 명세입니다.이 모듈은 JavaScript API를 통해 폰트의 로딩 과정을 프로그래밍 방식으로 제어할 수 있게 해줍니다. 웹 개발자는 이 API를 사용하여 웹 폰트의 로드 상태를 확인하고, 폰트가 준비되었을 때 CSS 클래스를 동적으로 추가하는 등의 작업을 할 수 있습니다.

새 FontFace 객체를 생성하고, 이를 document.fonts.add() 메서드를 사용하여 로드할 수 있습니다. 그리고 폰트가 로드되기를 기다리는 프로미스를 사용할 수 있습니다.

const font = new FontFace('Roboto', 'url(https://example.com/roboto.woff2)', {});

document.fonts.add(font);
font.load().then(function(loadedFontFace) {
  console.log(`Font ${loadedFontFace.family} has been loaded`);
});

document.fonts.ready() 프로미스를 사용하면 페이지의 모든 초기 폰트 로드가 완료되었을 때 실행될 코드를 작성할 수 있습니다. 이는 초기 페이지 로드 시 폰트가 준비될 때까지 기다린 후 특정 스타일이나 레이아웃 조정을 수행하고자 할 때 유용합니다.

document.fonts.ready.then(function() {
  console.log("All initial fonts have loaded or failed");
});

document.fonts.check() 메서드를 사용하면 특정 폰트가 사용 가능한 상태인지 확인할 수 있습니다. 이 메서드는 폰트가 로드되었고, 주어진 텍스트를 렌더링하는 데 사용될 수 있는지 여부를 불리언 값으로 반환합니다.

if (document.fonts.check("1em Roboto")) {
  console.log("Roboto font is available");
} else {
  console.log("Roboto font is not available yet");
}

Font Face Observer 사용

Font Face Observer는 웹 폰트 로딩을 비동기적으로 제어할 수 있도록 돕는 JavaScript 라이브러리입니다. 이 라이브러리는 CSS Font Loading API에 기반하지 않고, Promise를 사용하여 폰트가 로드되었는지 감지합니다. 개발자는 Font Face Observer를 사용하여 웹 폰트가 사용 가능한 상태가 되었을 때 콜백을 실행할 수 있으며, 이를 통해 사용자 경험을 개선할 수 있습니다.

두 방법을 비교하면
Font Loading Module
장점 : 브라우저가 직접 지원하는 표준 API를 사용하므로, 별도의 라이브러리 없이 웹 폰트 로딩 제어 가능
단점 : 모든 브라우저에서 완벽하게 지원되지 않을 수 있으며, 구버전 브라우저에서 호환성 문제가 있을 수 있음
Font Face Observer
장점 : 폭넓은 브라우저 호환성을 제공하며, 특히 CSS Font Loading API를 지원하지 않는 구버전 브라우저에서도 사용할 수 있음
단점 : 외부 라이브러리에 의존하므로, 프로젝트에 추가적인 의존성이 생김

@font-face의 font-display 설정 사용

font-display속성을 사용해 웹 폰트 로딩 동안의 행동을 제어할 수 있는데,다음과 같은 속성을 갖고 있습니다.

  • auto : 브라우저의 기본 동작을 따름 (보통 block)
  • block : 글꼴이 다운로드 되기 전까지 글을 숨기다, 글꼴이 로드되면 웹글꼴을 적용해 보여줌
  • swap : 글꼴이 다운로드 되기 전까지 시스템 글꼴을 사용하다, 글꼴이 로드되면 웹글꼴을 적용해 보여줌
  • fallback : 우선 100ms 동안 텍스트가 보이지 않고, 그 후 폴백 폰트로 렌더링한다. 특이한 점은 약 2초의 전환(swap) 시간이 있어 이 시간 안에 로딩이 완료되면 웹 폰트로 전환한다. 하지만 이 시간이 지나면 웹 폰트 다운로드가 완료되어도 웹 폰트로 전환하지 않고 폴백 폰트를 유지
    • 전환 시간 이후에 다운로드된 웹 폰트는 웹 페이지에 적용되지는 않지만 캐시에는 저장된다. 그래서 추후에 사용자가 다시 방문했을 때 바로 웹 폰트가 적용된다는 장점이 있다.
  • optional : optional 옵션은 fallback 옵션과 비슷하지만 다르게 작동하는 옵션이다. 우선 100ms 동안 텍스트가 보이지 않고 그 후 폴백 폰트로 전환한다. 웹 폰트를 다운로드하지만 브라우저가 네트워크 상태를 파악해 웹 폰트 전환 여부를 결정한다는 점이 이 옵션의 특이한 점이다. 예를 들어 네트워크의 연결 상태가 안 좋으면 웹 폰트의 다운로드가 완료되어도 캐시에 저장만 하고 전환은 하지 않는다.
@font-face {
  font-family: 'MyFont';
  src: url('/path/to/font.woff2') format('woff2');
  font-display: swap;
}

3. Font Style Matcher로 폰트 간 차이 줄이기

의도적으로 FOIN 방식을 FOUT 방식으로 변경했어도 문제가 완전히 해결되는 것은 아닙니다.
웹 폰트 적용 전후에 레이아웃이 깨져보이는 현상을 여전히 발견할 수 있는데, 이는 Font Style Matcher로 fallback 폰트와 최대한 이질감이 느껴지지 않도록 조정하는 방법이 있습니다.

아래의 코드는 font face obserber 라이브러리를 사용할 때 어떻게 적용하는지 예시입니다.

body { // 로드전 - fallback 폰트 스타일
	font-family: 'Nanum Gothic',sans-serif;
    font-size: 15px;
    line-height: 1.65;
    }
body.font-loaded { // 로드후 - 웹폰트 스타일
	font-family: 'Black Han Sans','Nanum Gothic',sans-serif;
    font-size: 18px;
    line-height: 1;
    }

4. 폰트 파일의 용량 줄이기

웹폰트는 네트워크를 통해 다운받는 자원이기 때문에 파일의 크기가 크면 웹폰트가 적용된 글자가 화면에 표시될 때까지 시간이 지연되는 문제가 발생하는데, 이를 폰트 파일의 용량을 줄여 해결할 수 있다.

WOFF 2.0 형식의 폰트 사용

확장자에 따라 폰트의 용량이 달라지는 것을 알 수 있는데, 특히 woff2의 크기가 작은 것을 알 수 있습니다.

NameTagSize
Pretendard-Black.woff2woff2814KB
Pretendard-Black.woffwoff1,153KB
Pretendard-Black.ttfttf2,615KB
Pretendard-Black.otfotf1,601KB

다음과 같이 용량이 작은 폰트 형식을 적용해 웹 폰트를 최적화해봅시다.

@font-face{
	font-family:Pretendard;
    src: url(Pretendard-Black.woff2) format('woff2'),
    	 url(Pretendard-Black.woff) format('woff'),
         url(Pretendard-Black.ttf) format('truetype');
}

WOFF2 형식과 WOFF 형식을 모두 사용할 수 없을 떄 TTF 형식을 폴백 폰트로 사용할 수 있게 TTF 형식도 추가해준 모습입니다.

서브셋 폰트

서브셋 폰트는 폰트 파일에서 갞,갟,뛟,훭 같은 실생활에서 거의 사용하지 않는 글자를 폰트에서 제거하고 사용할 글자만 남겨둔 폰트입니다.

최신 폰트는 대부분 서브셋 폰트이지만 직접 만들고싶다면 サブセットフォントメーカー(이하 서브셋 폰트 메이커)fontTools 라이브러리를 사용해 만들 수 있다.

unicode-range 속성

유니코드로 지정한 글자에만 웹 폰트를 적용하는 속성입니다.
아래와 같이 CSS의 @font-face 규칙에 unicode-range 속성을 선언해 사용합니다.

@font-face{
	font-family:Pretendard;
    src: url(Pretendard-Black.woff2) format('woff2');
    unicode-range: U+BC14, U+CC28;
}

U+BC14는 '바' U+CC28는 '차'에 해당하는 유니코드고, 텍스트를 작성했을 때 전체에서 '바'와 '차'에만 웹 폰트가 적용됩니다.

unicode-range의 장점은 글자가 텍스트에 없으면 웹 폰트 다운로드를 요청하지 않기 때문에 불필요한 다운로드를 막을 수 있습니다.

정리

이 글은 FOUT, FOIT 현상을 잘 해결하기 위한 글입니다.
해당 현상이 일어나는 이유는 렌더링 과정에서 폰트가 다운로드 되지 않았을 때 글자가 렌더링 되지 않기 때문이었습니다.
해결하기 위해선
4가지 방법이 있었는데,
1. 폰트 다운 시점을 이전으로 끌어올린다. <link rel='preload'>이용
2. FOIT 현상을 전부 FOUT으로 일어나게 한다.

  • Font Loading Module
  • Font Face Observer
  • font-display 속성 이용
  1. Font Style Matcher를 통해 폴백폰트와 설정한 웹폰트가 이질감이 들지 않도록 변경
  2. 폰트 용량 줄이기
  • WOFF2 2.0 형식의 폰트 사용
  • 서브셋 폰트 사용
  • unicode-range 속성 사용

참고
[네이버D2]웹 폰트 사용과 최적화의 최근 동향
[CSS/JS] 웹 폰트를 최적화하는 5가지 방법

profile
티스토리로 이사갑니다. https://useyhnha.tistory.com/

0개의 댓글

관련 채용 정보