v0 + Claude Code + LINEミニアプリで美容室予約アプリを作ってみた

v0 + Claude Code + LINEミニアプリで美容室予約アプリを作ってみた

Clock Icon2025.07.07

はじめに

今回、各種生成AIツールとLINEエコシステムを活用して、美容室予約アプリを一から開発・デプロイしました。Claude Codeと協力して、Next.js 15、Convex、Vercel、LINEミニアプリを組み合わせたリアルタイム予約アプリを構築した体験をまとめます。

  • LINEミニアプリとして動作する予約システム
  • LINEユーザーIDの自動DB記録とCRM機能
  • スタッフ向け予約管理ダッシュボード
  • マイ予約ページでの予約履歴管理

開発プロセス

1. 要件定義・プロトタイプ作成

開発開始前に、ChatGPTとv0を活用してプロジェクトの方向性を固めました。

ChatGPTでの要件整理

美容室向けの予約システムを作りたいです。
とりあえず1店舗で使うシステムを作ろうと思います。
美容室の経営者目線で要望を整理して欲しいです。

まずはシンプルなプロンプトで要望を出してもらい、その後、ユーザー体験、スタッフ体験、業務要件、機能要件、非機能要件を出してもらいました。

v0でのモックプロトタイプ生成

ユーザー体験、スタッフ体験、業務要件、機能要件、非機能要件をv0に投入し、Next.js + Tailwind CSS + shadcn/uiのモックプロトタイプを生成しました。
DBやAPIはあえて作らないようにしています。v0はUIデザインやフロントエンドが得意だと感じているので、その部分に注力させています。
VercelへのCI/CDまで構築してくれるので、Claude Codeで構築した内容も簡単にデプロイできて開発体験がとても良いです。

2. Claude Codeとの連携

v0とClaude Codeの連携はGitHubを通して実現しています。v0はGitHubのプライベートリポジトリやブランチを新規作成してPushすることができるので、とりあえず何も考えずにモックプロトタイプを作ってOKです。

GitHubからローカル端末へのCloneはGitHub Desktopを利用しています。

その後、ターミナルでCloneしたフォルダを開いて、ClaudeコマンドでClaude Codeを起動しています。

この流れであれば、非エンジニアの私でも簡単に実現できました。

ちなみにClaude CodeはVSCodeでIDE連携させて使っています。

IDEにClaude Codeを追加する

3. Convex統合

データベースはConvexを採用しました。VercelやTypeScriptとの親和性が高いので、Claude Codeに雑に実装を依頼してもエラーになることが少ない印象です。

4. LINEミニアプリ化の実装

ここからが今回の最も苦労した部分です。v0は通常のWebアプリを構築するので、LINEミニアプリ化の実装が必要になります。
基本的にClaude Codeで実装していきましたが、LINEミニアプリの実装に関する情報が少ないのか、何度やってもエラーになりました。最終的には弊社のアセットプログラムから1部ソースコードを拝借してClaude Codeに与えました。

LIFF SDK の導入

pnpm add @line/liff

LIFFサービスクラスの実装

// lib/liff.ts
import liff from '@line/liff';

class LiffService {
  private initialized = false;

  async init(liffId: string): Promise<void> {
    if (this.initialized) return;

    try {
      await liff.init({ liffId });
      this.initialized = true;
    } catch (error) {
      console.error('LIFF initialization failed:', error);
      throw error;
    }
  }

  isLoggedIn(): boolean {
    if (typeof window === 'undefined') return false;
    return liff.isLoggedIn();
  }

  async getProfile(): Promise<LiffProfile> {
    if (!this.isLoggedIn()) {
      throw new Error('User is not logged in');
    }
    const profile = await liff.getProfile();
    return {
      userId: profile.userId,
      displayName: profile.displayName,
      pictureUrl: profile.pictureUrl,
    };
  }

  // その他のLIFF機能...
}

LINEミニアプリは未認証LINEミニアプリを作って、開発環境のLIFF IDを指定しています。今回、Vercelを利用しているので、VercelのEnvironment Variablesに変数を作ってLIFF IDを登録しています。この辺りもClaude Codeに言われるがままに設定していきましたが、未認証LINEミニアプリの作成手順については最近追加された機能というのもあり、ハルシネーションが多かったです。

また、Messaging APIやLIFFのサーバーAPIについてはLINE OpenAPIを利用するとエラーが発生しにくくなる印象です。今回はPushメッセージの実装時にOpenAPIを指定しましたが、すんなり実装することができました。

5. LINEユーザーIDのDB記録機能

LINEミニアプリの価値を享受するため、LINEユーザーIDをデータベースに自動記録する機能を実装しました。

自動ユーザー登録機能

// convex/mutations.ts
export const upsertLineUser = mutation({
  args: {
    lineUserId: v.string(),
    displayName: v.string(),
    pictureUrl: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const existingUser = await ctx.db
      .query("lineUsers")
      .withIndex("by_line_user_id", (q) => q.eq("lineUserId", args.lineUserId))
      .first();

    const now = new Date().toISOString();

    if (existingUser) {
      // 既存ユーザーの更新
      await ctx.db.patch(existingUser._id, {
        displayName: args.displayName,
        pictureUrl: args.pictureUrl,
        updatedAt: now,
      });
    } else {
      // 新規ユーザーの作成
      await ctx.db.insert("lineUsers", {
        lineUserId: args.lineUserId,
        displayName: args.displayName,
        pictureUrl: args.pictureUrl,
        createdAt: now,
        updatedAt: now,
      });
    }
  },
});

ログイン時の自動DB保存

// hooks/use-liff.ts
if (loggedIn) {
  const userProfile = await liffService.getProfile();
  setProfile(userProfile);

  // 🆕 LINEユーザー情報を自動的にDBに保存
  try {
    await upsertLineUser({
      lineUserId: userProfile.userId,
      displayName: userProfile.displayName,
      pictureUrl: userProfile.pictureUrl,
    });
    console.log('LINE user saved to database:', userProfile.userId);
  } catch (dbError) {
    console.error('Failed to save LINE user to database:', dbError);
  }
}

React統合

// app/ConvexClientProvider.tsx
"use client";
import { ConvexProvider, ConvexReactClient } from "convex/react";

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

export default function ConvexClientProvider({ children }) {
  return <ConvexProvider client={convex}>{children}</ConvexProvider>;
}

6. リアルタイム機能の実装

ConvexのuseQueryuseMutationフックを使用:

// コンポーネント例
const MenuSelectionPage = () => {
  const convexMenus = useQuery(api.queries.getMenus) ?? [];
  const menus = convexMenus.map(convertConvexMenuToLegacy);

  // UIレンダリング...
};

まとめ

AIツールチェーンの威力

今回の開発では、複数のAIツールを段階的に活用しました。

  1. ChatGPT: 要件定義・ビジネス課題の整理(30分程度)
  2. v0: UI/UXプロトタイプ・基盤コードの生成(30分程度)
  3. Claude Code: 本格的な機能実装・Convex統合・デプロイ(5時間程度)

Claude Codeの5時間程度は、ほぼほぼLINE系の実装がうまくいかなかったことによるトライアンドエラーの時間になります。ソースコードや公式ドキュメントコピペで一発解決したので、最初から指定していれば1〜2時間で実装完了できたと思います。

v0でのUIデザイン

UIデザインについてはv0のデザインモードで修正することを考えていましたが、Divタグが邪魔をしてなかなかうまくいかず。現状はFigmaと連携してデザイン変更し、Figma MCPでClaude Codeに実装してもらうのが良い体験でした。Figmaへの連携はhtml to designプラグインを利用するのが良いです。

Convexの素晴らしさ

Convexはとある勉強会で知りましたが、これまで利用していたDBと比べて圧倒的にエラーが少なくなったので本当にありがたい存在です。
以下のような利点があると思います。

  1. 開発速度: スキーマ変更から自動API生成で爆速開発
  2. リアルタイム: WebSocket不要でリアルタイム機能
  3. デプロイの簡単さ: 一つのコマンドでDB+関数デプロイ
  4. 型安全性: TypeScriptとの完璧な統合

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.