next.js : google font ์ ์šฉํ•˜๊ธฐ๐Ÿงšโ€โ™€๏ธ(feat. tailwind)

๋‘์„ ์•„ Dusunaยท2023๋…„ 3์›” 1์ผ
19

short post

๋ชฉ๋ก ๋ณด๊ธฐ
4/8

next.js : google font ์ ์šฉํ•˜๊ธฐ๐Ÿงšโ€โ™€๏ธ (feat. tailwind)

next.js์˜ google font์™€, tailwind๋ฅผ ์‚ฌ์šฉํ•ด์„œ
ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•œ ๋‚ด์šฉ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ •๋ฆฌํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

next.js google font
https://nextjs.org/docs/basic-features/font-optimization#google-fonts

์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

  • next/font๋Š” ๋ชจ๋“  ๊ธ€๊ผด ํŒŒ์ผ์— ์ž๋™์ ์œผ๋กœ ์ž์ฒด ํ˜ธ์ŠคํŒ…์ด ๋‚ด์žฅ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ ˆ์ด์•„์›ƒ ์‰ฌํ”„ํŠธ ์—†์ด ํฐํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ตฌ๊ธ€ ํฐํŠธ์— ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

    // pages/_app.js
    import { Inter } from 'next/font/google' // ํ•ด๋‹น ํฐํŠธ์˜ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    const inter = Inter({ subsets: ['latin'] }) // ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ , ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์Šคํƒ€์ผ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

    export default function MyApp({ Component, pageProps }) {
      return (
        <main className={inter.className}> {/* className์„ ํ†ตํ•ด ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํฐํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. */}
          <Component {...pageProps} />
        </main>
      )
    }

์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ณต์‹๋ฌธ์„œ์—!

๊ตฌ๊ธ€ ํฐํŠธ https://nextjs.org/docs/basic-features/font-optimization#google-fonts
ํ•จ์ˆ˜ ์ธ์ˆ˜ https://nextjs.org/docs/api-reference/next/font#font-function-arguments


์‚ฌ์šฉํ•ด๋ณด๊ธฐ

๐Ÿ˜€ { ํ”„๋กœ์ ํŠธ์— ๊ธฐ๋ณธ ํฐํŠธ๋กœ ๋…ธํ† ์‚ฐ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ผ๋ถ€ ํฐํŠธ์— roboto๋ฅผ ์“ฐ๊ณ  ์‹ถ์–ด์š”. )

ํฐํŠธ importํ•˜๊ธฐ

    import { Roboto, Noto_Sans_KR } from "next/font/google"; // Roboto์™€ ํ•œ๊ธ€ NotoSans๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    
    const notoSansKr = Noto_Sans_KR({
      // preload: true, ๊ธฐ๋ณธ๊ฐ’
      subsets: ["latin"], // ๋˜๋Š” preload: false
      weight: ["100", "400", "700", "900"], // ๊ฐ€๋ณ€ ํฐํŠธ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ, ์‚ฌ์šฉํ•  fontWeight ๋ฐฐ์—ด
    });
    
    const roboto = Roboto({
      subsets: ["latin"], // preload์— ์‚ฌ์šฉํ•  subsets์ž…๋‹ˆ๋‹ค.
      weight: ["100", "400", "700"],
      variable: "--roboto", // CSS ๋ณ€์ˆ˜ ๋ฐฉ์‹์œผ๋กœ ์Šคํƒ€์ผ์„ ์ง€์ •ํ•  ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    });

subsets์ด ๋ญ”๊ฐ€์š”? โ€œkoreanโ€์ด ์—†์–ด์š”!

Noto_Sans_KR๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋‹ˆ subsets: [] ์•ˆ์— ์ž‘์„ฑํ•  ๊ฐ’์— korean์€ ์—†์Šต๋‹ˆ๋‹ค.

ํ•œ๊ตญ์–ด๋ฅผ ์“ฐ๊ณ  ์‹ถ์€๋ฐ subsets์—๋Š” latin, greek, vietnamese ๋ฐ–์— ์—†์œผ๋ฉด์„œ, subsets ์ธ์ˆ˜๋ฅผ ์‚ญ์ œํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.
์–ด๋–ป๊ฒŒ ํ•˜๋ผ๋Š” ๊ฑธ๊นŒ์š”? ๋‹นํ™ฉํ•˜์ง€ ์•Š๊ณ  ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์˜ ๋งํฌ๋ฅผ ๋ˆŒ๋Ÿฌ๋ด…๋‹ˆ๋‹ค.

subsets, preload

https://nextjs.org/docs/messages/google-fonts-missing-subsets

next.js์˜ ๊ตฌ๊ธ€ ํฐํŠธ๋Š” ์ž๋™์œผ๋กœ ํฐํŠธ๋ฅผ preloadํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์ง€์ •ํ•˜๋Š” ๊ฐ’์ดย 
subset (ํ•˜์œ„ ์ง‘ํ•ฉ)ย ์ž…๋‹ˆ๋‹ค.ย preload๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธ€๊ผด ํŒŒ์ผ์˜ ํฌ๊ธฐ๊ฐ€ ์ค„์–ด๋“ค๊ณ 
์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.ย ๋ฏธ๋ฆฌ ๋กœ๋“œํ•  ํ•˜์œ„ ์ง‘ํ•ฉ์„ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ํ•˜์œ„ ์ง‘ํ•ฉ์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉดย preload ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

vercel์˜ ์ž˜ ์ •๋ฆฌ๋œ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์ž๋ฉด, subsets์€ preloadํ•  ํ•˜์œ„ ์ง‘ํ•ฉ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธ€๊ผด ํŒŒ์ผ์„ ์ค„์ด๊ณ  ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  preload์˜ ๊ธฐ๋ณธ๊ฐ’์€ true์ด๊ธฐ ๋•Œ๋ฌธ์—, subsets๊ฐ€ ์—†์œผ๋ฉด preloadํ•  ๋Œ€์ƒ์„ ์•Œ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์•„์‰ฝ์ง€๋งŒ subsets์—์„œ ํ•œ๊ธ€์€ ์ œ๊ณตํ•˜์ง€ ์•Š๋„ค์š”.

๊ณต์‹๋ฌธ์„œ ํ•˜๋‹จ์˜ Noto_Sans_JP ์˜ˆ์‹œ์™€ ๊ฐ™์ด, preload๋ฅผ false๋กœ ์ง€์ •ํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์˜๋ฌธ์—๋„ ๋…ธํ† ์‚ฐ์Šค ํฐํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๊ณ„ํš์ด ์žˆ๋‹ค๋ฉด preload๋ฅผ ์•ˆํ•  ์ด์œ ๊ฐ€ ์žˆ์„๊นŒ์š”? ๋ฐ˜๋Œ€๋กœ presets์— ์กด์žฌํ•˜์ง€ ์•Š๋Š”, ์˜ˆ๋ฅผ ๋“ค์–ด ์˜์–ด๊ฐ€ ์•„๋‹Œ ๋‹ค๊ตญ์–ด์—๋งŒ ํ•ด๋‹น ํฐํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋ผ๋ฉด preload๋ฅผ false๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค.

const notoSansJp = Noto_Sans_JP({
  // preload: true, ๊ธฐ๋ณธ๊ฐ’
  preload: false,
  weight: ["100", "400", "700"]
});

๊ทธ๋ž˜์„œ ์–ด๋–ป๊ฒŒ ์ ์šฉํ•˜์ฃ ?

className์— ์ „๋‹ฌํ•˜๊ณ , CSS variable & tailwind์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ

ํ”„๋กœ์ ํŠธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

1. className์„ ํ•ฉ์น˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

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

2. ์ปดํฌ๋„ŒํŠธ์— ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•  ๋…ธํ† ์‚ฐ์Šค className๊ณผ, CSS variable๋กœ ์‚ฌ์šฉํ•  roboto๋ฅผ className์œผ๋กœ ๋„˜๊น๋‹ˆ๋‹ค.

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

3. tailwind.config์—์„œ theme extend์— fontfamily๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {
      fontFamily: { // ํฐํŠธํŒจ๋ฐ€๋ฆฌ
        roboto: ["var(--roboto)"], // ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐฐ์—ด ์•ˆ์— string์œผ๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
      },
    },
  },
  variants: {
    extend: {},
  },
  plugins: [require("tailwind-scrollbar-hide")],
};

4. ์ด์ œ tailwind ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด CSS ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

export default function MainComponent() {
  return (
    <div>
      ๋…ธํ† ์‚ฐ์Šค ๊ธ€์”จ์ž…๋‹ˆ๋‹ค.
      <p className="font-roboto">123 $&@ roboto ROBOTO ๋กœ๋ณดํ† ์ž…๋‹ˆ๋‹ค.</p>
    </div>
  );
}

profile
์•ˆ๋…•ํ•˜์„ธ์š”.

0๊ฐœ์˜ ๋Œ“๊ธ€