Post

Nextjs-Optimizing(1) - Font

๐Ÿ“Œ์‹œ์ž‘ํ•˜๋ฉฐ

ํ‡ด๊ทผ ์ „, ๋‹ค๋ฅธ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž ๋ถ„๊ณผ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐ๋ฅผ ๋‚˜๋ˆ„์—ˆ๋‹ค. ์ตœ์ ํ™”์˜ ์ค‘์š”์„ฑ์€ ๋„ˆ๋ฌด ๋‹น์—ฐํ•œ ๊ฒƒ์ด๊ธฐ๋„ ํ•ด์„œ, ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์ด๋‚˜, ์ตœ์ ํ™” ์ง„ํ–‰์— ๋Œ€ํ•ด ๊ณต๊ฐํ–ˆ๊ณ , ๊ธฐ๋ณธ์ ์œผ๋กœ ์•Œ๊ณ ์žˆ๋Š” ๋‚ด์šฉ์— ๋”ํ•ด Next js๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ตœ์ ํ™” ๋ฐฉ์‹๋„ ์•Œ์•„๋‘๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์„œ, ํ•œ๋™์•ˆ Next js์˜ ๊ณต์‹๋ฌธ์„œ์˜ Optimizing ๋ถ€๋ถ„์„ ํŒŒํ—ค์ณ ๋ณผ ์ƒ๊ฐ์ด๋‹ค.๐Ÿ˜Ž

๋จผ์ € ์˜ค๋Š˜์€ ๊ฐ€๋ณ๊ฒŒ(?) ๊ธฐ์กด์— ์ž์ฃผ ์‚ฌ์šฉํ•˜๋˜ Next/font์— ๊ด€ํ•œ ์ด์•ผ๊ธฐ๋‹ค.

์•„๋ž˜ ๋‚ด์šฉ์€ ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋ฒˆ์—ญํ•˜๊ฑฐ๋‚˜, ์ง์ ‘ ์‚ฌ์šฉํ•ด๋ณด๊ณ  ์ถ”๊ฐ€ํ•œ ๋‚ด์šฉ์ด ๋‹ด๊ฒจ์žˆ๋‹ค.

๐Ÿ”นGoogle Fonts

next/font/google๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋นŒ๋“œ ๊ณผ์ •์—์„œ ๊ตฌ๊ธ€ ํฐํŠธ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜์—ฌ ๋กœ์ปฌ๋กœ ํฌํ•จ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์—, ํฐํŠธ ํŒŒ์ผ์€ ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์— ํฌํ•จ๋œ๋‹ค. ๋”ฐ๋ผ์„œ, ์‚ฌ์šฉ์ž๊ฐ€ ์›น์‚ฌ์ดํŠธ๋ฅผ ๋ฐฉ๋ฌธํ•  ๋•Œ๋งˆ๋‹ค ๊ตฌ๊ธ€ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ , ๋กœ์ปฌ ์„œ๋ฒ„์—์„œ ํ•ด๋‹น ํฐํŠธ๊ฐ€ ์ œ๊ณต๋˜๋ฏ€๋กœ ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ์™€ ์„ฑ๋Šฅ์— ์ด์ ์ด ์žˆ๋‹ค.

์‚ฌ์šฉ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import "./globals.css"
import Provider from "./Provider"
import Header from "@/components/Header"
import { Noto_Sans_KR } from "next/font/google"

// ๋‹ค์–‘ํ•œ weight์˜ ๊ธ€๊ผด(Variable font)์„ ๋กœ๋“œํ•˜๋Š” ๊ฒฝ์šฐ ๋”ฐ๋กœ weight๋ฅผ ์ง€์ •ํ•  ํ•„์š” ์—†์Œ!
const noto = Noto_Sans_KR({
  subsets: ["latin"],
  display: "swap",
})

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="ko" className={noto.className}>
      <body>
        <Provider>
          <Header />
          {children}
        </Provider>
      </body>
    </html>
  )
}

๋งŒ์•ฝ ํŠน์ • weight์˜ ๊ธ€๊ผด๋งŒ ํ•„์š”ํ•˜๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด์ค€๋‹ค. ์ด ๊ฒฝ์šฐ์—, ์ง์ ‘ CSS๋กœ font-weight๋ฅผ ์กฐ์ •ํ•˜๋ ค๊ณ  ํ•˜๋”๋ผ๋„, bold์™€ border์˜ต์…˜ ์™ธ์— weight๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.

1
2
3
4
5
const noto = Noto_Sans_KR({
  weight: "400",
  subsets: ["latin"],
  display: "swap",
})
1
2
3
4
5
const noto = Noto_Sans_KR({
  weight: ["400", "700"],
  subsets: ["latin"],
  display: "swap",
})

๐Ÿ”นTailwind์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ

์‚ฌ์šฉํ•  ํฐํŠธ๊ฐ€ ๋งŽ์œผ๋ฉด, tailwind์— ํฐํŠธ๋ฅผ ์ •์˜ํ•ด๋‘๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํŽธ๋ฆฌํ•˜๋‹ˆ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด๋ณด์ž ๋จผ์ € ํฐํŠธ ๋‚ด์šฉ์„ ์ •์˜ํ•ด์ฃผ์—ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ๊ฑด variable ๋กœ ๊ฐ ํฐํŠธ๋ณ„๋กœ ์ด๋ฆ„์„ ์ง€์–ด์ค€๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// styles/font.ts
import { Noto_Sans_KR, Roboto, Source_Sans_3 } from "next/font/google"
import localFont from "next/font/local"

// ๋ณ€์ˆ˜ ํฐํŠธ ์ •์˜
const noto = Noto_Sans_KR({
  variable: "--font-noto",
  subsets: ["latin"],
})
const roboto = Roboto({
  weight: "400",
  subsets: ["latin"],
  variable: "--font-roboto",
})
// ํŠน์ • weight์„ ๊ฐ€์ง„ ํฐํŠธ ์ •์˜
const sourceCodePro400 = Source_Sans_3({
  weight: "400",
  variable: "--font-source-400",
  subsets: ["latin"],
})
const sourceCodePro700 = Source_Sans_3({
  weight: "700",
  variable: "--font-source-700",
  subsets: ["latin"],
})
// ๋กœ์ปฌ ํฐํŠธ ์ •์˜
const pretendard = localFont({
  src: "../app/font/Pretendard-Regular.otf",
  variable: "--font-pretendard",
})

export { noto, roboto, sourceCodePro400, sourceCodePro700, pretendard }

๊ทธ ๋‹ค์Œ ์ตœ์ƒ์œ„ layout.tsx์˜ className์— ์œ„์—์„œ return ํ•ด์ค€ ๊ฐ’๋“ค์„ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// app/layout.tsx
import "./globals.css"
import Provider from "./Provider"
import Header from "@/components/Header"
import { Noto_Sans_KR } from "next/font/google"
import {
  noto,
  roboto,
  sourceCodePro400,
  sourceCodePro700,
  pretendard,
} from "@/styles/font"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html
      lang="ko"
      className={`${noto.variable} ${roboto.variable} ${sourceCodePro400.variable} ${sourceCodePro700.variable} ${pretendard.variable}`}
    >
      <body>
        <Provider>
          <Header />
          {children}
        </Provider>
      </body>
    </html>
  )
}

๋งˆ์ง€๋ง‰์œผ๋กœ tailwind.config.ts์— ์ž‘์„ฑํ•ด์ฃผ์ž. ์ฐธ๊ณ ๋กœ ํ•ด๋‹น ๋‚ด์šฉ์€ tailwind 3๋ฒ„์ „ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋Š”๋ฐ, ํ•œ๋™์€ 3๋ฒ„์ „์„ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด๊ธด ํ•˜์ง€๋งŒ, 4๋ฒ„์ „ ๋„์ž…์‹œ ์–ด๋–ป๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ์ข‹์„์ง€๋Š” 4๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๋Š” ๋ ˆํฌ์ง€ํ† ๋ฆฌ์—์„œ ํ™•์ธํ•ด๋ณด๋Š”๊ฒŒ ์ข‹์„ ๋“ฏ ํ•˜๋‹ค.๐Ÿค”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/(root)/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      fontFamily: {
        noto: ["var(--font-noto)"],
        roboto: ["var(--font-roboto)"],
        sourceCodePro: ["var(--font-source-400)"],
        sourceCodeProBold: ["var(--font-source-700)"],
        pretendard: ["var(--font-pretendard)"],
      },
    },
  },
  plugins: [],
}

์‹ค์ œ ์‚ฌ์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์œ„์—์„œ ์ •์˜ํ•ด์ค€ ์ด๋ฆ„์„ ๋ฐ”ํƒ•์œผ๋กœ font-์ด๋ฆ„์œผ๋กœ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default function Page() {
  return (
    <>
      <p className="font-noto">
        ํฐํŠธ๊ฐ€ ์ ์šฉ๋˜์—ˆ๋Š”์ง€ ์‚ดํŽด๋ณผ๊นŒ์š”?
        <br />
        Let&apos;s check if the font has been applied properly!
      </p>
      <p className="font-roboto">
        ํฐํŠธ๊ฐ€ ์ ์šฉ๋˜์—ˆ๋Š”์ง€ ์‚ดํŽด๋ณผ๊นŒ์š”?
        <br />
        Let&apos;s check if the font has been applied properly!
      </p>
    </>
  )
}

๐Ÿ”นCSS์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ

๋งŒ์•ฝ Tailiwnd๊ฐ€ ์•„๋‹ˆ๋ผ CSSํŒŒ์ผ์—์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋ฉด, styles/font.ts์™€ app/layout.tsx๊นŒ์ง€๋Š” ์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•˜๊ณ , global.css์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•œ๋‹ค.

1
2
3
4
5
6
7
html {
  font-family: var(--font-noto);
}

h1 {
  font-family: var(--font-roboto);
}

๐Ÿ’ŒFont Module

Next js์˜ API Reference๋ฅผ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ next/font/google ๊ณผ next/font/local์„ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž.

Keyfont/googlefont/localTypeRequired
srcโŒโœ…๋ฌธ์ž์—ด or ๊ฐ์ฒด ๋ฐฐ์—ดํ•„์ˆ˜
weightโœ…โœ…๋ฌธ์ž์—ด or ๋ฐฐ์—ดํ•„์ˆ˜/์„ ํƒ
styleโœ…โœ…๋ฌธ์ž์—ด or ๋ฐฐ์—ด-
subsetsโœ…โŒ๋ฌธ์ž์—ด ๋ฐฐ์—ด-
axesโœ…โŒ๋ฌธ์ž์—ด ๋ฐฐ์—ด-
displayโœ…โœ…๋ฌธ์ž์—ด-
preloadโœ…โœ…๋ถˆ๋ฆฌ์–ธ-
fallbackโœ…โœ…๋ฌธ์ž์—ด ๋ฐฐ์—ด-
adjustFontFallbackโœ…โœ…๋ถˆ๋ฆฌ์–ธ or ๋ฌธ์ž์—ด-
variableโœ…โœ…๋ฌธ์ž์—ด-
declarationsโœ…โœ…๊ฐ์ฒด ๋ฐฐ์—ด-

๐Ÿ’Ÿsrc

ํฐํŠธ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ ๋ฌธ์ž์—ด ๋˜๋Š” ๊ฐ์ฒด ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•œ๋‹ค.

๐Ÿ’Ÿweight

ํฐํŠธ์˜ ๋‘๊ป˜๋ฅผ ์ง€์ •ํ•œ๋‹ค.

๐Ÿ’Ÿstyle

๐Ÿ’Ÿsubsets

subsets: ์ „์ฒด ๊ธ€๊ผด ํŒŒ์ผ์—์„œ ํŠน์ • ๋ฌธ์ž๋‚˜ ๊ธฐํ˜ธ๋งŒ์„ ํฌํ•จํ•˜๋Š” ๊ฒฝ๋Ÿ‰ํ™”๋œ ํฐํŠธ ํŒŒ์ผ

next/font/google๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, subsets ์˜ต์…˜์„ ํ†ตํ•ด ๋ฏธ๋ฆฌ ๋กœ๋“œํ•˜๊ณ  ์‹ถ์€ ๊ธ€๊ผด ์„œ๋ธŒ์…‹์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. preload ์˜ต์…˜์ด true๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค๋ฉด(default๊ฐ’์€ true), ์ด ์„œ๋ธŒ์…‹์˜ <link rel="preload"> ํƒœ๊ทธ๊ฐ€ <head>์— ์ž๋™์œผ๋กœ ์‚ฝ์ž…๋œ๋‹ค!

ํฐํŠธ์— ๋”ฐ๋ผ ์ง€์›๋˜๋Š” subset์ด ๋‹ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์—, Google Fonts์˜ ์‚ฌ์šฉ ์ค‘์ธ ๊ธ€๊ผด ํŽ˜์ด์ง€์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ชจ๋“  ๊ธ€๊ผด์ด ํฌํ•จ๋œ ํŒŒ์ผ์ด ์•„๋‹ˆ๋ผ, ํ•„์š”ํ•œ ์„œ๋ธŒ์…‹๋งŒ ๋กœ๋“œํ•ด ํฐํŠธ ์ตœ์ ํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

์•„์‰ฌ์šด ์ ์€ ํ•œ๊ธ€ ์„œ๋ธŒ์…‹์€ ์—†๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค๋Š” ๊ฒƒ.. ๐Ÿ˜…

Noto_Sans_KR์ด ์ง€์›ํ•˜๋Š” ์„œ๋ธŒ์…‹ ๋ชฉ๋ก์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

Available subsets: cyrillic, latin, latin-ext, vietnamese

์„œ๋ธŒ์…‹ ์ข…๋ฅ˜์„ค๋ช…
cyrillic๋Ÿฌ์‹œ์•„์–ด, ์šฐํฌ๋ผ์ด๋‚˜์–ด, ๋ถˆ๊ฐ€๋ฆฌ์–ด ๋“ฑโ€“
latin์˜์–ด, ํ”„๋ž‘์Šค์–ด, ๋…์ผ์–ด, ์ŠคํŽ˜์ธ์–ด ๋“ฑ
latin-extlatin์˜ ํ™•์žฅ ๋ฒ„์ „, ์ฃผ๋กœ ๋™์œ ๋Ÿฝ ์–ธ์–ด๋กœ ํด๋ž€๋“œ์–ด, ์ฒดํฌ์–ด, ํ—๊ฐ€๋ฆฌ์–ด ๋“ฑ
vietnamese๋ฒ ํŠธ๋‚จ์–ด

๐Ÿ’Ÿaxes

๐Ÿ’Ÿdisplay

๊ธ€๊ผด์„ ๋กœ๋“œํ•  ๋•Œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€๋ฅผ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

์ „๋žต์ฐจ๋‹จ ๊ธฐ๊ฐ„ (Blocking Period)๊ต์ฒด ๊ธฐ๊ฐ„ (Swap Period)์„ค๋ช…
auto๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ์„ค์ •์— ๋”ฐ๋ฆ„๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ์„ค์ •์— ๋”ฐ๋ฆ„๋ธŒ๋ผ์šฐ์ €์˜ ๊ธฐ๋ณธ ๋™์ž‘์— ๋”ฐ๋ฆ„
block๊ธด ์ฐจ๋‹จ ๊ธฐ๊ฐ„๋ฌด์ œํ•œ ๊ต์ฒด๊ธ€๊ผด์ด ๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€ ํ…์ŠคํŠธ๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š์Œ. ๋กœ๋“œ๋œ ํ›„ ๊ธ€๊ผด ๊ต์ฒด๋จ
swap๋งค์šฐ ์งง์€ ์ฐจ๋‹จ ๊ธฐ๊ฐ„๋ฌด์ œํ•œ ๊ต์ฒดํ…์ŠคํŠธ๋Š” ๊ธฐ๋ณธ ๊ธ€๊ผด๋กœ ํ‘œ์‹œ๋˜๋ฉฐ, ๊ธ€๊ผด์ด ๋กœ๋“œ๋˜๋ฉด ๋น ๋ฅด๊ฒŒ ๊ต์ฒด
fallback๋งค์šฐ ์งง์€ ์ฐจ๋‹จ ๊ธฐ๊ฐ„์งง์€ ๊ต์ฒด ๊ธฐ๊ฐ„ํ…์ŠคํŠธ๋Š” ๊ธฐ๋ณธ ๊ธ€๊ผด๋กœ ํ‘œ์‹œ๋˜๋ฉฐ, ๊ธ€๊ผด์ด ๋กœ๋“œ๋˜๋ฉด ๋น ๋ฅด๊ฒŒ ๊ต์ฒด
optional๋งค์šฐ ์งง์€ ์ฐจ๋‹จ ๊ธฐ๊ฐ„์งง์€ ๊ต์ฒด ๊ธฐ๊ฐ„๊ธ€๊ผด์ด ๋กœ๋“œ๋˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ ๊ธ€๊ผด๋กœ ๊ณ„์† ํ‘œ์‹œ๋จ (์„ฑ๋Šฅ ์ตœ์ ํ™”)

๐Ÿ’Ÿpreload

๐Ÿ’Ÿfallback

๐Ÿ’ŸadjustFontFallback

๐Ÿ’Ÿvariable

๐Ÿ’Ÿdeclarations

๐Ÿ’ŒํฐํŠธ ๊ฒฝ๋กœ ํŒŒ์ผ ์‚ฌ์šฉํ•˜๊ธฐ

localFont๋‚˜ googleFont๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๊ทธ ํฐํŠธ๋Š” ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋กœ ํ˜ธ์ถœ๋œ๋‹ค! ๋”ฐ๋ผ์„œ, ๋งค๋ฒˆ ํฐํŠธ๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํ•œ ํŒŒ์ผ์— ๋‹ค์–‘ํ•œ ํฐํŠธ๋ฅผ ์š”์ฒญํ•˜๊ณ , ๊ทธ ๋ชจ์•„๋†“์€ ํŒŒ์ผ์„ importํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

This post is licensed under CC BY 4.0 by the author.