【個人開発】Next.jsで犬タイプ診断アプリを作りました!
はじめに
12問の質問に答えることで、あなたの性格を10種類の犬タイプに分類するWebアプリケーションを作成しました🎉
↓実際のコードも公開しています
この記事では、Next.jsを学び始めた初学者が、犬タイプ診断アプリを一から作ってみた過程と、その中で気づいたことや学びを紹介します。
作成の経緯
自己紹介が遅れましたが、私は現在フロントエンドエンジニアへ転職を目指してNext.jsを勉強中です!
前職ではWebデザイナー(といってもデザインではなくコーディングがメイン)をしていたため、HTML/CSS/JSは業務でもバリバリ使っていましたが、Reactは実務経験があるものの基礎ができる程度で、Next.jsに関しては最近勉強を始めたばかりになります。
今回のアプリを作る以前に、本を読んだりUdemyの講座を受けるなどして、Next.jsの基本的な知識は身についてきたと感じたため、実際に手を動かして学ぶためにポートフォリオを兼ねて個人開発を始めてみたのが、今回のアプリ開発のきっかけです。
なぜ犬タイプ診断アプリか?については、色々ありますが、以下の点が理由です。
- Next.jsの基本技術を網羅できる: ルーティング、コンポーネント、状態管理など
- 構造がシンプル: 複雑になりすぎず、学習に集中できる
- データベース不要: 静的データで完結するため、インフラ構築の手間を省ける
- AI支援が効果的: 診断内容やUIデザインの生成にAIを活用できる
- 個人的な興味: 何より犬が好きで、楽しみながら開発できる
「AI支援が効果的」という点については、個人的に結構大事な決め手で、
そこまで時間をかけすぎずに技術をメインで学びながらポートフォリオを作成したい私にとっては、
具体的な内容をAIが決めてくれるというのは、開発時間の短縮になりました。
アプリの詳細
今回作成したアプリについて、もう少し詳しく内容を説明します。
診断の仕組み
質問は全部で12問。
各質問に4つの選択肢があり、各選択肢は複数の犬タイプに紐づけられています。
選択された回答に応じて各犬タイプのスコアを累積し、最終的に最も高いスコアの犬タイプを結果として表示しています。(同点の場合はランダムで選択)
実際のコードについてはこちらで詳しく説明しています。
ユーザー体験の工夫
-
自動スクロール機能
回答を選択すると、次の質問へ自動的にスクロールします -
バリデーション機能
未回答の質問がある場合はエラーメッセージを表示し、未回答の質問まで自動スクロールします
結果ページの詳細機能
-
相性診断
各犬タイプの結果ページでは、相性の良い他の犬タイプを表示し、クリックでその結果ページに遷移可能です
-
カルーセル機能
他の犬タイプの詳細を閲覧できるように、スライド形式で全タイプを表示しています
シェア機能
以下の3つの方法で診断結果をシェアすることができます。
- X(Twitter): カスタマイズされた投稿文で結果をシェア
- LINE: 専用のシェアボタンでLINEに結果を送信
- URLコピー: ワンクリックで結果ページのURLをクリップボードにコピー
結果画面は誰でもアクセスできるように、あえてURLはresult/[type]
の形式にしています。
結果に応じて共有するときの文言とOGP画像が動的に切り替わるようにして、シェアしたくなるようにこだわりました。
Xでシェアする時
技術スタック
エディター
- Cursor
フレームワーク
- Next.js 15.4.3
- React 19.1.0
- TypeScript 5
デザイン・スタイリング
- Tailwind CSS v4
- shadcn/ui
アイコン
- Lucide React
- Iconbuddy
デプロイ
- Vercel
開発の流れ
今回の開発は以下のような流れで行いました。
1. ChatGPTで診断のマッピングを作成
まず最初のステップとして、診断の核となる質問内容と結果マッピングをAIに作成してもらいました。
他の性格診断サイトを参考に、質問数は12問、結果は10タイプという構成に決定。
AIに依頼して、
- 12問の質問文とそれぞれの選択肢
- 診断結果となる10犬種の選定
- 各選択肢と犬種タイプの紐付け(スコア加算方式)
をまとめて出力してもらいました。
ただし、最初に生成された内容は、
- 診断結果が特定のタイプに偏る
- 質問内容が似通っていて冗長
といった問題がありました。
そこでAIと何度もやり取りを行い、質問の被りを減らしつつ内容に修正も加え、すべての犬タイプがバランスよく判定されるようマッピングを精査・調整しました。
AIにより生成した診断マッピング(一部抜粋)
2. WF(ワイヤーフレーム)の作成
おおよそ診断の内容が決まり、これで作れそう!という感じになったので、
次に、作成するページと各ページに出力する要素を決めてから簡単なWFを作成しました。
Figmaで作成したWF
3. 実装フェーズ
おそらく実務ではWFの後はデザインを作成し、実装に取り掛かるのはその後というケースが多いかと思いますが、今回はあくまでNext.jsを使ってアプリを作ってみるという学習の目的もあったので、ここではデザインを考える時間は取らずに、早速実装に移りました。すでにWFを作成して要件は固めているので、実装自体は可能でした。
今回エディターはCursorを使用していましたが、AIには実装のヒントをもらう程度にし、なるべく頼らずに実装をすることを心掛けました!
実装では以下の点に特に注意して取り組みました。
- 型安全性の確保: TypeScriptで質問データと結果データの型を定義し、ページ間のデータ受け渡しを安全に管理
- ロジックの設計: スコア計算ロジックをシンプルかつ拡張しやすい形で実装し、将来の機能追加に対応できるよう設計
- コンポーネント設計: 再利用可能なコンポーネントとして分割し、保守性を向上
- ユーザー体験の向上: 自動スクロールやバリデーション機能など、スムーズな診断体験を実現
この実装フェーズを通じて、Next.jsのApp RouterやTypeScriptの型システム、コンポーネント設計など、実践的なスキルを身につけることができました。
4. デザインの作成
デザインについては今回の本題ではなかったのですが、思ったより凝ってしまいました...。
こちらは次のセクションで詳しく説明しています。
5. デザインの適用
あとはデザインをコードに適用して完成です!
CSSについてはかなり得意な方なのですが、今回はTailwindを使用しており、Tailwindは実務経験がないのでまだまだ勉強中です。今回のアプリ制作でデザインを再現するためにTailwindでどうしたらスタイリングできるか、試行錯誤しながら作成できたので、大変学びになりました!
デザインについて
デザインはWFと同様に、Figmaで作成しました。
↓実際のFigmaファイルも公開しています。
まず、前提にはなりますが、前職はWebデザイナーであったとはいえコーディングが大好きだったので、担当の中心はコーディングで、純粋なデザイン制作の経験は多くありませんでした。
つまり、今回のアプリ制作ではデザイン作成がかなり大変でした...。
とはいえ、前職でデザイナーさんが作成した綺麗なFigmaを見ながらコーディングする日々でしたので、Figmaの基本操作やコンポーネント運用などは自然と身についていました。
今回は診断アプリという特性上、アクセスの多くがスマホからになると想定し、モバイルファーストで設計しています。
デザインの進め方は概ね次のとおりです。
- コンセプトの整理(ターゲット層・与えたい印象)
- ベースカラーの選定(今回はcolorhuntからイメージに合う配色を採用)
- フォントの選定
- 参考サイトを踏まえ、WFから高解像度のデザインへ落とし込み(同時にコンポーネントも作成)
- イラストの選定(フリー素材をリサーチ)
- ロゴ、結果画像の作成
今回はデザインよりもコンテンツ内容重視の診断アプリなので、グラフィカルなデザインにする必要がないとは思いつつも、ユーザーに診断体験を“楽しい”と感じてもらえるように、結果としてデザインにも十分な時間をかけました。
結果画像
特に力を入れたのが、結果画面に表示する各犬タイプのカード型画像です。
今回シェア機能を実装しているため、そのままOGP画像としても使えるように作成しました。
そこで大事になるのがイラストなのですが、私は絵を描くのは得意ではないので、フリー素材を探しました。
サイトの色味に合うもので、結果として扱いたい10種類の犬のイラストを探すのが結構大変でした;
結果ごとに違うテイストの犬のイラストを使用するのは、もちろんデザインの統一性がなく良くないので、全て同じイラストレーターさんが書いたものである必要があり、なかなか見つかりませんでした。
が、根気よく探し続けていると、イラストACでけちゃっぷさんの作品に出会い、しかも使いたい10犬種全て揃っていたんです!!✨
めちゃくちゃ可愛くてサイトのイメージにもぴったりなイラストで本当に救われました...。
早速イラストが決まったので画像の制作に入りました。診断系の参考サイトでは結果ごとに色分けされている例が多く、わかりやすいなと感じたので、今回作成する画像も各犬タイプごとに色分けをすることにしました。
10パターンの色を自力で考えるのは大変なので、ここはAIの出番です。
ChatGPTに作成するサイトのベースカラーを伝えて、その色味に合った色で、かつ各犬タイプの特徴に合う色を選定してもらいました。
出来上がった画像はこんな感じです↓
他の画像は結果画面から閲覧できるようにしてあるので、ぜひ診断して見てみてもらえると嬉しいです!
デザインについては想定より時間をかけて作成してしまいましたが、結果的に納得のいくものにできたので満足しております!
苦労した点・学び
診断ロジック
診断は、各選択肢に当てはまる犬タイプを予め紐づけておき、スコアを加算する方式で判定しています。
この仕組みを実施にコードに落とすのは自力では難しかったため、Cursorにお願いしました。
自分自身の復習も兼ねて、該当部分のコードについて簡単に説明します。
以下のように、質問データで各選択肢に該当する犬タイプを紐づけます。
犬種名は、golden-retrieverなど長い名前のものもあるので、コード上では犬タイプはgld
のようにそれぞれ短縮コードで管理しています。
{
id: 1,
question: "休日の過ごし方として一番近いのは?",
options: [
{
id: "q1-1",
value: "social",
label: "友達とワイワイ外で遊ぶ!",
types: ["dax", "bgl", "pap"],
},
{
id: "q1-2",
value: "solitude",
label: "一人で本や動画をゆっくり楽しむ",
types: ["sam", "pld", "shz"],
},
{
id: "q1-3",
value: "adventure",
label: "気になるカフェやスポットに行ってみる",
types: ["pap", "bgl"],
},
{
id: "q1-4",
value: "family_time",
label: "家族とゆったり過ごす時間が好き",
types: ["gld", "shz"],
},
],
},
実際にスコアの計算をするのは以下の関数です。
export function calculateDogType(answers: Record<string, string>): string {
const scores: Record<string, number> = {
dax: 0,
gld: 0,
shz: 0,
pld: 0,
shb: 0,
bgl: 0,
sam: 0,
crg: 0,
chw: 0,
pap: 0,
};
Object.entries(answers).forEach(([questionId, choiceValue]) => {
const question = questions.find((q) => q.id === parseInt(questionId));
if (question) {
const selectedOption = question.options.find(
(option) => option.value === choiceValue
);
if (selectedOption) {
selectedOption.types.forEach((type) => {
scores[type]++;
});
}
}
});
const maxScore = Math.max(...Object.values(scores));
const candidates = Object.entries(scores)
.filter(([_, score]) => score === maxScore)
.map(([type]) => type);
const result = candidates[Math.floor(Math.random() * candidates.length)];
return result;
}
処理の流れ
- スコア初期化: 10種類の犬タイプのスコアを0で初期化
-
回答処理: 各回答を順番に処理
- 質問IDから該当する質問を検索
- 選択された選択肢を特定
- その選択肢に紐づく犬タイプにスコアを+1
-
結果判定: 最高スコアの犬タイプを結果として返す
- 同点の場合はランダムで選択
今回のコードは生成AIに助けてもらいながら理解したもので、まだ自力でゼロから書くのは難しい部分もあります。ただ、仕組みを追って整理することで「どう動いているか」は把握できたと感じています!
商標問題
当初このアプリは、MBTIをもじって「INUBTI」(イヌ・ビー・ティー・アイ)という名前で公開しようと考えてました。
しかし調べたところ、「MBTI」は米国のThe Myers-Briggs Companyが保有する登録商標であると分かり、リスク回避のため使用を断念したという経緯があります。
AIに聞いたところ、「構造(16タイプや4軸など)を直接模倣していなければ著作権や特許には抵触しにくい」とのことでしたが、サイトを作って公開する以上、そこのリスクは避けるべきだと判断しました。
「INUBTI」という名前を思いついた時は、キャッチーで良い名前だと気に入っていたので使えないとなった時は残念でした;
また、商標問題に気づくのが遅かったのもあり、ロゴも既に作成してコーディングも「INUBTI」で進めていたので、完成間近で出戻りが発生してしまいました。
しかし、今回の経験で商標問題について気がつくことができたので、今後は個人開発であっても思いつきだけで無闇矢鱈に作ってしまわないように気をつけようという学びを得られました。
結果的に、現在の「犬タイプ診断」というシンプルで分かりやすい名前に落ち着き、今ではこちらの方がしっくりきています。
まとめ
今回のアプリ開発では、Next.jsのApp RouterやTailwind CSSといった最新の技術を実際に使いながら、一通りの実装フローを経験することができました。
自習した基礎の部分をしっかり生かしながらできたのが自分の成長としては大きく、AIに頼りすぎずコード設計から実装まで取り組めたことは、大きな自信につながりました。
また、自分の好きな「犬」をテーマに楽しみながら作成に取り組むことができ、オリジナルの診断コンテンツを作ることで、企画力やユーザー体験を考える力も自然に向上したように感じます。
今後ももっと技術力を身につけ、より複雑な機能も実装できるように、フロントエンドエンジニアとしてのスキルを高めていきたいです!
最後まで読んでいただき、ありがとうございました✨
Discussion