
給与確認アプリのリデザイン
実際のユーザーインタビューとフィードバックをもとに、給与確認体験を調査・再設計しました。
フードブログをVanilla JavaScriptからReact・Next.jsへ再構築しながら、フロントエンドの基礎を学びました。
TASTEBUDSは、クラスメートと取り組んだソーシャルメディアマーケティングの課題として始まりました。バンクーバーのレストランやカフェを紹介するフードブログです。
制作当初はReactを学ぶ前だったため、HTML、CSS(Sass)、Vanilla JavaScriptでサイトを構築しました。開発を進める中で、より効率的な制作方法を求めてReactとNext.jsを学びました。FigmaでUIも再設計し、プロジェクトを一から作り直しました。
MVPでは、Webの基礎技術を使ってプロジェクトを構築しました。静的HTMLでブログカードを一つずつ作る中で、「もっと効率よく複製できないか」と考えるようになりました。そこで、ブラウザ標準機能を使ってコンテンツを動的に生成する、再利用可能な仕組みを試しました。重複を減らし、拡張しやすい構成にできました。
Vanilla JavaScriptと<template>要素を使い、再利用可能なカードコンポーネントを作成しました。
export const posts = [
{
img: "https://picsum.photos/200/300",
imgAlt: "City night view",
title: "Exploring Vancouver at Night",
excerpt: "A short walk through downtown Vancouver after sunset. Neon lights and quiet streets.",
location: "Vancouver, BC",
date: "2026-02-01",
url: "https://example.com/article/vancouver-night"
},
{
img: "https://picsum.photos/seed/picsum/200/300",
imgAlt: "Mountain landscape",
title: "Weekend Hiking Escape",
excerpt: "A refreshing hike just outside the city. Perfect for a quick weekend reset.",
location: "North Vancouver, BC",
date: "2026-01-28",
url: "https://example.com/article/hiking-escape"
},
{
img: "https://picsum.photos/200/300?grayscale",
imgAlt: "Cafe interior",
title: "Hidden Cafés You Should Know",
excerpt: "Three small cafés with great coffee and calm vibes for focused work.",
location: "Burnaby, BC",
date: "2026-01-20",
url: "https://example.com/article/hidden-cafes"
}
];
}, <template id="tpl-card">
<article class="card">
<figure>
<img class="tpl-img" src="" alt="">
</figure>
<div class="text-container">
<h3 class="tpl-title"></h3>
<p class="tpl-excerpt"></p>
</div>
<footer>
<div>
<p class="tpl-location"></p>
<p class="tpl-date"></p>
</div>
<div>
<a class="tpl-url" href="">Read</a>
</div>
</footer>
</article>
</template>import {posts} from "/data.js";
posts.forEach(post => {
const tplCard = document.getElementById("tpl-card");
const content = tplCard.content.cloneNode(true);
content.querySelector(".tpl-img").src = post.img;
content.querySelector(".tpl-img").alt = post.imgAlt;
content.querySelector(".tpl-title").textContent = post.title;
content.querySelector(".tpl-excerpt").textContent = post.excerpt;
content.querySelector(".tpl-location").textContent = post.location;
content.querySelector(".tpl-date").textContent = post.date;
content.querySelector(".tpl-url").href = post.url;
document.body.appendChild(content)
});MVPを通して、DOM操作、再利用可能なUI設計、構造・データ・スタイルを分離する基礎を学びました。
一方で、状態管理が複雑になり、ルーティングの保守も難しく、拡張しにくい構成になりました。
この課題をきっかけに、Next.jsでプロジェクトを再構築しました。

プロジェクトの拡張性を高めるため、ReactとNext.jsを使ってアプリケーションを再設計しました。UIをReactコンポーネントとして再構築し、データから宣言的に画面を生成できるようにしました。さらにNext.jsでページ構成を整理し、投稿ごとのルーティングを実装することで、保守・拡張しやすいアーキテクチャに改善しました。
MVPでは、ブログコンテンツをフロントエンドのデータ配列として保存していました。初期の試作では機能しましたが、新しい投稿を追加するたびに手作業で更新する必要があり、規模が大きくなるにつれて管理効率が下がりました。
そこで投稿データの扱いを見直しました。より拡張しやすい構成にするため、コンテンツをアプリケーションコードから分離し、投稿管理をSupabaseへ移行しました。
これにより保守しやすいワークフローになり、将来的にCMSのような仕組みへ発展できる基盤を作れました。
export type PostCard = {
id: string;
slug: string;
title: string;
excerpt: string | null;
published_at: string | null;
cover_image_path: string | null;
};export async function getAllPosts(): Promise<PostCard[]> {
const { data, error } = await supabase
.from("posts")
.select("id, slug, title, excerpt, published_at, cover_image_path")
.order("published_at", { ascending: false });
if (error) {
console.error("Failed to fetch posts", error);
return [];
}
return data ?? [];
}構造化したデータをもとに、UIコンポーネントからブログカードと記事ページを動的に生成できます。
UIを再利用可能なReactコンポーネントで再構築しました。投稿データをカードコンポーネントへ渡して記事カードを生成し、コンテンツが増えても拡張しやすい構成にしました。

最新記事を目立たせるため、トップページではデータから特定の投稿を選び、編集されたレイアウトで表示します。
{posts[0] && <CardL posts={posts[0]} />}
{posts[1] && <CardM posts={posts[1]} />}
{posts[2] && <CardM posts={posts[2]} />}
{posts[3] && <CardM posts={posts[3]} />}
ブログページでは、絞り込んだ投稿データをカードコンポーネントへ割り当て、記事一覧を動的に生成します。
{filteredPosts.map((post) => (
<CardM key={post.id} posts={post} />
))}
以前の課題:MVPでは記事ごとに個別ページを管理していたため、コンテンツが増えるほど拡張が難しい構成でした。
解決方法:Next.jsの動的ルーティングを/blog/[slug]の構成で実装し、1つの共通テンプレートからページを生成できるようにしました。
// Static pages (MVP) // Dynamic routing
pages/ app/
└── posts/ └── blog/
├── post-1/ └── [slug]/
│ └── index.html └── page.tsx
├── post-2/
│ └── index.html
├── post-3/
│ └── index.html
.
.
. 結果:slugに応じてコンテンツを取得することで、新しいページを個別に作らず記事を追加でき、拡張性と保守性が向上しました。
このプロジェクトでは、JavaScriptの基礎から始め、より良い実装方法を継続的に探りました。調査、実験、改善を繰り返す中で理解が深まり、試行錯誤する過程そのものに面白さと手応えを感じました。
AIを使った管理画面やCMS機能の構築も試しました。しかし、機能を実装できることと、その土台となる設計を理解することは同じではないと気づきました。CMSの一部では複数の機能が1つのコンポーネントに集まり、保守・拡張しにくい構成になりました。この経験から、コンポーネント設計と関心の分離の重要性を学びました。

今後はAIを目的に応じて活用しながらアーキテクチャへの理解を深め、より拡張しやすく保守性の高いシステムを作ることを目指します。