ポートフォリオをGolang + Graphql + Next.jsで大改造した話

## はじめに

先日ポートフォリオ(http://www.piny940.com )を大改修したため、その記録を残します。

http://www.piny940.com

完成したコードは以下のGithubで公開していますので参考程度にどうぞ。

https://github.com/piny940/portfolio

## 環境
- Next.js 14.1.0
- Golang 1.21.1

## 抱えていた課題
今まで、ポートフォリオ上の「ブログ」や「技術スタック」「プロジェクト」のデータはすべてYAMLファイルで管理していました。  
しかし、この方法には次の2つの問題点がありました。
- YAMLファイルに書かれたデータの整合性を保証できない
- データ更新にいちいちYAMLファイルを書き換える必要がある

特に2点目の問題についてはかなり深刻で、現状だとデータを書き換えるのに毎回ファイルを書き換えてPull Requestを出して・・・という作業が必要で、結果的にポートフォリオの更新をさぼってデータが古いまま放置されてしまっているという実情がありました。  
また、最近ではQiitaブログを頻繁に書くようになり、ポートフォリオ上の「ブログ」のデータをQiitaから自動で取得して更新する仕組みを作りたいと思うようになりました。しかし、データをYAMLファイルで管理している場合、データを自動で書き換えるためには、自動でファイルを書き換えるようなPullRequestを出すGithubApp等を作る必要があり、かなり手間になってしまうという問題がありました。

以上の問題を解消すべく、意を決してデータをYAMLベースからDBベースに完全移行することにしました。

## やったこと

### DB設計
DB設計は写真のようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330232/74175e71-35b5-1893-4275-c310c382dd78.png)
`technologies`と`blogs`・`projects`を多対多の関係で結んでタグとして扱うようにしました。理由としては、「技術スタック」に表示されている技術を選択するとそれに紐付けられたブログ・プロジェクトが表示されるようにしたいと考えたからです。↓

(技術スタックのページ。`Rails`などのアイコンをクリックするとそれに紐付けられたブログやプロジェクトが表示される。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330232/6d7f303d-9f6c-3f4d-2f1d-7267fc2e1ac3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330232/ea239a6a-91c9-6625-560e-9c0b85e3c986.png)

### バックエンド
データをDBで管理するということは、DBのデータを公開するためのバックエンドを作成するということになります。今回はバックエンドはGo + Graphqlで作成することにしました。

(REST APIではなくGraphqlにした技術的な理由は特になく、Graphqlの勉強がしたいという理由で採用しました。)

### 管理者画面
この大改修は「データ更新を手軽に行えるようにする」という目的で始めたものだったので、データ更新に毎度postgresのコマンドを打たないといけないのでは意味がありません。というわけで管理者画面のUIを作成しました。

管理者画面は基本的に自分しか触らないものですが、使いやすさにはとことんこだわりました。

まず、技術スタックとしては次の3つを採用しました。
- Next.js
- Urql
- MaterialUI

使いやすさのために、データ更新のたびに毎回リロードが走るのを防ぎたいというこだわりから、まずフロントエンドフレームワークとしてNext.jsを採用しました。

Graphql ClientとしてはUrqlを採用しました。これについてはApolloを採用するか迷ったのですが、調べてみたところurqlの方がシンプルなライブラリと言われており、実際ドキュメントをぱっと見た感じでも自分の必要な情報がわかりやすく載っていたので、シンプルさを重宝してUrqlを採用しました。

CSSフレームワークとしては、MUIを採用しました。比較対象としてはBootstrapとTailwindが挙がりましたが、
- DatePickerなどのリッチなUIが整っている
- Reactと相性がいい

という理由からMUIに軍配が上がりました。

実際に作成した管理画面は写真のようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330232/306096b4-1762-865c-24d1-e2b5012a7aa4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330232/38a810e3-e7de-57cc-0c6e-644b81ff4219.png)

こだわりポイントは、ブログやプロジェクトのタグの追加・更新・削除をUI上で直感的に操作できるようにしたことです。タグについては内部的には中間テーブルを用いた多対多の関係になっているのですが、内部のデータ構造を意識させないような直感的なUIを作成することができて、かなり使いやすく出来たと感じています(配列のフィールドには[react-hook-form](https://react-hook-form.com)の`useFieldArray`を使用しました)。

データの取得・更新にはUrqlをふんだんに用いており、リロード等を一切することなくデータの追加・更新・削除ができるようになっています。

### アプリ画面
アプリ画面でのデータの取得は、描画速度を重視してgetStaticPropsを使用していました。実際、データ更新の頻度もそれほど高くないため、リクエストごとにわざわざ毎回データを見に行く必要はないと考えていました。

しかし、ここで問題が発覚します。データの取得はビルド時に行われるため、管理者画面でデータを書き換えても、再度ビルドをし直さないとデータがポートフォリオに反映されないのです。

本番環境のビルドについては、[こちらの記事](https://qiita.com/piny940/items/4f4158b889db19418588)で解説しているように、GithubActionを使ってデプロイまで自動でされるようにしているのですが、GithubActionの発火についてはGithub上から手動でボタンを押さないといけないようにしていました(知らないうちにバグってるコードがデプロイされたら困るので)。しかし今回のようなデータ更新は、ボタンを手で押すことなく本番環境に反映されてほしいものです。

そこで、今まであったビルド用のGithubActionとは別に、データ更新用のビルドを行うGithubActionを作成し、これを毎日1回発火させることで、自動でデータが本番環境に適用される仕組みを作りました。

コード↓
https://github.com/piny940/portfolio/blob/main/.github/workflows/update-data.yml

### ブログ自動更新
今回の大改造で、ブログのデータをQiitaから自動で取得してデータを更新する仕組みを作成しました。これについては別記事で詳細に綴ろうと思っているのでこの記事では特に詳細については記さないことにします。

## 最後に
今回はポートフォリオのデータをUI上で更新できるようにし、結果的にデータ更新の頻度を上げることに成功しました。はじめは「データ管理をYAMLからDBに移すだけやろ」と舐めていたのですが、色々考えたらBackendやら管理者画面やらを作らないといけなくて想像以上に大変なものになってしまいました(汗)。

現状、「プロジェクト」の順序を並び替える仕組みが出来ていないので、次回は順序を並び替えるUIを作っていきたいと思います。