本記事では、Next.js 14 の App Router と Google Gemini API(gemini-2.0-flash-exp)を使用して、本格的な AI 画像生成機能をダッシュボードに実装する方法を詳しく解説します。Server Actions、クレジット管理システム、Cloudflare R2 との統合など、実践的な実装方法をコード例と共に紹介します。
🚀 デモサイト:
📦 ソースコード: https://github.com/yourrepo/dashboard
📋 目次
- 概要とアーキテクチャ
- 環境構築とセットアップ
- Server Actions の実装
- フロントエンド実装
- クレジット管理システム
- 画像保存と Cloudflare R2 統合
- エラーハンドリングとセキュリティ
- パフォーマンス最適化
- デプロイと本番環境対応
- まとめとベストプラクティス
🏗️ アーキテクチャ概要
技術スタック
- フレームワーク: Next.js 14 (App Router)
- AI API: Google Gemini API (gemini-2.0-flash-exp)
- データベース: Supabase (PostgreSQL)
- 画像ストレージ: Cloudflare R2
- 認証: Supabase Auth
- スタイリング: Tailwind CSS + shadcn/ui
- 状態管理: Server Actions + React Hooks
システム構成図
[ユーザー] → [Next.js App] → [Server Actions]
↓ ↓
[Supabase Auth] [Gemini API]
↓ ↓
[PostgreSQL] [画像生成]
↓ ↓
[クレジット管理] [Cloudflare R2]
🛠️ 環境構築
1. 必要なパッケージのインストール
npm install @google/generative-ai
npm install @supabase/supabase-js @supabase/ssr
npm install @aws-sdk/client-s3
npm install sonner lucide-react
npm install framer-motion
2. 環境変数の設定
.env.local
ファイルに以下の環境変数を設定します:
# Gemini API
GEMINI_API_KEY=your-gemini-api-key
# Supabase
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
# Cloudflare R2
R2_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
R2_ACCESS_KEY_ID=your-r2-access-key
R2_SECRET_ACCESS_KEY=your-r2-secret-key
R2_BUCKET_NAME=your-bucket-name
R2_PUBLIC_URL=https://your-r2-public-domain.com
💻 実装詳細
Server Action 実装(画像生成ロジック)
以下は、Gemini API を使用した画像生成の Server Action 実装です:
📝 Server Action 実装例
1. Gemini API を使用した画像生成ロジック
app/actions/generate-image-gemini.ts での Server Action 実装:
"use server";
import { GoogleGenerativeAI } from "@google/generative-ai";
import { getCurrentUserWithProfile, consumeUserCredits } from "./user";
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || "");
export async function generateImageWithGemini(
prompt: string,
options: { style?: string; aspectRatio?: string; quality?: number }
): Promise<GenerateImageResult> {
// ユーザー認証const userData = await getCurrentUserWithProfile();
if (!userData) return { success: false, error: "ログイン必須" };
// クレジット確認const requiredCredits = 5;
if (userData.credits.total_credits < requiredCredits) {
return { success: false, error: "クレジット不足" };
}
// プロンプト強化let finalPrompt = enhancePrompt(prompt, options);
// Geminiモデルで生成const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash-exp" });
const images = await generateMultipleImages(model, finalPrompt, 4);
// クレジット消費await consumeUserCredits(userData.user.id, requiredCredits);
return {
success: true,
images,
creditsRemaining: creditResult.balance_after,
};
}
2. プロンプト強化ロジック
スタイルごとのプロンプト最適化:
const styleEnhancements: Record<string, string> = {
realistic: "、写実的、高解像度、詳細、プロ写真",
anime: "、アニメスタイル、日本アニメ、鮮やかな色彩",
illustration: "、デジタルアート、イラスト、芸術的",
watercolor: "、水彩画、柔らかい色合い",
"3d-render": "、3Dレンダリング、CGI",
};
if (options.style) {
finalPrompt += styleEnhancements[options.style];
}
finalPrompt += "、傑作、最高品質";
🎭 フロントエンド UI 実装
React コンポーネントの設計ポイント
- リアルタイムフィードバック: 生成状態を即座に表示
- アニメーション: Framer Motion で滑らかな UX
- エラーハンドリング: Sonner でトースト通知
- レスポンシブデザイン: モバイル対応
💾 Cloudflare R2 統合
画像ストレージの最適化
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const r2Client = new S3Client({
region: "auto",
endpoint: process.env.R2_ENDPOINT,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
});
export async function uploadImageToR2(
imageData: Buffer,
fileName: string
): Promise<string> {
const key = `ai-images/${Date.now()}-${fileName}`;
await r2Client.send(
new PutObjectCommand({
Bucket: process.env.R2_BUCKET_NAME,
Key: key,
Body: imageData,
ContentType: "image/png",
CacheControl: "public, max-age=31536000",
})
);
return `${process.env.R2_PUBLIC_URL}/${key}`;
}
🛡️ セキュリティ対策
1. レート制限の実装
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "1 h"),
});
export async function checkRateLimit(userId: string) {
const { success, limit, reset, remaining } = await ratelimit.limit(userId);
if (!success) {
throw new Error(`レート制限: ${new Date(reset).toLocaleString()}に再試行`);
}
return { limit, remaining };
}
2. 入力検証
import { z } from "zod";
const generateImageSchema = z.object({
prompt: z.string().min(1).max(1000),
style: z.enum(["realistic", "anime", "illustration"]),
aspectRatio: z.enum(["1:1", "16:9", "9:16", "4:3"]),
quality: z.number().min(50).max(100),
numberOfImages: z.number().min(1).max(4),
});
export async function validateInput(input: unknown) {
return generateImageSchema.parse(input);
}
📊 パフォーマンス最適化 Tips
- 並列処理: Promise.all で複数画像を同時生成
- キャッシュ戦略: CDN で画像配信を高速化
- 最適化: WebP 形式への自動変換
- ストリーミング: 大容量データの効率的転送
🎯 ベストプラクティス
プロンプトエンジニアリング
- 具体的な説明: 詳細な描写で精度向上
- スタイル指定: 明確なアートスタイル
- ネガティブプロンプト: 避けたい要素を明示
ユーザー体験の向上
- プログレッシブ表示: 順次生成結果を表示
- プリセット機能: 人気スタイルを保存
- 履歴機能: 過去の結果を再利用
🚢 デプロイメント
Vercel へのデプロイ手順
# 環境変数設定
vercel env add GEMINI_API_KEY
vercel env add NEXT_PUBLIC_SUPABASE_URL
vercel env add R2_ENDPOINT
# デプロイ実行
vercel --prod
🔮 今後の拡張機能
- 他 AI API 統合: DALL-E 3, Stable Diffusion
- 高度な編集: 画像修正、スタイル転送
- ビジネス機能: API 提供、チーム共有
- AI 拡張: プロンプト自動生成
📚 参考リソース
まとめ
本記事では、Next.js 14 と Google Gemini API を使用した AI 画像生成機能の実装方法を解説しました。Server Actions、クレジット管理、Cloudflare R2 統合など、本番環境で即活用できる実践的な実装を紹介しました。
この実装をベースに、さらに高度な機能を追加して、より強力な画像生成サービスを構築できます。
🏷️ #Next.js #Gemini #AI #画像生成 #Supabase #CloudflareR2
💻 Server Action 実装コード例
Gemini API を使用した画像生成のメイン処理部分:
// app/actions/generate-image-gemini.ts"use server";
import { GoogleGenerativeAI } from "@google/generative-ai";
import { getCurrentUserWithProfile, consumeUserCredits } from "./user";
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || "");
export async function generateImageWithGemini(
prompt: string,
options: {
style?: string;
aspectRatio?: string;
quality?: number;
numberOfImages?: number;
} = {}
): Promise<GenerateImageResult> {
// ユーザー認証とクレジット確認const userData = await getCurrentUserWithProfile();
if (!userData) {
return { success: false, error: "ログインが必要です" };
}
const requiredCredits = 5;
if (userData.credits.total_credits < requiredCredits) {
return { success: false, error: "クレジット不足" };
}
// プロンプト強化let finalPrompt = enhancePrompt(prompt, options);
// Geminiモデルで生成const model = genAI.getGenerativeModel({
model: "gemini-2.0-flash-exp",
});
// 複数画像を並列生成const images = await generateMultipleImages(
model,
finalPrompt,
options.numberOfImages || 4
);
// クレジット消費await consumeUserCredits(
userData.user.id,
requiredCredits,
`画像生成: ${prompt.substring(0, 50)}...`
);
return {
success: true,
images,
creditsRemaining: userData.credits.total_credits - requiredCredits,
};
}
🎨 画像スタイル定義
利用可能な画像スタイル一覧:
- 📷 リアル - 写実的で高解像度な画像
- 🎌 アニメ - 日本のアニメスタイル
- 🎨 イラスト - アート風のイラスト
- 🖌️ 水彩画 - 柔らかい水彩画風
- 🎭 油絵 - クラシックな油絵風
- 🎮 3D レンダー - 3DCG のような表現
- ⚪ ミニマル - シンプルでモダン
- 🌀 抽象画 - 抽象的な芸術作品
🔒 セキュリティ対策
レート制限の実装例:
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "1 h"),// 1時間に10回まで
});
export async function checkRateLimit(userId: string) {
const { success, limit, reset, remaining } = await ratelimit.limit(userId);
if (!success) {
throw new Error(
`レート制限に達しました。${new Date(
reset
).toLocaleString()}に再試行してください。`
);
}
return { limit, remaining };
}
☁️ Cloudflare R2 での画像保存
生成された画像を Cloudflare R2 に保存し、高速配信:
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const r2Client = new S3Client({
region: "auto",
endpoint: process.env.R2_ENDPOINT,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
});
export async function uploadImageToR2(
imageData: Buffer,
fileName: string
): Promise<string> {
const key = `ai-images/${Date.now()}-${fileName}`;
await r2Client.send(
new PutObjectCommand({
Bucket: process.env.R2_BUCKET_NAME,
Key: key,
Body: imageData,
ContentType: "image/png",
CacheControl: "public, max-age=31536000",// 1年間キャッシュ
})
);
return `${process.env.R2_PUBLIC_URL}/${key}`;
}
🎯 ベストプラクティス
💡 プロンプトエンジニアリングの Tips
- 具体的な説明:「美しい風景」→「夕暮れの山々、紫とオレンジのグラデーション空」
- スタイル指定:アートスタイル、雰囲気、色調を明確に
- ネガティブプロンプト:避けたい要素を明示的に指定
🚀 パフォーマンス最適化
- 並列処理:Promise.all で複数画像を同時生成
- キャッシュ戦略:CDN で画像配信を高速化
- 最適化:WebP 形式への自動変換
📊 データベーススキーマ
Supabase でのテーブル構造:
-- ユーザークレジットテーブルCREATE TABLE user_credits (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
total_credits INT DEFAULT 100,
used_credits INT DEFAULT 0,
subscription_tier TEXT DEFAULT 'free',
updated_at TIMESTAMPTZ DEFAULT now()
);
-- 画像生成履歴CREATE TABLE image_generation_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
prompt TEXT NOT NULL,
style TEXT,
image_urls TEXT[],
credits_used INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
-- RLSポリシーALTER TABLE user_credits ENABLE ROW LEVEL SECURITY;
ALTER TABLE image_generation_history ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view own data"
ON user_credits FOR SELECT
USING (auth.uid() = user_id);
🔮 今後の拡張機能
- 他 AI API 統合:DALL-E 3、Stable Diffusion
- 高度な編集:画像修正、スタイル転送
- ビジネス機能:API 提供、チーム共有
- AI 拡張:プロンプト自動生成
📚 参考リソース
- Google Gemini API Documentation - https://ai.google.dev/
- Next.js 14 Documentation - https://nextjs.org/docs
- Supabase Documentation - https://supabase.io/docs
- Cloudflare R2 Documentation - https://developers.cloudflare.com/r2/
- shadcn/ui Components - https://ui.shadcn.com/
🎯 まとめ
本記事では、Next.js 14 と Google Gemini API を使用した AI 画像生成機能の実装方法を解説しました。Server Actions、クレジット管理、Cloudflare R2 統合など、本番環境で即活用できる実践的な実装を紹介しました。
この実装をベースに、さらに高度な機能を追加して、より強力な画像生成サービスを構築できます。
🏷️ タグ:#Next.js #Gemini #AI #画像生成 #Supabase #CloudflareR2 #React #TypeScript
このドキュメントで一部のコンテンツが無効になっています