ChatGPTでJamstackなポートフォリオサイトを作成しました

はじめに

普段仕事ではLaravelを使ってバックエンドのエンジニアリングをしています。
フロントエンドは、jQueryのAjaxを改修する程度で、VueやReactなど最近のフロントエンドライブラリは使用したことがありません。
フロントエンドからバックエンド、AWSなどのインフラも自分ですべて使用できるようになり、自由自在にWebアプリケーションを作れたら楽しそうだなと思い、フロントエンドの勉強を始めました。
しかし、ハンズオン形式の書籍でReactの勉強を始めましたが、あまり楽しくありません。
そんな中、2023年2月頃からChatGPTやBing AIなどのAIチャットサービスが話題になりました。
ChatGPTを使ったら楽にWebサイトを作りながら、ReactやNext.jsの学習ができるのではないかと思い、ポートフォリオサイトの作成を開始しました。

デザイン

ポートフォリオサイトの見た目については、Figmaなどのデザインツールを使用せず、色々なポートフォリオサイトやTech Blogの見た目を参考にして考えました。
サイドメニューや全体レイアウトはJackさんのポートフォリオサイトを参考にしました。
ブログ記事一覧ページはYahoo! JAPAN Tech Blogを参考にしました。
他にも様々なポートフォリオサイトを参考にして実装しながらデザインを固めていきました。
Figmaなどのデザインツールも使用できれば良かったのですが、とりあえずChatGPTを使いながら実装したい!という思いから使用しませんでした。
次にWebアプリケーションを作成する際は、Figmaを使用して、Reactのコンポーネント設計に活かしたいです。

設計

Next.js 13を使用したSSGのJamstackアーキテクチャにしています。
バックエンドAPIはmicroCMSを使用しています。
Next.jsのディレクトリ構成については、Next.jsのディレクトリ構成を考えてみたを参考にさせていただきました。
最近のフロントエンドは、バックエンドと同じような感じでクリーンなアーキテクチャにしているんだな~と思いました。
今後はこのポートフォリオサイトか別のWebアプリケーションでTypeScriptやtRPCを使用して、型安全な設計を試していきたいです。

実装

ChatGPTに質問したことを一部抜粋して紹介します。

Q. 下記のコンポーネントを改良したいです。
/aboutのページを開いているときには、Aboutのリンクの背景色が有効になるようにしたいです。
他のページも同様です。
// components/NavItem.js
import Link from 'next/link';

const NavItem = ({ href, title }) => {
  return (
    <>
      <hr className="my-6 border-gray-200 dark:border-gray-600" />
      <Link href={href}>
        <a className="mt-5 flex transform items-center justify-center rounded-md px-4 py-2 text-gray-600 
transition-colors duration-300 hover:bg-gray-100 hover:text-gray-700
dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200">
          <span className="font-medium">{title}</span>
        </a>
      </Link>
    </>
  );
};

export default NavItem;
A. 下記のコードでは、NavItemコンポーネントが現在のページと一致する場合に背景色を変更するようになっています。useRouterフックを使用して現在のページのパスを取得し、hrefと一致するかどうかをチェックしています。
// components/NavItem.js
import Link from 'next/link';
import { useRouter } from 'next/router';

const NavItem = ({ href, title }) => {
  const router = useRouter();
  const isActive = router.pathname === href;

  return (
    <>
...

また、ChatGPTにリファクタリングをしてもらいました。

Q. 下記のコードの繰り返し部分をコンポーネントにして簡潔に書き直せますか?
<nav className="items-center">
            <hr className="my-6 border-gray-200 dark:border-gray-600" />
            <Link
              href="/"
              className="mt-5 flex transform items-center justify-center rounded-md px-4 py-2 text-gray-600 transition-colors duration-300
 hover:bg-gray-100 hover:text-gray-700
 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
            >
              <span className="font-medium">Home</span>
            </Link>

            <hr className="my-6 border-gray-200 dark:border-gray-600" />
            <Link
              href="/about"
              className="mt-5 flex transform items-center justify-center rounded-md px-4 py-2 text-gray-600 transition-colors duration-300
 hover:bg-gray-100 hover:text-gray-700
 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
            >
              <span className="font-medium">About</span>
            </Link>

            <hr className="my-6 border-gray-200 dark:border-gray-600" />

            <Link
              href="/skills"
              className="mt-5 flex transform items-center justify-center rounded-md px-4 py-2 text-gray-600 transition-colors duration-300
 hover:bg-gray-100 hover:text-gray-700
 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
            >
              <span className="font-medium">Skills</span>
            </Link>

            <hr className="my-6 border-gray-200 dark:border-gray-600" />

            <Link
              href="/work"
              className="mt-5 flex transform items-center justify-center rounded-md px-4 py-2 text-gray-600 transition-colors duration-300
 hover:bg-gray-100 hover:text-gray-700
 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
            >
              <span className="font-medium">Work</span>
            </Link>

            <hr className="my-6 border-gray-200 dark:border-gray-600" />

            <Link
              href="/contact"
              className="mt-5 flex transform items-center justify-center rounded-md px-4 py-2 text-gray-600 transition-colors duration-300
 hover:bg-gray-100 hover:text-gray-700
 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
            >
              <span className="font-medium">Contact</span>
            </Link>

            <hr className="my-6 border-gray-200 dark:border-gray-600" />

            <Link
              href="/blog"
              className="mt-5 flex transform items-center justify-center rounded-md px-4 py-2 text-gray-600 transition-colors duration-300
 hover:bg-gray-100 hover:text-gray-700
 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
            >
              <span className="font-medium">Blog</span>
            </Link>

            <hr className="my-6 border-gray-200 dark:border-gray-600" />
          </nav>
A. 繰り返し部分をコンポーネント化し、簡潔に書き直すことができます。まず、NavItemというコンポーネントを作成します。
// components/NavItem.js
import Link from 'next/link';

const NavItem = ({ href, title }) => {
  return (
    <>
      <hr className="my-6 border-gray-200 dark:border-gray-600" />
      <Link href={href}>
        <a className="mt-5 flex transform items-center justify-center rounded-md px-4 py-2 text-gray-600 transition-colors duration-300
 hover:bg-gray-100 hover:text-gray-700
 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200">
          <span className="font-medium">{title}</span>
        </a>
      </Link>
    </>
  );
};

export default NavItem;
次に、NavItemコンポーネントをnavタグ内で使用し、繰り返し部分を削除します。
// 例: components/Sidebar.js
import NavItem from './NavItem';

function Sidebar() {
  return (
    <aside className="...">
      <nav className="items-center">
        <NavItem href="/" title="Home" />
        <NavItem href="/about" title="About" />
        <NavItem href="/skills" title="Skills" />
        <NavItem href="/work" title="Work" />
        <NavItem href="/contact" title="Contact" />
        <NavItem href="/blog" title="Blog" />
        <hr className="my-6 border-gray-200 dark:border-gray-600" />
      </nav>
    </aside>
  );
}

export default Sidebar;


ChatGPTを使った学習

ペアプログラミングをしてもらっているような感覚で質問ができました。

Q. routerオブジェクトにはpathnameの他にどのようなプロパティがありますか?
よく使用されるプロパティを教えてください


A.
routerオブジェクトは、Next.jsのルーティングシステムで使用される多くのプロパティを持っています。以下は、よく使用されるプロパティの一部です
router.query: 現在のページのクエリパラメータを含むオブジェクトです。例えば、URLが/posts?page=2の場合、router.query{ page: '2' }を返します。
router.asPath: 現在のページのパスとクエリパラメータを含む文字列です。例えば、/posts?page=2の場合、router.asPath/posts?page=2を返します。
router.route: 現在のページのルートパスを返します。これは、動的ルートパラメータを含むことがあります。例えば、/posts/[id]の場合、router.route/posts/[id]を返します。
router.push(url, as, options): ページ遷移を実行する関数です。urlは遷移先のページのパス、asは表示用のURL(オプション)、optionsは遷移オプション(オプション)です。この関数は、ブラウザの履歴スタックに新しいエントリを追加します。
router.replace(url, as, options): router.push()と同様にページ遷移を実行しますが、ブラウザの履歴スタックに新しいエントリを追加せず、現在のエントリを置き換えます。
router.prefetch(url): 指定されたURLのページリソースを事前にフェッチします。これにより、ページ遷移が高速化されます。
router.back(): ブラウザの履歴スタックを1つ戻ります。これはブラウザの戻るボタンと同様の動作をします。
これらのプロパティとメソッドは、ページ遷移やURLの操作を簡単に行うためにNext.jsで提供されています。また、これらはクライアントサイドでのみ利用可能です。

ホスティング

SSGのホスティングサービスは、下記のように様々あります。

  • AWS Amplify Hosting
  • Netlify
  • Vercel
  • Cloudflare Pages
  • Github Pages

AWSでインフラの勉強もしていきたいこともあり、今回はAWS Amplify Hostingを使用してホスティングを行いました。
ホスティングの方法についてもChatGPTに教えてもらいました。

ChatGPTでエンジニアリングする注意点

生成されたコードを何も考えずにコピペするとエラーや表示崩れが発生した際に困る

これはChatGPTに限らず、ググってでてきたサイトからコードをコピペするときも同じでしょう。
ただし、ChatGPTはコピペしたコードを直さなくても良いくらい出来が良いコードを生成してくれることもあるので、より注意が必要かなと思いました。
生成されたコードを完成物ではなく、提案と捉えて、しっかり読んでから使用することが必要でしょう。

学習データが最新ではないので、新しいバージョンの開発ツールを使用している際には注意が必要

ChatGPTのGPT-4モデルは、2021年の9月までの情報を元に学習されています。
今回、Next.jsのバージョンは13を使用していたので、ChatGPTで出力されたImageコンポーネントのコードはバージョン13では非推奨でした。
最新バージョンのライブラリやフレームワークを使用している際は、最新の書き方が出力されないと思った方が良いでしょう。

出力された情報が正しいとは限らない

学習データが最新ではないだけでなく、学習元のデータが正しいとも限らないので、出力された情報を鵜呑みにしないことが必要でしょう。
情報の裏取りや検証やレビューは、行う必要があるでしょう。

今後実装したい機能

ブログとしての機能が最低限なので、下記の機能を追加していきたいと考えています。

  • 記事へのコメント投稿
  • Zennのような目次メニュー
  • カテゴリ別記事一覧
  • Twitterへの共有ボタンとモーダルウィンドウ
  • 記事を読むのに妨げにならない範囲での広告やアフィリエイト

使用したツール

  • ChatGPT-plus(GPT-4モデル)
  • Tailwind CSS
  • Next.js13.2.4
  • Prettier
  • ESLint
  • microCMS
  • AWS Amplify Hosting