next.js v13 @next/font 적용기

SangBooom·2023년 1월 23일
14

최근에 프로젝트를 react(v18) + next(v13)으로 마이그레이션했다.
next v13 소식중에 가장 간단히 적용해서 좋은 성능 변화를 이끌어낼 수 있을 것 같아보여서 바로 적용해보았다.

@next/font 를 적용하면 세가지 정도 이점이 있어보인다.

  • 커스텀 폰트와 next에서 제공하는 google font를 자체 호스팅 한다.
  • 구글 폰트를 next에서 제공하기 때문에 네트워크를 통해 요청 할 필요가 없다.
  • size-adjust를 이용해 layout Shifting를 방지해준다.

사전작업

제 환경은 next v13.1.1 @next/font v13.1.2 입니다. 추후에 @next/font가 내장되어 있을 확률이 높습니다.

우선, 다운받자.

yarn add @next/font

package.json 에 패키지가 다운받아진 걸 확인했으면 바로 next.config.js 를 열어서 아래 코드를 넣자.

...
 experimental: {
    fontLoaders: [{ loader: '@next/font/google', options: { subsets: ['latin'] } }],
 },

그다음 기존에 구글 폰트를 cdn으로 사용하고 있던 코드를 찾아 없애주자.
필자는 아래와 같이 사용하는 font-family와 font-weight, display 방법을 적용했었다.

<Head>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true" />
  <link rel="preconnect" href="https://cdn.jsdelivr.net" crossOrigin="true" />
  <link
    href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap&family=Montserrat:wght@500;700&family=Poppins:wght@600;700&display=swap"
    rel="stylesheet" />
  ...

네트워크를 통해 요청하고 있는 코드를 삭제해주자.

className + css-variables로 적용시키기

공식 문서에 기재되어있는 일반적인 방법이다.
custom app 폴더에 들어가서 pageProps를 넘기고있는 코드를 감싸는 엘리먼트를 하나 만들고 적용 시키고 싶은 폰트의 className을 className props에 넘겨주면 적용된다.

export const cls = (...classnames: string[]) => {
  return classnames.join(' ');
}

<main className={cls(notoSansKr.className, poppins.variable, montserrat.variable)}>
  <Component {...pageProps} />
</main>

내가 적용해야 할 구글 폰트는 Noto-Sans-KR, poppins, montserrat 총 3개이다.

const notoSansKr = Noto_Sans_KR({
  weight: ['100', '300', '400', '500', '700', '900'],
  display: 'swap',
  fallback: [
    // 디자이너분과 상의한 폴백 폰트 넣으시면 됩니다
    '-apple-system',
    'Malgun Gothic',
    'Apple SD Gothic Neo',
    'Roboto',
    'Apple Color Emoji',
    'Segoe UI Emoji',
    'Segoe UI Symbol',
    'sans-serif',
  ],
});
const montserrat = Montserrat({
  weight: ['500', '700'],
  display: 'swap',
  variable: '--montserrat',
});
const poppins = Poppins({
  weight: ['600', '700'],
  display: 'swap',
  variable: '--poppins',
});

css-variables 방법을 통해 poppins와 monserrat를 적용시키고 전체폰트로 적용시켜야 할 Noto-Sans-KR 모듈을 찾아 import 하고 className 메서드를 통해 자체 호스팅 된 폰트를 적용시켰다.

이렇게 나오면 잘 적용된거다.

// as-is
font-family : Poppins;
font-family : 'Montserrat';

// to-be
font-family : var(--poppins);
font-family : var(--montserrat);

위와 같이 폰트 적용한 코드를 삭제하고 css variables 방법으로 사용하면 된다.

그리고 전역적으로 적용해놓았던 Noto-Sans-KR도 삭제하면 된다.

className 방법 대신 global css로 적용시키기

엘리먼트로 한번 감싸서 className으로 넘기는 방법 말고 global css로 적용시키는 방법도 있다.

// globalStyles.ts
...
* {
    font-family: ${notoSansKr.style.fontFamily};
    ...
  }

이렇게 하면 user agent stylesheet도 덮어쓸 수 있어서 좋다.

효과

AS IS


TO BE

@next/font 적용 결과, 폰트 관련 네트워크 페이로드가 3646kb => 0kb로 완전히 사라졌다.
fonts.gstatic.com과 같은 타사 리소스를 사용하는 대신 글꼴을 로컬에서 호스팅하여 렌더링 프로세스 속도를 높일 수 있었다.
그리고 CLS 점수에 악영향을 미치는 일반적인 문제인 FOIT(보이지 않는 텍스트 깜박임)를 방지할 수도 있었다.

profile
끊임없이 떨어지는 물방울이 바위를 뚫는다

0개의 댓글