정의
웹폰트(브라우저에 기본 내장되지 않아 CSS @font-face로 따로 내려받는 글꼴)를 쓰고 싶은데, 글꼴이 늦게 도착해서 화면이 한 번 출렁이는 걸 막고 싶을 때 쓰는 CSS 속성이 font-display: optional이다.
정확히는, font-display는 웹폰트가 아직 안 왔을 때 브라우저가 어떻게 행동할지 정하는 정책이다. optional 값은 "짧은 시간(block period, 브라우저가 폰트를 기다리는 유예 시간, 보통 100ms 안팎) 안에 안 오면 그냥 대체 글꼴(fallback)로 확정하고, 이번 페이지에서는 웹폰트로 바꿔치기(swap)하지 않는다"는 뜻이다. 목표는 레이아웃이 흔들리지 않게 하는 것, 즉 CLS(Cumulative Layout Shift, 화면 요소가 예기치 않게 밀려난 정도를 재는 Core Web Vitals 지표)를 낮추는 것이다.
왜 필요한가
기본값에 가까운 swap을 쓰면 이런 일이 생긴다: 페이지가 대체 글꼴로 먼저 그려지고, 잠시 뒤 웹폰트가 도착하면 그 글꼴로 갈아끼운다. 그런데 두 글꼴은 글자 폭·높이(glyph metrics, 글자 하나가 차지하는 치수)가 달라서, 갈아끼우는 순간 태그(pill·badge)나 본문 줄바꿈 위치가 밀린다. 사용자 눈에는 "다 읽으려는데 글자가 툭 움직이는" 현상으로 보이고, 이게 CLS 점수를 깎는다.
문서·리스트처럼 글자가 많은 UI에서는, 브랜드 글꼴을 100% 보여주는 것보다 첫 화면이 흔들리지 않는 것이 사용자 경험과 Core Web Vitals(구글이 정의한 핵심 웹 성능 지표)에 더 유리한 경우가 많다. optional은 그 트레이드오프를 "흔들림 없음" 쪽으로 고정한다.
동작 원리
- 브라우저가 짧은 block period 동안 웹폰트 로드를 기다린다.
optional: 기간 안에 도착하지 못하면 이번 방문(navigation)에서는 웹폰트를 포기하고 대체 글꼴로 확정한다.- 대체 글꼴 스택(fallback stack,
font-family에 나열한 후보 글꼴 순서)이 끝까지 렌더링되므로 도중에 치수가 바뀌지 않는다. - 느린 네트워크로 테스트했을 때 CLS가 0에 가깝게 유지되는지 확인한다.
실무 적용
- 본문·mono 배지·큰 제목(display)에 각각
optional을 적용한다. 단, 화면 맨 위 대표 영역(hero, 첫 화면에서 가장 눈에 띄는 큰 영역)만은 preload(브라우저에 "이 폰트 먼저 받아둬" 하고 우선순위를 올리는<link rel="preload">)와swap을 예외로 둘 수 있다. - 대체 글꼴에
size-adjust(대체 글꼴의 글자 크기를 웹폰트에 맞춰 비율 조정하는@font-face속성)를 얹으면,optional로 웹폰트를 포기하더라도 글자 치수가 웹폰트에 가깝게 유지된다. - 느린 네트워크를 흉내 내는 throttling(브라우저 개발자도구에서 Slow 4G 등으로 속도를 강제로 낮추는 것) 상태에서 Lighthouse로 CLS가 다시 나빠지지 않는지 회귀 확인한다.
트레이드오프
optional은 브랜드 글꼴이 실제로 보이는 비율을 떨어뜨린다 — hero 영역만 예외로 살릴 수 있다.- 첫 방문 + 느린 3G에서는 대체 글꼴만 보게 될 수 있다 — 흔들림을 없애는 대가로 감수하는 트레이드오프다.
사용하면 안 되는 경우
- 브랜드 타이포그래피가 제품 정체성의 핵심이라, CLS보다 그 글꼴을 100% 보여주는 것이 더 중요할 때.
흔한 실수
optional을 걸었는데 대체 글꼴 스택이system-ui하나뿐 — 웹폰트 대신 보이는 글꼴 가독성이 떨어진다.swap과optional을 역할별 정책 없이 뒤섞어 써서, 어떤 텍스트는 흔들리고 어떤 텍스트는 안 흔들린다.
관련 개념
- [[cls-skeleton-layout-reservation]]
- [[critical-rendering-path]]