Next.js+TypeScriptで作ったWebUIをGoのバイナリに組み込んで動かしてみる2

Mar 13, 2022 05:00 · 2278 words · 5 minute read Next.js TypeScript Go

先日のブログで「Next.jsで作ったWebのUIを静的化し、Goのバイナリに埋め込んでみた」という内容を書きました。

Next.js+TypeScriptで作ったWebUIをGoのバイナリに組み込んで動かしてみる · kapieciiのブログ

実際に試したところ、いくつか問題があることがわかりました。
その後Nuxt.jsを試すなど紆余曲折しながらも調査をしていたところ、課題のいくつかが解決しました。今回は解決策について書こうと思います。

目次

判明していた問題

前回の記事に記載していた問題は下記でした。

  1. 静的ファイルにビルドするとLinkタグのリンクが切れる
  2. ビルド実行時に「Error: Missing “key” prop for element in array react/jsx-key」エラーが発生する
  3. 静的ファイルにビルドすると、一部のアニメーションが動かない

解決方法

それぞれの問題点の原因と解決方法です。

「静的ファイルにビルドするとLinkタグのリンクが切れる」「静的ファイルにビルドすると、一部のアニメーションが動かない」

この2つは、静的にビルドされた後のjsファイルとCSSファイルが読み込めていないことで発生していました。

前回、静的出力したファイルを確認したのは下記の方法です。

  • 出力されたファイルを直接ブラウザにドラッグアンドドロップ
  • Goの「net/http」で実行しているWebサーバに載せてブラウザからアクセス

まず、ブラウザにドラッグアンドドロップする方法では、

Loading failed for the <script> with source “file:///_next/static/chunks/webpack-69bfa6990bb9e155.js”.

というエラーが発生していました。

続いてGoの「net/http」の場合は、

The resource from “http://localhost:8888/_next/static/chunks/main-f635b472c367d1c7.js” was blocked due to MIME type (“text/plain”) mismatch (X-Content-Type-Options: nosniff).

というエラーで読み込みに失敗していました。

X-Content-Type-Options - HTTP | MDN (mozilla.org)

Goのnet/httpではなくnpmのhttp-serverを使った場合には、適切なContent-Typeが自動で設定されます。結果、jsファイルとcssファイルが正常に読み込まれ、Linkも動作します。

http-server - npm (npmjs.com)

$ npm run build
$ cd out/
$ npx http-server

前回の記事を書いた時点では、出力されるファイルの形式に問題があると考えていたのですが、違いました。

Next.js+TypeScriptで作ったWebUIをGoのバイナリに組み込んで動かしてみる · kapieciiのブログ

ちなみに、前回問題だと考えていた「出力するファイルの形式」は、Next.jsのnext.config.jsで「exportTrailingSlash」を有効にすることで変更できます。

「exportTrailingSlash」の詳細はこちら。

next.config.js: exportPathMap | Next.js (nextjs-ja-translation-docs.vercel.app)

「exportTrailingSlash」を有効にする前の状態がこちら。
「pages/page2.tsx」のようなファイルがある場合、ビルド後には「out/page2.html」というファイルが出力されます。

$ tree -L 2 out/
out/
├── 404.html
├── favicon.ico
├── index.html
├── _next
│   ├── LKyRh4rlfV-VYvOehcGch
│   └── static
├── page2.html
└── vercel.svg

「exportTrailingSlash」を有効にした状態がこちら。
先程と同様に「pages/page2.tsx」のようなファイルがある場合、「out/page2/index.html」というファイルが出力されます。

$ tree -L 2 out/
out/
├── 404
│   └── index.html
├── favicon.ico
├── index.html
├── _next
│   ├── static
│   └── XgY0nMz9OtOCcQjCYkx1v
├── page2
│   └── index.html
└── vercel.svg

どちらの形式で出力した場合も、jsとcssが読み込まれていれば問題なく動作します。

ビルド実行時に「Error: Missing “key” prop for element in array react/jsx-key」エラーが発生する

こちらのエラーは、macOSで動かしていたことに起因していると考えています。
全く同じ方法をWSL2とVMのUbuntuで実行したところ、このエラーは発生しませんでした。
開発をするなら、WSL2なりDockerなりを使って、Ubuntuの上で開発をしたほうがトラブルが少なそうですね。

次の問題

  • Goの「net/http」でjsとcssを返す場合に「content-type」をいい感じに設定する

現状、Goの部分は下記のようにしています。「embed」を使って静的ファイルをまるっと読み込む形です。

package main

import (
	"embed"
	"fmt"
	"net/http"
	"os"
	"io/fs"
)

//go:embed html/*
var html embed.FS

func main() {
	fmt.Println("Open http://localhost:8888/")
	fmt.Println("Press ctrl+c to stop")

	public, err := fs.Sub(html, "html/public")
	if err != nil {
		panic(err)
	}
	http.Handle("/", http.FileServer(http.FS(public)))
	if err := http.ListenAndServe(":8888", nil); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}
$ tree -L 3
.
├── html
│   └── public
│       ├── 404
│       ├── favicon.ico
│       ├── index.html
│       ├── _next
│       ├── page2
│       └── vercel.svg
└── sample.go

Next.jsのUIをビルドした後は、下記のようなファイル構成になるので、いい感じに「content-type」を設定して返す形にしたいですね。

$ tree -L 3 out/_next/
out/_next/
├── static
│   ├── chunks
│   │   ├── framework-e70c6273bfe3f237.js
│   │   ├── main-01df828e572375b9.js
│   │   ├── pages
│   │   ├── polyfills-5cd94c89d3acac5f.js
│   │   └── webpack-69bfa6990bb9e155.js
│   ├── css
│   │   ├── 149b18973e5508c7.css
│   │   └── 27d177a30947857b.css
│   └── XxyPy8Pu1_cOkBUmMMmDD
│       ├── _buildManifest.js
│       ├── _middlewareManifest.js
│       └── _ssgManifest.js
└── XxyPy8Pu1_cOkBUmMMmDD

最後に

以前の記事に書いていた問題が解決したので、解決編としてブログに残しておきます。
次の問題もみつかったので、引き続き合間を見つけて調査検証していきます。

tweet Share