next 14 폰트 레이아웃 쉬프트 해결하기
next 14 폰트 레이아웃 쉬프트 해결하기
Layout Shift?
레이아웃 쉬프트란 웹 페이지의 렌더링 과정에서 발생하는 사용자 경험과 관련된 성능 지표 중 하나다. Core Web Vitals 중 하나로, Google에서 웹 페이지의 사용자 경험을 평가하는 데 사용되는 중요한 지표 중 하나다.
레이아웃 쉬프트는 웹 페이지가 로드되는 동안 요소들의 위치가 변경되어 사용자 경험에 영향을 미치는 현상을 나타낸다. 사용자가 페이지를 열었을 때, 원치 않는 레이아웃의 변화가 발생하면 사용자가 의도한대로 상호작용하는 것을 방해할 수 있다.
레이아웃 쉬프트는 다음과 같은 상황에서 발생한다.
-
이미지의 크기를 지정하지 않고 로딩: 이미지가 로드되기 전에 해당 이미지의 크기를 명시적으로 지정하지 않으면, 이미지가 로드되면서 레이아웃이 변경될 수 있다.
-
동적으로 로드되는 콘텐츠: 페이지가 로드된 후에 JavaScript 또는 비동기적으로 로드되는 콘텐츠가 있다면, 이로 인해 레이아웃이 변할 수 있다.
-
폰트의 지연된 로딩: 페이지에 사용된 폰트가 로드되기 전에 기본 폰트로 표시되다가 폰트가 로드되면 레이아웃이 변할 수 있다.
Next + tailwindcss를 이용해서 개발을 하고 있는 도중, 새로고침을 하게 되면 위 3번 폰트 레이아웃 쉬프트가 발생하고 있었다. 해당 폰트는 웹 폰트로 사용하고 있었다. 해당 레이아웃 쉬프트를 없애기 위해 구글링하던 도중, next 13부터 next font를 지원해주고 있었다. next/font를 사용하면 최적화할 수 있을 뿐만이 아니라 레이아웃 없이 최적으로 폰트를 로드할 수 있다고 해서 사용했다.
레이아웃 쉬프트 발생 화면
우선 기존 웹 폰트를 걷어내고, 로컬 폰트로 받아오기 위해서 기존 웹폰트 코드들을 삭제해줬다.
@font-face {
font-family: 'SCoreDream';
font-weight: 100;
font-style: normal;
src: url(https://cdn.jsdelivr.net/gh/webfontworld/SCoreDream/SCoreDream1.woff2) format('woff2'), url(https://cdn.jsdelivr.net/gh/webfontworld/SCoreDream/SCoreDream1.woff)
format('woff');
font-display: swap;
}
위 코드들을 삭제해줬다. 그리고 해당 폰트를 저장한 뒤 next app 라우터를 사용하기 때문에, app/fonts 폴더에 폰트들을 놓아줬다.
그 후 layout.tsx에서 폰트 설정을 해줬다. _ layout.tsx _
import type { Metadata } from 'next';
import './globals.css';
import 'react-toastify/dist/ReactToastify.css';
import { ToastContainer } from 'react-toastify';
import localFont from 'next/font/local';
export const metadata: Metadata = {
title: 'Petmunity',
description: '',
};
const myFont = localFont({
src: [
{
path: './fonts/SCDream1.otf',
weight: '100',
},
{
path: './fonts/SCDream2.otf',
weight: '200',
},
{
path: './fonts/SCDream3.otf',
weight: '300',
},
{
path: './fonts/SCDream4.otf',
weight: '400',
},
{
path: './fonts/SCDream5.otf',
weight: '500',
},
{
path: './fonts/SCDream6.otf',
weight: '600',
},
{
path: './fonts/SCDream7.otf',
weight: '700',
},
{
path: './fonts/SCDream8.otf',
weight: '800',
},
{
path: './fonts/SCDream9.otf',
weight: '900',
},
],
display: 'swap',
variable: '--font-scdream',
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko" className={`bg-gray-50 ${myFont.className} `}>
<body className="md:max-w-[375px] w-full relative m-auto min-h-screen bg-white p-0 font-sans lining-nums text-gray-900 outline-none">
<div id="modal-root" />
{children}
<ToastContainer position="top-right" autoClose={5000} />
</body>
</html>
);
}
tailwindcss를 사용하고 있기 때문에, variable로 폰트 변수 이름을 지정해줬다.
_ tailwind.config.ts _
const config: Config = {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
fontFamily: {
sans: ["var(--font-scdream)"],
},
... 생략
이런식으로 아까 만들어준 폰트 변수를 넣어주면 사용할 수 있게 된다!
_ 폰트 최적화 후 _
아까랑 다르게 폰트 레이아웃 쉬프트 현상이 사라졌다.