Nuxt+Markdownで運用楽々爆速ヘルプページを作った

はじめに

はじめましてHRBrainでCTOをしております。鈴木です。

先日人事評価管理クラウドのHRBrainのフルリニューアルしました。それに合わせてヘルプページもリニューアルしたので今回はそれについて話していきたいと思います。

こんなものを作りたかった

ヘルプページを作る時の要件として次のようなものがありました。

  • カテゴリーごとに記事を投稿できる
  • 任意のキーワードで記事本文検索できる
  • 関連記事を登録できる
  • エンジニアいらず(リリース作業いらず)で記事の更新が行える
  • 弊社のサービスにログインしている人だけに見える
  • 公開しないのでSEOは考慮しなくて良い
  • リリース後の記事の更新頻度は月に1回程度の予定

help page sample design
ヘルプページの仮デザイン

要件ではないのですが、PMに予めヘルプの内容をMarkdown形式で書き溜めてもらっていました。

また以前のヘルプページはWordPressで作っていたのですが、弊社のサービスにログインしている人だけに見える の要件を満たすために認証用にプラグインを開発したり不要にFatな構成になってしまっていました。

そのため管理や運用が楽になるような構成にしたいと思っていました。

こんな風に作った

更新頻度が少ないかつ特別にサーバーの運用をしたくなかったので、WordPressのようなCMSを使わず、静的サイトBuilderのようなものを使って実装していく方針にしました。

MarkdownをJSONに変換

まずストックされていたMarkdownをJavaScriptで扱いやすいJSONに変換することを考えました。

MarkdownからHTMLは以前プライベートで使ったことのあったMarkedを使うことにしました。

ただしシンプルなMarkdownだと記事IDやタイトル、カテゴリーなど記事のメタ情報をかけません。(もしくは独自のお約束ごとになりがちです。)

そこでJekyllHugoなどでも利用されているFront Matterの仕様に乗っ取り記事のメタ情報をつけることにしました。

Front Matterは下記のようにMarkdownの先頭にメタ情報を書くようにします。

---
title: 六本木で安くて美味しいランチ見つけたよ
category: ランチ
---

## 今日のお店

中華が食べたくなって本格的な中華が食べれるお店に行きました。

こちらにfront matterを実行することによって下記のようなJSON(一部のパラメータのみ抜粋)に変換されました。

{
    "attributes": {
        "title": "六本木で安くて美味しいランチ見つけたよ",
        "category": "ランチ"
    },
    "body": "## 今日のお店\n中華が食べたくなって本格的な中華が食べれるお店に行きました。"
}

これでJavaScriptの世界でタイトルやカテゴリーが使いやすくなりました。

こちらのbodyの内容にMarkedを通してあげると

{
    "attributes": {
        "title": "六本木で安くて美味しいランチ見つけたよ",
        "category": "ランチ"
    },
    "body": "<h2>今日のお店</h2><p>中華が食べたくなって本格的な中華が食べれるお店に行きました。</p>"
}

のようになりました。

これらを全ての記事で行い、まとめて配列にして一つのJSONに変換しました。 f:id:hrb-suzuki:20190409234447p:plain

コードにするとこのようになっています。

サンプルの実装

JSONからページ生成

GatsbyHugoなどを利用することも考えたのですが、フロントチームリーダーの熱い推薦でNuxt.jsを使うことにしました。

Nuxtではnuxtコマンドのイベントに対してhookして処理を行うことができ、今回は build:before を使ってbuildの前にMarkdownをJSONに変換する処理を差し込むようにしました。

また要件にあった記事内の全文検索の機能は先ほど作ったJSONを読み込んでFilterをかけて行うことにしました。

const posts = require('~/dist/posts.json')
const results = posts.filter(post => post.body.includes(keyword))

今回用途としてはヘルプページなので全体の記事数をまとめてもそこまで大きくならないと思っているため相性が良く、記事の容量が増え続けるものであればAPIを用意して取得できるようにするべきだと考えています。

これらを nuxt generate して静的Fileを作成しています。

認証について

今回認証については極力疲弊しない設計にしたく、先ほど生成したFileをストレージに置き、プロダクト本体のアプリケーションで認証済みの場合のみプロキシしてFileを返すようにしました。 プロダクト本体のアプリケーション内のミドルウェアを再利用することで管理が楽になりました。

記事の更新フローについて

記事の更新については現状PMが行っていて、Github上で直接Markdownを変更し直接Commitしてもらっています。その変更をCIが検知しストレージにアップロードしています。 プロダクト本体のアプリケーションからはプロキシしているだけなのでストレージの中身が変更された際には自動で内容が反映されるようになっています。

運用してみて

本プロダクトを作っている合間の息抜きで作った割には満足いく出来になりました。 ヘルプページのためにサーバーを管理する必要もなくなり、エンジニアは何も気にせず記事の変更ができるのでオススメの構成です。