NEXT.jsでpublicフォルダ以外から画像を使用する方法

 ・ 2 min

photo by Jon Tyson on Unsplash

Next.jsで開発ブログを作る際、必ずやらなければならないけど一番面倒だったのが画像の処理でした。
以前はGatsbyを使っていて、Gatsbyが自動でやってくれるので本当に楽でした。
GatsbyよりもNext.jsに移行することに決めましたが、Next.jsでは画像処理が意外と厄介で苦労しました。

まず知っておいていただきたいのは、Next.jsでSSG(Static Site Generator)を使っているということです。つまり、すべてのHTMLはビルド段階で生成され、GitHub Pagesでホスティングされます。これはNGINXで静的ファイルだけを配置するのと似ています。本番環境でサーバーレンダリングを通じてHTMLを返す部分はないとお考えください。

そして、このプロジェクト内部でObsidianを通じて記事を書いています。
プロジェクト内のblogフォルダに.obsidian設定フォルダがあります。Obsidianプログラムではプロジェクトルート/blogフォルダで記事を作成しています。
記事が作成される場所はblog/postsで、画像が保存される場所はblog/assetsです。

HTMLで画像を表示するには2つの方法があります。

  1. 画像ファイルのパスを指定する方法
  2. 画像ファイルをbase64に変換して非常に長い文字列にして表示する方法

リモート画像の場合は、通常サーバーレンダリング時に画像をbase64に変換するか、そのままCDNパスから取得すればOKです。
リモート画像のパスとしてはUnsplashとYes24を使用しています。

大きな問題だったのはローカル画像でした。Obsidianで画像を添付すると、Obsidian上では画像が正しく表示されますが、Web環境(開発・本番ともに)では画像が壊れた状態で表示されていました。

画像の問題を解決するために参考にしたブログ記事は2つでした。

  1. Use relative paths in markdown and MDX images with Next.js -> 画像パスの変換
  2. Contentlayer with next/image -> 画像をbase64に変換

最初に画像の問題を解決するために試みたのは、ローカル画像ファイルをMarkdownからHTMLに変換する過程で画像を見つけ、srcのパスの代わりにbase64に変換した文字列に全て置き換えることでした。
こうすれば簡単に問題は解決しますが、ブログ記事の画像が増えるほどWebサイトが遅くなってしまいました。

画像が表示されるようになったもののサイトが遅くなったため、画像をパスから取得するように変更する必要がありました。remarkPluginsに自作の関数(remarkSourceRedirect)を追加して、Obsidianのパスを開発・本番環境で画像パスが表示されるようにsrc値を修正する処理を追加しました。
そしてNext.jsではpublicフォルダからのみ画像を認識するため、プロジェクトルート/blog/assets -> プロジェクトルート/public/blog/assetsにコピーする処理をpredevprebuildスクリプトとして追加しました。

SSGの場合は、以下のようにnext.config.jsで画像最適化オプションを無効にする必要があります。

const isDev = process.env.NODE_ENV === 'development'
 
const nextConfig = {
  output: isDev ? 'standalone' : 'export',
  reactStrictMode: true,
  swcMinify: true,
  images: {
    unoptimized: true, // ここです!
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'image.yes24.com',
        pathname: '/**',
      },
      {
        protocol: 'https',
        hostname: 'images.unsplash.com',
      },
    ],
  },
}
 
module.exports = withContentlayer(nextConfig)

より改善された処理方法はNext.jsのビルドサイズを105MBから16MBに削減するを参考にしてください!


Make the best use of what is in your power and take the rest as it happens.

— Epictetus


他の投稿
ブログに適用すること! 커버 이미지
 ・ 1 min

ブログに適用すること!