feat: 사이트 3구역 개편 + AI 상품 결제 연결 + SEO 업데이트

- 사이드바: AI상품/무료도구/외주의뢰 3그룹 구조로 개편 (ARIA 시맨틱)
- 홈페이지: AI 상품 중심 재작성 (히어로+상품카드+무료도구+외주축소)
- SEO: 메타데이터·OG태그·JSON-LD를 AI 상품 포지셔닝으로 변경
- 프롬프트 페이지: 프리미엄 상품 5개에 PortOne PaymentButton 연결
- AI 키트 페이지: 월 구독 CTA 2곳에 PaymentButton 연결
- 사주: 유료 전환 복원(4,900원) + PaymentButton 연결
- 코드 품질: 인라인 스타일→globals.css, emoji→SVG, 미사용 데이터 제거
- DB 마이그레이션 005: 전체 18개 상품 등록 SQL 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-08 01:29:42 +09:00
parent 769544b453
commit 9433a3664c
12 changed files with 590 additions and 677 deletions

View File

@@ -2,7 +2,7 @@
import { useEffect, useState } from 'react';
import Link from 'next/link';
// PaymentButton 비활성화 — 토스페이먼츠 결제 일시 중단
import PaymentButton from '@/app/components/PaymentButton';
import { createClient } from '@/lib/supabase/client';
const faqItems = [
@@ -202,7 +202,7 @@ export default function SajuPage() {
<div className="text-center mb-8">
<p className="text-[#1a56db] text-xs font-bold uppercase tracking-widest mb-2">PRICING</p>
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b] tracking-tight"> </h2>
<p className="text-slate-500 text-sm mt-2"> AI </p>
<p className="text-slate-500 text-sm mt-2"> , AI 4,900</p>
</div>
<div className="grid md:grid-cols-2 gap-6">
@@ -256,8 +256,8 @@ export default function SajuPage() {
backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 30px)',
}}
>
<div className="absolute top-4 right-4 bg-emerald-400 text-[#04102b] text-xs font-bold px-2 py-0.5 rounded-lg">
<div className="absolute top-4 right-4 bg-amber-400 text-[#04102b] text-xs font-bold px-2 py-0.5 rounded-lg">
4,900
</div>
<div className="flex items-center gap-3 mb-5 relative">
<div className="w-10 h-10 rounded-xl bg-violet-500/20 border border-violet-400/30 flex items-center justify-center">
@@ -277,7 +277,7 @@ export default function SajuPage() {
'용신·희신·기신 추정',
'대운 (10년 주기) 분석',
'올해 세운 흐름',
'GPT-4o AI 12가지 상세 해석',
'Gemini 2.5 Pro AI 12가지 상세 해석',
].map((item) => (
<li key={item} className="flex items-center gap-2.5 text-sm text-blue-200">
<div className="w-4 h-4 rounded-full bg-amber-400/20 border border-amber-400/40 flex items-center justify-center flex-shrink-0">
@@ -288,13 +288,16 @@ export default function SajuPage() {
))}
</ul>
<div className="mt-6 pt-5 border-t border-white/10 relative">
<div className="text-lg font-bold text-emerald-400 mb-1"> </div>
<div className="text-xs text-blue-300/70 mt-1 mb-4"> · 12 AI </div>
<div className="flex items-baseline gap-2 mb-1">
<span className="text-2xl font-extrabold text-white">4,900</span>
<span className="text-xs text-blue-300/50">/ 1</span>
</div>
<div className="text-xs text-blue-300/70 mt-1 mb-4"> · 12 AI </div>
<Link
href="/saju/input"
className="block w-full text-center py-3 rounded-xl text-sm font-bold transition bg-amber-400 text-[#04102b] hover:bg-amber-300"
>
</Link>
</div>
</div>

View File

@@ -3,7 +3,7 @@
import { useState, useEffect, useRef } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
// PaymentButton 비활성화 — 토스페이먼츠 결제 일시 중단, 무료 제공 중
import PaymentButton from '@/app/components/PaymentButton';
interface BirthKey {
birth_year: number;
@@ -313,13 +313,13 @@ export default function SajuAISection({
))}
</div>
{/* 결제 일시 중단 — hasPaid=true이므로 이 분기는 표시되지 않음 */}
<a
href={process.env.NEXT_PUBLIC_KAKAO_CHANNEL_URL ?? '/freelance?service=AI사주분석'}
<PaymentButton
productId="saju_detail"
className="inline-flex items-center gap-2 bg-amber-400 hover:bg-amber-300 text-[#04102b] font-bold px-7 py-3 rounded-xl transition-all"
>
AI
</a>
AI 4,900
</PaymentButton>
<p className="text-blue-200/40 text-xs mt-3"> AI · </p>
</div>
</div>
);

View File

@@ -76,8 +76,7 @@ export default async function SajuResultPage({ searchParams }: PageProps) {
const solarTermName = getSolarTermName(solarTermIndex);
// ── 결제 여부 + 저장된 AI 해석 + 로또 구독 확인 ─────────────────────
// 토스페이먼츠 결제 일시 중단 — AI 사주 해석 무료 제공 중
let hasPaid = true;
let hasPaid = false;
let savedInterpretation: string | null = null;
let hasLottoSubscription = false;
try {