font-display: 스왑으로 글꼴을 로드하는 사이트에서는 웹 글꼴이 로드되고 대체 글꼴로 전환될 때 레이아웃 변경 (CLS)이 발생하는 경우가 많습니다.
대체 글꼴의 크기를 기본 글꼴 크기와 일치하도록 조정하여 CLS를 방지할 수 있습니다. @font-face
규칙의 size-adjust
, ascent-override
, descent-override
, line-gap-override
와 같은 속성은 대체 글꼴의 측정항목을 재정의하여 개발자가 글꼴이 표시되는 방식을 더 효과적으로 제어할 수 있도록 합니다. 글꼴 대체 및 재정의 속성에 관한 자세한 내용은 이 게시물을 참고하세요. 이 데모에서 이 기법의 실제 구현을 확인할 수도 있습니다.
이 도움말에서는 대체 글꼴 CSS를 생성하고 CLS를 줄이기 위해 글꼴 크기 조정이 Next.js 및 Nuxt.js 프레임워크에서 구현되는 방식을 살펴봅니다. 또한 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;
}
다음/글꼴로 이전하려면 다음 단계를 따르세요.
'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는 글꼴 유형 (각각 Serif와 Sans Serif)에 따라 대체 글꼴을 Arial
또는 Times New Roman
로 자동 설정합니다.
@next/font/local
: 레이아웃 변경 횟수를 줄이기 위해 자동 대체 글꼴을 사용해야 하는지를 설정하는 문자열 또는 불리언 false 값입니다. 가능한 값은 Arial
, Times New Roman
또는 false
입니다. 기본값은 Arial
입니다. Serif 글꼴을 사용하려면 이 값을 Times New Roman
로 설정하는 것이 좋습니다.
Google 글꼴의 또 다른 옵션은
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
디렉터리로 이 기능을 지원할 계획은 없습니다. 장기적으로는 next/font
를 사용하는 것이 이상적입니다.
Nuxt로 글꼴 대체를 조정하는 방법
@nuxtjs/fontaine은 Nuxt.js 프레임워크용 모듈로, 대체 글꼴 측정항목 값을 자동으로 계산하고 대체 @font-face
CSS를 생성합니다.
모듈 구성에 @nuxtjs/fontaine
를 추가하여 모듈을 사용 설정합니다.
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: ['@nuxtjs/fontaine'],
})
Google Fonts를 사용하거나 글꼴에 @font-face
선언이 없으면 추가 옵션으로 선언할 수 있습니다.
대부분의 경우 모듈은 CSS에서 @font-face
규칙을 읽고 글꼴 모음, 대체 글꼴 모음, 디스플레이 유형과 같은 세부정보를 자동으로 추론할 수 있습니다.
글꼴이 모듈에서 검색할 수 없는 위치에 정의된 경우 다음 코드 스니펫과 같이 측정항목 정보를 전달할 수 있습니다.
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 변환기를 제공하여 다음 자바스크립트에 표시된 대로 플러그인을 사용 설정합니다.
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는 createFontStack
라는 @capsize/core 패키지의 일부로, 글꼴 스택을 지정하는 것과 동일한 순서로 글꼴 측정항목의 배열을 허용합니다 (font-family
속성).
여기에서 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%;
}
}
]
}
fontFamily 및 fontFaces 코드를 CSS에 추가해야 합니다. 다음 코드는 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));
감사의 말씀
히어로 이미지: Alexander Andrews, Unsplash