自社コーポレートサイトにNext.js+HeadlessCMSを導入してみた

こんにちは。カンカクでフロントエンドを担当しているKeita(@KeitaBangkok)です。

最近、自社のコーポレートサイトにNext.js(TypeScript)とHeadlessCMSを導入しました。

社内的に意義のあるアウトプットになりつつ、比較的モダンな技術を扱う良い機会となったので、簡潔に概要をまとめていこうと思います。

導入の経緯

社内の採用強化を目的としたプロジェクトの一つとして、コーポレートサイトのコンテンツ拡充の話が挙がりました。

元々静的構築なサイトでしたが、これにNEWSセクションとCAREERセクション(採用職種とリンクを掲載)を追加し、諸情報の露出を増やしていく狙いです。

このため元々はwebpack構築だったサイトに、エンジニア以外のコーポレート担当の方でも容易に更新できる仕組みとしてHeadlessCMSを導入、そして今後も考慮し思い切ってこのタイミングでReact(Next.js)にリプレイスしよう、という流れでした。

サイト外観の変化以上にリポジトリ内のディレクトリ構造がガラッと変わる改修タスクになりました。

ちなみに、弊社では自社運営カフェのブランディングサイトで既にNext+HeadlessCMSを導入済みでした。

HeadlessCMSの選定

HeadlessCMSにはContentfulを採用しています。

最終候補としてContentfulとmicroCMSが挙がり、無料枠の多さに分があるContentfulに決定しました。microCMSと比較するとContentfulは日本語非対応になっていますが、それぞれのコンテントモデル(CMS化する項目)のタイトル等はこちらで設定するため、コーポレート担当の方が触る部分は実質日本語にすることができ、操作に抵抗感のない管理画面になっているように思います。

f:id:kankak_inc:20220329182546p:plain
contentful操作画面

実際に担当の方に管理画面の操作方法を共有しましたが、特に滞りなく理解してもらえた印象です。

実装概要

簡潔に開発の概要をまとめていきます。
実装にあたりNext+TypeScriptな環境を構築しました。

ディレクトリ構造

tsxファイルを格納しているディレクトリ構造は以下の通りです。

⁝
├─ src
    ├─ pages
    ├─ section
    ├─ components
    └─ ...

この内、CMS関連のファイルが格納されているのはsrc/pagessrc/sectionです。

create next appで環境構築するとルーティング用のpagesフォルダがルートに配置されますが、個人的にはこの上で別途componentsフォルダ等を設けると雑多な構造になる印象があります。そのため、Next.jsがデフォルトでサポートしているsrc/pagesの構造を採択しました。

Contentful用の型定義ファイルやライブラリファイルもsrc配下にまとめ、ルートからの枝分かれを最小限にした構成としています。

CMS導入部分

以下はページ表示の大元となっているsrc/pages/index.tsx内メインコンテンツ部分の抜粋です。

<main>
  <Hero />
  <News content={news} />
  <Career content={career} />
  <Vision />
  <Brands />
  <About />
  <Contact />
</main>

実際のサイトを見ながらだとわかりやすいと思いますが、セクション毎にコンポーネント化しています。NewsとCareerタグでは下位コンポーネントにCMSのデータ受け渡しを行なっています。

例として、データを受け取ったNewsセクションsrc/section/News.tsxの中身は次の通りです。

import { EntryCollection } from 'contentful';
import { ICorpNewsFields } from '../@types/generated/contentful'; // Contentful用型定義ファイル

// Contentfulデータの型定義
interface NewsProps {
  content: EntryCollection<ICorpNewsFields>;
}

const News = (props: NewsProps) => { // 受け取ったContentfulデータの型指定
  return (
    <section className="news" id="news">
      <div className="section__inner">
        <div className="sectionTitle__border">
          <div className="sectionTitle__wrap">
            <h2 className="sectionTitle">NEWS</h2>
            <span className="sectionTitle__sub">プレスリリース</span>
          </div>
        </div>
        <ul className="newsList">

          {props.content &&
            props.content.items.map((value, i) => (
              // Contentfulデータ(Newsタイトル,URL,日付)をループ出力
              <li key={i}>
                <a href={value.fields.url} target="_blank" rel="noopener noreferrer">
                  <div className="newsList__text">{value.fields.title}</div>
                  <span>{value.fields.date}</span>
                </a>
              </li>
            ))}
        </ul>
        <div className="newsMore">
          <a href="https://prtimes.jp/main/html/searchrlp/company_id/47640" target="_blank" rel="noopener noreferrer">
            Learn more &gt;
          </a>
        </div>
      </div>
    </section>
  );
};

export default News;

value.fields.xxxにそれぞれContentfulで設定した値が入っています。Contentfulのライブラリを用い生成したICorpNewsFieldsにてデータの型安全を担保しました。

これを画面表示した結果が以下キャプチャです。

f:id:kankak_inc:20220329212310p:plain
CMS化したNewsセクション

導入後の所感

デザインにこだわった既存サイトにCMS機能を持たせたいニーズは結構あると思いますが、その際にはやはりHeadlessCMS導入が有効ですね。簡単なテキスト修正レベルのタスクのために都度コードをいじりpushを繰り返すことは地味に負荷がありますが、これを解消できるメリットも大きいです。

また、採用強化を主目的としたタスクでしたが、弊社で利用実績のなかったNext.jsやHeadlessCMSを技術環境に追加できたこと自体も、エンジニアの方のアトラクトにプラスに働くのではないかと思ったりもしています。

最後に

以上、最近のフロントエンドエンジニアとしてのアウトプットを語ってみました。

カンカクでは現在、全方位的にエンジニアを募集しています。
興味をお持ちいただけたら、まずはカジュアルにお話してみませんか?以下リンクから気軽にご応募ください!