2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.js + microCMS + Vercel でポートフォリオサイトを作った記録

2
Posted at

はじめに

自分の作品や経歴をまとめるポートフォリオサイトを、Next.js・microCMS・Vercel で作って、独自ドメインで公開しました。

この記事では、サイトを作る中で使った技術や、どんな流れで進めたか、どこでつまずいたかを中心に振り返ってみます。

技術構成

使ったものはこんな感じです。

役割 使ったもの
フレームワーク Next.js(App Router)、React、TypeScript
コンテンツ管理 microCMS(microcms-js-sdk)
ホスティング Vercel(Hobby プラン)
ソース管理 GitHub
フォント Adobe Fonts、Google Fonts
素材制作 Illustrator
開発環境 VS Code
AI ツール Claude(相談・指示の設計)、Claude Code(実装)
ドメイン 独自ドメイン(.jp)

アニメーションは CSS・SVG・Canvas だけで作っていて、アニメーション用のライブラリは入れていません。

制作の進め方(AI との役割分担)

実装の多くは Claude Code にお願いしました。その分、自分は「何を作りたいか」と「それをどう伝えるか」を考えることに時間を使っています。

AIの使い方は以下の通りです。

  1. Claude(チャット) では、やりたいことを相談しながら、Claude Code に渡す指示を一緒に組み立てました
  2. Claude Code(VS Code) には、その指示を渡して実装してもらい、ビルドや表示の確認までやってもらいました
  3. Claude(ブラウザ操作) には、microCMS や Vercel の管理画面の設定を代わりに操作してもらいました。ログインや支払いのように自分でやるべき場面では、ちゃんと止まってもらうようにしています

指示を書くときに気をつけたこと

何度かやり取りする中で、指示の書き方で結果がかなり変わることに気づきました。意識していたのは次のようなことです。

  • 「既存のレイアウトは変えない」のように、触ってほしくないところをはっきり書く
  • いきなり実装させずに、先に変更する内容の一覧を出してもらう。ズレていたらそこで止められます
  • 動きや大きさは「0.3秒」「最大2000px」みたいに、数字で伝える
  • 色や時間、サイズは CSS 変数や定数にまとめてもらって、あとから自分でも調整しやすくする
  • API キーはチャットに貼らず、.env.local に自分で書き込む

実際に渡した指示の一部を載せておきます。

# 作業前に確認すること
- 現在の作品データの持ち方と、一覧・詳細ページの実装を確認する
- 実装方針を提示し、確認を取ってから実装する

# 守ること
- 既存のデザイン・レイアウトは変えない
- API キーはサーバー側でのみ使う(NEXT_PUBLIC_ は付けない)
- 閲覧のたびに API を呼ばない静的ページにする

レイアウトとアニメーション

レイアウトは画像ではなく CSS で組む

最初は「Illustrator で作った画面を画像で貼れば、崩れにくいんじゃないか」と思っていました。でも実際は逆で、画像だとスマホでそのまま縮小されてしまい、文字が読めなくなります。結局、文字とレイアウトは HTML/CSS で組んで、装飾だけを SVG にする形に落ち着きました。

一覧は grid で並べていて、画面の幅に合わせて列の数が自動で変わるようにしています。

.works {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
}

途中で配色をガラッと変えたことがあったのですが、色があちこちに直接書かれていて、直すのがかなり大変でした。それからは、色・余白・文字サイズを全部 CSS 変数にまとめるようにしています。

スクロールに合わせた演出

装飾は IntersectionObserver を使って、セクションが画面に入ったタイミングで表示しています。

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add("is-visible");
      observer.unobserve(entry.target); // 一度出したら監視をやめる
    }
  });
});

動きをつけるときに気をつけたのは、このあたりです。

  • 動かすのは transform と opacity だけにして、レイアウトの再計算が起きないようにしました
  • 画面を覆った色を少しずつ消して、下のページを見せる演出は、Canvas の globalCompositeOperation = "destination-out" で穴を空けていく方法で作りました
  • OS で「視差効果を減らす」を設定している人には、動きを止めるようにしています
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

GitHub + Vercel で公開

公開の手順はシンプルでした。

  1. VS Code のプロジェクトを GitHub に push する
  2. Vercel に GitHub アカウントでログインして、「Add New → Project」からリポジトリを選ぶ
  3. Application Preset が「Next.js」になっているのを確認して、Deploy を押す

あとは main ブランチに push するたびに、自動でデプロイされます。もしビルドに失敗しても、Vercel は前の正常な状態のまま公開を続けてくれるので、本番が壊れる心配はありません。これはかなり安心でした。

microCMS でコンテンツを管理

最初は作品のデータをコードの中のファイルで持っていたのですが、作品を追加するたびに push しないといけないのが面倒で、microCMS に移しました。今はスマホやパソコンから管理画面で「公開」を押すだけで、1分くらいで本番に反映されます。

microCMS にデータを取りに行くのは、ビルドのときだけです。見に来た人には作り置きのページを返すので表示が速く、microCMS の転送量もほとんど使いません。

microCMS 側でやったこと

  1. リスト形式の API を作りました(作品用の works と、経歴用の career)
  2. フィールドを作りました。作品なら slug・title・thumbnail(画像)・category(セレクト)・tools(複数選択)・description(リッチエディタ)・images(複数画像)などです
  3. 読み取り専用(GET だけ)の API キーを用意しました

Next.js 側でやったこと

接続情報は .env.local に書いて、.gitignore で GitHub に上がらないようにしています。Vercel にも同じ値を環境変数として登録しました。

.env.local
MICROCMS_SERVICE_DOMAIN=サービスID
MICROCMS_API_KEY=読み取り専用のAPIキー

microCMS とつなぐ部分は1つのファイルにまとめて、server-only を使ってブラウザ側からは読み込めないようにしました。環境変数が入っていないときは、ビルドがエラーで止まるようにしています。

lib/microcms.ts
import "server-only";
import { createClient } from "microcms-js-sdk";

if (!process.env.MICROCMS_SERVICE_DOMAIN || !process.env.MICROCMS_API_KEY) {
  throw new Error("microCMS の環境変数が設定されていません");
}

export const client = createClient({
  serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
  apiKey: process.env.MICROCMS_API_KEY,
});

作品の詳細ページ /work/[slug] は、generateStaticParams を使ってビルドのときに全部作っておきます。

app/work/[slug]/page.tsx
export const dynamicParams = false;

export async function generateStaticParams() {
  const works = await getWorks();
  return works.map((work) => ({ slug: work.slug }));
}

ちゃんと静的に作れているかは、npm run build の最後に出てくる記号で確認できます。

○  (Static)   作り置きのページ
●  (SSG)      データから作り置きしたページ
ƒ  (Dynamic)  閲覧のたびに作るページ

/work/[slug] が ● になっていれば、狙いどおりです。

画像

microCMS の画像は、URL にパラメータを付けるだけでサイズや形式を変えて配信できます。今は、一覧では幅 384px、詳細では 1080px の WebP で出しています。next/image で使うために、next.config.ts の remotePatterns に images.microcms-assets.io を追加しました。アップロードする前にも、WebP にして横幅 2000px 以内に収めるようにしています。

公開したら自動で再ビルドされるようにする

microCMS で公開しただけでは、そのままだとサイトに反映されません。そこで、公開をきっかけに Vercel がビルドし直すように設定しました。

  1. Vercel の Settings → Git → Deploy Hooks で、main ブランチ用の URL を作る
  2. microCMS の API 設定 → Webhook に、その URL を登録する
  3. 通知のタイミングは「公開・更新」「公開終了」「削除」にする

ひとつハマりかけたのが、Webhook は API ごとに設定が必要だということです。経歴用の API を後から追加したときに、そちらにも登録しないといけませんでした。それと、Deploy Hook の URL は知っている人なら誰でもビルドを動かせてしまうので、人に見せないように気をつけています。

独自ドメイン

ドメインの設定は、次の流れで進めました。

  1. ドメインを購入する
  2. Vercel の Settings → Domains でドメインを追加する
  3. Vercel に表示された DNS レコードを、購入先の DNS 設定に入力する
  4. www. 付きも追加して、www なしに転送されるようにする
  5. 環境変数 NEXT_PUBLIC_SITE_URL に新しいドメインを設定して、再デプロイする

学んだこと

作ってみて、特に大事だなと感じたことが3つあります。

ひとつ目は、見た目と中身を分けておくと、変更にすごく強くなるということです。デザインはコード、コンテンツは microCMS と分けていたおかげで、途中でデザインを大きく変えても、データには何の影響もありませんでした。

ふたつ目は、AI には「やってほしいこと」だけでなく「変えないもの」と「確認のタイミング」も伝えることです。触ってほしくない範囲と、実装する前に方針を見せてほしいことを書くようにしてから、やり直しがぐっと減りました。

みっつ目は、重さは最初から気にしておくことです。静的生成、WebP、transform と opacity だけのアニメーションを意識していたので、演出をたくさん入れても軽く動いてくれました。

おわりに

Next.js・microCMS・Vercel を組み合わせることで、デザインは自由に作り込みつつ、中身は管理画面から気軽に更新できる構成を、無料枠の範囲で運用できています。
費用はほぼドメイン代だけで済みますし、作品が増えるたびにスマホから追加できるので、就職活動やイベントの前にも慌てずに更新できます。何より、自分でサイトを作って公開するまでの経験そのものが、ひとつの作品になるので、同じような構成を考えている人、特に学生の方の参考になればうれしいです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?