본 글은 Next.js@14.1.3 + Tailwind@3.3.0 + Storybook@8.0.1 기준으로 작성되었습니다. 버전이 다를 경우 본 설정과 달라질 수 있습니다.
Next.js + Tailwind 조합으로 Storybook을 사용하던 중 컴포넌트에 폰트 적용이 제대로 되지 않는 이슈가 있었다. 현재 페이지의 폰트는 최상위 레이아웃에 Next.js의 localFont API를 불러 와 variable 설정 후 className으로 주입, tailwind의 theme extend로 fontFamily를 등록하여 사용하는 방식이다.
같은 방식으로 Storybook에서도 폰트를 사용하기 위해서는 몇 가지 설정이 필요하다.
보다 자세한 설정은 Storybook 공식문서의 Next.js font optimization를 참고하자.
기존의 Next.js 폰트 설정 방식
스토리북 설정에 들어가기 전 현재 프로젝트에서 폰트를 사용하는 방식에 대해서 설명하겠다.
현재 진행 중인 프로젝트는 특정 상황에서 사용하는 폰트 2가지를 제외하고 전역으로 Pretendard 폰트를 사용하고 있다. 사용할 폰트를 등록하기 위해 폰트를 관리할 fonts.ts파일을 만들고 폰트 최적화를 위해 Next.js의 localFont API를 불러와 각 폰트 정보를 입력해 주었다.
tailwind에 등록해 사용할 것이기 때문에 폰트를 CSS 변수로 등록하고자 variable에 변수 이름을 적어주었다.
// src/app/fonts.ts
import localFont from 'next/font/local';
export const LogoFont = localFont({
src: '../../public/fonts/Library3AM.woff2',
display: 'swap',
variable: '--font-logo', // css 변수로 등록하기 위함
});
export const Pretendard = localFont({
src: '../../public/fonts/PretendardVariable.woff2',
display: 'swap',
variable: '--font-pretendard', // css 변수로 등록하기 위함
});
export const Chab = localFont({
src: '../../public/fonts/chab.ttf',
display: 'swap',
variable: '--font-chab', // css 변수로 등록하기 위함
});
className으로 폰트를 편하게 적용하도록 tailwind config에서 theme를 추가해 주었다.
// tailwind.config.ts
import type { Config } from 'tailwindcss';
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: {
colors: {
},
extend: {
// ...
fontFamily: {
logo: ['var(--font-logo)'],
pretendard: ['var(--font-pretendard)'],
chab: ['var(--font-chab)'],
},
// ...
},
},
// ...
};
export default config;
이제 설정한 변수를 Next.js에서 사용될 수 있도록 주입해주어야 한다. 전역 레이아웃에서 변수들을 불러와 주입해주고, 대표로 사용할 폰트 또한 선언해 주자.
// src/app/layout.tsx
import { clsx } from 'clsx';
import { LogoFont, Pretendard, Chab } from './fonts'; // 앞서 fonts.ts에서 export했던 폰트들을 불러온다.
import './globals.css';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ko" className={clsx(LogoFont.variable, Pretendard.variable, Chab.variable)}>
<body className="font-pretendard">{children}</body>
</html>
);
}
이러면 body 아래의 모든 요소들은 Pretendard 폰트로 지정되고, 특정 컴포넌트에서 폰트를 사용하고자 할 때는 className="font-logo"
와 같이 사용하면 된다.
기존 설정 방식 그대로 Storybook에서 사용하기
Storybook을 처음 설치하면 루트 디렉토리에 .storybook
이라는 폴더가 생기고, 그 밑에 main.ts
와 preview.ts
파일이 위치한다. 이 중 preview.ts
에는 보여질 story에 대한 기본 설정을 할 수 있는데, decorators라는 옵션을 이용하면 컴포넌트가 렌더링되는 스토리 영역의 디자인을 변경해줄 수 있다. 우리는 이를 이용하여 기존 설정처럼 폰트를 불러와 상위 요소에 변수를 주입시켜줄 것이다. 컴포넌트를 사용해야 하므로 preview.ts
파일의 확장자를 preview.tsx
로 변경하고 decorator를 다음과 같이 입력한다.
// .storybook/preview.tsx
import type { Preview } from '@storybook/react';
import React from 'react';
import { Chab, LogoFont, Pretendard } from '../src/app/fonts';
import '../src/app/globals.css';
import { clsx } from 'clsx';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
// ...
},
decorators: [
(Story) => (
<div
className={clsx(
Pretendard.variable,
LogoFont.variable,
Chab.variable,
'font-pretendard',
)}
>
<Story />
</div>
),
],
};
export default preview;
여기서 Story
가 실제 렌더링될 컴포넌트 부분이다. 우리는 렌더링될 실제 영역을 div로 한번 감싸 폰트의 변수를 등록해주고, pretendard 폰트를 사용하겠다고 선언해준 것이다.
다음으로 Storybook에게 우리의 폰트가 어느 위치에 있는지 알려주어야 한다. main.ts
에 staticDirs
부분에 폰트가 위치해 있는 폴더를 입력해주자.
// .storybook/main.ts
import { StorybookConfig } from '@storybook/nextjs';
const config: StorybookConfig = {
// ...
staticDirs: [
{
from: '../src/components/fonts',
to: 'src/components/fonts',
},
],
};
export default config;
이제 Storybook을 실행해서 폰트가 제대로 적용되는지 확인해 보자.
폰트가 제대로 출력되는 것을 확인할 수 있다.
결론
무언가를 적용하려 할 때는 일단 공식문서를 찬찬히 훑어보는 것이 제일 빠른 길이라고 느낀다. 열심히 블로그 글을 찾다 보면 오래된 버전 기준이라 현재 버전에선 적용이 되지 않거나, 효율적인 방법이 아닌 경우가 종종 생긴다. 영어 읽기 싫어서 블로그 글을 찾다 보면 결국 해답은 공식문서에 다 있더라...