NextJS 에 custom fonts 불러오기

Maliethy·2022년 1월 13일
0

react

목록 보기
7/7

1. issue

next-compose-plugins로 다양한 plugins(withBundleAnalyzer, withAntdLess 등)를 함께 사용중인 상태에서 custom fonts를 _document.ts에서 Link로 불러와 사용하고 있었다. 이 상태에서 build를 하면 No CSS Tags warning이 나온다.

next 공식문서를 보면 HTML 링크 요소가 외부 스타일시트에 연결하는 데 사용되면서 이것이 웹 페이지의 CSS 리소스 로딩에 부정적인 영향을 미칠 수 있다고 나온다.

Why This Error Occurred
An HTML link element was used to link to an external stylesheet. This can negatively affect CSS resource loading on your web page.
참고:
https://nextjs.org/docs/messages/no-css-tags

pages/_document.tsx

import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <link href="/assets/fonts/style.css" rel="stylesheet" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

next.config.js

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

const withPlugins = require('next-compose-plugins');
const withAntdLess = require('next-plugin-antd-less');
const withAntdLessConfig = {
  // optional
  modifyVars: { '@primary-color': '#348fe2' },
  // optional
  lessVarsFilePath: './pages/antd-custom.less',
  // optional
  lessVarsFilePathAppendToEndOfContent: false,
  // optional https://github.com/webpack-contrib/css-loader#object
  cssLoaderOptions: {},
};
const plugins = [[withBundleAnalyzer], [withAntdLess, withAntdLessConfig]];
const nextConfig = {
  images: {
    domains: [
      'modument-openmarket-icon.s3.ap-northeast-2.amazonaws.com',
      'modu-company-document.s3.ap-northeast-2.amazonaws.com',
          ...
    ],
  },
  webpack: (config, { webpack }) => {
    const prod = process.env.NODE_ENV === 'production';
    const newConfig = {
      ...config,
      mode: prod ? 'production' : 'development',
      plugins: [...config.plugins, new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /^\.\/ko$/)],
    };
    if (prod) {
      newConfig.devtool = 'hidden-source-map';
    }

    return newConfig;
  },
};

module.exports = withPlugins(plugins, nextConfig);

public/assets/fonts/style.css

@font-face {
  font-family: 'OpenSans-Light';
  src: url('OpenSans-Light.ttf') format('ttf'), url('OpenSans-Light.ttf') format('ttf'),
    url('OpenSans-Light.ttf') format('truetype');
}

@font-face {
  font-family: 'OpenSans-Regular';
  src: url('OpenSans-Regular.ttf') format('ttf'), url('OpenSans-Regular.ttf') format('ttf'),
    url('OpenSans-Regular.ttf') format('truetype');
}

@font-face {
  font-family: 'OpenSans-SemiBold';
  src: url('OpenSans-SemiBold.ttf') format('ttf'), url('OpenSans-SemiBold.ttf') format('ttf'),
    url('OpenSans-SemiBold.ttf') format('truetype');
}

@font-face {
  font-family: 'OpenSans-Bold';
  src: url('OpenSans-Bold.ttf') format('ttf'), url('OpenSans-Bold.ttf') format('ttf'),
    url('OpenSans-Bold.ttf') format('truetype');
}

font 파일의 구조는 다음 블로그의 도움을 받았다.
font 파일의 구조
https://stackoverflow.com/questions/61909298/how-to-use-self-hosted-fonts-face-using-nextjs

2. solution

(1) webpack error

_app.tsx에서 public/assets/fonts/style.css를 불러온 뒤 build를 하면 아래와 같은 webpack error가 발생한다.

Failed to compile.

./public/assets/fonts/style.css
HookWebpackError: Module parse failed: Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
-- inner error --
Error: Module parse failed: Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
Generated code for /Users/juyoungjung/oms-web-admin_react/public/assets/fonts/OpenSans-Light.ttf
1 | throw new Error("Module parse failed: Unexpected character '\u0000' (1:0)\nYou may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders\n(Source code omitted for this binary file)");

_app.tsx

import '../public/assets/fonts/style.css';//이 부분
import React from 'react';
import Head from 'next/head';
import wrapper from '@redux/store/configureStore';

function OMSApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <title>MODUMENT OMS</title>
      </Head>
      <Component {...pageProps} />
    </>
  );
}

export default wrapper.withRedux(OMSApp);

이는 next.config.js에 다음과 같은 module.rules설정을 해주면 해결된다.

next.config.js

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

const withPlugins = require('next-compose-plugins');
const withAntdLess = require('next-plugin-antd-less');

const withAntdLessConfig = {
  ...
};
const plugins = [[withBundleAnalyzer], [withAntdLess, withAntdLessConfig]];
const nextConfig = {
  images: {
    domains: [
        ...
    ],
  },
  webpack: (config, { webpack }) => {
    const prod = process.env.NODE_ENV === 'production';
    const newConfig = {
      ...config,
      mode: prod ? 'production' : 'development',
      module: { 
        rules: [ //이 부분
          ...config.module.rules,
          {
            test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
            use: {
              loader: 'url-loader',
              options: {
                limit: 100000,
                name: '[name].[ext]',
              },
            },
          },
        ],
      },
      plugins: [...config.plugins, new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /^\.\/ko$/)],
    };

    if (prod) {
      newConfig.devtool = 'hidden-source-map';
    }

    return newConfig;
  },
  async rewrites() {
    return [
      {
        source: '/:path*',
        destination: `https://oms.modument.co.kr/:path*`,
      },
      // {
      //   source: '/:path*',
      //   destination: `http://192.168.0.17:7000/:path*`,
      // },
    ];
  },
};

module.exports = withPlugins(plugins, nextConfig);

참고: https://stackoverflow.com/questions/59455259/nextjs-module-parse-failed-unexpected-character-70

2. Failed to decode downloaded font, OTS parsing error

다음 스크린샷과 같이 font를 읽어오지 못하는 문제가 발생했다.
Failed to decode downloaded font, OTS parsing error

이는 font path를 제대로 읽어오지 못해 발생하는 문제로 다음과 같이 font path를 수정하니 해결되었다.

public/assets/fonts/style.css

@font-face {
  font-family: 'OpenSans-Light';
  src: url('OpenSans-Light.ttf') format('ttf');
}

@font-face {
  font-family: 'OpenSans-Regular';
  src: url('OpenSans-Regular.ttf') format('ttf');
}

@font-face {
  font-family: 'OpenSans-SemiBold';
  src: url('OpenSans-SemiBold.ttf') format('ttf');
}

@font-face {
  font-family: 'OpenSans-Bold';
  src: url('OpenSans-Bold.ttf') format('ttf');
}

참고: https://stackoverflow.com/questions/34288778/failed-to-decode-downloaded-font-ots-parsing-error-invalid-version-tag-rails

profile
바꿀 수 있는 것에 주목하자

0개의 댓글