logo
/
個人サイトのブログ部をサブドメインに切り出して、NuxtからNextに移行した
2022-03-24
ブログ

目的

技術的負債

1.Nuxt(Vue)の問題

旧サイトは Nuxt(Vue)アプリを採用して 2020 年中旬に作成した。 Node、javascript Framework、仮想 DOM 等の概念をあまり理解していないがモダンなフロントエンド開発を学習しなければいけないと思っている状態だった。 仮想 DOM を採用してフロントエンドでリッチな組み換えを行うのが主流になりつつあること、そのフレームワークは React と Vue がおすすめだという情報を入手。WEB サイトのフロントエンド構築は HTML + CSS + javascript しか採用したことがない状態では Vue の Template 構文は非常に魅力的に見えたので採用した。 ある程度仮想 DOM の概念に慣れた後に React 及び Next も学習した所、最初は理解が難しかったが JSX の自由度、Typescript との親和性が圧倒的に React のほうが高く、新規開発はすべて Vue は使わずに React を採用していた。特に Typescript は敬遠していたが、型を先につけることで自然と test diven に近い開発ができたことは、とても驚くとともに良い開発体験となった。 Vue は Vue3 + Composition API により Typescript フレンドリーになりつつ合ったが、新旧の仕組みが入り混じった状態ではライブラリの相性問題等に時間を取られることが多かった。インターネット上の情報も新旧入り混じっていたので、ストレスが非常に高かった。Vue にはもう触りたくなかったが、せっかく構築したサイトを継続開発せずに放置するのももったいないと感じたため、React(Next)への移行を決めた。

2.設計の問題

学習のため Wordpress や Hatenablog を使用せずブログシステムを自分で作成したが、URL 設計やコンポーネント設計をきちんとせずにゴリ押しで開発をすすめた。ブログもサイトのサブディレクトリに設置したため、構成が複雑になり修正がしにくい状態となっていた。 将来的に CMS やブログサービスに移行する可能性も考えて、サブディレクトリ式ではなくサブドメインで切り分け、移行しやすい状態に変更した。サブドメイン式にすれば機能ごとに移行がしやすいというのも大きな利点である(複数のマイクロサービスを組み合わせるイメージ)。

構成

旧構成

DNS 等

ドメイン:ムームードメインで取得
DNS:Cloudflare を参照
Cloudflare:SSL 化 と Heroku への DNS プロキシ

メインサイト

Hosting : Heroku
Framework : Nuxt(SSR)
Loading...

作業

Hosting 先の選定

前提

サイトにはアドセンスやアフィリエイトを一応配置はしてあるが、本格的に広告収入を見込む予定は無い。そのため維持費は可能な限り抑えたい。
旧サイトはすべて Heroku の 1dyno で起動している。
Heroku は非常に使いやすいが、無料ユーザでは 1000h の dyno 起動制限があり、常に立ち上げておくのは 1dyno が限界である。

候補

  1. Heroku 有料プラン($7/月)
    • メリット
      • 1000h 制限がなくなる
      • SSL のために Cloudflare をプロキシする必要がなくなる
    • デメリット
      • 課金はユーザごとではなくプロジェクト毎のため、2つプロジェクトを動かすには 2× $7 = $14/月 がかかる
  2. firebase hosting(無料プラン)
    • メリット
      • Heroku ほどではないが手軽
      • コストは掛からない
    • デメリット
      • 月の転送制限が低い(10G)
  3. Netlify(無料プラン)
    • メリット
      • 手軽
      • 無料のホスティングサービスの中では制限が少ない
    • デメリット
      • 静的 Hosting のため、SSR やサーバサイドの処理はできない
  4. その他 VPS
    • メリット
      • 性能制限に達しなければプロジェクト数は自由
    • デメリット
      • 運用・保守の手間がかかる

結論

ブログサイトではサーバサイドの処理をする予定はないため静的ファイルの Hosting で十分なこと、触ってみた所 Heroku よりも簡単なくらいにすぐにデプロイできたことから Netlify を選択した。

DNS やデプロイの設定

DNS は Cloudflare の DNS の CNAME にサブドメインを追加するだけですぐに Netlify に接続された。
デプロイは Heroku と同様に Github のリポジトリをセットし push されたら Continuous Deployment が行われる構成にした。
build settings は各自設定が異なると思うが、nuxt と next のビルドコマンドは違うので注意する。
また、package.json には export(静的出力)のスクリプトがデフォルトで記載されていないため追加する。
  • package.json
Loading...
  • build settings
Build command: npm run build
Publish directory: out

URL

URL 設計

旧サイトではk4a.me/document/topic?id=XXXという URL を採用していたが、新サイトではシンプルにlog.k4a.me/XXXで記事にアクセスできるようにする。
Next.js のDynsmic Routesを使用し、pages 配下に存在しないルートは記事 ID としてアクセスさせる。
L pages
    - [id].tsx -> 記事
    - _app.tsx
    - index.tsx
    ...other

リダイレクト

旧サイトの document 配下にアクセスした場合は、新サイトにリダイレクトされるように nuxt プロジェクトを更新する。
$route.push()を使用したリダイレクトはサイト内に変換されてしまうため、window.location.replaceを使用する。
Loading...

記事の管理

マークダウンファイル

記事はマークダウンで記述する。
public/assets/articles配下に作成したディレクトリを記事 ID として、その中に配置したindex.mdを記事内容として扱う。
public
    L /assets
        L /articles
            L /XXX
                L index.md
                L /image

記事の取得

Next.js で Markdown ブログを作るを参考に記事一覧、内容を取得する module を作成する。
Loading...
pages/index.tsxからはgetAllPostsで記事一覧を、pages/[id].tsxからはgetPostByIdで記事内容を取得する。

マークダウンの変換

既存のマークダウンパーサはたくさん存在するが、ここでは nuxt 時代に自作したパーサを使用する。
このパーサは markdown→html に変換するのではなく、マークダウンを解析し階層化したオブジェクトに格納する。
React 等の仮想 DOM では HTML に変換するよりオブジェクトにしたほうがコンポーネントが組み立てやすい。また、nuxt 時代のブログではアウトライナーのように階層を折り畳めるようなしくみにしたかったのでこのパーサを作成した。
  • markdown
# タイトル1
## タイトル2
こんにちは
  • object
Loading...
独自パーサを使用すると、マークダウンファイル内に<ad/>等と記述すると Google Adsence ブロックが挿入されるなどのカスタマイズがしやすいため、新ブログでも引き続き使用しようと考えている。ただし、ソースコードが非常に汚いのでリファクタリングをする必要がある。

サイトマップの追加

next-sitemap

最初にnext-sitemapを試してみた。
公式を参考プロジェクトルートにnext-sitemap.config.jsを追加してスクリプトに追加し実行する。
Loading...
Loading...
開発環境でも Netlify でも正常に動作したが、Google Search Console 上で問題が発生した。
next-sitemap は sitemap.xml をインデックスサイトマップにして、sitemap-1.xml、sitemap-2.xml...に実際の sitemap を記載する形で出力される。
sitemap.xml を GSC に登録すると、検出 URL が 0 になり、sitemap-1.xml を登録するとエラーとなった。他ツール等で確認し XML 記法に間違いがないことは確認した。 この情報が正しいかは定かではないが、Search Console で検出された URL が 0 になるバグを回避する方法。によると「ハイフン」が含まれている、sitemap の url が長い、などの条件で正しい sitemap でも GSC 上ではエラーとなる場合があることがわかった。
next-sitemap でこの命名規則を変更できないかを調べたが、ハイフン前は変更できるがハイフン自体は変更する方法が見つからなかった。
他の有用なパッケージが見つからなかったため、パッケージは使わないことにした。

getServerSideProps

まず、Next.js で動的に XML サイトマップを生成するを参考に、page/sitemap.xml.tsxを作成し、xxx/sitemap にアクセスした時に、動的にgetServerSidePropsからxmlを返却する方式を採用した。 next devnext startでサーバ動作をさせているときは正常に動いたが、next exportで静的出力するとエラーが出力された。
静的出力ではgetServerSidePropsは使用できないためgetStaticPropsに変更したが、getStaticPropsではGetServerSidePropsContextを使用した responce は返却できない(サーバが存在しないため)。
そのため、この方法も断念した。

postbuild

次に、Create a Dynamic Sitemap with Next.jsを参考に、ビルド時にカスタムスクリプトを起動し、outsitemap.xmlを吐き出す方式を採用した。
Loading...
Loading...
ローカルの動作、Netlify 上での動作ともに正常で、GSC でも sitemap が認識されたためこの方式で決定する。

Google Adsence の追加

Next.js に Google Adsense を設置する
サブドメインサイトはどうするべき?Google Analytics のプロパティ設定方法

その他

Netlify 上のプラグインが失敗

ローカルでは問題ないのに、Netlify 上でPlugin "@netlify/plugin-nextjs" failedとなる。
Netlify サイトのPluginsタブにあるplugin-nextjsを削除した所正常に動作した。