본문 바로가기

카테고리 없음

Next font 실전의 모든것 (local font 적용(Pretendard)/ google-font 적용/ i18n 으로 global font 변경 / 특정 element 만 font 변경)

About 
next/font를 활용한 웹폰트 성능 최적화

 

▶️ next/font 의 핵심 기능

1. 폰트 로딩시간 단축

2. Fallback 폰트가 layout shift 를 발생시키지 않도록 함

  • Layout shift
    • 폰트 변경에 의해 레이아웃이 변경되는 경우(UX 불편)
    • CSS size-adjust 속성을 사용하면 layout shift 없이 로드가능
      • adjustFallbackFont 라는 기능을 통해 기존 Fallback 폰트의 size-adjust 속성을 조정하기 때문에 Fallback 폰트와 실제 폰트간의 크기 차이가 발생하지 않는다
      • 폰트 때문에 레이아웃이 변경되는 경우가 발견되면 사용하기로..

▶️ Background about Font

1. 웹 폰트란? (출처 링크)

  • 웹폰트 : 사용자가 폰트를 설치하지 않아도 디자이너가 원하는 타이포그래피를 웹 페이지에 구현할 수 있게 하는 기술

2. 기본 사용법

  • CSS 의 @font-face 규칙을 사용한다
  • @font-face 규칙을 사용해 설정한 다음, 웹 폰트가 필요한 선택자의 font-family 속성에서 사용할 웹 폰트의 이름을 호출해서 사용한다

 

3. 웹폰트의 문제점

  • 브라우저 단에서 웹폰트를 다운받고 렌더링함
  • 폰트 다운로드가 늦어지는 경우 컨텐츠가 늦게 로딩된다

4. 최적화 방법

1) 폰트파일의 용량 줄이기

  • 폰트 패키지 전체를 불러오는 것이 아니라, 사용할 weight 나 파일 형식을 명시하여 필요한 것만 font-face 지정하여 가져온다
@font-face {
	font-family : NanumSquareWeb;
	src: url(NanumSquareR.woff2) format('woff2'),
			url(NanumSquareR.woff) format('woff'),
			url(NanumSquareR.ttf) format('trueType')
  • 서브셋 폰트
    • 폰트파일에서 불필요한 글자를 제거하고 사용할 글자만 남겨둔 폰트
    • 한글의 경우 자음&모음 모든 경우를 조합하면 11,172자나 된다.
    • 하지만 그중에서 실생활에서 사용하지 않는 폰트들이 많이 있다. (종종 어떤 사이트에서 노란색 형광펜 글씨들은 빈칸으로 렌더링 되지 않는 경우들을 봤을 것)

 

  • unicode-range 속성
    • 명시된 unicode-range 에 해당하는 폰트만 다운받는다
    • 폰트별로 다이나믹 서브셋 css 를 제공하기도 함
@font-face {
	font-family: 'Pretendard';
	font-style: normal;
	font-display: swap;
	font-weight: 100;
	src: url(../../../packages/pretendard/dist/web/static/woff2-dynamic-subset/Pretendard-Thin.subset.42.woff2) format('woff2'), url(../../../packages/pretendard/dist/web/static/woff-dynamic-subset/Pretendard-Thin.subset.42.woff) format('woff');
	unicode-range: U+bae6-bafb, U+bafd-bb17, U+bb19-bb33, U+bb37, U+bb39-bb3a, U+bb3d-bb43, U+bb45-bb46, U+bb48, U+bb4a-bb4f, U+bb51-bb53, U+bb55-bb57, U+bb59-bb62, U+bb64-bb8f;
}
@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard-dynamic-subset.css");
/**
 * 다이나믹 서브셋 폰트(경량화)로 우선 사용해보고 문제시 아래 웹폰트로 대체
 * 참고: https://leetaewook.github.io/dynamic-subset-font
 *
 * @import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.7/dist/web/static/pretendard.css");
 */
  • 이 외에도 다양한 방법이 존재함 (자세한 내용은 출처 참고)

▶️ Next font 사용법

  • Next.js 에서 제공하는 폰트 최적화

1. Next12 vs Nest13 기본 사용법

  • next12 에서의 폰트 로드와 next13에서 폰트로드가 어떻게 바뀌었는가
  • next12 에서 폰트 사용 방법
export default function Home() {
  return (
    <div>
      <Head>
        <title>Font Test</title>
        <meta name="description" content="Font Test" />
        <link rel="icon" href="/favicon.ico" />
        <link rel="preconnect" href="https://fonts.googleapis.com"/>
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
        <link href="https://fonts.googleapis.com/css2?family=Homemade+Apple&display=swap" rel="stylesheet"/>
      </Head>
      <main>
        Homemade Apple Font (Next 12)
        <Image src={Cat} alt="cat"/>
      </main>
    </div>
  )
}
  • next13 에서 폰트 사용 방법
import { Homemade_Apple } from '@next/font/google';
const homemadeApple = Homemade_Apple({ subsets: ['latin'], weight: ['400'], display: 'swap' })

export default function Home() {
  return (
    <main className={homemadeApple.className}>
      Homemade Apple Font (Next 13)
      <Image src={Cat} alt="cat"/>
    </main>
  )
}

 

import localFont from '@next/font/local'
const myFont = localFont({src : './my-font.woffw' })

 

2.  Use Case 

  • 우리는 어떻게 사용할 것인가?
  • 사용하는 폰트
    • 한글
      • Pretendard (600 / 500 / 400)
    • 영어
      • ABC Monument Grotesk Unlicensed Trial
      • Plus Jakarta Sans
      • 영문은 유료폰트? 로컬 파일 받을 수 있는건지? 확인 필요

 

1) google-font 설정해보기

  • Akshar : Variable font
import { Akshar } from "next/font/google";

const akshar = Akshar({
  subsets: ["latin"],
});

<main className={akshar.className}>
  <Component {...pageProps} />
</main>,
  • Sources 탭에서 확인
    • 시스템 폰트 & Akshar 폰트 확인

  • Element 탭에서 확인

 

1-1) font-weight 설정하기

  • 글로벌로 폰트가 설정되어있는 상태
  • /src/components/page/main/index.tsx
  • 자체 <Main> 컴포넌트
<div className={styles.test}>TEST</div>
  • ./Main.module.scss
.test {
  color: red;
  font-weight: 900;
  font-style: italic;
}
  • 그냥 추가적인 font-weight 를 지정하면 됨

 

 

 

1-2) Google Web Font 사용시 한글 폰트의 subset 사용 이슈

  • Noto_Serif_KR 폰트를 사용 (next/font/google 에서 제공)
  • 폰트를 세팅하면 기본적으로 subset 을 지정하도록 되어있다. subset 을 지정하지 않을 거면 preload 를 false 로 해야함
    (관련 링크 : https://nextjs.org/docs/messages/google-fonts-missing-subsets)
  • 하지만 subset 에 "korean" 이 없다. 한글 폰트인데, 한글에서 안쓰는 글자를 빼주는 subset 이 제공되지 않음
  • subset을 지정하지 않고, preload 를 false 로 세팅하는 경우 영어를 사용할 수 없다는 포스트를 발견
  • 테스트 해보기로 함

테스트 결과 영문폰트도 잘 적용이 되었다

 

 

subset 과 preload 변경해 가면서 테스트

 

✅ 영문도 폰트가 잘 적용되어 렌더링된걸 확인할 수 있다.

✅ preload 를 false 로 하는 경우, 특정 subset을 미리 다운받아 놓지만 않는다는 의미

 Noto_Serif_KR 이 제공하는 다른 다국어들(한글/라틴/greek/히라가나/가타카나/한자 등등) 을 폰트 적용하는데 문제가 없다.

 영문&다국어&한글 혼용하여 Noto_Serif_KR 을 사용하는 경우, 일단 제공해주는 latin이라도 subset 으로 미리 다운받아놓아서 손해볼것 없음

 미리 다운 안받은 다른 언어들은 그냥 폰트파일 다운 받아 지는대로 적용됨 -> local-font 와 동일한 로직

 

2) local-font 적용하기

  • 글로벌로 pretendard 설정 하기
  • Pretendard 공식홈페이지에서 폴더 zip 파일을 다운로드
    • 어떤 폰트 파일을 사용할 것인가?

 

2-1) 가변 폰트 파일 사용 하는 경우

  • 하나의 폰트 파일에 다양한 스타일을 가지고 있어, 사용자가 직접 수치를 조정하여 사용
  • Pretendard Variable 파일이 있음
  • src/styles/fonts/PretendardVariable.woff2 추가
  • src/pages/_app.tsx
import localFont from "next/font/local";

const pretendard = localFont({
  src: "../styles/fonts/PretendardVariable.woff2",
});

<main className={pretendard.className}>
  <Component {...pageProps} />
</main>,

 

 

2-2) subset 폰트 파일 사용하는 경우

  • Pretendared-1.3.9/web/static/woff2-subset 내부에 woff2 파일들이 있음
  • pretendard-subset.css 는 파일명과 weight 매칭 시켜주는 css 파일 -> 참조하여 아래 localFont 의 weight 를 세팅
  • src/styles/fonts/PretendardVariable.woff2 추가
  • src/pages/_app.tsx
import localFont from "next/font/local";

const pretendard = localFont({
  src: [
    {
      path: "../fonts/helvetica/WOFF2/Pretendard-Black.subset.woff2.woff2", // 14KB
      weight: "900",
      style: "normal",
    },
    {
      path: "../fonts/helvetica/WOFF2/Pretendard-ExtraBold.subset.woff2.woff2",
      weight: "800",
      style: "normal",
    },
    {
      path: "../fonts/helvetica/WOFF2/Pretendard-Bold.subset.woff2.woff2",
      weight: "700",
      style: "normal",
    },
    {
      path: "../fonts/helvetica/WOFF2/Pretendard-Medium.subset.woff2.woff2",
      weight: "500",
      style: "normal",
    },
  ]
});

<main className={pretendard.className}>
  <Component {...pageProps} />
</main>,

 

 

Q. 어떤 폰트 파일을 사용할 것인가?
A. Variable 폰트 파일을 사용해도 무방해 보이지만, subset 폰트 파일을 사용하면 다운로드 속도가 월등히 빠르다.
(위의 예시는 fast 3G로 테스트하여 결과가 강조된 경향이 있긴함)

 

[테스트 화면]

2.1MB 의 variable 폰트를 다운 받을 때 까지 fallback 폰트가 노출되다가, 다운 완료 후 pretendard 로 변경된다

 

✅ 가능한 subset 폰트를 사용하는게 낫지 않을까 하는 생각

 

2-3) Header 와 Footer 는 Layout 으로 들어가서 따로 설정해야함

  • font 인스턴스를 하나만 쓰고자함
  • font 정의용 파일을 하나 뺌
    • styles/fonts/index.ts
// font definition file

import localFont from "next/font/local";

const pretendard = localFont({
  src: "./PretendardVariable.woff2",
});

export { pretendard };
  • Header / Footer
import { pretendard } from "@/styles/fonts";
<footer className={pretendard.className}>

 

 

3) 특정 element 만 다른 폰트로 사용하기

  • 특정 element 는 Akshar (variable font) 폰트를 사용한다고 가정 (google-font 에서 다운로드)
  • styles/fonts/아래 ttf 추가
  • fonts/index.ts 에 localFont 인스턴스 추가
const pretendard = localFont({
  src: "./PretendardVariable.woff2",
});

const akshar = localFont({
  src: "./Akshar-VariableFont_wght.ttf",
});

export { pretendard, akshar };
  • <Main> 컴포넌트에서 특정 텍스트만 다른 폰트를 사용해야함
  • /src/components/page/main/index.tsx
import { akshar } from "@/styles/fonts";
<div className={`${styles.collectionEng} ${akshar.className}`}>DRESS YOUR TABLE</div>

 

 

4) i18n 적용시 국가에 따라 달라지는 텍스트 (참고)

  • 영어와 한글 폰트 적용을 어떻게 하는가?
  • 전역 폰트 세팅을 달리한다
    • _app.tsx
const { lang } = useTranslationCustom();

{getLayout(
  <main className={lang === "us" ? akshar.className : pretendard.className}>
    <Component {...pageProps} />
  </main>,
)}

 


 

 

 

 

추가작업

  • css 파일에서 따로 지정해줬던 font-family 모두 제거
  • button 속성에 user agent stylesheet 로 먹어서 Arial 적용되는 경우?
    • globals.css 에서 inherit 로 설정