font-display: swap으로 글꼴을 로드하는 사이트는 웹 글꼴이 로드되고 대체 글꼴로 전환될 때 레이아웃 변경(CLS)이 발생하는 경우가 많습니다.
대체 글꼴의 크기를 기본 글꼴의 크기와 일치하도록 조정하여 CLS를 방지할 수 있습니다. @font-face
규칙의 size-adjust
, ascent-override
, descent-override
, line-gap-override
와 같은 속성을 사용하면 개발자가 글꼴 표시 방식을 더 세부적으로 제어할 수 있도록 대체 글꼴의 측정항목을 재정의하는 데 도움이 됩니다. font-fallbacks 및 재정의 속성에 관한 자세한 내용은 이 게시물을 참고하세요. 이 데모에서 이 기법의 작동하는 구현을 확인할 수도 있습니다.
이 도움말에서는 Next.js 및 Nuxt.js 프레임워크에서 글꼴 크기 조정을 구현하여 대체 글꼴 CSS를 생성하고 CLS를 줄이는 방법을 살펴봅니다. 또한 Fontaine 및 Capsize와 같은 교차 컷 도구를 사용하여 대체 글꼴을 생성하는 방법도 보여줍니다.
배경
font-display: swap은 일반적으로 FOIT(보이지 않는 텍스트의 플래시)를 방지하고 화면에 콘텐츠를 더 빠르게 표시하는 데 사용됩니다. swap
값은 글꼴을 사용하는 텍스트가 시스템 글꼴을 사용하여 즉시 표시되어야 하고 맞춤 글꼴이 준비되었을 때만 시스템 글꼴을 대체해야 한다고 브라우저에 알립니다.
swap
의 가장 큰 문제는 두 글꼴의 글자 크기 차이로 인해 화면 콘텐츠가 움직이는 불편한 효과입니다. 이로 인해 특히 텍스트가 많은 웹사이트의 CLS 점수가 낮아집니다.
다음 이미지는 이 문제의 예를 보여줍니다. 첫 번째 이미지는 대체 글꼴의 크기를 조정하지 않고 font-display: swap
를 사용합니다. 두 번째는 CSS @font-face
규칙을 사용하여 크기를 조정하면 로드 환경이 개선되는 방식을 보여줍니다.
글꼴 크기 조정 안함
body {
font-family: Inter, serif;
}
글꼴 크기 조정 후
body {
font-family: Inter, fallback-inter, serif;
}
@font-face {
font-family: "fallback-inter";
ascent-override: 90.20%;
descent-override: 22.48%;
line-gap-override: 0.00%;
size-adjust: 107.40%;
src: local("Arial");
}
대체 글꼴의 크기를 조정하는 것이 글꼴 로드 레이아웃 전환을 방지하는 효과적인 전략이 될 수 있지만, 글꼴 대체에 관한 이 게시물에 설명된 대로 로직을 처음부터 구현하는 것은 쉽지 않을 수 있습니다. 다행히 앱을 개발하는 동안 이 작업을 더 쉽게 할 수 있도록 이미 몇 가지 도구 옵션이 마련되어 있습니다.
Next.js로 글꼴 대체를 최적화하는 방법
Next.js는 대체 글꼴 최적화를 사용 설정하는 기본 제공 방법을 제공합니다. 이 기능은 @next/font 구성요소를 사용하여 글꼴을 로드할 때 기본적으로 사용 설정됩니다.
@next/font 구성요소는 Next.js 버전 13에서 도입되었습니다. 이 구성요소는 Google Fonts 또는 맞춤 글꼴을 페이지로 가져오는 API를 제공하며 글꼴 파일의 자동 자체 호스팅을 기본적으로 포함합니다.
이 글꼴을 사용하면 대체 글꼴 측정항목이 자동으로 계산되어 CSS 파일에 삽입됩니다.
예를 들어 Roboto 글꼴을 사용하는 경우 일반적으로 CSS에서 다음과 같이 정의합니다.
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
font-weight: 700;
}
body {
font-family: Roboto;
}
next/font로 이전하려면 다음 단계를 따르세요.
'next/font'에서 'Roboto' 함수를 가져와 Roboto 글꼴 선언을 자바스크립트로 이동합니다. 함수의 반환 값은 구성요소 템플릿에서 활용할 수 있는 클래스 이름입니다. 이 기능을 사용 설정하려면 구성 객체에
display: swap
를 추가해야 합니다.import { Roboto } from '@next/font/google'; const roboto = Roboto({ weight: '400', subsets: ['latin'], display: 'swap' // Using display swap automatically enables the feature })
구성요소에서 생성된 클래스 이름(
javascript export default function RootLayout({ children }: { children: React.ReactNode; }) { return ( <html lang="en" className={roboto.className}> <body>{children}</body> </html> ); }
)을 사용합니다.
adjustFontFallback 구성 옵션:
@next/font/google
의 경우: 누적 레이아웃 전환을 줄이기 위해 자동 대체 글꼴을 사용해야 하는지 여부를 설정하는 불리언 값입니다. 기본값은 true입니다. Next.js는 글꼴 유형(각각 서체와 산세리프)에 따라 대체 글꼴을 Arial
또는 Times New Roman
로 자동 설정합니다.
@next/font/local
: 레이아웃 변경 횟수를 줄이기 위해 자동 대체 글꼴을 사용해야 하는지 설정하는 문자열 또는 불리언 false 값입니다. 가능한 값은 Arial
, Times New Roman
또는 false
입니다. 기본값은 Arial
입니다. Serif 글꼴을 사용하려면 이 값을 Times New Roman
로 설정하는 것이 좋습니다.
Google Fonts의 다른 옵션
next/font
구성요소를 사용할 수 없는 경우 optimizeFonts
플래그를 통해 Google Fonts에서 이 기능을 사용하는 다른 방법이 있습니다. Next.js에는 optimizeFonts 기능이 이미 기본적으로 사용 설정되어 있습니다. 이 기능은 HTML 응답에 Google Font CSS를 인라인 처리합니다. 또한 다음 스니펫과 같이 next.config.js에서 experimental.adjustFontFallbacksWithSizeAdjust
플래그를 설정하여 글꼴 대체 조정 기능을 사용 설정할 수 있습니다.
// In next.config.js
module.exports = {
experimental: {
adjustFontFallbacksWithSizeAdjust: true,
},
}
참고: 새로 도입된 app
dir로는 이 기능을 지원할 계획이 없습니다. 장기적으로는 next/font
를 사용하는 것이 가장 좋습니다.
Nuxt로 글꼴 대체를 조정하는 방법
@nuxtjs/fontaine는 대체 글꼴 측정항목 값을 자동으로 계산하고 대체 @font-face
CSS를 생성하는 Nuxt.js 프레임워크용 모듈입니다.
모듈 구성에 @nuxtjs/fontaine
를 추가하여 모듈을 사용 설정합니다.
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: ['@nuxtjs/fontaine'],
})
Google Fonts를 사용하거나 글꼴에 대한 @font-face
선언이 없는 경우 추가 옵션으로 선언할 수 있습니다.
대부분의 경우 모듈은 CSS에서 @font-face
규칙을 읽고 font-family, 대체 글꼴 모음, 디스플레이 유형과 같은 세부정보를 자동으로 추론할 수 있습니다.
글꼴이 모듈에서 검색할 수 없는 위치에 정의된 경우 다음 코드 스니펫과 같이 측정항목 정보를 전달할 수 있습니다.
export default defineNuxtConfig({
modules: ['@nuxtjs/fontaine'],
fontMetrics: {
fonts: ['Inter', { family: 'Some Custom Font', src: '/path/to/custom/font.woff2' }],
},
})
이 모듈은 CSS를 자동으로 검사하여 @font-face 선언을 읽고 대체 @font-face 규칙을 생성합니다.
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
font-weight: 700;
}
/* This will be generated. */
@font-face {
font-family: 'Roboto override';
src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Roboto'), local('Helvetica Neue'),
local('Arial'), local('Noto Sans');
ascent-override: 92.7734375%;
descent-override: 24.4140625%;
line-gap-override: 0%;
}
이제 다음 예와 같이 Roboto override
를 CSS에서 대체 글꼴로 사용할 수 있습니다.
:root {
font-family: 'Roboto';
/* This becomes */
font-family: 'Roboto', 'Roboto override';
}
CSS 직접 생성
독립형 라이브러리를 사용하면 대체 글꼴 크기 조정을 위한 CSS를 생성하는 데도 도움이 됩니다.
Fontaine 라이브러리 사용
Nuxt나 Next.js를 사용하지 않는다면 Fontaine을 사용할 수 있습니다. Fontaine는 @nuxtjs/fontaine을 지원하는 기본 라이브러리입니다. 이 라이브러리를 프로젝트에서 사용하여 Vite 또는 Webpack 플러그인을 사용하여 대체 글꼴 CSS를 자동으로 삽입할 수 있습니다.
CSS 파일에 Roboto 글꼴이 정의되어 있다고 가정해 보겠습니다.
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
font-weight: 700;
}
Fontaine은 빌드 체인에 쉽게 연결할 수 있는 Vite 및 Webpack 변환기를 제공합니다. 다음 JavaScript와 같이 플러그인을 사용 설정하세요.
import { FontaineTransform } from 'fontaine'
const options = {
fallbacks: ['BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'],
// You may need to resolve assets like `/fonts/Roboto.woff2` to a particular directory
resolvePath: (id) => 'file:///path/to/public/dir' + id,
// overrideName: (originalName) => `${name} override`
// sourcemap: false
}
Vite를 사용하는 경우 다음과 같이 플러그인을 추가합니다.
javascript
// Vite
export default {
plugins: [FontaineTransform.vite(options)]
}
Webpack을 사용하는 경우 다음과 같이 사용 설정합니다.
// Webpack
export default {
plugins: [FontaineTransform.webpack(options)]
}
모듈이 파일을 자동으로 스캔하여 @font-face 규칙을 수정합니다.
css
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
font-weight: 700;
}
/* This will be generated. */
@font-face {
font-family: 'Roboto override';
src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Roboto'), local('Helvetica Neue'),
local('Arial'), local('Noto Sans');
ascent-override: 92.7734375%;
descent-override: 24.4140625%;
line-gap-override: 0%;
}
이제 CSS에서 Roboto override
을 대체 글꼴로 사용할 수 있습니다.
css
:root {
font-family: 'Roboto';
/* This becomes */
font-family: 'Roboto', 'Roboto override';
}
Capsize 라이브러리 사용
Next.js, Nuxt, Webpack 또는 Vite를 사용하지 않는 경우 Capsize 라이브러리를 사용하여 대체 CSS를 생성하는 방법도 있습니다.
새로운 createFontStack API
이 API는 글꼴 스택(font-family
속성)을 지정할 때와 동일한 순서로 글꼴 측정항목 배열을 허용하는 createFontStack
이라는 @capsize/core 패키지의 일부입니다.
Capsize 사용에 관한 문서는 여기에서 참고하세요.
예
다음 예를 생각해 보세요. 원하는 웹 글꼴은 Lobster이고, Helvetica Neue, Arial 순으로 대체됩니다. CSS의 경우 font-family: Lobster, 'Helvetica Neue', Arial
입니다.
핵심 패키지에서 createFontStack을 가져옵니다.
import { createFontStack } from '@capsizecss/core';
원하는 각 글꼴의 글꼴 측정항목을 가져옵니다 (위의 글꼴 측정항목 참고).
javascript import lobster from '@capsizecss/metrics/lobster'; import helveticaNeue from '@capsizecss/metrics/helveticaNeue'; import arial from '@capsizecss/metrics/arial';`
글꼴 스택을 만들고 글꼴 모음 CSS 속성을 통해와 동일한 순서로 측정항목을 배열로 전달합니다.
javascript const { fontFamily, fontFaces } = createFontStack([ lobster, helveticaNeue, arial, ]);
이는 다음을 반환합니다.
{
fontFamily: Lobster, 'Lobster Fallback: Helvetica Neue', 'Lobster Fallback: Arial',
fontFaces: [
{
'@font-face' {
'font-family': '"Lobster Fallback: Helvetica Neue"';
src: local('Helvetica Neue');
'ascent-override': '115.1741%';
'descent-override': '28.7935%';
'size-adjust': '86.8251%';
}
'@font-face' {
'font-family': '"Lobster Fallback: Arial"';
src: local('Arial');
'ascent-override': 113.5679%;
'descent-override': 28.392%;
'size-adjust': 88.053%;
}
}
]
}
CSS에 fontFamily 및 fontFaces 코드를 추가해야 합니다. 다음 코드는 CSS 스타일 시트 또는 <style>
블록 내에서 이를 구현하는 방법을 보여줍니다.
<style type="text/css">
.heading {
font-family:
}
</style>
그러면 다음과 같은 CSS가 생성됩니다.
.heading {
font-family: Lobster, 'Lobster Fallback: Helvetica Neue',
'Lobster Fallback: Arial';
}
@font-face {
font-family: 'Lobster Fallback: Helvetica Neue';
src: local('Helvetica Neue');
ascent-override: 115.1741%;
descent-override: 28.7935%;
size-adjust: 86.8251%;
}
@font-face {
font-family: 'Lobster Fallback: Arial';
src: local('Arial');
ascent-override: 113.5679%;
descent-override: 28.392%;
size-adjust: 88.053%;
}
@capsize/metrics 패키지를 사용하여 재정의 값을 계산하고 CSS에 직접 적용할 수도 있습니다.
const fontMetrics = require(`@capsizecss/metrics/inter`);
const fallbackFontMetrics = require(`@capsizecss/metrics/arial`);
const mainFontAvgWidth = fontMetrics.xAvgWidth / fontMetrics.unitsPerEm;
const fallbackFontAvgWidth = fallbackFontMetrics.xAvgWidth / fallbackFontMetrics.unitsPerEm;
let sizeAdjust = mainFontAvgWidth / fallbackFontAvgWidth;
let ascent = fontMetrics.ascent / (unitsPerEm * fontMetrics.sizeAdjust));
let descent = fontMetrics.descent / (unitsPerEm * fontMetrics.sizeAdjust));
let lineGap = fontMetrics.lineGap / (unitsPerEm * fontMetrics.sizeAdjust));