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

Feb 27, 2022 11:30 · 2288 words · 5 minute read Next.js TypeScript Go

数年前に「PugやjQueryで作ったWebのUI」を組み込んだGoのアプリを作りました。

Getting Started – Pug (pugjs.org)

周囲にも好評で、結構ガッツリと使ってもらえていたのですが、地味な不具合がありました。

「不具合修正と合わせてWebUIの部分をNext.js + TypeScriptに置き換えたら、一緒にメンテナンスしてくれる人が増えるのでは?」ということで、Next.js + TypeScriptへの置き換えを試してみした。

目次

最初に結論

Next.js + TypeScriptを初めて使ったのですが、サクサクと画面が出来上がって楽しいですね。 しかし、静的化して使うとなると、いくつか課題があることもわかりました。

  • ビルドして静的コンテンツとして出力すると、コンポーネントのアニメーションが動かない場合がある
  • ビルドして静的コンテンツとして出力すると、ReactのLinkがそのままでは動作しない

環境

  "dependencies": {
    "next": "12.1.0",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "recharts": "^2.1.9"
  },
  "devDependencies": {
    "@types/node": "17.0.21",
    "@types/react": "17.0.39",
    "eslint": "8.9.0",
    "eslint-config-next": "12.1.0",
    "typescript": "4.5.5"
  }

アプリの構成

Pugで作ったHTMLをGoの「net/http」で動かし、ブラウザからアクセスしています。

Getting Started – Pug (pugjs.org)

この構成にすることで、「Goのバイナリを1つで相手に渡すことが可能」かつ「サーバのメンテナンスが不要になる」というメリットがあります。
Dockerで動かす方法もあるのですが、「使ってくれる人ができるだけ簡単に実行できる形にしたい」という想いがあり、この構成を選択していました。

今回はPugで作っていたWeb UI部分をNext.jsに置き換え、静的コンテンツとしてビルドした上でGoの「net/http」で動かしてみました。

Next.jsの実装

最終的なソースコードはこちら

kapiecii/nextjs-typescript-sample (github.com)

Next.jsアプリを作成

まずはcreate-next-app。

$ npx create-next-app@latest
$ cd sample-app
$ npm install --save-dev @types/react @types/node

あとはPagesの下にコンテンツを追加します。今回は追加したコンポーネントの動作も確認したかったので、Rechartsというライブラリを使ってみました。

Recharts

実行した様子がこちら。


困ったところ

本来であれば下記のように「–ts」オプションをつけることでTypeScriptに対応したNext.jsアプリを作ることができます。

$ npx create-next-app@latest --ts

Basic Features: TypeScript | Next.js (nextjs.org)

しかし、この方法でアプリを作った場合、アプリを静的コンテンツにビルドする際に「Error: Missing “key” prop for element in array react/jsx-key」のエラーが大量に発生しました。
エラーの内容から「下記が原因か?」と考えたのですが、エラー発生箇所ではListは使っていません。

Lists and Keys – React (reactjs.org)

ビルド時のLint設定も確認したのですが、問題は解決できず。
今回はエラーを解消するのに時間がかかりそうだったので、

$ npx create-next-app@latest

したあとに

$ npm install --save-dev @types/react @types/node

することで、エラーを回避していました。
原因がわからなくて気持ち悪いので、引き続き調査します。

Goの実装

Goのコードはこちら。
「net/http」でhttp serverを起動し、「embed」とファイルシステムでバイナリに組み込んだWebコンテンツを表示するシンプルな内容です。
以前は「go:generate go-assets-builder -s="/html" -o assets.go html/」のようにしていたのですが、「embed」が標準に組み込まれて便利になりました。

package main

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

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

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

	http.Handle("/", http.FileServer(http.FS(static)))
	if err := http.ListenAndServe(":8888", nil); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

ビルド時のコマンドはこちら。

$ go build -ldflags="-s -w" sample-app.go

動かしてみる

Goで作ったバイナリを実行し、Webブラウザからアクセスした様子がこちら。 Rechartsのチャートも表示されています。

しかし、いくつか課題があることもわかりました。

判明した課題

「Next.js+TypeScriptで作ったWebコンテンツをGoのバイナリに組み込む」というのを試してわかった課題は下記です。

  • Rechartsのようなコンポーネントも表示はできるが、描画時のアニメーションは動かない場合がある。
  • Next.jsのコンテンツを静的コンテンツにビルドする場合には、ページ間のリンクに一工夫必要。元々「localhost:{port}/chart」だったURLが「localhost:{port}/chart.html」のようになり、Reactの「Link」で作成したリンクが切れる。

最後に

Next.js + TypeScriptで作ったWebコンテンツをGoのバイナリに入れて実行してみました。 Next.jsはチュートリアルをやったことがある程度でしたが、サクサクと画面を作ることができて楽しいですね。

しかし、ビルドして静的コンテンツとして使おうと思うと課題があることもわかりました。

  • ビルドして静的コンテンツとして出力すると、コンポーネントのアニメーションが動かない場合がある
  • ビルドして静的コンテンツとして出力すると、ReactのLinkがそのままでは動作しない

また、検証中に出会った下記のエラーも気持ち悪いので調査が必要です。

  • 「$ npx create-next-app@latest –ts」をすると、静的ビルド時に「Error: Missing “key” prop for element in array react/jsx-key」のエラーが発生する

このあたりを上手く扱う方法があるのか、引き続き調査してみようと思います。
解決方法をご存知の方がいらっしゃれば、Twitterで教えていただけると嬉しいです🙏

解決編

後日追記。
その後の検証で問題が解決したので、解決方法を書きました。

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

tweet Share