記事一覧に戻る

ReactからNext.JSに移行しました。

対象にしている人

Reactを触りはじめで、Next.jsもいつか触ってみたいと思っている人。とりわけ、私のように趣味でやっている非職業プログラマーの人を想定しています。実際に私がNextjsに移行するにあたって、何故移行を決めたか、どのような点でつまずいたかを共有することで、なにかの役に立てばと思います。私自身もReact自体も3ヶ月前に覚えたので、同じような学習過程の方向けに書いています。バリバリ使える方にとっては、役に立つ情報はほとんどないと思います。

はじめに

ホームページは、今年の2月頃にはじめてドメインを取得してリリースしました。 せっかくドメイン取ったのに、機能が極めて限定的でもったいなかったので、8月にリニューアルしています。その際にReactやTypeScriptを導入しました。

  • 2月 :素のJS、HTML、CSS
  • 8月 :React導入
  • 10月:Next.JS移行 ← 今ココ

Reactを試してみた理由

2月にはじめて作ったホームページは、1つのページしかない非情にシンプルなものでした。しかし、ボタンを押したときのモーダル表示などDOMで直接HTML要素を操作することになるので、機能の割りにコード量は多くなってしまったと思います。機能追加をするのに、またゴリゴリ書くのも嫌だなぁ、、、と感じていたので名前を良く聞くReactを試してみることにしました。

例えば、ボタンを押したときに、サーバから取得した仮想通貨の銘柄数分、div要素を動的に作成したい場合、素のJSだと以下のような記述になるかとおもいます。

/*素のJS*/

// サーバから取ってきたとしましょう
const serverData = ["btc","etc","xrp","xem"]

const btn = document.querySelector(".some-button");
const parent = doucment.querySelector(".node-to-append-to");

btn.addEventListener("click",e =>{
    serverData.forEach(coin=>{
        const node = doucment.createElement("div");
        node.innerText = coin;
        parent.appendChild(node);
    });    
});

DOM操作だと、要素を取得するのにHTMLで設定したidやclass名で指定が必要なので、 HTML,CSS,JSセットで直すことになります。また、動的にHTML要素を生成する場合、 大抵親要素にappendしますので、親要素にも依存してしまいます。つまり、HTMLの構成を変更すると、大きなダメージを負ってしまう可能性が出てきます。実際私も一度一から作り直すことになりました。このあたりは経験である程度カバー出来るのかもしれませんが。

Reactだとこんな感じ。

/*React*/

function Page(){
  // 実際にはuseEffect内で書くのでしょうが、割愛
  const severData = ["btc","etc","xrp","xem"];

  return (
    <>
      <button className="some-button"></button>
      <div className="node-to-append-to">
        {serverData.map(pair=><div>{pair}</div>)}
      </div>
    </>
  );
}

直接DOMを操作せず、HTMLの要素を部品化して再利用しやすくしてくれるのがReactの強味とのことです。正直、初見では「HTMLとJSが一緒になっててカオス」くらいの感触でしたが、ひと昔JQUERYが流行ったように、今の流行りとのことで、せっかくなので使ってみることにしました。

※HTMLのタグみたいなものが直接記述されているのは、Reactの機能ではなく JSXというパッケージの機能のようです。

Reactで生じた課題

そんなこんなでReactで作り直しをし、8月にリリースしました。厳密には、もともとあったページは作り直しはせずそのままリンクを貼っただけです。

公式ドキュメントも充実しており、思っていたよりかは容易に構築が出来ましたのですが、大きな課題が3つ生じてしまいました。

戻るボタン問題。

どのページでブラウザの「戻る」ボタンを押しても、トップページに戻されてしまいます。ブログが目玉の追加機能だったのですが、「トップページ」⇒「記事一覧」⇒「記事」と遷移し、戻るボタンでトップページまで戻されてしまっては、使い勝手が悪いです。苦肉の作で、自作の戻るボタンを記事ページに設置し、そのボタン押下で記事一覧に戻るようにしました。

Reactはシングル・ページ・アプリケーションで、表示の切替はURLに応じたHTMLファイルをサーバから取ってくるのではなく、JSで部品を差し替えて実現している、ということは頭では理解していたのですが、、、。こういう動きをすることになるとは。状態管理をしっかりやって、react-routerなどのパッケージを利用すれば、ある程度辿って戻ることは出来るかもしれません。しかし、どちらにしてもそれなりに直しが出そうですぐに対応はできなそうです。

リンク問題。

ブログの記事を追加したら、その記事のリンクをツイッター等のSNSに貼って拡散したいですよね、しかし、上と同じ理由で、トップページのリンクしか作成出来ません。~~.com/blogs/article1のようなリンクに対して、Expressなどで工夫をすれば出来てたかもしれませんが、これまた時間がかかりそうです。

最後に、アクセス数問題。

これが一番深刻です。
頑張って新しい技術も取り入れてリニューアルしたはいいものの、一向にアクセス数が増えません。なにかのボットか、私のPC・スマホ以外からのアクセスがほとんど無いといった有様です。

Reactのような、ページの切り替えを全てJSで行うシングル・ページ・アプリケーションはSEO面で弱いそうです。検索結果のどの順位に表示されるかは、インターネットの大海を徘徊している専用のボットが決めるようですが、あまり上手にJSからHTMLを構築が出来ない(もしくは途中で諦めてる)ようです。

私のアクセス数が少ないのも、決してコンテンツが弱いのではなく、きっとこれが原因でしょう。そうに決まっています。

Next.js移行で苦労したこと

公式ドキュメントが充実しているので、思っていたよりは苦戦しませんでした。Reactの基本的な使い方を理解していれば、そこまで移行のハードルは高くないと思います。フロント側はページ単位で既存のコンポーネントを組み合わせていく作業なので、難易度はそこまで高く無い気がします。

バックエンドは全て書き換えた。。。

Next.jsはいわゆるwebサーバ側の処理もフレームワークに組み込まれています。もともとExpress.jsで実装していたので、書き換えることになり、結構時間がかかりました。Express専用の外部パッケージを大量に使っていると、Next用のパッケージを探すことになるので、場合によってはかなり大変な作業になるかもしれません。私の場合はファイルアップロード用にexpress-fileuploadを使っていたので、multerに変えることになりました。

ExpressをNext.jsで使うことももちろんできるようですが、公式ドキュメントには以下のように記載されています。

Before deciding to use a custom server, please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like serverless functions and Automatic Static Optimization.

『あなたのアプリの要求を満たせない場合にのみ使用されるべきです。』とのことなので、基本は組み込みのものを使ってくださいとのことなのでしょう。殊更、はじめてのNext.jsなので、いきなり別サーバアプリを使うのも気が引けます。

組み込みのAPIは少し不便で簡易的なExpressといった感じで、Expressを触ったことがある人ならそこまで迷わないかと思います。 ただ、同じようにほぼほぼ書き換えることは覚悟しておいたほうが良いかもしれません。

Next.jsだとnext-connectのような外部パッケージを入れないとミドルウェアの追加が出来ないのも不便に感じます。
後、ExpressだとrequestのContent-Typeに応じてパーサを追加できます。

// express
import express from "express";
const app = express();
app.use(express.json());

一方Next.jsだとjsonとかは自動でパースしてくれます。multerのように外部パッケージのパーサを追加する時は、デフォルトのパーサをオフにしないと動いてくれません。

// /pages/api/some-api.js
export default handler(req,res){
  // 何かの処理
  res.end();
}
// ここでbodyParserをfalseにしないと動かない
export const config = {
    api: {
        bodyParser: false,
    },
}

何を自動でパースしてくれているのか、公式ドキュメントを見てもよく分からないので少し不便です。

bodyParser is automatically enabled. If you want to consume the body as a Stream or with raw-body, you can set this to false.

とのことなので、requestのbody部をraw-bodyやstreamとして扱うパーサを使う時には、オフにしてあげる必要があるとのことですかね。そもそもraw-bodyやstreamがよく分かりませんが。

ビルドが本番でこける

ビルドが本番でこけることがあり、まだ原因がよく分かっていません。素のReactのときは基本開発環境でビルドが出来ていれば、本番ではこけることはなかったのですが、、、。Next.jsだと、SSGしているページは、開発環境(NODE_ENVが'production'以外のとき)はSSGせず、SSRに近い動きをするようです。なので本番と環境でのビルド方法に、素のReact以上に違いがあると考えたほうが良いかもしれません。開発途中でも、こまめに本番でビルドを試しておけば良かったです。

結論

このホームページくらいの規模なら素のHTMLでも良かったかも。

参考リンク

記事一覧に戻る