🏋️

ヘッドレスCMSで開発するWebサイトでエンジニアの運用負担を減らすための工夫

2023/12/09に公開

システムをリリースすると運用が始まります。運用と言ってもさまざまな作業がありますが、この記事では「事業部や顧客からの要望に応じてシステムを改修する」ことに絞ります。
ヘッドレス CMS を組み合わせて開発する Web サイトで、この運用作業をなるべくエンジニアの手をかけずに行えるように工夫してみました。

今回、Next.js (App Router)と microCMS で自社の Web メディアを立ち上げたので、これらの技術スタックを例として説明します。ヘッドレス CMS で記事に関わるデータしか管理していなかったような方には特に参考になるかと思います。

変更されそうな項目を洗い出す

今回開発したのは一般的な Web メディアです。下記のような項目は後から変更が入りそうなので、簡単に更新できるようにしておいた方が良いと考えました。
※記事やカテゴリ、著者等の CMS で管理するのが当然なコンテンツは除いています。

  • 基本情報
    • サイト名
    • 説明文(meta description)
    • サムネイル(OGP)画像
    • フッターの copyright
  • ページ
    • 利用規約
    • プライバシーポリシー
    • このサイトについて

CMS 上で基本情報を管理する

基本情報についてはまとめて取得できるように管理するのが良さそうです。
microCMS ではオブジェクト形式の API を作成できます。説明文で使用用途や画像サイズを補足したり、文字数に制限をかけて意図しないデータの入力を防ぐことができます。

API から取得するデータはこのようになります。

{
  "name": "すごいウェブサイト",
  "description": "すごいウェブサイトです。すごい情報を発信していきます。",
  "thumbnail": { "url": "https://...", "height": 630, "width": 1200 },
  "copyright": "Sugoi Web Saito ©2023. All Rights Reserved"
}

Next.js 上でメタデータとして埋め込む

基本情報にはメタデータとして埋め込む項目が多いですね。Next.js App Router では generateMetadata を使うことで head や title 等のメタデータを動的に生成できます。

generateMetadata の実装例です:

app/layout.tsx
import { createClient } from "microcms-js-sdk";

export async function generateMetadata(): Promise<Metadata> {
  const client = createClient({ /* ... */ });

  const { name, description, thumbnail } = await client.getObject({
    endpoint: "information",
  });

  return {
    // https://developer.mozilla.org/ja/docs/Web/HTML/Element/meta/name
    title: name,
    description,

    // https://ogp.me
    openGraph: {
      type: "website",
      title: name,
      description,
      images: {
        url: thumbnail.url,
        width: thumbnail.width,
        height: thumbnail.height,
      },
      siteName: name,
    },
  };
}

On-demand Revalidation を組み合わせることで、ヘッドレス CMS でデータが更新されたときに自動で再検証することもできます。microCMS での実装をチームメンバーが記事にしているので確認してみてください!

https://qiita.com/shunuke-y/items/5da0201b588771307e60

CMS 上で個別のページを管理できるようにする

利用規約やプライバシーポリシー等の個別のページもヘッドレス CMS 上で管理できるようにします。はてなブログや WordPress では同様の機能が固定ページという名前で存在していると思います。

ページ用の API を作成します。

今回は「このサイトについて(/about)」ページを例に説明します。
「タイトル」と「内容(リッチエディタで作成したコンテンツ)」があれば良いです。

上記画像のピンク枠のところに注目してください。コンテンツの ID を about としました。後でページのパスに使用します。

API で取るデータはこのようになります。

{
  "id": "about",
  "title": "すごいウェブサイトについて",
  "content": "<h2 id=\"h0d0b2ab722\">私たちについて</h2><p>「すごいウェブサイト」では...",
  "publishedAt": "2023-12-08T14:29:38.340Z",
  "revisedAt": "2023-12-08T14:29:38.340Z"
}

Next.js で表示する

ここからは通常のブログ記事の表示とさほど変わりませんが、Next.js App Router のDynamic Routesを使って動的ページを表示します。

編集頻度が少なく管理するページ数も少ないと思いますので、generateStaticParamsを使ってビルド時に生成するのが良いかと思います。

app/[pageId].tsx
// generateStaticParamsのリスト外のページにリクエストが来た時にページを生成しない
export const dynamicParams = false;

export async function generateStaticParams() {
  const client = createClient({ /* ... */ });

  const pageIds = await client.getAllContentIds({
    endpoint: "pages",
  });

  return pageIds.map((pageId) => ({ pageId }));
}

export async function generateMetadata() {
  // あまり関係ないので省略します
}

type CustomPageProps = {
  params: {
    pageId: string;
  };
};

export default async function CustomPage({ params }: CustomPageProps) {
  const { pageId } = params;

  const client = createClient({ /* ... */ });

  const { title, content } = await client.getListDetail({
    endpoint: "pages",
    contentId: pageId,
  });

  // スタイルは省略します
  return (
    <main>
      <h1>{title}</h1>
      <div dangerouslySetInnerHTML={{ __html: content }} />
    </main>
  );
}

これで /about にアクセスすると「すごいウェブサイトについて」ページが表示されるようになりました。
同じように利用規約やプライバシーポリシー等のページも表示できます。

今回は省略しますが、プレビュー機能を実装すれば公開前に確認できるので安心して編集できます。

さいごに

今回紹介した実装は 1-2 日あればできるものです。それが今後何年もの運用を楽にするのであればやっておいて損はないと思います。
またエンジニアが作業する時にも「開発環境の立ち上げ・コミット・PR レビュー・デプロイ」のような開発の手間が省けるのでおすすめです。
運用コストを下げて 楽をしましょう 時間を有効活用しましょう!

GitHubで編集を提案

Discussion