Technical Implementation Plan - LeadFive Website
アーキテクチャ概要
システム構成図
┌─────────────────────────────────────────────────────────────┐
│ Frontend (Vercel) │
│ Next.js 14 App Router │
│ React + TypeScript + Tailwind │
└─────────────────────────┬───────────────────────────────────┘
│
├──────────────┬────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────────────┐ ┌───────────────┐ ┌─────────────┐
│ Headless WordPress │ │ OpenAI API │ │ Analytics │
│ (WP Engine/Kinsta) │ │ GPT-4 + │ │ GA4 + │
│ - Content Management │ │ DALL-E 3 │ │ Mixpanel │
│ - REST API / GraphQL │ │ │ │ │
└─────────────────────────────┘ └───────────────┘ └─────────────┘
詳細技術スタック
フロントエンド構成
// package.json
{
"name": "leadfive-website",
"version": "2.0.0",
"dependencies": {
// Core
"next": "^14.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.0.0",
// Styling & Animation
"tailwindcss": "^3.4.0",
"framer-motion": "^10.0.0",
"@react-three/fiber": "^8.0.0",
"@react-three/drei": "^9.0.0",
"three": "^0.160.0",
// State & Data
"zustand": "^4.4.0",
"@tanstack/react-query": "^5.0.0",
"axios": "^1.6.0",
// AI Integration
"ai": "^2.2.0", // Vercel AI SDK
"openai": "^4.0.0",
// UI Components
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^2.0.0",
"react-hook-form": "^7.48.0",
"zod": "^3.22.0",
// Analytics
"@vercel/analytics": "^1.1.0",
"mixpanel-browser": "^2.48.0",
// SEO & Performance
"next-seo": "^6.4.0",
"next-sitemap": "^4.2.0"
}
}
プロジェクト構造
leadfive-website/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── layout.tsx # ルートレイアウト
│ │ ├── page.tsx # トップページ
│ │ ├── api/ # API Routes
│ │ │ ├── ai-chat/ # AIチャット
│ │ │ └── contact/ # お問い合わせ
│ │ └── blog/ # ブログ(別システム)
│ │
│ ├── components/ # コンポーネント
│ │ ├── ui/ # 基本UIコンポーネント
│ │ ├── sections/ # ページセクション
│ │ │ ├── Hero.tsx
│ │ │ ├── DesireOctagon.tsx
│ │ │ ├── AIInnovation.tsx
│ │ │ └── CaseStudies.tsx
│ │ └── three/ # 3Dコンポーネント
│ │
│ ├── lib/ # ユーティリティ
│ │ ├── ai/ # AI関連
│ │ ├── wordpress/ # WordPress連携
│ │ └── analytics/ # 分析
│ │
│ ├── hooks/ # カスタムフック
│ ├── styles/ # グローバルスタイル
│ └── types/ # TypeScript型定義
│
├── public/ # 静的ファイル
├── scripts/ # ビルドスクリプト
└── tests/ # テスト
コンポーネント実装例
1. ヒーローセクション with 3D
// components/sections/Hero.tsx
import { Canvas } from '@react-three/fiber'
import { Suspense } from 'react'
import { motion } from 'framer-motion'
import BrainNetwork from '@/components/three/BrainNetwork'
export default function Hero() {
return (
<section className="relative h-screen overflow-hidden bg-gradient-to-br from-indigo-950 via-purple-900 to-pink-900">
{/* 3D Background */}
<div className="absolute inset-0 opacity-60">
<Canvas camera=>
<Suspense fallback={null}>
<BrainNetwork />
</Suspense>
</Canvas>
</div>
{/* Content */}
<div className="relative z-10 flex h-full items-center justify-center px-4">
<div className="max-w-4xl text-center">
<motion.h1
initial=
animate=
transition=
className="mb-6 text-5xl font-bold text-white md:text-7xl"
>
人の本能を科学し、
<br />
<span className="bg-gradient-to-r from-pink-400 to-indigo-400 bg-clip-text text-transparent">
AIで未来を創る
</span>
</motion.h1>
<motion.p
initial=
animate=
transition=
className="mb-8 text-xl text-gray-200 md:text-2xl"
>
8つの根源的欲求 × 最先端AI = ビジネス革新
</motion.p>
<motion.div
initial=
animate=
transition=
className="flex flex-col gap-4 sm:flex-row sm:justify-center"
>
<button className="rounded-full bg-white px-8 py-4 text-lg font-semibold text-indigo-900 transition hover:bg-gray-100">
無料相談を予約する
</button>
<button className="rounded-full border-2 border-white px-8 py-4 text-lg font-semibold text-white transition hover:bg-white hover:text-indigo-900">
AI診断を試す
</button>
</motion.div>
</div>
</div>
{/* Scroll Indicator */}
<motion.div
animate=
transition=
className="absolute bottom-8 left-1/2 -translate-x-1/2 text-white"
>
<svg className="h-8 w-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 14l-7 7m0 0l-7-7m7 7V3" />
</svg>
</motion.div>
</section>
)
}
2. 8つの欲求インタラクティブ図
// components/sections/DesireOctagon.tsx
import { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
const desires = [
{
id: 1,
name: '生存欲求',
icon: '🛡️',
color: 'from-red-500 to-red-600',
description: 'セキュリティとリスク管理への根源的な欲求',
marketing: '安心・安全を訴求するメッセージング戦略'
},
{
id: 2,
name: '食欲',
icon: '🍽️',
color: 'from-orange-500 to-orange-600',
description: '満足と充実を求める消費行動の原動力',
marketing: '五感に訴える体験型マーケティング'
},
// ... 他の6つの欲求
]
export default function DesireOctagon() {
const [selectedDesire, setSelectedDesire] = useState(null)
return (
<section className="py-20 bg-gray-50">
<div className="container mx-auto px-4">
<motion.div
initial=
whileInView=
transition=
className="text-center mb-16"
>
<h2 className="text-4xl md:text-5xl font-bold mb-4">
人間の8つの根源的欲求
</h2>
<p className="text-xl text-gray-600">
すべてのマーケティングは、これらの欲求から始まる
</p>
</motion.div>
<div className="relative max-w-4xl mx-auto">
{/* Octagon Container */}
<div className="relative w-full aspect-square">
{desires.map((desire, index) => {
const angle = (index * 45 - 90) * (Math.PI / 180)
const radius = 45 // percentage
const x = 50 + radius * Math.cos(angle)
const y = 50 + radius * Math.sin(angle)
return (
<motion.div
key={desire.id}
className="absolute w-32 h-32 -translate-x-1/2 -translate-y-1/2"
style={{ left: `${x}%`, top: `${y}%` }}
whileHover={{ scale: 1.1 }}
onClick={() => setSelectedDesire(desire)}
>
<div className={`w-full h-full rounded-full bg-gradient-to-br ${desire.color} flex flex-col items-center justify-center cursor-pointer shadow-lg`}>
<span className="text-3xl mb-1">{desire.icon}</span>
<span className="text-white font-semibold text-sm">{desire.name}</span>
</div>
</motion.div>
)
})}
{/* Center Logo */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-40 h-40 bg-white rounded-full shadow-xl flex items-center justify-center">
<div className="text-center">
<div className="text-3xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
LEAD5
</div>
<div className="text-sm text-gray-600">AI Marketing</div>
</div>
</div>
</div>
{/* Detail Panel */}
<AnimatePresence>
{selectedDesire && (
<motion.div
initial=
animate=
exit=
className="mt-12 p-8 bg-white rounded-2xl shadow-xl"
>
<div className="flex items-start justify-between mb-4">
<h3 className="text-2xl font-bold flex items-center gap-3">
<span className="text-3xl">{selectedDesire.icon}</span>
{selectedDesire.name}
</h3>
<button
onClick={() => setSelectedDesire(null)}
className="text-gray-400 hover:text-gray-600"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<p className="text-gray-600 mb-4">{selectedDesire.description}</p>
<div className="p-4 bg-gradient-to-r from-indigo-50 to-purple-50 rounded-lg">
<h4 className="font-semibold text-indigo-900 mb-2">AIマーケティング応用</h4>
<p className="text-gray-700">{selectedDesire.marketing}</p>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</section>
)
}
3. AIチャットボット実装
// components/ui/AIChat.tsx
import { useState } from 'react'
import { useChat } from 'ai/react'
import { motion, AnimatePresence } from 'framer-motion'
export default function AIChat() {
const [isOpen, setIsOpen] = useState(false)
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/ai-chat',
initialMessages: [
{
id: '1',
role: 'assistant',
content: 'こんにちは!人間の欲求とAIマーケティングについて、何でもお聞きください。'
}
]
})
return (
<>
{/* Chat Button */}
<motion.button
className="fixed bottom-8 right-8 w-16 h-16 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full shadow-lg flex items-center justify-center text-white"
whileHover=
whileTap=
onClick={() => setIsOpen(!isOpen)}
>
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" />
</svg>
</motion.button>
{/* Chat Window */}
<AnimatePresence>
{isOpen && (
<motion.div
initial=
animate=
exit=
className="fixed bottom-28 right-8 w-96 h-[500px] bg-white rounded-2xl shadow-2xl flex flex-col overflow-hidden"
>
{/* Header */}
<div className="bg-gradient-to-r from-indigo-600 to-purple-600 p-4 text-white">
<div className="flex items-center justify-between">
<h3 className="font-semibold">AI Marketing Assistant</h3>
<button
onClick={() => setIsOpen(false)}
className="text-white/80 hover:text-white"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((message) => (
<div
key={message.id}
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[80%] p-3 rounded-lg ${
message.role === 'user'
? 'bg-indigo-600 text-white'
: 'bg-gray-100 text-gray-800'
}`}
>
{message.content}
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-gray-100 p-3 rounded-lg">
<div className="flex space-x-2">
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" />
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-100" />
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-200" />
</div>
</div>
</div>
)}
</div>
{/* Input */}
<form onSubmit={handleSubmit} className="p-4 border-t">
<div className="flex gap-2">
<input
type="text"
value={input}
onChange={handleInputChange}
placeholder="質問を入力..."
className="flex-1 px-4 py-2 border rounded-full focus:outline-none focus:ring-2 focus:ring-indigo-600"
/>
<button
type="submit"
disabled={isLoading}
className="px-4 py-2 bg-indigo-600 text-white rounded-full hover:bg-indigo-700 disabled:opacity-50"
>
送信
</button>
</div>
</form>
</motion.div>
)}
</AnimatePresence>
</>
)
}
4. WordPress連携
// lib/wordpress/client.ts
import axios from 'axios'
const WP_URL = process.env.NEXT_PUBLIC_WP_URL || 'https://blog.leadfive138.com'
const WP_API = `${WP_URL}/wp-json/wp/v2`
export const wpClient = axios.create({
baseURL: WP_API,
headers: {
'Content-Type': 'application/json',
},
})
// 記事取得
export async function getPosts(params?: {
page?: number
per_page?: number
categories?: number[]
}) {
const response = await wpClient.get('/posts', { params })
return {
posts: response.data,
totalPages: parseInt(response.headers['x-wp-totalpages']),
total: parseInt(response.headers['x-wp-total']),
}
}
// 特定の記事取得
export async function getPost(slug: string) {
const response = await wpClient.get('/posts', {
params: { slug, _embed: true },
})
return response.data[0]
}
// カテゴリー取得
export async function getCategories() {
const response = await wpClient.get('/categories')
return response.data
}
5. AI自動記事生成API
// app/api/ai-blog/route.ts
import { NextResponse } from 'next/server'
import OpenAI from 'openai'
import { wpClient } from '@/lib/wordpress/client'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
export async function POST(request: Request) {
try {
const { topic, category } = await request.json()
// 1. GPT-4で記事生成
const articleResponse = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content: `あなたは人間の根源的欲求とAIを専門とするマーケティングコンサルタントです。
以下の構成で魅力的なブログ記事を作成してください:
1. 導入(読者の関心を引く)
2. 人間の欲求との関連性
3. AI活用の具体例
4. 実践的なアドバイス
5. まとめとCTA`
},
{
role: 'user',
content: `トピック: ${topic}\n2000-3000字の記事を書いてください。`
}
],
temperature: 0.7,
})
const articleContent = articleResponse.choices[0].message.content
// 2. タイトル生成
const titleResponse = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'user',
content: `以下の記事に魅力的なタイトルをつけてください(30文字以内):\n\n${articleContent?.substring(0, 500)}...`
}
],
})
const title = titleResponse.choices[0].message.content
// 3. DALL-E 3でアイキャッチ画像生成
const imageResponse = await openai.images.generate({
model: 'dall-e-3',
prompt: `Blog header image for "${title}". Modern, minimalist design with purple and indigo gradient. Professional marketing theme with subtle AI elements.`,
size: '1792x1024',
quality: 'hd',
})
const imageUrl = imageResponse.data[0].url
// 4. WordPressに投稿
const wpPost = await createWordPressPost({
title: title || 'Untitled',
content: articleContent || '',
status: 'draft',
categories: [category],
featured_media_url: imageUrl,
})
return NextResponse.json({
success: true,
post: wpPost,
})
} catch (error) {
console.error('AI Blog Generation Error:', error)
return NextResponse.json(
{ error: 'Failed to generate blog post' },
{ status: 500 }
)
}
}
async function createWordPressPost(data: any) {
// WordPress REST API を使った投稿処理
const response = await fetch(`${process.env.WP_API_URL}/posts`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${Buffer.from(
`${process.env.WP_USERNAME}:${process.env.WP_APP_PASSWORD}`
).toString('base64')}`,
},
body: JSON.stringify(data),
})
return response.json()
}
デプロイメント設定
Vercel設定
// vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "nextjs",
"regions": ["hnd1"],
"env": {
"NEXT_PUBLIC_WP_URL": "@wp_url",
"OPENAI_API_KEY": "@openai_api_key",
"WP_USERNAME": "@wp_username",
"WP_APP_PASSWORD": "@wp_app_password",
"MIXPANEL_TOKEN": "@mixpanel_token"
},
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "SAMEORIGIN"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
}
]
}
]
}
GitHub Actions CI/CD
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check
- run: npm run test
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- uses: amondnet/vercel-action@v25
with:
vercel-token: $
vercel-org-id: $
vercel-project-id: $
vercel-args: '--prod'
パフォーマンス最適化
1. 画像最適化
// next.config.js
module.exports = {
images: {
domains: ['blog.leadfive138.com', 'oaidalleapiprodscus.blob.core.windows.net'],
formats: ['image/avif', 'image/webp'],
},
experimental: {
optimizeCss: true,
},
}
2. コンポーネントの遅延読み込み
// Dynamic imports for heavy components
const BrainNetwork = dynamic(() => import('@/components/three/BrainNetwork'), {
loading: () => <div className="h-full w-full bg-gray-900" />,
ssr: false,
})
const AIChat = dynamic(() => import('@/components/ui/AIChat'), {
ssr: false,
})
3. API Route キャッシング
// app/api/blog/posts/route.ts
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const page = searchParams.get('page') || '1'
// Vercel Edge Cache
const posts = await fetch(`${WP_API}/posts?page=${page}`, {
next: { revalidate: 3600 }, // 1時間キャッシュ
})
return NextResponse.json(await posts.json())
}
セキュリティ対策
1. 環境変数管理
# .env.local (Vercelで暗号化)
OPENAI_API_KEY=sk-...
WP_APP_PASSWORD=...
NEXT_PUBLIC_GA_ID=G-...
2. APIレート制限
// lib/rate-limit.ts
import { LRUCache } from 'lru-cache'
const tokenCache = new LRUCache<string, number>({
max: 500,
ttl: 1000 * 60 * 60, // 1時間
})
export async function rateLimit(request: Request) {
const ip = request.headers.get('x-forwarded-for') ?? 'anonymous'
const tokenCount = tokenCache.get(ip) ?? 0
if (tokenCount > 10) {
return new Response('Rate limit exceeded', { status: 429 })
}
tokenCache.set(ip, tokenCount + 1)
return null
}
監視とアナリティクス
1. Vercel Analytics
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react'
export default function RootLayout({ children }) {
return (
<html lang="ja">
<body>
{children}
<Analytics />
</body>
</html>
)
}
2. カスタムイベントトラッキング
// lib/analytics/mixpanel.ts
import mixpanel from 'mixpanel-browser'
mixpanel.init(process.env.NEXT_PUBLIC_MIXPANEL_TOKEN!)
export const trackEvent = (eventName: string, properties?: any) => {
mixpanel.track(eventName, {
...properties,
timestamp: new Date().toISOString(),
})
}
// 使用例
trackEvent('CTA_Click', {
button: 'Free Consultation',
section: 'Hero',
})
Document Version: 1.0 Date: 2025年7月30日 Status: 技術実装計画