Next์˜ css์—์„œ ์“ด font asset์„ ๋นŒ๋“œ ์‹œ ์ œ๋Œ€๋กœ ๋ฐ›์•„์˜ค์ง€ ๋ชปํ•˜๋Š” ์ด์Šˆ

์š”์•ฝ

css ๋‚ด๋ถ€์˜ font asset์„ ๋ฐ›์•„์˜ค๋Š” path๋ฅผ ์ ˆ๋Œ€๊ฒฝ๋กœ๊ฐ€ ์•„๋‹Œ ์ƒ๋Œ€๊ฒฝ๋กœ๋กœ ๋ฐ”๊พธ๋ฉด ๋œ๋‹ค.

 

๋ฌธ์ œ

css์— url๋กœ ์ •์˜ํ•ด ๋‘” font asset์ด ๊ฐœ๋ฐœ๋ชจ๋“œ(dev)์—์„œ๋Š” ์ž˜ ์ ์šฉ๋˜๋‹ค๊ฐ€ ์‹ค์„œ๋ฒ„(production)์—์„œ ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š” ์ด์Šˆ

 

์‹ค์ œ ๊ตฌํ˜„

/* style.css */
@font-face {
  font-family: spoqaSansNeo;
  font-weight: 700;
  src:url('/font/SpoqaHanSansNeo-Bold.ttf');
  src:
    url('/font/SpoqaHanSansNeo-Bold.otf') format("opentype"),
    url('/font/SpoqaHanSansNeo-Bold.ttf') format("truetype");
}

/* next.config.js */
module.exports = withTM({
  assetPrefix: isProd ? 'https://assets.com' : '',
    /* ์ƒ๋žต */
});
/* ํŒŒ์ผ ๊ฒฝ๋กœ */
public
ใ„ด font
        ใ„ด SpoqaHanSansNeo-Bold.otf
    ใ„ด SpoqaHanSansNeo-Bold.ttf
src
ใ„ด styles
      ใ„ด style.css

์œ„์˜ ์ฝ”๋“œ๊ฐ€ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•œ ์ฝ”๋“œ์˜ ์ƒ๋žต ๋ฒ„์ „์ด๋‹ค.

style.css์—์„œ url๋กœ ๋ฐ›์•„์˜ค๋Š” ๊ฒฝ๋กœ๋Š” ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ๋˜์–ด์žˆ๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ url์„ ์„ค์ •ํ•  ๊ฒฝ์šฐ dev ๋ชจ๋“œ์—์„œ๋Š” ์ž˜ ๋™์ž‘ํ•˜์ง€๋งŒ buildํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์˜ฌ๋ฆฌ๊ณ  ๋‚˜๋ฉด production ๋ชจ๋“œ์—์„œ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

๋ฌธ์ œ ๋ถ„์„

  • css ๋‚ด๋ถ€์—์„œ url์„ ์ด์šฉํ•ด์„œ asset์„ ๋ฐ›์•„์˜ฌ ๋•Œ, path๋ฅผ ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋กœ ํ•˜๋Š”๊ฐ€, ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ํ•˜๋Š”๊ฐ€์— ๋”ฐ๋ผ ๋™์ž‘์ด ๋‹ค๋ฅด๋‹ค.
    • ๋งŒ์•ฝ path๋ฅผ ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋กœ ๋ฐ›์•„์˜จ๋‹ค๋ฉด ํ•ด๋‹น ๊ฒฝ๋กœ์˜ asset์„ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์—†์ด css ๋‚ด๋ถ€์—๋„ path๊ฐ€ ๊ทธ๋Œ€๋กœ ์˜ฌ๋ผ๊ฐ€๊ฒŒ ๋œ๋‹ค. ์œ„์˜ ์˜ˆ์ œ์—์„œ์™€ ๊ฐ™์ด /font/SpoqaHanSansNeo-Bold.otf path ๊ทธ๋Œ€๋กœ ์˜ฌ๋ผ๊ฐ€๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.
    • ๋งŒ์•ฝ path๋ฅผ ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ๋ฐ›์•„์˜จ๋‹ค๋ฉด next์˜ webpack build ์‹œ์— ํ•ด๋‹น ๊ฒฝ๋กœ์˜ asset์„ ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ๋‚ด๋ณด๋‚ด๊ณ , css ์ฝ”๋“œ์—์„œ์˜ url์„ ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ์˜ path๋กœ ์ˆ˜์ •ํ•œ๋‹ค. ์œ„์˜ ์˜ˆ์ œ์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด css ํŒŒ์ผ์ด ์žฌ๊ตฌ์„ฑ๋  ๊ฒƒ์ด๋‹ค.
    • /* style.css */
      
      @font-face {
          font-family: spoqaSansNeo;
          font-weight: 700;
          src:
            url('../../public/font/SpoqaHanSansNeo-Bold.otf') format("opentype"),
            url('../../public/font/SpoqaHanSansNeo-Bold.ttf') format("truetype");
      }
    • ๋นŒ๋“œ ํ›„์—๋Š” webpack์— ์˜ํ•ด font asset๋“ค์ด .next/static/media ๊ฒฝ๋กœ์— ์ƒ์„ฑ๋˜๋ฉฐ, ๋ฒˆ๋“ค๋œ css ํŒŒ์ผ(.next/static/css์˜ cssํŒŒ์ผ)์„ ์‚ดํŽด๋ณด๋ฉด ๊ทธ์— ๋งž์ถฐ path๊ฐ€ url(/_next/static/media/SpoqaHanSansNeo-Bold.ํ•ด์‹œ๊ฐ’.ttf)๋กœ ๋ฐ”๋€Œ์–ด์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
  • prod์—์„œ๋Š” assetPrefix๊ฐ€ ๋ถ™๋Š”๋‹ค.
    • assetPrefix๋Š” asset๋“ค์˜ path์— ์ ‘๋‘์‚ฌ๋กœ ์ถ”๊ฐ€๋˜๋Š”๋ฐ, prod์ผ ๋•Œ๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก next.config.js์— ์ •์˜ํ•ด๋‘์—ˆ์œผ๋ฏ€๋กœ prod(์‹ค์„œ๋ฒ„)์—์„œ๋Š” url(https://assets.com/_next/static/media/SpoqaHanSansNeo-Bold.ํ•ด์‹œ๊ฐ’.ttf) ๋กœ css ํŒŒ์ผ์ด ๊ตฌ์„ฑ๋œ๋‹ค.
    • ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋Š” github action ํŒŒ์ผ์„ ๊ตฌ์„ฑํ•˜์—ฌ ๋กœ์ปฌ์—์„œ .next/static ๊ฒฝ๋กœ์— ์žˆ๋˜ asset๋“ค์„ ๋ชจ๋‘ https://assets.com์ด๋ผ๋Š” CDN์— _next/static ๊ฒฝ๋กœ๋กœ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ณ  ์žˆ๋‹ค.
    • ๋”ฐ๋ผ์„œ prod์—์„œ ๋นŒ๋“œํ•˜๊ธฐ ์ „ .next/static ์— font asset๋“ค์ด ์žˆ์–ด์•ผ ์œ„์˜ ๊ฒฝ๋กœ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

 

ํ•ด๊ฒฐ

๊ฒฐ๊ตญ dev์™€ prod ๋ชจ๋‘์—์„œ font๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ํ‘œ์‹œ๋˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์šฐ์„  .next/static ํด๋”์— font asset๋“ค์ด build๋  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ณ , ๋™์‹œ์— css ๋‚ด๋ถ€์— url์—๋Š” ํ•ด๋‹น asset์˜ ๊ฒฝ๋กœ๊ฐ€ ์ •ํ™•ํžˆ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” url ๋‚ด๋ถ€์˜ path๋ฅผ ์ƒ๋Œ€๊ฒฝ๋กœ๋กœ ๋ฐ”๊พธ์–ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

๊ทธ๋Ÿผ font asset๋“ค์€ build์‹œ webpack์— ์˜ํ•ด .next/static/media ๊ฒฝ๋กœ์— ์ƒ์„ฑ๋˜๊ณ , css ๋‚ด๋ถ€์˜ path๋„ ๊ทธ์— ๋งž์ถฐ _next/static/media๋กœ ์„ธํŒ…๋  ๊ฒƒ์ด๋‹ค.

๋”ํ•ด์„œ prod ํ™˜๊ฒฝ์ด๋ผ๋ฉด github action ํŒŒ์ผ์— ์˜ํ•ด CDN์˜ _next/static/media๋กœ ์˜ฎ๊ฒจ์งˆ ๊ฒƒ์ด๋ฉฐ, css ๋‚ด๋ถ€์˜ path๋Š” assetPrefix๊นŒ์ง€ ๋ถ™์€ ๊ฒฝ๋กœ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์น  ๊ฒƒ์ด๋‹ค.

 

 

Bonus

์›์ธ์„ ๋ถ„์„ํ•˜๋ฉด์„œ next ๋‚ด๋ถ€์˜ webpack ์ฝ”๋“œ๋ฅผ ์ข€ ํ›‘์–ด๋ณด์•˜๋‹ค.

css ํŒŒ์ผ์€ webpack/config/blocks/css/index.ts์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ดํŽด๋ณด์ž.

if (ctx.isClient) {
    // Automatically transform references to files (i.e. url()) into URLs
    // e.g. url(./logo.svg)
    fns.push(
      loader({
        oneOf: [
          markRemovable({
            // This should only be applied to CSS files
            issuer: regexLikeCss,
            // Exclude extensions that webpack handles by default
            exclude: [
              /\.(js|mjs|jsx|ts|tsx)$/,
              /\.html$/,
              /\.json$/,
              /\.webpack\[[^\]]+\]$/,
            ],
            // `asset/resource` always emits a URL reference, where `asset`
            // might inline the asset as a data URI
            type: 'asset/resource',
          }),
        ],
      })
    )
  }

css ํŒŒ์ผ๋งŒ์„ ๋ชจ์•„์„œ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, type์„ ๋ณด๋ฉด asset/resource๋กœ ๋˜์–ด์žˆ๋‹ค. ์ด๋Š” file-loader์—์„œ์˜ ์ฒ˜๋ฆฌ์™€ ๋™์ผํ•˜๊ณ  (์ฐธ๊ณ  : https://webpack.kr/guides/asset-modules/) ํŒŒ์ผ์„ ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ๋‚ด๋ณด๋ƒ„๊ณผ ๋™์‹œ์— ํ•ด๋‹น ๊ฒฝ๋กœ๋ฅผ css ๋ฒˆ๋“ค์— ์‚ฝ์ž…ํ•ด์ฃผ๋Š” ์˜ต์…˜์ด๋‹ค.

→ ๋”ฐ๋ผ์„œ ์ด ๋ถ€๋ถ„์—์„œ font asset์ด ์ฒ˜๋ฆฌ๋˜์–ด ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ๋ณด๋‚ด์ง€๊ณ  ํ•ด๋‹น ๊ฒฝ๋กœ๊ฐ€ css ๋ฒˆ๋“ค์— ์‚ฝ์ž…๋œ ๊ฒƒ์ด๋‹ค.

๋ฐ˜์‘ํ˜•