refactor: AI 음악 메인 개편 — 로또/프롬프트/자동화 삭제, 음악/블로그 팩 신규
- 삭제: services/{lotto,prompt,automation,ai-kit,stock,tools} + api/{lotto,tools}
- 노출 제거: /freelance, /services/website (noindex + robots/sitemap 제외, 외부 지원서 링크 유지)
- 신규: /services/music (3-tier 39k/99k/149k, 4단계 프로세스)
- 신규: /services/blog (블로그 자동화 팩 29k 1회성)
- 신규: PurchaseAgreementModal (전자상거래법 17조 동의 + 계좌이체)
- 개편: 홈 대시보드 (음악 Hero + 사주/블로그팩/일반문의 서브카드)
- 사이드바 재구성, sitemap/robots/JSON-LD 갱신
- 환불정책 신규 상품 반영 + 법적 근거 명시
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,571 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import ContactModal from '../../components/ContactModal';
|
||||
import PaymentButton from '../../components/PaymentButton';
|
||||
|
||||
const KAKAO_CHANNEL_URL = process.env.NEXT_PUBLIC_KAKAO_CHANNEL_URL ?? null;
|
||||
|
||||
const AI_KIT_CHECKLIST = [
|
||||
'주로 반복하는 업무 종류 (일지, 이메일, 보고서 등)',
|
||||
'현재 주로 사용하는 AI 도구 (ChatGPT / Claude / Gemini 등)',
|
||||
'하루 또는 주 단위로 같은 작업을 몇 번이나 반복하는지',
|
||||
'사용 목적 (개인 효율화 / 팀 도입 / 소상공인 업무 등)',
|
||||
];
|
||||
|
||||
/* ──────────────────────────────────────────────────────────────
|
||||
Before / After 데이터 — 각 도구별 실제 시간 비교
|
||||
마케팅 카피의 핵심: 추상적 "빠름"이 아닌 구체적 숫자
|
||||
────────────────────────────────────────────────────────────── */
|
||||
const TOOLS = [
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
),
|
||||
title: '업무 일지 자동 작성기',
|
||||
desc: '하루 업무 키워드 5개만 입력하면 AI가 전문적인 일지를 즉시 완성.',
|
||||
tag: '직장인 필수',
|
||||
tagColor: 'bg-blue-500/15 text-blue-400 border-blue-500/25',
|
||||
before: '15분',
|
||||
after: '40초',
|
||||
beforeLabel: '직접 쓸 때',
|
||||
afterLabel: 'AI 사용 시',
|
||||
failCase: '대충 쓰면 상사 피드백 → 재작성 → 결국 30분',
|
||||
saving: '월 4.7시간 절약',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
title: '이메일 자동 답장 생성기',
|
||||
desc: '받은 이메일을 붙여넣으면 상황에 맞는 정중한 답장 3가지 버전을 즉시 생성.',
|
||||
tag: '소상공인 필수',
|
||||
tagColor: 'bg-violet-500/15 text-violet-400 border-violet-500/25',
|
||||
before: '23분',
|
||||
after: '2분',
|
||||
beforeLabel: '직접 쓸 때',
|
||||
afterLabel: 'AI 사용 시',
|
||||
failCase: '어조가 애매하면 상대방 기분 상함 → 계약 취소 위험',
|
||||
saving: '월 6.4시간 절약',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4h16v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4z" />
|
||||
</svg>
|
||||
),
|
||||
title: '월간 매출 분석 리포트',
|
||||
desc: '숫자만 입력하면 전월 대비 분석·인사이트·개선 방향을 리포트로 정리.',
|
||||
tag: '소상공인 필수',
|
||||
tagColor: 'bg-emerald-500/15 text-emerald-400 border-emerald-500/25',
|
||||
before: '2시간 30분',
|
||||
after: '5분',
|
||||
beforeLabel: '엑셀 + 직접 분석',
|
||||
afterLabel: 'AI 사용 시',
|
||||
failCase: '분석 없이 감으로 운영 → 손실 트렌드 한 달 늦게 발견',
|
||||
saving: '월 2.4시간 절약',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
||||
</svg>
|
||||
),
|
||||
title: 'SNS 콘텐츠 캘린더',
|
||||
desc: '업종과 키워드를 입력하면 한 달치 인스타·블로그 콘텐츠 기획안을 자동 생성.',
|
||||
tag: 'SNS 마케팅',
|
||||
tagColor: 'bg-pink-500/15 text-pink-400 border-pink-500/25',
|
||||
before: '1시간 30분/주',
|
||||
after: '10분/월',
|
||||
beforeLabel: '매주 기획할 때',
|
||||
afterLabel: 'AI로 한 번에',
|
||||
failCase: 'SNS 3일 공백 → 인스타 도달 -40% · 팔로워 이탈 시작',
|
||||
saving: '월 5.7시간 절약',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
|
||||
</svg>
|
||||
),
|
||||
title: '회의록·미팅 노트 정리',
|
||||
desc: '대화 내용이나 메모를 입력하면 결정 사항·액션아이템·다음 단계를 즉시 구조화.',
|
||||
tag: '직장인 필수',
|
||||
tagColor: 'bg-blue-500/15 text-blue-400 border-blue-500/25',
|
||||
before: '40분',
|
||||
after: '3분',
|
||||
beforeLabel: '직접 정리할 때',
|
||||
afterLabel: 'AI 사용 시',
|
||||
failCase: '액션아이템 누락 → 후속 지연 → "그때 얘기했잖아요" 분쟁',
|
||||
saving: '월 3.7시간 절약',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
),
|
||||
title: '상품 설명·리뷰 답변 자동화',
|
||||
desc: '상품명과 특징을 입력하면 스마트스토어·쿠팡 최적화 상품 설명 + 리뷰 답변 즉시 생성.',
|
||||
tag: '온라인 판매자',
|
||||
tagColor: 'bg-orange-500/15 text-orange-400 border-orange-500/25',
|
||||
before: '25분/개',
|
||||
after: '30초/개',
|
||||
beforeLabel: '상품 1개 설명 직접 쓸 때',
|
||||
afterLabel: 'AI 사용 시',
|
||||
failCase: '밋밋한 설명 → 클릭율 낮음 → 검색 노출 하락 → 매출 감소',
|
||||
saving: '상품 10개 기준 월 4시간 절약',
|
||||
},
|
||||
];
|
||||
|
||||
const TESTIMONIALS = [
|
||||
{
|
||||
name: '김하윤',
|
||||
job: '카페 운영 3년차',
|
||||
text: '매일 SNS 올릴 내용 고민하다 지쳤는데, 이제 30초면 한 달치 아이디어가 나와요. 매출도 15% 올랐어요.',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
name: '박도현',
|
||||
job: '중소기업 팀장',
|
||||
text: '주간 보고서 작성이 2시간에서 20분으로 줄었습니다. 팀원들한테도 공유했어요.',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
name: '이서진',
|
||||
job: '프리랜서 디자이너',
|
||||
text: '클라이언트 이메일 답장을 AI로 생성하니까 전문적으로 보인다는 피드백을 많이 받아요.',
|
||||
rating: 5,
|
||||
},
|
||||
];
|
||||
|
||||
const FAQ = [
|
||||
{
|
||||
q: 'AI를 전혀 써본 적 없어도 가능한가요?',
|
||||
a: 'ChatGPT나 Claude에 복사·붙여넣기만 할 수 있으면 됩니다. 모든 도구에 단계별 사용 가이드가 포함되어 있습니다.',
|
||||
},
|
||||
{
|
||||
q: '매달 어떤 것이 업데이트되나요?',
|
||||
a: '매월 1일에 새로운 자동화 도구 1~2종이 추가됩니다. 트렌드 변화와 구독자 요청을 반영하여 지속적으로 개선합니다.',
|
||||
},
|
||||
{
|
||||
q: '해지는 언제든지 가능한가요?',
|
||||
a: '네, 언제든지 마이페이지에서 구독을 취소할 수 있습니다. 해지 후에도 해당 월 말일까지 사용 가능합니다.',
|
||||
},
|
||||
{
|
||||
q: '스마트스토어·쿠팡 판매자도 쓸 수 있나요?',
|
||||
a: '네. 상품 설명 자동화, 리뷰 답변 자동화 등 온라인 판매자를 위한 전용 도구가 포함되어 있습니다.',
|
||||
},
|
||||
];
|
||||
|
||||
function useScrollReveal() {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: '0px 0px -40px 0px' }
|
||||
);
|
||||
el.querySelectorAll('.reveal').forEach((child) => observer.observe(child));
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
return ref;
|
||||
}
|
||||
|
||||
export default function AiKitPage() {
|
||||
const totalMonthlySaving = 27;
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const containerRef = useScrollReveal();
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="min-h-full bg-[#f0f4ff]">
|
||||
<style>{`
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(1.5rem);
|
||||
transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.reveal.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.reveal-d1 { transition-delay: 80ms; }
|
||||
.reveal-d2 { transition-delay: 160ms; }
|
||||
.reveal-d3 { transition-delay: 240ms; }
|
||||
.reveal-d4 { transition-delay: 320ms; }
|
||||
`}</style>
|
||||
<ContactModal
|
||||
isOpen={modalOpen}
|
||||
onClose={() => setModalOpen(false)}
|
||||
service="AI 자동화 키트 — 월 19,900원"
|
||||
checklist={AI_KIT_CHECKLIST}
|
||||
accentColor="text-indigo-400"
|
||||
headerFrom="#0a0f2e"
|
||||
headerTo="#0f1a5c"
|
||||
/>
|
||||
|
||||
{/* ─── Hero ─── */}
|
||||
<div className="relative overflow-hidden bg-[#0a0f2e] px-6 py-14 lg:px-12" style={{ backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 40px)' }}>
|
||||
|
||||
<div className="relative max-w-3xl mx-auto">
|
||||
<Link href="/" className="inline-flex items-center gap-1.5 text-indigo-300/60 hover:text-indigo-300 text-sm mb-8 transition">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /></svg>
|
||||
홈으로
|
||||
</Link>
|
||||
|
||||
<p className="text-indigo-400 text-xs font-bold uppercase tracking-widest mb-4 font-mono">AI 자동화 키트 · 월 구독</p>
|
||||
|
||||
{/* 핵심 카피 */}
|
||||
<h1 className="text-4xl md:text-5xl font-extrabold text-white mb-4 tracking-tight leading-tight">
|
||||
오늘도 반복 업무에<br />
|
||||
<span className="text-red-400">{totalMonthlySaving}시간을 낭비하고 있습니다</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-indigo-100/60 text-base md:text-lg leading-relaxed max-w-2xl mb-8">
|
||||
일지 작성, 이메일 답장, 보고서, SNS 기획…<br />
|
||||
<strong className="text-white">혼자 하면 월 {totalMonthlySaving}시간 이상 소비되는 일들,</strong> AI로 90% 줄일 수 있습니다.
|
||||
</p>
|
||||
|
||||
{/* 가격 카드 */}
|
||||
<div className="inline-flex flex-col bg-white/5 border border-white/10 rounded-xl px-8 py-5 mb-6">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="text-xs font-bold bg-red-500 text-white px-2 py-0.5 rounded">런칭 특가</span>
|
||||
<span className="text-sm line-through text-white/30">월 39,900원</span>
|
||||
</div>
|
||||
<div className="text-4xl font-extrabold text-white">
|
||||
19,900<span className="text-xl font-semibold text-white/50">원</span>
|
||||
<span className="text-base font-normal text-white/40 ml-1">/ 월</span>
|
||||
</div>
|
||||
<p className="text-indigo-300/60 text-xs mt-1">언제든 해지 가능 · 해지 후 월말까지 이용</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3 items-center">
|
||||
<PaymentButton
|
||||
productId="ai_kit_monthly"
|
||||
className="inline-flex items-center justify-center gap-2 bg-indigo-500 hover:bg-indigo-400 text-white text-base font-bold px-8 py-4 rounded-xl transition-colors w-full max-w-xs"
|
||||
>
|
||||
월 구독 시작하기 — 19,900원/월
|
||||
</PaymentButton>
|
||||
<button
|
||||
onClick={() => setModalOpen(true)}
|
||||
className="text-indigo-300/60 hover:text-indigo-300 text-sm underline underline-offset-2 transition-colors"
|
||||
>
|
||||
문의 후 구독하기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 시간 낭비 가시화 섹션 ─── */}
|
||||
<div className="bg-white px-6 py-12 lg:px-12 border-b border-slate-100">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="reveal text-center mb-8">
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-slate-800 mb-2">
|
||||
지금 이 순간도 낭비되고 있는 당신의 시간
|
||||
</h2>
|
||||
<p className="text-slate-500 text-sm">직장인·소상공인 평균 작업 시간 기준 / 월 22 영업일 계산</p>
|
||||
</div>
|
||||
|
||||
{/* 총합 카드 */}
|
||||
<div className="reveal bg-red-50 border border-red-200 rounded-2xl p-6 mb-8">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-sm font-bold text-red-600 mb-1">6가지 반복 업무를 혼자 할 때</p>
|
||||
<p className="text-4xl font-extrabold text-slate-800">
|
||||
월 <span className="text-red-500">{totalMonthlySaving}시간 19분</span> 낭비
|
||||
</p>
|
||||
<p className="text-slate-500 text-sm mt-1">
|
||||
시급 15,000원 기준 → <span className="font-bold text-slate-700">월 409,000원어치 시간 손실</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center md:text-right">
|
||||
<div className="text-2xl font-extrabold text-indigo-600">AI 도입 후</div>
|
||||
<div className="text-4xl font-extrabold text-emerald-500">2시간 6분</div>
|
||||
<div className="text-slate-500 text-sm mt-1">전체 작업 시간 <span className="font-bold text-emerald-600">92.3% 감소</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 개별 도구 Before/After 바 차트 */}
|
||||
<div className="reveal space-y-3">
|
||||
{TOOLS.map((tool, i) => {
|
||||
const beforeVal = tool.before;
|
||||
const afterVal = tool.after;
|
||||
return (
|
||||
<div key={i} className="bg-slate-50 rounded-xl p-4 border border-slate-100">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-bold text-slate-800">{tool.title}</span>
|
||||
<span className="text-xs font-bold text-emerald-600 bg-emerald-50 border border-emerald-200 px-2 py-0.5 rounded-md">
|
||||
{tool.saving}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-xs">
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
<span className="text-slate-400 w-20 text-right flex-shrink-0">{tool.beforeLabel}</span>
|
||||
<div className="flex-1 bg-red-100 rounded-full h-2">
|
||||
<div className="bg-red-400 h-2 rounded-full" style={{ width: '100%' }} />
|
||||
</div>
|
||||
<span className="font-extrabold text-red-500 w-20 flex-shrink-0">{beforeVal}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-xs mt-1.5">
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
<span className="text-slate-400 w-20 text-right flex-shrink-0">{tool.afterLabel}</span>
|
||||
<div className="flex-1 bg-emerald-100 rounded-full h-2">
|
||||
<div className="bg-emerald-400 h-2 rounded-full" style={{ width: '8%' }} />
|
||||
</div>
|
||||
<span className="font-extrabold text-emerald-600 w-20 flex-shrink-0">{afterVal}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── "안 쓰면 생기는 실패 비용" 섹션 ─── */}
|
||||
<div className="px-6 py-12 lg:px-12 bg-[#0a0f2e]">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="reveal text-center mb-8">
|
||||
<div className="inline-flex items-center gap-2 bg-red-500/15 border border-red-500/30 text-red-400 text-xs font-extrabold px-4 py-1.5 rounded-full uppercase tracking-widest mb-3">
|
||||
<svg className="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 9v4m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" /></svg>
|
||||
AI를 안 쓸 때 생기는 실패 비용
|
||||
</div>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-white mb-2">
|
||||
시간만 낭비되는 게 아닙니다
|
||||
</h2>
|
||||
<p className="text-slate-400 text-sm">반복 업무를 수작업으로 할 때 실제로 발생하는 손실들</p>
|
||||
</div>
|
||||
|
||||
<div className="reveal grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{TOOLS.map((tool, i) => (
|
||||
<div key={i} className="bg-slate-900/60 border border-red-900/40 rounded-2xl p-5">
|
||||
<div className="flex items-start gap-3 mb-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-red-500/15 border border-red-500/25 flex items-center justify-center text-red-400 flex-shrink-0">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</div>
|
||||
<p className="text-xs font-bold text-slate-300">{tool.title} 수작업 시</p>
|
||||
</div>
|
||||
<p className="text-sm text-red-300/80 leading-relaxed border-l-2 border-red-500/30 pl-3">
|
||||
{tool.failCase}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="reveal mt-8 bg-[#0a0f2e] border border-indigo-500/30 rounded-2xl p-6 text-center">
|
||||
<p className="text-white text-lg font-extrabold mb-1">
|
||||
실수 1번이 계약 1건을 날립니다.
|
||||
</p>
|
||||
<p className="text-indigo-300/70 text-sm">
|
||||
월 19,900원으로 시간 손실과 실수 비용을 동시에 없애세요.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 포함 도구 ─── */}
|
||||
<div className="px-6 py-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="reveal text-center mb-8">
|
||||
<div className="inline-flex items-center gap-2 bg-indigo-500/10 border border-indigo-500/25 text-indigo-500 text-xs font-extrabold px-4 py-1.5 rounded-full uppercase tracking-widest mb-3">
|
||||
6가지 AI 자동화 도구 포함
|
||||
</div>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-slate-800">구독하면 바로 쓸 수 있는 도구들</h2>
|
||||
<p className="text-slate-500 text-sm mt-2">ChatGPT · Claude에 복사·붙여넣기만으로 즉시 사용 가능</p>
|
||||
</div>
|
||||
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{TOOLS.map((tool, i) => (
|
||||
<div key={i} className={`reveal reveal-d${(i % 3) + 1} bg-white rounded-2xl p-5 border border-slate-200 hover:border-indigo-300 hover:shadow-lg transition-all group`}>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="w-11 h-11 rounded-xl bg-indigo-50 border border-indigo-100 flex items-center justify-center text-indigo-500 group-hover:bg-indigo-100 transition-colors">
|
||||
{tool.icon}
|
||||
</div>
|
||||
<span className={`text-xs font-bold px-2 py-0.5 rounded-md border ${tool.tagColor}`}>
|
||||
{tool.tag}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-sm font-extrabold text-slate-800 mb-1.5 leading-snug">{tool.title}</h3>
|
||||
<p className="text-xs text-slate-500 leading-relaxed mb-3">{tool.desc}</p>
|
||||
{/* 인라인 Before/After */}
|
||||
<div className="flex items-center gap-2 bg-slate-50 rounded-lg px-3 py-2 border border-slate-100">
|
||||
<span className="text-xs text-red-500 font-bold">{tool.before}</span>
|
||||
<svg className="w-3 h-3 text-slate-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
||||
</svg>
|
||||
<span className="text-xs text-emerald-600 font-bold">{tool.after}</span>
|
||||
<span className="text-xs text-slate-400 ml-auto">{tool.saving.replace('월 ', '').replace(' 절약', '')}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 업데이트 알림 */}
|
||||
<div className="reveal mt-6 bg-indigo-50 border border-indigo-200 rounded-2xl p-5 flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-indigo-100 flex items-center justify-center text-indigo-600 flex-shrink-0">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-bold text-indigo-700">매월 1일 — 새 도구 자동 추가</p>
|
||||
<p className="text-xs text-indigo-600/70 mt-0.5">구독자 요청과 트렌드를 반영해 매달 새 자동화 도구 1~2종이 추가됩니다. 추가 비용 없음.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 누구에게 필요한가 ─── */}
|
||||
<div className="px-6 py-10 lg:px-12 bg-white">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="reveal text-center mb-8">
|
||||
<h2 className="text-2xl font-extrabold text-slate-800">이런 분들이 가장 많이 씁니다</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{[
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||
</svg>
|
||||
),
|
||||
title: '소상공인',
|
||||
pain: '"매일 SNS, 이메일, 리뷰 답변에 2~3시간씩 쓰고 있어요."',
|
||||
gain: '도구 3개만 써도 월 12시간 이상 확보',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
title: '직장인',
|
||||
pain: '"보고서 쓰다가 퇴근 시간 넘기는 게 일상이에요."',
|
||||
gain: '보고서·일지·회의록 시간 90% 감소',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
),
|
||||
title: '온라인 판매자',
|
||||
pain: '"상품 50개 설명 쓰는 데 이틀이 걸렸어요."',
|
||||
gain: '50개 상품 설명 → 25분 완성',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" />
|
||||
</svg>
|
||||
),
|
||||
title: '1인 마케터',
|
||||
pain: '"콘텐츠 아이디어 고갈로 업로드를 자꾸 건너뛰어요."',
|
||||
gain: '한 달치 콘텐츠 기획 → 10분 완성',
|
||||
},
|
||||
].map((item, i) => (
|
||||
<div key={i} className={`reveal reveal-d${i + 1} p-5 rounded-2xl bg-slate-50 border border-slate-100`}>
|
||||
<div className="w-9 h-9 rounded-lg bg-indigo-100 text-indigo-600 flex items-center justify-center mb-3">{item.icon}</div>
|
||||
<p className="text-sm font-extrabold text-slate-800 mb-2">{item.title}</p>
|
||||
<p className="text-xs text-slate-500 italic leading-relaxed mb-3">{item.pain}</p>
|
||||
<p className="text-xs font-bold text-indigo-600 bg-indigo-50 border border-indigo-100 rounded-lg px-2 py-1.5">
|
||||
→ {item.gain}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 사용 후기 ─── */}
|
||||
<div className="px-6 py-10 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="reveal text-center mb-8">
|
||||
<h2 className="text-2xl font-extrabold text-slate-800">실제 사용 후기</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-3 gap-4">
|
||||
{TESTIMONIALS.map((t, i) => (
|
||||
<div key={i} className={`reveal reveal-d${i + 1} bg-white rounded-2xl p-5 border border-slate-200 shadow-sm`}>
|
||||
<div className="flex gap-0.5 mb-3">
|
||||
{Array.from({ length: t.rating }).map((_, j) => (
|
||||
<svg key={j} className="w-4 h-4 text-yellow-400" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
||||
</svg>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-sm text-slate-700 leading-relaxed mb-4">"{t.text}"</p>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-slate-800">{t.name}</p>
|
||||
<p className="text-xs text-slate-400">{t.job}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── FAQ ─── */}
|
||||
<div className="px-6 py-10 lg:px-12 bg-white">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="reveal text-center mb-8">
|
||||
<h2 className="text-2xl font-extrabold text-slate-800">자주 묻는 질문</h2>
|
||||
</div>
|
||||
<div className="reveal space-y-3">
|
||||
{FAQ.map((item, i) => (
|
||||
<div key={i} className="border border-slate-200 rounded-xl p-5">
|
||||
<p className="text-sm font-bold text-slate-800 mb-2">Q. {item.q}</p>
|
||||
<p className="text-sm text-slate-500 leading-relaxed">A. {item.a}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 최하단 CTA ─── */}
|
||||
<div className="px-6 py-14 lg:px-12 bg-[#0a0f2e]" style={{ backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.012) 0px, rgba(255,255,255,0.012) 1px, transparent 1px, transparent 40px)' }}>
|
||||
<div className="reveal max-w-2xl mx-auto text-center">
|
||||
{/* 마지막 카피: 기회비용 프레이밍 */}
|
||||
<p className="text-indigo-300/60 text-sm font-bold uppercase tracking-widest mb-3">구독 안 하면 내일도 동일합니다</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-white mb-3">
|
||||
월 19,900원 vs<br />
|
||||
<span className="text-red-400">
|
||||
월 409,000원어치 시간 낭비
|
||||
</span>
|
||||
</h2>
|
||||
<p className="text-indigo-200/50 text-sm mb-8">
|
||||
도구를 쓰지 않아도 내일은 옵니다. 단, 오늘과 똑같이.
|
||||
<br />언제든 해지 가능하니 한 달만 써보세요.
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<PaymentButton
|
||||
productId="ai_kit_monthly"
|
||||
className="inline-flex items-center justify-center gap-2 bg-indigo-500 hover:bg-indigo-400 text-white text-base font-bold px-8 py-4 rounded-xl transition-colors w-full max-w-sm"
|
||||
>
|
||||
지금 구독 시작하기 — 19,900원/월
|
||||
</PaymentButton>
|
||||
<p className="text-white/25 text-xs">로그인 필요 · 월 19,900원 · 언제든 해지 가능</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'AI 업무 자동화 개발 | 엑셀·이메일·RPA 외주',
|
||||
description:
|
||||
'매일 반복하는 엑셀 정리, 이메일 발송, 보고서 작성을 AI와 파이썬으로 자동화합니다. ChatGPT 연동 자동화, Make.com 플로우, Python RPA 개발. 5만원~, 계약서 포함, 1개월 무상 AS.',
|
||||
keywords: [
|
||||
'업무 자동화 외주',
|
||||
'AI 업무 자동화',
|
||||
'엑셀 자동화 외주',
|
||||
'파이썬 자동화 개발',
|
||||
'RPA 개발 외주',
|
||||
'이메일 자동화',
|
||||
'반복업무 자동화',
|
||||
'ChatGPT 자동화',
|
||||
'Make.com 자동화',
|
||||
'텔레그램 봇 개발',
|
||||
'업무 자동화 비용',
|
||||
'자동화 프리랜서',
|
||||
],
|
||||
openGraph: {
|
||||
title: 'AI 업무 자동화 개발 | 쟁승메이드',
|
||||
description:
|
||||
'엑셀·이메일·보고서 반복 업무를 AI로 자동화. 현직 대기업 개발자가 직접 개발. 5만원~, 계약서 포함, 납기 패널티 적용.',
|
||||
url: 'https://jaengseung-made.com/services/automation',
|
||||
},
|
||||
};
|
||||
|
||||
export default function AutomationLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import ContactModal from '../../components/ContactModal';
|
||||
import { trackCTAClick } from '../../../lib/gtag';
|
||||
|
||||
function useScrollReveal() {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: '0px 0px -40px 0px' }
|
||||
);
|
||||
el.querySelectorAll('.reveal').forEach((child) => observer.observe(child));
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
return ref;
|
||||
}
|
||||
|
||||
const tools = [
|
||||
{
|
||||
id: 'excel',
|
||||
title: '엑셀 자동화 도구',
|
||||
subtitle: 'Excel Macro Toolkit v1.2',
|
||||
desc: '반복 업무를 버튼 하나로 처리하는 엑셀 매크로 모음. 데이터 정리·집계·보고서 자동 생성 기능 포함.',
|
||||
tags: ['VBA', 'Excel', '매크로', '무료'],
|
||||
color: '#16a34a',
|
||||
bgColor: '#f0fdf4',
|
||||
borderColor: '#bbf7d0',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-7 h-7">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.375 19.5h17.25m-17.25 0a1.125 1.125 0 01-1.125-1.125M3.375 19.5h1.5C5.496 19.5 6 18.996 6 18.375m-3.75.125v-1.125c0-.621.504-1.125 1.125-1.125h1.5m0 0v1.25m0-1.25c0-.621.504-1.125 1.125-1.125h1.5m0 0V7.875m0 0c0-.621.504-1.125 1.125-1.125h8.25c.621 0 1.125.504 1.125 1.125v8.25m0 0v1.125m0-1.125c0 .621-.504 1.125-1.125 1.125H6m12-8.25v8.25" />
|
||||
</svg>
|
||||
),
|
||||
href: '/services/automation/tools/excel',
|
||||
ready: true,
|
||||
},
|
||||
{
|
||||
id: 'scraper',
|
||||
title: '웹 스크래핑 도구',
|
||||
subtitle: 'Web Scraper v1.0',
|
||||
desc: '공공데이터·쇼핑몰 가격·뉴스를 자동 수집해 엑셀로 저장하는 Python 기반 수집 도구.',
|
||||
tags: ['Python', 'BeautifulSoup', 'Excel 출력', '무료'],
|
||||
color: '#2563eb',
|
||||
bgColor: '#eff6ff',
|
||||
borderColor: '#bfdbfe',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-7 h-7">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||
</svg>
|
||||
),
|
||||
href: '/services/automation/tools/scraper',
|
||||
ready: true,
|
||||
},
|
||||
{
|
||||
id: 'ppt',
|
||||
title: 'PPT 제작 자동화 도구',
|
||||
subtitle: 'PPT Automation v1.0',
|
||||
desc: '엑셀 데이터를 읽어 표지·내용·마무리 슬라이드를 자동 생성하는 Python 기반 PPT 도구.',
|
||||
tags: ['Python', 'python-pptx', 'openpyxl', '무료'],
|
||||
color: '#7c3aed',
|
||||
bgColor: '#f5f3ff',
|
||||
borderColor: '#ddd6fe',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-7 h-7">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3v11.25A2.25 2.25 0 006 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0118 16.5h-2.25m-7.5 0h7.5m-7.5 0l-1 3m8.5-3l1 3m0 0l.5 1.5m-.5-1.5h-9.5m0 0l-.5 1.5M9 11.25v1.5M12 9v3.75m3-6v6" />
|
||||
</svg>
|
||||
),
|
||||
href: '/services/automation/tools/ppt',
|
||||
ready: true,
|
||||
},
|
||||
];
|
||||
|
||||
const premiumTools = [
|
||||
{
|
||||
id: 'realestate',
|
||||
title: '부동산 매물 크롤링 프로그램',
|
||||
subtitle: 'Real Estate Crawler v1.0',
|
||||
desc: '직방·다방·피터팬·네이버부동산 4개 플랫폼을 동시 수집. 지역·거래유형·매물유형 선택 후 플랫폼별 시트로 Excel 자동 저장.',
|
||||
tags: ['Python', '4개 플랫폼', '중복제거', 'Excel 출력'],
|
||||
price: '3만원',
|
||||
features: ['직방·다방·피터팬·네이버부동산 동시 수집', '지역·거래유형·매물유형 선택', '자동 중복 제거', '플랫폼별 시트 분리 Excel 저장'],
|
||||
color: '#b45309',
|
||||
bgFrom: '#451a03',
|
||||
bgTo: '#78350f',
|
||||
accentColor: '#f59e0b',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-7 h-7">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
||||
</svg>
|
||||
),
|
||||
downloadPath: '/downloads/real_estate_crawler_v1.0.py',
|
||||
},
|
||||
{
|
||||
id: 'accounting',
|
||||
title: '사업장 회계 장부 자동화 프로그램',
|
||||
subtitle: 'Accounting Automation v1.0',
|
||||
desc: '쇼핑몰·식당·제조업·서비스업 업종별 수입/지출 입력 → 매출총이익·영업이익·순이익 자동 계산 + 회계사 관점 조언 리포트.',
|
||||
tags: ['Python', '업종별 맞춤', '손익계산서', '회계 조언'],
|
||||
price: '5만원',
|
||||
features: ['업종별 맞춤 항목 (쇼핑몰·식당·제조·서비스업)', '매출총이익·영업이익·순이익 자동 계산', '부가세 신고 준비 자료 자동 생성', '회계 전문가 관점 경고·조언 리포트'],
|
||||
color: '#0f766e',
|
||||
bgFrom: '#042f2e',
|
||||
bgTo: '#134e4a',
|
||||
accentColor: '#2dd4bf',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-7 h-7">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
|
||||
</svg>
|
||||
),
|
||||
downloadPath: '/downloads/accounting_automation_v1.0.py',
|
||||
},
|
||||
];
|
||||
|
||||
const CHECKLIST = [
|
||||
'자동화하고 싶은 업무를 구체적으로 설명해주세요',
|
||||
'현재 사용 중인 프로그램/시스템 (엑셀, ERP, 쇼핑몰 등)',
|
||||
'자동화 빈도 (매일 / 주 1회 / 월 1회 등)',
|
||||
'희망 납품 일정과 예산 범위',
|
||||
'데이터 민감도 여부 (개인정보 포함 여부)',
|
||||
];
|
||||
|
||||
const automationTypes = [
|
||||
{
|
||||
title: '엑셀 / 구글 시트 자동화',
|
||||
desc: '매일 2시간씩 엑셀에 손가락 아파하던 업무, 밤 11시에 자동 실행되도록 바꿔드립니다.',
|
||||
examples: ['수작업 매출 집계 → 버튼 하나로 완료', '시트 간 데이터 복사 반복 → 전부 자동화', '보고서 양식이 매번 깨짐 → 서식 고정 자동화'],
|
||||
accentColor: 'border-emerald-200 bg-emerald-50', dotColor: 'bg-emerald-500', labelColor: 'text-emerald-700 bg-emerald-100 border-emerald-200',
|
||||
},
|
||||
{
|
||||
title: '웹 스크래핑 · 데이터 수집',
|
||||
desc: '경쟁사 가격 확인하러 탭 10개 열어놓고 복사·붙여넣기 하던 그 시간을 되돌려 드립니다.',
|
||||
examples: ['경쟁사 가격 변동 → 엑셀에 자동 수집', '상품 리뷰 수백 개 → 자동 취합 분석', '공공입찰 공고 → 조건 맞으면 카톡 알림'],
|
||||
accentColor: 'border-blue-200 bg-blue-50', dotColor: 'bg-blue-500', labelColor: 'text-blue-700 bg-blue-100 border-blue-200',
|
||||
},
|
||||
{
|
||||
title: '이메일 자동 발송',
|
||||
desc: '"이 이메일 또 보내야 해?"라는 생각이 드는 모든 발송 업무를 없애드립니다.',
|
||||
examples: ['주문 접수·발송 확인 이메일 자동 발송', '월말 보고서 관련자에게 자동 배포', '미수금 고객에게 단계별 안내 자동 발송'],
|
||||
accentColor: 'border-violet-200 bg-violet-50', dotColor: 'bg-violet-500', labelColor: 'text-violet-700 bg-violet-100 border-violet-200',
|
||||
},
|
||||
{
|
||||
title: '업무 프로세스 RPA',
|
||||
desc: 'ERP에 데이터 입력하고, 파일 정리하고, 같은 버튼 수십 번 클릭하는 일을 대신 합니다.',
|
||||
examples: ['ERP 입력 작업 → 완전 무인 자동화', '월말 파일 정리·백업 → 예약 실행', '발주서 웹 입력 → 로봇이 대신 클릭'],
|
||||
accentColor: 'border-orange-200 bg-orange-50', dotColor: 'bg-orange-500', labelColor: 'text-orange-700 bg-orange-100 border-orange-200',
|
||||
},
|
||||
{
|
||||
title: '텔레그램 봇 개발',
|
||||
desc: '서버가 죽었는데 아무도 모르는 상황, 매출이 터졌는데 혼자만 아는 상황을 없애드립니다.',
|
||||
examples: ['서버 다운·이상 → 즉시 카톡/텔레그램 알림', '하루 매출 현황 → 저녁 6시에 자동 리포트', '신규 주문 들어오면 → 담당자에게 즉시 알림'],
|
||||
accentColor: 'border-cyan-200 bg-cyan-50', dotColor: 'bg-cyan-500', labelColor: 'text-cyan-700 bg-cyan-100 border-cyan-200',
|
||||
},
|
||||
{
|
||||
title: 'API 연동 · 시스템 통합',
|
||||
desc: '두 프로그램이 따로 놀아서 중간에 손으로 옮기고 있다면, 그 사이를 연결해드립니다.',
|
||||
examples: ['CRM → ERP 고객 정보 자동 동기화', '결제 완료 → 재고 차감 자동 연동', '온라인몰 주문 → 배송 시스템 자동 등록'],
|
||||
accentColor: 'border-indigo-200 bg-indigo-50', dotColor: 'bg-indigo-500', labelColor: 'text-indigo-700 bg-indigo-100 border-indigo-200',
|
||||
},
|
||||
];
|
||||
|
||||
const plans = [
|
||||
{ name: '단순 자동화', price: '5만원~', desc: '단일 작업 · 1~3일 소요', examples: '엑셀 매크로, 단순 스크래핑, 이메일 자동화', highlight: false, productId: 'automation_basic' },
|
||||
{ name: '자동화 심화', price: '15만원~', desc: '복합 작업 · 1~2주 소요', examples: 'RPA 프로세스, API 연동, 텔레그램 봇', highlight: true, productId: 'automation_advanced' },
|
||||
{ name: '대형 자동화', price: '협의', desc: '시스템 통합 · 2주 이상 소요', examples: '전사 업무 자동화, 멀티 시스템 통합', highlight: false, productId: null },
|
||||
];
|
||||
|
||||
const process = [
|
||||
{ step: '01', title: '무료 상담', desc: '반복 업무 파악 및 자동화 가능 여부 확인' },
|
||||
{ step: '02', title: '요구사항 분석', desc: '상세 프로세스 분석 및 자동화 범위 결정' },
|
||||
{ step: '03', title: '개발 및 테스트', desc: '실제 데이터로 테스트하며 단계적 개발' },
|
||||
{ step: '04', title: '납품 및 교육', desc: '사용 방법 교육 + 가이드 문서 제공' },
|
||||
{ step: '05', title: 'AS 지원', desc: '1개월 무상 기술 지원 및 버그 수정' },
|
||||
];
|
||||
|
||||
export default function AutomationPage() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalService, setModalService] = useState('업무 자동화');
|
||||
const containerRef = useScrollReveal();
|
||||
|
||||
const openModal = (service: string) => {
|
||||
trackCTAClick(service, '/services/automation');
|
||||
setModalService(service);
|
||||
setModalOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="min-h-full bg-[#f0f5ff]">
|
||||
<style>{`
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(1.5rem);
|
||||
transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.reveal.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.reveal-d1 { transition-delay: 80ms; }
|
||||
.reveal-d2 { transition-delay: 160ms; }
|
||||
.reveal-d3 { transition-delay: 240ms; }
|
||||
`}</style>
|
||||
<ContactModal
|
||||
isOpen={modalOpen}
|
||||
onClose={() => setModalOpen(false)}
|
||||
service={modalService}
|
||||
checklist={CHECKLIST}
|
||||
accentColor="text-cyan-400"
|
||||
headerFrom="#012030"
|
||||
headerTo="#013d50"
|
||||
/>
|
||||
|
||||
{/* ─── Hero ─── */}
|
||||
<div className="relative overflow-hidden bg-[#012030] px-6 py-14 lg:px-12" style={{ backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 40px)' }}>
|
||||
|
||||
<div className="relative max-w-3xl mx-auto">
|
||||
<Link href="/" className="inline-flex items-center gap-1.5 text-cyan-300/60 hover:text-cyan-300 text-sm mb-8 transition">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /></svg>
|
||||
홈으로
|
||||
</Link>
|
||||
<p className="text-cyan-400 text-xs font-bold uppercase tracking-widest mb-4 font-mono">RPA · 업무 자동화 개발</p>
|
||||
<h1 className="text-4xl md:text-5xl font-extrabold text-white mb-4 tracking-tight leading-tight">
|
||||
반복 업무를<br />
|
||||
완전 자동화
|
||||
</h1>
|
||||
<p className="text-cyan-100/50 text-base md:text-lg leading-relaxed max-w-xl mx-auto mb-6">
|
||||
“이 작업 매일 하기 너무 귀찮다”는 생각이 드는 순간, 자동화할 수 있습니다.<br />
|
||||
엑셀, 이메일, 데이터 수집, RPA까지 직접 개발해드립니다.
|
||||
</p>
|
||||
<div className="inline-grid grid-cols-3 gap-px bg-cyan-400/10 border border-cyan-400/20 rounded-2xl overflow-hidden">
|
||||
{[{ v: '1~3일', l: '단순 작업' }, { v: '1~2주', l: '복합 작업' }, { v: '1개월', l: '무상 AS' }].map((s) => (
|
||||
<div key={s.l} className="bg-[#012030]/80 px-5 py-3 text-center">
|
||||
<div className="text-white font-extrabold text-base">{s.v}</div>
|
||||
<div className="text-cyan-400/50 text-xs mt-0.5">{s.l}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 자동화 유형 ─── */}
|
||||
<div className="px-6 py-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-cyan-600 text-xs font-bold uppercase tracking-widest mb-2">AUTOMATION TYPES</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">자동화 유형</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{automationTypes.map((at, idx) => (
|
||||
<div key={at.title} className={`bg-white rounded-2xl border-2 ${at.accentColor} p-5 reveal reveal-d${(idx % 3) + 1}`}>
|
||||
<span className={`inline-block text-xs font-bold px-2 py-0.5 rounded-md border mb-3 ${at.labelColor}`}>{at.title.split(' ')[0]}</span>
|
||||
<h3 className="font-bold text-[#04102b] text-sm mb-2">{at.title}</h3>
|
||||
<p className="text-slate-500 text-xs leading-relaxed mb-3">{at.desc}</p>
|
||||
<div className="space-y-1.5">
|
||||
{at.examples.map((ex) => (
|
||||
<div key={ex} className="flex items-center gap-2 text-xs text-slate-600">
|
||||
<div className={`w-1.5 h-1.5 rounded-full flex-shrink-0 ${at.dotColor}`} />
|
||||
{ex}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 프로세스 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-cyan-600 text-xs font-bold uppercase tracking-widest mb-2">PROCESS</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">진행 프로세스</h2>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="hidden sm:block absolute top-10 left-[10%] right-[10%] h-0.5 bg-[#dbe8ff]" />
|
||||
<div className="grid grid-cols-1 sm:grid-cols-5 gap-4">
|
||||
{process.map((p, idx) => (
|
||||
<div key={p.step} className={`relative text-center reveal reveal-d${(idx % 3) + 1}`}>
|
||||
<div className="w-20 h-20 mx-auto rounded-2xl bg-[#012030] border border-cyan-400/20 flex flex-col items-center justify-center mb-3">
|
||||
<span className="text-cyan-400 text-xs font-bold">STEP</span>
|
||||
<span className="text-white font-extrabold text-lg leading-none">{p.step}</span>
|
||||
</div>
|
||||
<div className="font-bold text-[#04102b] text-sm mb-1">{p.title}</div>
|
||||
<div className="text-slate-500 text-xs leading-relaxed">{p.desc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 예상 비용 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<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]">예상 비용</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-3 gap-5">
|
||||
{plans.map((plan, idx) => (
|
||||
<div key={plan.name} className={`rounded-2xl border p-6 relative flex flex-col reveal reveal-d${idx + 1} ${
|
||||
plan.highlight
|
||||
? 'bg-[#012030] border-cyan-400/30 shadow-2xl shadow-cyan-900/20 scale-105'
|
||||
: 'bg-white border-[#dbe8ff]'
|
||||
}`}>
|
||||
{plan.highlight && (
|
||||
<div className="absolute -top-3.5 left-1/2 -translate-x-1/2 bg-cyan-400 text-[#012030] text-xs font-extrabold px-4 py-1 rounded-full tracking-wide">가장 많이 의뢰</div>
|
||||
)}
|
||||
<div className={`text-xs font-bold mb-2 tracking-wide ${plan.highlight ? 'text-cyan-400' : 'text-slate-400'}`}>{plan.name.toUpperCase()}</div>
|
||||
<div className={`text-3xl font-extrabold mb-1 ${plan.highlight ? 'text-white' : 'text-[#04102b]'}`}>{plan.price}</div>
|
||||
<div className={`text-xs mb-4 ${plan.highlight ? 'text-cyan-300/50' : 'text-slate-400'}`}>{plan.desc}</div>
|
||||
<div className={`text-xs leading-relaxed mb-6 flex-1 p-3 rounded-xl ${plan.highlight ? 'bg-cyan-400/10 text-cyan-100/70' : 'bg-[#f0f5ff] text-slate-600'}`}>
|
||||
예: {plan.examples}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => openModal(`업무 자동화 - ${plan.name}`)}
|
||||
className={`block w-full text-center py-3 rounded-xl text-sm font-bold transition ${
|
||||
plan.highlight ? 'bg-cyan-400 text-[#012030] hover:bg-cyan-300' : 'bg-[#04102b] text-white hover:bg-[#0a1f5c]'
|
||||
}`}
|
||||
>
|
||||
무료 상담 신청
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-center text-slate-400 text-xs mt-4">
|
||||
* 실제 비용은 작업 복잡도, 데이터 양, 연동 시스템에 따라 달라집니다. 무료 상담 후 정확한 견적을 드립니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 프리미엄 툴 ─── */}
|
||||
<div className="px-6 pb-4 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<span className="inline-flex items-center gap-1.5 bg-amber-500/10 border border-amber-400/30 text-amber-700 text-xs font-bold px-3 py-1 rounded-full mb-3">
|
||||
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
||||
PREMIUM TOOLS
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b] mb-2">프리미엄 자동화 프로그램</h2>
|
||||
<p className="text-slate-500 text-sm">전문 분야별 고급 자동화 프로그램. 구매 후 소스코드 전달 + 1개월 무상 지원.</p>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 gap-6">
|
||||
{premiumTools.map((tool, idx) => (
|
||||
<div key={tool.id}
|
||||
className={`rounded-2xl overflow-hidden border border-white/10 shadow-xl flex flex-col reveal reveal-d${idx + 1}`}
|
||||
style={{ background: `linear-gradient(145deg, ${tool.bgFrom}, ${tool.bgTo})` }}>
|
||||
{/* 카드 헤더 */}
|
||||
<div className="p-6 pb-4">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div style={{ color: tool.accentColor }} className="opacity-90">{tool.icon}</div>
|
||||
<div className="flex flex-col items-end gap-1.5">
|
||||
<span className="bg-white/10 text-white text-[10px] font-bold px-2.5 py-1 rounded-full border border-white/20">PREMIUM</span>
|
||||
<span style={{ color: tool.accentColor }} className="text-xl font-extrabold">{tool.price}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ color: tool.accentColor }} className="text-xs font-bold mb-1 opacity-80">{tool.subtitle}</div>
|
||||
<h3 className="text-white font-extrabold text-base mb-2 leading-snug" style={{ wordBreak: 'keep-all' }}>{tool.title}</h3>
|
||||
<p className="text-white/50 text-xs leading-relaxed" style={{ wordBreak: 'keep-all' }}>{tool.desc}</p>
|
||||
</div>
|
||||
{/* 기능 목록 */}
|
||||
<div className="px-6 pb-4 flex-1">
|
||||
<div className="border-t border-white/10 pt-4 space-y-2">
|
||||
{tool.features.map((f) => (
|
||||
<div key={f} className="flex items-start gap-2">
|
||||
<svg className="w-3.5 h-3.5 flex-shrink-0 mt-0.5" style={{ color: tool.accentColor }} viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd"/>
|
||||
</svg>
|
||||
<span className="text-white/70 text-xs" style={{ wordBreak: 'keep-all' }}>{f}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* 태그 + CTA */}
|
||||
<div className="px-6 pb-6">
|
||||
<div className="flex flex-wrap gap-1.5 mb-4">
|
||||
{tool.tags.map((tag) => (
|
||||
<span key={tag} className="text-[10px] font-bold px-2 py-0.5 rounded-full border border-white/20 text-white/60">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => openModal(`업무 자동화 프리미엄 - ${tool.title}`)}
|
||||
className="w-full py-3 rounded-xl text-sm font-extrabold transition-all hover:opacity-90 active:scale-[0.98]"
|
||||
style={{ backgroundColor: tool.accentColor, color: tool.bgFrom }}
|
||||
>
|
||||
구매 문의 · 소스코드 전달 →
|
||||
</button>
|
||||
<p className="text-white/30 text-[10px] text-center mt-2">구매 후 Python 소스코드 + 사용 가이드 전달</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 자동화 툴 무료 다운로드 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-cyan-600 text-xs font-bold uppercase tracking-widest mb-2">FREE TOOLS</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b] mb-2">자동화 도구 무료 다운로드</h2>
|
||||
<p className="text-slate-500 text-sm">직접 만들어 사용 중인 자동화 도구를 무료로 공유합니다.<br />필요에 맞게 수정해서 쓰실 수 있어요.</p>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-3 gap-5">
|
||||
{tools.map((tool, idx) => (
|
||||
<div key={tool.id} style={{ borderColor: tool.borderColor, backgroundColor: tool.bgColor }}
|
||||
className={`rounded-2xl border-2 p-5 flex flex-col relative reveal reveal-d${idx + 1}`}>
|
||||
{!tool.ready && (
|
||||
<div className="absolute top-3 right-3 bg-slate-200 text-slate-500 text-[10px] font-bold px-2 py-0.5 rounded-full">준비중</div>
|
||||
)}
|
||||
<div style={{ color: tool.color }} className="mb-3">{tool.icon}</div>
|
||||
<div style={{ color: tool.color }} className="text-xs font-bold mb-0.5">{tool.subtitle}</div>
|
||||
<div className="font-extrabold text-[#04102b] text-sm mb-2">{tool.title}</div>
|
||||
<p className="text-slate-500 text-xs leading-relaxed mb-4 flex-1">{tool.desc}</p>
|
||||
<div className="flex flex-wrap gap-1.5 mb-4">
|
||||
{tool.tags.map((tag) => (
|
||||
<span key={tag} style={{ color: tool.color, borderColor: tool.borderColor }}
|
||||
className="text-[10px] font-bold px-2 py-0.5 rounded-full border bg-white">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
{tool.ready ? (
|
||||
<Link href={tool.href}
|
||||
style={{ backgroundColor: tool.color }}
|
||||
className="block w-full text-center py-2.5 rounded-xl text-white text-sm font-bold hover:opacity-90 transition">
|
||||
상세보기 · 다운로드 →
|
||||
</Link>
|
||||
) : (
|
||||
<button disabled
|
||||
className="block w-full text-center py-2.5 rounded-xl bg-slate-200 text-slate-400 text-sm font-bold cursor-not-allowed">
|
||||
준비 중입니다
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── CTA ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="bg-[#012030] rounded-2xl border border-cyan-400/20 p-8 text-center reveal" style={{ backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 30px)' }}>
|
||||
<p className="text-cyan-400 text-xs font-bold uppercase tracking-widest mb-2">FREE CONSULTATION</p>
|
||||
<h3 className="text-white text-2xl font-extrabold mb-2">어떤 업무든 상담해보세요</h3>
|
||||
<p className="text-cyan-100/40 text-sm mb-6">자동화 가능한 업무라면 무엇이든 도와드립니다</p>
|
||||
<button
|
||||
onClick={() => openModal('업무 자동화')}
|
||||
className="inline-flex items-center gap-2 bg-cyan-400 hover:bg-cyan-300 text-[#012030] px-8 py-3 rounded-xl font-extrabold text-sm transition-all shadow-lg shadow-cyan-900/30"
|
||||
>
|
||||
자동화 상담 신청 (계약서 포함) →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 010 3.75H5.625a1.875 1.875 0 010-3.75z" />
|
||||
</svg>
|
||||
),
|
||||
title: '중복 데이터 자동 제거',
|
||||
desc: '여러 시트에 흩어진 데이터를 하나로 합치고 중복 행을 자동으로 찾아 제거합니다. 작업 시간 90% 단축.',
|
||||
color: 'text-emerald-600',
|
||||
bg: 'bg-emerald-50',
|
||||
border: 'border-emerald-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M7.5 14.25v2.25m3-4.5v4.5m3-6.75v6.75m3-9v9M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z" />
|
||||
</svg>
|
||||
),
|
||||
title: '일별/월별 집계 자동화',
|
||||
desc: '날짜 컬럼 기준으로 일별·주별·월별 합계를 자동 계산하고 별도 시트에 보고서를 생성합니다.',
|
||||
color: 'text-blue-600',
|
||||
bg: 'bg-blue-50',
|
||||
border: 'border-blue-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
|
||||
</svg>
|
||||
),
|
||||
title: '폴더 내 파일 일괄 처리',
|
||||
desc: '지정한 폴더의 엑셀 파일 전체를 자동으로 열고 데이터를 통합합니다. 파일 수가 많아도 OK.',
|
||||
color: 'text-violet-600',
|
||||
bg: 'bg-violet-50',
|
||||
border: 'border-violet-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9.53 16.122a3 3 0 00-5.78 1.128 2.25 2.25 0 01-2.4 2.245 4.5 4.5 0 008.4-2.245c0-.399-.078-.78-.22-1.128zm0 0a15.998 15.998 0 003.388-1.62m-5.043-.025a15.994 15.994 0 011.622-3.395m3.42 3.42a15.995 15.995 0 004.764-4.648l3.876-5.814a1.151 1.151 0 00-1.597-1.597L14.146 6.32a15.996 15.996 0 00-4.649 4.763m3.42 3.42a6.776 6.776 0 00-3.42-3.42" />
|
||||
</svg>
|
||||
),
|
||||
title: '서식·색상 자동 지정',
|
||||
desc: '값 조건에 따라 셀 색상, 굵기, 테두리를 자동으로 지정합니다. 조건부 서식보다 빠릅니다.',
|
||||
color: 'text-orange-600',
|
||||
bg: 'bg-orange-50',
|
||||
border: 'border-orange-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||
</svg>
|
||||
),
|
||||
title: '키워드 일괄 검색·치환',
|
||||
desc: '전체 시트에서 특정 단어를 찾아 일괄 변경하거나 해당 행만 별도 추출합니다.',
|
||||
color: 'text-cyan-600',
|
||||
bg: 'bg-cyan-50',
|
||||
border: 'border-cyan-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
||||
</svg>
|
||||
),
|
||||
title: 'PDF / CSV 자동 저장',
|
||||
desc: '작업 완료 후 PDF 또는 CSV 형식으로 자동 저장하고 지정한 폴더에 날짜별로 백업합니다.',
|
||||
color: 'text-rose-600',
|
||||
bg: 'bg-rose-50',
|
||||
border: 'border-rose-200',
|
||||
},
|
||||
];
|
||||
|
||||
const howToUse = [
|
||||
{ step: '01', title: '파일 다운로드', desc: '아래 버튼으로 .xlsm 파일을 받습니다. 매크로 포함 형식입니다.' },
|
||||
{ step: '02', title: '매크로 실행 허용', desc: '파일을 열면 상단 노란 바에서 "콘텐츠 사용" 버튼을 클릭합니다.' },
|
||||
{ step: '03', title: '데이터 시트에 붙여넣기', desc: '"Data" 시트에 내 데이터를 붙여넣습니다. A1부터 시작하면 됩니다.' },
|
||||
{ step: '04', title: '매크로 버튼 클릭', desc: '"Control" 시트에서 원하는 기능 버튼을 클릭하면 자동 실행됩니다.' },
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
q: '맥(Mac)에서도 사용할 수 있나요?',
|
||||
a: 'Excel for Mac에서도 대부분 동작하나, VBA 일부 기능(파일 다이얼로그 등)은 Windows 전용입니다. Mac 사용자는 상담을 통해 호환 버전으로 수정 가능합니다.',
|
||||
},
|
||||
{
|
||||
q: '파일이 열리지 않거나 오류가 발생하면요?',
|
||||
a: 'Excel 2016 이상 버전을 권장합니다. 보안 정책으로 매크로가 차단된 경우 Excel 옵션 → 보안 센터 → 매크로 설정에서 "알림과 함께 VBA 매크로 사용"으로 변경해 주세요.',
|
||||
},
|
||||
{
|
||||
q: '내 업무에 맞게 수정이 가능한가요?',
|
||||
a: '파일 내 VBA 코드는 자유롭게 수정할 수 있습니다. 수정이 어려우시면 맞춤 자동화 개발 서비스로 문의해 주세요. 내 업무에 딱 맞는 버전을 만들어 드립니다.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function ExcelToolPage() {
|
||||
const [openFaq, setOpenFaq] = useState<number | null>(null);
|
||||
|
||||
return (
|
||||
<div className="min-h-full bg-[#f0f5ff]">
|
||||
|
||||
{/* Hero */}
|
||||
<div className="bg-gradient-to-br from-[#052e16] via-[#14532d] to-[#052e16] px-6 py-12 lg:px-12">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<Link href="/services/automation"
|
||||
className="inline-flex items-center gap-1.5 text-emerald-300/60 hover:text-emerald-300 text-sm mb-6 transition">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
업무 자동화 서비스로 돌아가기
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-6">
|
||||
<div className="w-20 h-20 rounded-2xl bg-emerald-400/15 border border-emerald-400/30 flex items-center justify-center flex-shrink-0">
|
||||
<svg className="w-10 h-10 text-emerald-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.375 19.5h17.25m-17.25 0a1.125 1.125 0 01-1.125-1.125M3.375 19.5h1.5C5.496 19.5 6 18.996 6 18.375m-3.75.125v-1.125c0-.621.504-1.125 1.125-1.125h1.5m0 0v1.25m0-1.25c0-.621.504-1.125 1.125-1.125h1.5m0 0V7.875m0 0c0-.621.504-1.125 1.125-1.125h8.25c.621 0 1.125.504 1.125 1.125v8.25m0 0v1.125m0-1.125c0 .621-.504 1.125-1.125 1.125H6m12-8.25v8.25" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-emerald-400 text-xs font-bold uppercase tracking-widest">FREE TOOL</span>
|
||||
<span className="bg-emerald-400/20 border border-emerald-400/40 text-emerald-300 text-[10px] font-bold px-2 py-0.5 rounded-full">v1.2</span>
|
||||
<span className="bg-white/10 text-white/50 text-[10px] font-bold px-2 py-0.5 rounded-full">VBA · Excel</span>
|
||||
</div>
|
||||
<h1 className="text-3xl md:text-4xl font-extrabold text-white mb-2 leading-tight">
|
||||
엑셀 자동화 도구<br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-emerald-400 to-teal-300">
|
||||
Excel Macro Toolkit
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-emerald-100/50 text-sm leading-relaxed">
|
||||
매일 반복하는 엑셀 작업을 버튼 하나로 처리하는 VBA 매크로 모음입니다.<br />
|
||||
데이터 정리, 중복 제거, 집계, 보고서 생성까지 실무에서 검증된 기능들만 담았습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 통계 배지 */}
|
||||
<div className="mt-8 inline-grid grid-cols-3 gap-px bg-emerald-400/10 border border-emerald-400/20 rounded-2xl overflow-hidden">
|
||||
{[
|
||||
{ v: '6가지', l: '핵심 기능' },
|
||||
{ v: '무료', l: '완전 무료' },
|
||||
{ v: 'Excel 2016+', l: '지원 버전' },
|
||||
].map((s) => (
|
||||
<div key={s.l} className="bg-[#052e16]/80 px-5 py-3 text-center">
|
||||
<div className="text-white font-extrabold text-base">{s.v}</div>
|
||||
<div className="text-emerald-400/50 text-xs mt-0.5">{s.l}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-10 lg:px-12">
|
||||
<div className="max-w-4xl mx-auto space-y-10">
|
||||
|
||||
{/* 다운로드 카드 */}
|
||||
<div className="bg-white rounded-2xl border-2 border-emerald-200 p-6 flex flex-col sm:flex-row items-center gap-6">
|
||||
<div className="flex-1">
|
||||
<div className="text-emerald-700 text-xs font-bold uppercase tracking-widest mb-1">DOWNLOAD</div>
|
||||
<div className="font-extrabold text-[#04102b] text-lg mb-1">Excel_Macro_Toolkit_v1.2.xlsm</div>
|
||||
<div className="text-slate-500 text-xs mb-3">크기: 약 85KB · 매크로 포함 형식 · 상업적 이용 가능</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{['VBA 매크로', '6가지 기능', 'Control 시트 UI', '가이드 시트 포함'].map((t) => (
|
||||
<span key={t} className="text-[10px] font-bold px-2 py-0.5 rounded-full border border-emerald-200 text-emerald-700 bg-emerald-50">{t}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-full sm:w-auto">
|
||||
<a
|
||||
href={`${process.env.NEXT_PUBLIC_CDN_URL ?? ''}/downloads/Excel_Macro_Toolkit_v1.2.xlsm`}
|
||||
download
|
||||
className="flex items-center justify-center gap-2 bg-emerald-600 hover:bg-emerald-500 text-white px-6 py-3 rounded-xl font-extrabold text-sm transition-all shadow-lg shadow-emerald-900/20 w-full sm:w-48"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
||||
</svg>
|
||||
무료 다운로드
|
||||
</a>
|
||||
<p className="text-[10px] text-slate-400 text-center">로그인 없이 즉시 다운로드</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 기능 소개 */}
|
||||
<div>
|
||||
<div className="text-center mb-6">
|
||||
<p className="text-emerald-600 text-xs font-bold uppercase tracking-widest mb-1">FEATURES</p>
|
||||
<h2 className="text-2xl font-extrabold text-[#04102b]">포함된 기능 6가지</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{features.map((f) => (
|
||||
<div key={f.title} className={`${f.bg} border-2 ${f.border} rounded-2xl p-5`}>
|
||||
<div className={`${f.color} mb-3`}>{f.icon}</div>
|
||||
<div className="font-bold text-[#04102b] text-sm mb-1.5">{f.title}</div>
|
||||
<p className="text-slate-500 text-xs leading-relaxed">{f.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 사용 방법 */}
|
||||
<div>
|
||||
<div className="text-center mb-6">
|
||||
<p className="text-emerald-600 text-xs font-bold uppercase tracking-widest mb-1">HOW TO USE</p>
|
||||
<h2 className="text-2xl font-extrabold text-[#04102b]">사용 방법</h2>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="hidden sm:block absolute top-10 left-[10%] right-[10%] h-0.5 bg-gradient-to-r from-emerald-200 via-teal-100 to-emerald-200" />
|
||||
<div className="grid grid-cols-1 sm:grid-cols-4 gap-4">
|
||||
{howToUse.map((h) => (
|
||||
<div key={h.step} className="text-center">
|
||||
<div className="w-20 h-20 mx-auto rounded-2xl bg-gradient-to-br from-[#052e16] to-[#14532d] border border-emerald-400/20 flex flex-col items-center justify-center mb-3 shadow-lg">
|
||||
<span className="text-emerald-400 text-[10px] font-bold">STEP</span>
|
||||
<span className="text-white font-extrabold text-lg leading-none">{h.step}</span>
|
||||
</div>
|
||||
<div className="font-bold text-[#04102b] text-sm mb-1">{h.title}</div>
|
||||
<div className="text-slate-500 text-xs leading-relaxed">{h.desc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 미리보기 (목업) */}
|
||||
<div>
|
||||
<div className="text-center mb-6">
|
||||
<p className="text-emerald-600 text-xs font-bold uppercase tracking-widest mb-1">PREVIEW</p>
|
||||
<h2 className="text-2xl font-extrabold text-[#04102b]">화면 미리보기</h2>
|
||||
</div>
|
||||
<div className="bg-white rounded-2xl border border-[#dbe8ff] overflow-hidden shadow-sm">
|
||||
{/* Excel 목업 */}
|
||||
<div className="bg-[#1e7145] px-4 py-2 flex items-center gap-3">
|
||||
<div className="flex gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-white/30" />
|
||||
<div className="w-3 h-3 rounded-full bg-white/30" />
|
||||
<div className="w-3 h-3 rounded-full bg-white/30" />
|
||||
</div>
|
||||
<span className="text-white/80 text-xs font-medium">Excel_Macro_Toolkit_v1.2.xlsm</span>
|
||||
</div>
|
||||
{/* 탭 */}
|
||||
<div className="flex border-b border-slate-200 bg-slate-50 px-2 pt-1 gap-1 overflow-x-auto">
|
||||
{['Control', 'Data', 'Report', 'Guide'].map((tab, i) => (
|
||||
<div key={tab}
|
||||
className={`px-4 py-1.5 text-xs font-bold rounded-t-lg border-t border-x ${i === 0 ? 'bg-white border-slate-200 text-[#1e7145]' : 'bg-slate-100 border-slate-200 text-slate-400'}`}>
|
||||
{tab}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Control 시트 목업 */}
|
||||
<div className="p-6 bg-white">
|
||||
<div className="text-center mb-5">
|
||||
<div className="text-lg font-extrabold text-[#1e7145] mb-0.5">📊 Excel Macro Toolkit v1.2</div>
|
||||
<div className="text-slate-400 text-xs">원하는 기능 버튼을 클릭하세요</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
{[
|
||||
{ label: '중복 제거', color: '#16a34a' },
|
||||
{ label: '일별 집계', color: '#2563eb' },
|
||||
{ label: '파일 통합', color: '#7c3aed' },
|
||||
{ label: '서식 자동화', color: '#ea580c' },
|
||||
{ label: '키워드 검색', color: '#0891b2' },
|
||||
{ label: 'PDF 저장', color: '#dc2626' },
|
||||
].map((btn) => (
|
||||
<div key={btn.label}
|
||||
style={{ borderColor: btn.color, color: btn.color }}
|
||||
className="border-2 rounded-lg py-3 text-center text-sm font-bold cursor-default hover:opacity-80 transition bg-white shadow-sm">
|
||||
{btn.label}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4 p-3 bg-slate-50 rounded-xl border border-slate-200">
|
||||
<div className="text-[10px] text-slate-400 font-bold mb-1">상태 로그</div>
|
||||
<div className="text-xs text-emerald-600 font-medium">✓ 준비 완료. 데이터 시트에 작업할 데이터를 붙여넣은 후 버튼을 클릭하세요.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* FAQ */}
|
||||
<div>
|
||||
<div className="text-center mb-6">
|
||||
<p className="text-emerald-600 text-xs font-bold uppercase tracking-widest mb-1">FAQ</p>
|
||||
<h2 className="text-2xl font-extrabold text-[#04102b]">자주 묻는 질문</h2>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{faqs.map((faq, i) => (
|
||||
<div key={i} className="bg-white rounded-2xl border border-[#dbe8ff] overflow-hidden">
|
||||
<button
|
||||
onClick={() => setOpenFaq(openFaq === i ? null : i)}
|
||||
className="w-full flex items-center justify-between px-5 py-4 text-left">
|
||||
<span className="font-bold text-[#04102b] text-sm">{faq.q}</span>
|
||||
<svg
|
||||
className={`w-4 h-4 text-slate-400 flex-shrink-0 transition-transform ${openFaq === i ? 'rotate-180' : ''}`}
|
||||
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openFaq === i && (
|
||||
<div className="px-5 pb-4 text-slate-600 text-sm leading-relaxed border-t border-slate-100 pt-3">
|
||||
{faq.a}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 하단 CTA */}
|
||||
<div className="bg-gradient-to-r from-[#052e16] to-[#14532d] rounded-2xl border border-emerald-400/20 p-8 text-center">
|
||||
<p className="text-emerald-400 text-xs font-bold uppercase tracking-widest mb-2">CUSTOM AUTOMATION</p>
|
||||
<h3 className="text-white text-xl font-extrabold mb-2">내 업무에 맞게 수정이 필요하신가요?</h3>
|
||||
<p className="text-emerald-100/40 text-sm mb-6">
|
||||
기본 도구로 부족하다면, 업무 프로세스에 딱 맞는 전용 자동화 도구를 제작해 드립니다.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-center">
|
||||
<a
|
||||
href={`${process.env.NEXT_PUBLIC_CDN_URL ?? ''}/downloads/Excel_Macro_Toolkit_v1.2.xlsm`}
|
||||
download
|
||||
className="inline-flex items-center justify-center gap-2 bg-emerald-400 hover:bg-emerald-300 text-[#052e16] px-6 py-3 rounded-xl font-extrabold text-sm transition-all"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
||||
</svg>
|
||||
무료 다운로드
|
||||
</a>
|
||||
<Link href="/freelance?service=업무+자동화"
|
||||
className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white border border-white/20 px-6 py-3 rounded-xl font-extrabold text-sm transition-all">
|
||||
맞춤 개발 문의 →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'PPT 제작 자동화 도구',
|
||||
description:
|
||||
'엑셀 데이터로 PPT를 자동 생성하는 Python 스크립트. 표지·내용·마무리 슬라이드 자동 구성, 색상 테마 커스터마이징. python-pptx 기반. 무료 다운로드.',
|
||||
keywords: [
|
||||
'PPT 자동화',
|
||||
'파워포인트 자동 생성',
|
||||
'python-pptx',
|
||||
'PPT 제작 도구',
|
||||
'엑셀 PPT 변환',
|
||||
'프레젠테이션 자동화',
|
||||
'무료 PPT 도구',
|
||||
],
|
||||
openGraph: {
|
||||
title: 'PPT 제작 자동화 도구 | 쟁승메이드',
|
||||
description: 'Python + python-pptx 기반 PPT 자동 생성 도구 무료 다운로드. 엑셀 연동 지원.',
|
||||
url: 'https://jaengseung-made.com/services/automation/tools/ppt',
|
||||
},
|
||||
};
|
||||
|
||||
export default function PptLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
@@ -1,365 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3v11.25A2.25 2.25 0 006 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0118 16.5h-2.25m-7.5 0h7.5m-7.5 0l-1 3m8.5-3l1 3m0 0l.5 1.5m-.5-1.5h-9.5m0 0l-.5 1.5M9 11.25v1.5M12 9v3.75m3-6v6" />
|
||||
</svg>
|
||||
),
|
||||
title: '표지 · 내용 · 마무리 자동 생성',
|
||||
desc: '표지(제목/날짜), 내용 슬라이드(불릿 포인트), 마무리 슬라이드까지 3가지 레이아웃을 자동으로 구성합니다.',
|
||||
color: 'text-orange-600',
|
||||
bg: 'bg-orange-50',
|
||||
border: 'border-orange-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.375 19.5h17.25m-17.25 0a1.125 1.125 0 01-1.125-1.125M3.375 19.5h1.5C5.496 19.5 6 18.996 6 18.375m-3.75.125v-1.125c0-.621.504-1.125 1.125-1.125h1.5m0 0v1.25m0-1.25c0-.621.504-1.125 1.125-1.125h1.5m0 0V7.875m0 0c0-.621.504-1.125 1.125-1.125h8.25c.621 0 1.125.504 1.125 1.125v8.25m0 0v1.125m0-1.125c0 .621-.504 1.125-1.125 1.125H6m12-8.25v8.25" />
|
||||
</svg>
|
||||
),
|
||||
title: '엑셀에서 데이터 일괄 생성',
|
||||
desc: 'data.xlsx 파일의 A열(제목), B~열(불릿 내용)을 읽어 슬라이드를 자동 생성합니다. 수십 장도 한 번에 처리.',
|
||||
color: 'text-emerald-600',
|
||||
bg: 'bg-emerald-50',
|
||||
border: 'border-emerald-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4.098 19.902a3.75 3.75 0 005.304 0l6.401-6.402M6.75 21A3.75 3.75 0 013 17.25V4.125C3 3.504 3.504 3 4.125 3h5.25c.621 0 1.125.504 1.125 1.125v4.072M6.75 21a3.75 3.75 0 003.75-3.75V8.197M6.75 21h13.125c.621 0 1.125-.504 1.125-1.125v-5.25c0-.621-.504-1.125-1.125-1.125h-4.072M10.5 8.197l2.88-2.88c.438-.439 1.15-.439 1.59 0l3.712 3.713c.44.44.44 1.152 0 1.59l-2.879 2.88" />
|
||||
</svg>
|
||||
),
|
||||
title: '색상 테마 커스터마이징',
|
||||
desc: '상단 설정 영역에서 PRIMARY, SECONDARY, ACCENT 색상을 RGB로 변경하면 전체 슬라이드에 즉시 반영됩니다.',
|
||||
color: 'text-violet-600',
|
||||
bg: 'bg-violet-50',
|
||||
border: 'border-violet-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z" />
|
||||
</svg>
|
||||
),
|
||||
title: '슬라이드 번호 자동 추가',
|
||||
desc: '각 내용 슬라이드 우측 상단에 슬라이드 번호(01, 02...)가 자동으로 표시됩니다. 따로 설정할 필요 없음.',
|
||||
color: 'text-blue-600',
|
||||
bg: 'bg-blue-50',
|
||||
border: 'border-blue-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 17.25v1.007a3 3 0 01-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0115 18.257V17.25m6-12V15a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 15V5.25m18 0A2.25 2.25 0 0018.75 3H5.25A2.25 2.25 0 003 5.25m18 0V12a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 12V5.25" />
|
||||
</svg>
|
||||
),
|
||||
title: '16:9 비율 · 맑은 고딕 폰트',
|
||||
desc: '발표 표준 비율인 16:9(13.33×7.5인치)로 설정되며, 한글 가독성이 좋은 맑은 고딕 폰트를 기본 적용합니다.',
|
||||
color: 'text-cyan-600',
|
||||
bg: 'bg-cyan-50',
|
||||
border: 'border-cyan-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
),
|
||||
title: '예시 데이터 자동 실행',
|
||||
desc: 'data.xlsx 파일이 없어도 내장 예시 데이터로 바로 실행됩니다. 처음 사용할 때 결과를 즉시 확인 가능.',
|
||||
color: 'text-rose-600',
|
||||
bg: 'bg-rose-50',
|
||||
border: 'border-rose-200',
|
||||
},
|
||||
];
|
||||
|
||||
const howToUse = [
|
||||
{
|
||||
step: '01',
|
||||
title: '패키지 설치',
|
||||
desc: '터미널에서 필요한 Python 패키지를 설치합니다.',
|
||||
code: 'pip install python-pptx openpyxl',
|
||||
color: 'bg-orange-500',
|
||||
},
|
||||
{
|
||||
step: '02',
|
||||
title: '설정 수정',
|
||||
desc: '스크립트 상단 설정 영역에서 제목, 날짜, 색상 테마를 수정합니다.',
|
||||
code: 'TITLE_TEXT = "발표 제목"\nCOLOR_PRIMARY = RGBColor(0x1D, 0x4E, 0xD8)',
|
||||
color: 'bg-emerald-500',
|
||||
},
|
||||
{
|
||||
step: '03',
|
||||
title: '엑셀 데이터 준비',
|
||||
desc: 'data.xlsx를 만들어 A열=슬라이드 제목, B~열=불릿 내용을 입력합니다. (없으면 예시 데이터로 실행)',
|
||||
code: 'A열: 슬라이드 제목\nB~열: 불릿 포인트 내용',
|
||||
color: 'bg-violet-500',
|
||||
},
|
||||
{
|
||||
step: '04',
|
||||
title: '실행 후 확인',
|
||||
desc: '터미널에서 스크립트를 실행하면 같은 폴더에 PPT 파일이 자동 저장됩니다.',
|
||||
code: 'python ppt_automation_v1.0.py',
|
||||
color: 'bg-blue-500',
|
||||
},
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
q: '엑셀 파일이 없어도 실행되나요?',
|
||||
a: 'data.xlsx 파일이 없으면 내장 예시 데이터로 자동 실행됩니다. 먼저 결과를 확인한 뒤 자신의 데이터로 교체하면 됩니다.',
|
||||
},
|
||||
{
|
||||
q: '슬라이드 수에 제한이 있나요?',
|
||||
a: '제한 없습니다. 엑셀에 입력한 행 수만큼 슬라이드가 생성됩니다. 단, 슬라이드당 불릿 포인트는 최대 8개입니다.',
|
||||
},
|
||||
{
|
||||
q: '챕터 구분 슬라이드도 넣을 수 있나요?',
|
||||
a: 'create_divider_slide() 함수가 포함되어 있습니다. main() 함수에서 원하는 위치에 호출하면 챕터 구분 슬라이드를 추가할 수 있습니다.',
|
||||
},
|
||||
{
|
||||
q: '맥(Mac)에서도 사용할 수 있나요?',
|
||||
a: '맥에서도 동일하게 사용 가능합니다. 단, 맥에는 맑은 고딕 폰트가 없으므로 FONT_NAME을 "AppleGothic" 또는 "Nanum Gothic"으로 변경하세요.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function PptToolPage() {
|
||||
return (
|
||||
<div className="min-h-full bg-[#f0f5ff]">
|
||||
|
||||
{/* ─── Hero ─── */}
|
||||
<div className="relative overflow-hidden bg-gradient-to-br from-[#1a0a3d] via-[#2d1560] to-[#1a0a3d] px-6 py-14 lg:px-12">
|
||||
<div className="absolute inset-0 opacity-[0.05]">
|
||||
<svg width="100%" height="100%" viewBox="0 0 800 300" preserveAspectRatio="xMidYMid slice">
|
||||
<rect x="50" y="40" width="200" height="130" rx="6" fill="none" stroke="#c084fc" strokeWidth="1.5"/>
|
||||
<line x1="70" y1="70" x2="230" y2="70" stroke="#c084fc" strokeWidth="1"/>
|
||||
<line x1="70" y1="90" x2="200" y2="90" stroke="#c084fc" strokeWidth="1"/>
|
||||
<line x1="70" y1="110" x2="210" y2="110" stroke="#c084fc" strokeWidth="1"/>
|
||||
<rect x="320" y="60" width="200" height="130" rx="6" fill="none" stroke="#c084fc" strokeWidth="1.5"/>
|
||||
<rect x="340" y="75" width="160" height="20" rx="3" fill="#c084fc" fillOpacity="0.2"/>
|
||||
<line x1="340" y1="110" x2="500" y2="110" stroke="#c084fc" strokeWidth="1"/>
|
||||
<line x1="340" y1="130" x2="480" y2="130" stroke="#c084fc" strokeWidth="1"/>
|
||||
<rect x="590" y="40" width="160" height="130" rx="6" fill="none" stroke="#c084fc" strokeWidth="1.5"/>
|
||||
<line x1="610" y1="100" x2="730" y2="100" stroke="#c084fc" strokeWidth="1.5"/>
|
||||
<line x1="660" y1="110" x2="660" y2="160" stroke="#c084fc" strokeWidth="1"/>
|
||||
<path d="M610 160 L660 110 L710 145 L730 120" fill="none" stroke="#c084fc" strokeWidth="1.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="relative max-w-3xl mx-auto text-center">
|
||||
<Link href="/services/automation" className="inline-flex items-center gap-1.5 text-purple-300/60 hover:text-purple-300 text-sm mb-6 transition">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /></svg>
|
||||
업무 자동화로
|
||||
</Link>
|
||||
|
||||
<div className="w-16 h-16 mx-auto rounded-2xl bg-purple-400/15 border border-purple-400/25 flex items-center justify-center mb-5">
|
||||
<svg className="w-9 h-9 text-purple-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3v11.25A2.25 2.25 0 006 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0118 16.5h-2.25m-7.5 0h7.5m-7.5 0l-1 3m8.5-3l1 3m0 0l.5 1.5m-.5-1.5h-9.5m0 0l-.5 1.5M9 11.25v1.5M12 9v3.75m3-6v6" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<p className="text-purple-400/70 text-xs font-bold uppercase tracking-widest mb-2">PPT AUTOMATION · 프레젠테이션 자동화</p>
|
||||
<h1 className="text-4xl md:text-5xl font-extrabold text-white mb-4 tracking-tight leading-tight">
|
||||
PPT 제작을<br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-400">코드로 자동화</span>
|
||||
</h1>
|
||||
<p className="text-purple-100/50 text-base md:text-lg leading-relaxed max-w-xl mx-auto mb-6">
|
||||
엑셀 데이터만 준비하면 표지·내용·마무리 슬라이드를 자동 생성.<br />
|
||||
python-pptx 기반으로 디자인까지 자동 적용됩니다.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-3">
|
||||
<a
|
||||
href="/downloads/ppt_automation_v1.0.py"
|
||||
download
|
||||
className="inline-flex items-center gap-2 bg-purple-400 hover:bg-purple-300 text-[#1a0a3d] px-8 py-3.5 rounded-xl font-extrabold text-sm transition-all shadow-lg shadow-purple-900/30"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
|
||||
무료 다운로드 (Python .py)
|
||||
</a>
|
||||
<Link href="/freelance?service=PPT+자동화+맞춤+개발"
|
||||
className="inline-flex items-center gap-2 bg-white/10 hover:bg-white/20 text-white border border-white/20 px-6 py-3.5 rounded-xl font-bold text-sm transition-all">
|
||||
맞춤 개발 문의 →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 다운로드 카드 ─── */}
|
||||
<div className="px-6 py-10 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="bg-white rounded-2xl border-2 border-purple-200 p-6 flex flex-col md:flex-row items-start md:items-center gap-6">
|
||||
<div className="w-14 h-14 rounded-2xl bg-purple-50 border border-purple-200 flex items-center justify-center flex-shrink-0">
|
||||
<svg className="w-7 h-7 text-purple-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3v11.25A2.25 2.25 0 006 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0118 16.5h-2.25m-7.5 0h7.5m-7.5 0l-1 3m8.5-3l1 3m0 0l.5 1.5m-.5-1.5h-9.5m0 0l-.5 1.5M9 11.25v1.5M12 9v3.75m3-6v6" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="text-purple-600 text-xs font-bold">PPT AUTOMATION v1.0</span>
|
||||
<span className="bg-purple-100 text-purple-700 text-[10px] font-bold px-2 py-0.5 rounded-full border border-purple-200">무료</span>
|
||||
</div>
|
||||
<div className="font-extrabold text-[#04102b] text-lg mb-1">PPT 제작 자동화 도구</div>
|
||||
<p className="text-slate-500 text-sm">python-pptx 기반 · 엑셀 연동 · 표지/내용/마무리 자동 생성 · 색상 테마 커스터마이징</p>
|
||||
<div className="flex flex-wrap gap-2 mt-3">
|
||||
{['Python 3.8+', 'python-pptx', 'openpyxl', '한글 지원', '엑셀 연동'].map((tag) => (
|
||||
<span key={tag} className="text-[10px] font-bold px-2 py-0.5 rounded-full border border-purple-200 text-purple-600 bg-purple-50">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href="/downloads/ppt_automation_v1.0.py"
|
||||
download
|
||||
className="flex-shrink-0 inline-flex items-center gap-2 bg-purple-600 hover:bg-purple-700 text-white px-6 py-3 rounded-xl font-bold text-sm transition"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
|
||||
다운로드
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 기능 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<p className="text-purple-600 text-xs font-bold uppercase tracking-widest mb-2">FEATURES</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">주요 기능</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{features.map((f) => (
|
||||
<div key={f.title} className={`bg-white rounded-2xl border-2 ${f.border} p-5`}>
|
||||
<div className={`w-9 h-9 rounded-xl ${f.bg} flex items-center justify-center mb-3 ${f.color}`}>
|
||||
{f.icon}
|
||||
</div>
|
||||
<h3 className="font-bold text-[#04102b] text-sm mb-2">{f.title}</h3>
|
||||
<p className="text-slate-500 text-xs leading-relaxed">{f.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 사용법 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<p className="text-purple-600 text-xs font-bold uppercase tracking-widest mb-2">HOW TO USE</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">사용 방법</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
{howToUse.map((h) => (
|
||||
<div key={h.step} className="bg-white rounded-2xl border border-slate-200 p-6">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className={`w-8 h-8 rounded-lg ${h.color} flex items-center justify-center text-white text-xs font-extrabold flex-shrink-0`}>
|
||||
{h.step}
|
||||
</div>
|
||||
<h3 className="font-bold text-[#04102b] text-sm">{h.title}</h3>
|
||||
</div>
|
||||
<p className="text-slate-500 text-xs leading-relaxed mb-3">{h.desc}</p>
|
||||
<div className="bg-[#1a0a3d] rounded-xl px-4 py-3 font-mono text-[11px] text-purple-200 whitespace-pre leading-relaxed">
|
||||
{h.code}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 코드 미리보기 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-6">
|
||||
<p className="text-purple-600 text-xs font-bold uppercase tracking-widest mb-2">PREVIEW</p>
|
||||
<h2 className="text-2xl font-extrabold text-[#04102b]">설정 영역 미리보기</h2>
|
||||
<p className="text-slate-500 text-sm mt-1">이 부분만 수정하면 원하는 PPT가 완성됩니다</p>
|
||||
</div>
|
||||
<div className="bg-[#1a0a3d] rounded-2xl overflow-hidden border border-purple-900/40">
|
||||
<div className="flex items-center gap-2 px-5 py-3 border-b border-purple-900/40">
|
||||
<div className="w-3 h-3 rounded-full bg-rose-400/70" />
|
||||
<div className="w-3 h-3 rounded-full bg-amber-400/70" />
|
||||
<div className="w-3 h-3 rounded-full bg-emerald-400/70" />
|
||||
<span className="ml-2 text-purple-300/50 text-xs font-mono">ppt_automation_v1.0.py</span>
|
||||
</div>
|
||||
<pre className="px-5 py-5 text-xs font-mono text-purple-100 leading-6 overflow-x-auto">{`<span class="text-purple-400"># ── 설정 (이 부분을 수정하세요) ──────────────</span>
|
||||
|
||||
DATA_FILE = <span class="text-amber-300">"data.xlsx"</span> <span class="text-slate-400"># 입력 엑셀 파일</span>
|
||||
OUTPUT_FILE = f<span class="text-amber-300">"발표자료_{datetime}.pptx"</span>
|
||||
|
||||
<span class="text-slate-400"># 표지 정보</span>
|
||||
TITLE_TEXT = <span class="text-amber-300">"발표 제목을 입력하세요"</span>
|
||||
SUBTITLE_TEXT = <span class="text-amber-300">"부제목 또는 발표자 이름"</span>
|
||||
DATE_TEXT = <span class="text-amber-300">"2025년 01월 01일"</span>
|
||||
|
||||
<span class="text-slate-400"># 색상 테마 (RGB 값으로 변경)</span>
|
||||
COLOR_PRIMARY = RGBColor(<span class="text-emerald-400">0x1D, 0x4E, 0xD8</span>) <span class="text-slate-400"># 파란색</span>
|
||||
COLOR_SECONDARY = RGBColor(<span class="text-emerald-400">0x0F, 0x17, 0x2A</span>) <span class="text-slate-400"># 다크 네이비</span>
|
||||
COLOR_ACCENT = RGBColor(<span class="text-emerald-400">0x60, 0xA5, 0xFA</span>) <span class="text-slate-400"># 라이트 블루</span>
|
||||
|
||||
FONT_NAME = <span class="text-amber-300">"맑은 고딕"</span> <span class="text-slate-400"># 한글 폰트</span>`}
|
||||
</pre>
|
||||
</div>
|
||||
<p className="text-center text-slate-400 text-xs mt-3">
|
||||
* 코드 미리보기는 실제 파일의 일부입니다. 다운로드 후 설정 영역 전체를 수정해서 사용하세요.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── FAQ ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="text-center mb-8">
|
||||
<p className="text-purple-600 text-xs font-bold uppercase tracking-widest mb-2">FAQ</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">자주 묻는 질문</h2>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{faqs.map((faq) => (
|
||||
<div key={faq.q} className="bg-white rounded-2xl border border-slate-200 p-5">
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="text-purple-600 font-extrabold text-sm flex-shrink-0 mt-0.5">Q.</span>
|
||||
<div>
|
||||
<div className="font-bold text-[#04102b] text-sm mb-1.5">{faq.q}</div>
|
||||
<div className="text-slate-500 text-xs leading-relaxed">{faq.a}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── CTA ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="bg-gradient-to-r from-[#1a0a3d] to-[#2d1560] rounded-2xl border border-purple-400/20 p-8 text-center">
|
||||
<p className="text-purple-400 text-xs font-bold uppercase tracking-widest mb-2">CUSTOM DEVELOPMENT</p>
|
||||
<h3 className="text-white text-2xl font-extrabold mb-2">더 복잡한 PPT 자동화가 필요하신가요?</h3>
|
||||
<p className="text-purple-100/40 text-sm mb-6">
|
||||
이미지 삽입, 차트 자동 생성, 브랜드 템플릿 적용 등<br />
|
||||
맞춤 PPT 자동화를 개발해드립니다.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-3">
|
||||
<a
|
||||
href="/downloads/ppt_automation_v1.0.py"
|
||||
download
|
||||
className="inline-flex items-center gap-2 bg-purple-400 hover:bg-purple-300 text-[#1a0a3d] px-8 py-3 rounded-xl font-extrabold text-sm transition-all shadow-lg shadow-purple-900/30"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
|
||||
무료 버전 다운로드
|
||||
</a>
|
||||
<Link href="/freelance?service=PPT+자동화+맞춤+개발"
|
||||
className="inline-flex items-center gap-2 bg-white/10 hover:bg-white/20 text-white border border-white/20 px-6 py-3 rounded-xl font-bold text-sm transition-all">
|
||||
맞춤 개발 문의 →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '웹 크롤링·스크래핑 도구',
|
||||
description:
|
||||
'공공데이터·쇼핑몰 가격·뉴스를 자동 수집해 엑셀로 저장하는 Python 웹 스크래퍼. requests + BeautifulSoup4 + openpyxl 기반. 무료 다운로드.',
|
||||
keywords: [
|
||||
'웹 크롤링',
|
||||
'웹 스크래핑',
|
||||
'파이썬 크롤러',
|
||||
'데이터 수집 자동화',
|
||||
'BeautifulSoup',
|
||||
'엑셀 자동화',
|
||||
'무료 크롤러',
|
||||
],
|
||||
openGraph: {
|
||||
title: '웹 크롤링 자동화 도구 | 쟁승메이드',
|
||||
description: 'Python 기반 웹 크롤러 무료 다운로드. 페이지네이션·재시도·엑셀 저장 지원.',
|
||||
url: 'https://jaengseung-made.com/services/automation/tools/scraper',
|
||||
},
|
||||
};
|
||||
|
||||
export default function ScraperLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||
</svg>
|
||||
),
|
||||
title: '웹 페이지 데이터 자동 수집',
|
||||
desc: '공공데이터, 쇼핑몰 가격, 뉴스 기사 등 원하는 페이지의 데이터를 자동으로 수집합니다.',
|
||||
color: 'text-blue-600', bg: 'bg-blue-50', border: 'border-blue-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m0 0H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V9.375c0-.621-.504-1.125-1.125-1.125H8.25zM6.75 12h.008v.008H6.75V12zm0 3h.008v.008H6.75V15zm0 3h.008v.008H6.75V18z" />
|
||||
</svg>
|
||||
),
|
||||
title: '엑셀 자동 저장',
|
||||
desc: '수집한 데이터를 열 서식, 헤더 스타일이 적용된 엑셀 파일로 자동 저장합니다.',
|
||||
color: 'text-emerald-600', bg: 'bg-emerald-50', border: 'border-emerald-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z" />
|
||||
</svg>
|
||||
),
|
||||
title: '페이지네이션 자동 탐색',
|
||||
desc: '다음 페이지 링크를 자동으로 찾아 여러 페이지의 데이터를 연속으로 수집합니다.',
|
||||
color: 'text-violet-600', bg: 'bg-violet-50', border: 'border-violet-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
|
||||
</svg>
|
||||
),
|
||||
title: '재시도 로직 내장',
|
||||
desc: '네트워크 오류나 일시적 접속 실패 시 자동으로 재시도합니다. 수집 실패 최소화.',
|
||||
color: 'text-orange-600', bg: 'bg-orange-50', border: 'border-orange-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
),
|
||||
title: '요청 간격 자동 조절',
|
||||
desc: '서버에 부하를 주지 않도록 요청 간격을 자동으로 조절합니다. 차단 위험 최소화.',
|
||||
color: 'text-cyan-600', bg: 'bg-cyan-50', border: 'border-cyan-200',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
|
||||
</svg>
|
||||
),
|
||||
title: '로그 파일 자동 저장',
|
||||
desc: '수집 과정 전체를 로그로 남겨 나중에 어떤 URL에서 몇 건을 수집했는지 확인 가능합니다.',
|
||||
color: 'text-rose-600', bg: 'bg-rose-50', border: 'border-rose-200',
|
||||
},
|
||||
];
|
||||
|
||||
const howToUse = [
|
||||
{ step: '01', title: 'Python 설치', desc: 'python.org에서 Python 3.10 이상을 설치하세요. "Add to PATH" 체크 필수.' },
|
||||
{ step: '02', title: '패키지 설치', desc: '터미널에서 pip install requests beautifulsoup4 openpyxl lxml 실행.' },
|
||||
{ step: '03', title: 'URL 설정', desc: '파일 상단 TARGET_URL에 크롤링할 주소를 입력하세요.' },
|
||||
{ step: '04', title: '실행', desc: 'python web_scraper_v1.0.py 실행 → 같은 폴더에 엑셀 파일이 생성됩니다.' },
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
q: '크롤링이 법적으로 문제없나요?',
|
||||
a: '공개된 정보 수집 자체는 일반적으로 허용되지만, 사이트의 robots.txt와 이용약관을 반드시 확인하세요. 로그인이 필요한 페이지, 개인정보, 저작권 데이터 수집은 법적 문제가 생길 수 있습니다.',
|
||||
},
|
||||
{
|
||||
q: '자바스크립트로 렌더링되는 사이트도 되나요?',
|
||||
a: 'requests + BeautifulSoup은 정적 HTML만 수집합니다. JS 렌더링 사이트(React, Vue 등)는 Selenium/Playwright가 필요하며, 맞춤 개발 서비스로 문의 주시면 구현해 드립니다.',
|
||||
},
|
||||
{
|
||||
q: '원하는 항목만 골라서 수집할 수 있나요?',
|
||||
a: '파일 내 extract_data 함수를 수정하면 됩니다. HTML 선택자(CSS Selector)로 원하는 요소만 지정할 수 있으며, 코드 내 주석에 예시가 포함되어 있습니다.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function ScraperToolPage() {
|
||||
const [openFaq, setOpenFaq] = useState<number | null>(null);
|
||||
|
||||
return (
|
||||
<div className="min-h-full bg-[#f0f5ff]">
|
||||
|
||||
{/* Hero */}
|
||||
<div className="bg-gradient-to-br from-[#1e3a8a] via-[#1d4ed8] to-[#1e3a8a] px-6 py-12 lg:px-12">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<Link href="/services/automation"
|
||||
className="inline-flex items-center gap-1.5 text-blue-300/60 hover:text-blue-300 text-sm mb-6 transition">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
업무 자동화 서비스로 돌아가기
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-6">
|
||||
<div className="w-20 h-20 rounded-2xl bg-blue-400/15 border border-blue-400/30 flex items-center justify-center flex-shrink-0">
|
||||
<svg className="w-10 h-10 text-blue-300" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-blue-400 text-xs font-bold uppercase tracking-widest">FREE TOOL</span>
|
||||
<span className="bg-blue-400/20 border border-blue-400/40 text-blue-300 text-[10px] font-bold px-2 py-0.5 rounded-full">v1.0</span>
|
||||
<span className="bg-white/10 text-white/50 text-[10px] font-bold px-2 py-0.5 rounded-full">Python · BeautifulSoup</span>
|
||||
</div>
|
||||
<h1 className="text-3xl md:text-4xl font-extrabold text-white mb-2 leading-tight">
|
||||
웹 크롤링 자동화 도구<br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-300 to-cyan-300">
|
||||
Web Scraper
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-blue-100/50 text-sm leading-relaxed">
|
||||
공공데이터, 가격 비교, 뉴스 수집까지 — 원하는 웹 페이지의 데이터를 자동으로 수집해<br />
|
||||
엑셀 파일로 저장합니다. Python 기초 지식만 있으면 바로 사용 가능합니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 inline-grid grid-cols-3 gap-px bg-blue-400/10 border border-blue-400/20 rounded-2xl overflow-hidden">
|
||||
{[
|
||||
{ v: '6가지', l: '핵심 기능' },
|
||||
{ v: '무료', l: '완전 무료' },
|
||||
{ v: 'Python 3.10+', l: '지원 버전' },
|
||||
].map((s) => (
|
||||
<div key={s.l} className="bg-[#1e3a8a]/60 px-5 py-3 text-center">
|
||||
<div className="text-white font-extrabold text-base">{s.v}</div>
|
||||
<div className="text-blue-400/50 text-xs mt-0.5">{s.l}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-10 lg:px-12">
|
||||
<div className="max-w-4xl mx-auto space-y-10">
|
||||
|
||||
{/* 다운로드 카드 */}
|
||||
<div className="bg-white rounded-2xl border-2 border-blue-200 p-6 flex flex-col sm:flex-row items-center gap-6">
|
||||
<div className="flex-1">
|
||||
<div className="text-blue-700 text-xs font-bold uppercase tracking-widest mb-1">DOWNLOAD</div>
|
||||
<div className="font-extrabold text-[#04102b] text-lg mb-1">web_scraper_v1.0.py</div>
|
||||
<div className="text-slate-500 text-xs mb-3">크기: 약 8KB · Python 스크립트 · 상업적 이용 가능</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{['Python 3.10+', '페이지네이션', '재시도 로직', '엑셀 자동 저장', '로그 저장'].map((t) => (
|
||||
<span key={t} className="text-[10px] font-bold px-2 py-0.5 rounded-full border border-blue-200 text-blue-700 bg-blue-50">{t}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-full sm:w-auto">
|
||||
<a
|
||||
href="/downloads/web_scraper_v1.0.py"
|
||||
download
|
||||
className="flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-500 text-white px-6 py-3 rounded-xl font-extrabold text-sm transition-all shadow-lg shadow-blue-900/20 w-full sm:w-48"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
||||
</svg>
|
||||
무료 다운로드
|
||||
</a>
|
||||
<p className="text-[10px] text-slate-400 text-center">로그인 없이 즉시 다운로드</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 기능 목록 */}
|
||||
<div>
|
||||
<h2 className="text-xl font-extrabold text-[#04102b] mb-5">포함된 기능</h2>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{features.map((f) => (
|
||||
<div key={f.title} className={`rounded-xl border p-4 ${f.bg} ${f.border}`}>
|
||||
<div className={`${f.color} mb-3`}>{f.icon}</div>
|
||||
<div className={`text-xs font-bold mb-1 ${f.color}`}>{f.title}</div>
|
||||
<p className="text-slate-600 text-xs leading-relaxed">{f.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 사용 방법 */}
|
||||
<div>
|
||||
<h2 className="text-xl font-extrabold text-[#04102b] mb-5">사용 방법</h2>
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
{howToUse.map((h) => (
|
||||
<div key={h.step} className="bg-white rounded-xl border border-[#dbe8ff] p-5 flex gap-4">
|
||||
<div className="text-blue-600 text-2xl font-black leading-none flex-shrink-0">{h.step}</div>
|
||||
<div>
|
||||
<div className="font-bold text-[#04102b] text-sm mb-1">{h.title}</div>
|
||||
<p className="text-slate-500 text-xs leading-relaxed">{h.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 코드 예시 */}
|
||||
<div className="bg-[#0f172a] rounded-2xl p-6 overflow-x-auto">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="text-xs font-bold text-blue-400 uppercase tracking-widest">CODE PREVIEW</span>
|
||||
<span className="text-slate-600 text-xs">extract_data 함수 수정 예시</span>
|
||||
</div>
|
||||
<pre className="text-sm text-slate-300 leading-relaxed font-mono whitespace-pre">{`def extract_data(soup, page_url):
|
||||
items = []
|
||||
# 상품 목록 수집 예시
|
||||
for item in soup.select(".product-item"):
|
||||
name = item.select_one(".name")
|
||||
price = item.select_one(".price")
|
||||
items.append({
|
||||
"상품명": name.get_text(strip=True),
|
||||
"가격": price.get_text(strip=True),
|
||||
"URL": page_url,
|
||||
})
|
||||
return items`}</pre>
|
||||
</div>
|
||||
|
||||
{/* FAQ */}
|
||||
<div>
|
||||
<h2 className="text-xl font-extrabold text-[#04102b] mb-5">자주 묻는 질문</h2>
|
||||
<div className="space-y-3">
|
||||
{faqs.map((faq, i) => (
|
||||
<div key={i} className="bg-white rounded-xl border border-[#dbe8ff] overflow-hidden">
|
||||
<button
|
||||
onClick={() => setOpenFaq(openFaq === i ? null : i)}
|
||||
className="w-full flex items-center justify-between px-5 py-4 text-left"
|
||||
>
|
||||
<span className="font-bold text-[#04102b] text-sm">{faq.q}</span>
|
||||
<svg className={`w-4 h-4 text-slate-400 transition-transform ${openFaq === i ? 'rotate-180' : ''}`}
|
||||
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openFaq === i && (
|
||||
<div className="px-5 pb-4 text-slate-500 text-sm leading-relaxed border-t border-[#dbe8ff] pt-3">
|
||||
{faq.a}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="bg-gradient-to-r from-[#1e3a8a] to-[#1d4ed8] rounded-2xl p-8 text-center">
|
||||
<p className="text-blue-300 text-xs font-bold uppercase tracking-widest mb-2">CUSTOM DEVELOPMENT</p>
|
||||
<h3 className="text-white text-xl font-extrabold mb-2">더 복잡한 크롤링이 필요하다면?</h3>
|
||||
<p className="text-blue-100/50 text-sm mb-6 leading-relaxed">
|
||||
JS 렌더링 사이트, 로그인 필요, 대용량 수집, 자동 스케줄링까지<br />
|
||||
맞춤 개발로 정확히 원하는 데이터를 가져옵니다.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-center">
|
||||
<a
|
||||
href="/downloads/web_scraper_v1.0.py"
|
||||
download
|
||||
className="inline-flex items-center justify-center gap-2 bg-blue-400 hover:bg-blue-300 text-[#1e3a8a] px-6 py-3 rounded-xl font-extrabold text-sm transition-all"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
||||
</svg>
|
||||
무료 다운로드
|
||||
</a>
|
||||
<Link href="/freelance?service=업무+자동화"
|
||||
className="inline-flex items-center justify-center gap-2 bg-white/10 hover:bg-white/20 text-white border border-white/20 px-6 py-3 rounded-xl font-extrabold text-sm transition-all">
|
||||
맞춤 크롤러 개발 문의 →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
26
app/services/blog/layout.tsx
Normal file
26
app/services/blog/layout.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '블로그 자동화 솔루션 팩 | 쟁승메이드',
|
||||
description:
|
||||
'쿠팡파트너스·네이버 애드포스트·브랜드커넥트 수익을 자동화하는 프롬프트 조합법 45종 + 구조 템플릿 PDF 80p + 샘플 글 10편. ₩29,000 한 번 결제, 평생 무료 업데이트.',
|
||||
keywords: [
|
||||
'블로그 자동화',
|
||||
'AI 블로그 글쓰기',
|
||||
'쿠팡파트너스 자동화',
|
||||
'애드포스트 수익화',
|
||||
'네이버 블로그 SEO',
|
||||
'ChatGPT 블로그',
|
||||
'블로그 프롬프트',
|
||||
],
|
||||
openGraph: {
|
||||
title: '블로그 자동화 솔루션 팩 | 쟁승메이드',
|
||||
description:
|
||||
'쿠팡파트너스·애드포스트 수익을 자동화하는 프롬프트 + 템플릿 + 샘플. ₩29,000.',
|
||||
url: 'https://jaengseung-made.com/services/blog',
|
||||
},
|
||||
};
|
||||
|
||||
export default function BlogLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
243
app/services/blog/page.tsx
Normal file
243
app/services/blog/page.tsx
Normal file
@@ -0,0 +1,243 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import PurchaseAgreementModal from '../../components/PurchaseAgreementModal';
|
||||
|
||||
const PACK_ITEMS = [
|
||||
{
|
||||
icon: '📝',
|
||||
title: '프롬프트 조합법 45종',
|
||||
desc: '상품리뷰 / 정보글 / 후기 / 비교 / 하우투 글별 최적 프롬프트 조합',
|
||||
meta: 'PDF 80p',
|
||||
},
|
||||
{
|
||||
icon: '📐',
|
||||
title: '블로그 글 구조 템플릿 12종',
|
||||
desc: '쿠팡파트너스 · 애드포스트 클릭을 유도하는 검증된 글 구조',
|
||||
meta: 'Notion 템플릿',
|
||||
},
|
||||
{
|
||||
icon: '💰',
|
||||
title: '샘플 글 10편',
|
||||
desc: '실제로 수익이 발생한 블로그 글 전문 + 해설 주석',
|
||||
meta: '.docx · .md',
|
||||
},
|
||||
{
|
||||
icon: '🔍',
|
||||
title: '네이버 SEO 체크리스트',
|
||||
desc: 'C-Rank · D.I.A. 알고리즘 대응 14가지 체크 포인트',
|
||||
meta: 'PDF 20p',
|
||||
},
|
||||
];
|
||||
|
||||
const FAQS = [
|
||||
{
|
||||
q: '초보자도 쓸 수 있나요?',
|
||||
a: 'ChatGPT나 Claude 계정만 있으면 됩니다. 프롬프트를 복붙하는 것부터 시작해서 점차 응용하도록 설계했습니다.',
|
||||
},
|
||||
{
|
||||
q: '어떤 플랫폼에 맞나요?',
|
||||
a: '네이버 블로그·티스토리·브런치 모두 대응. 쿠팡파트너스·애드포스트·브랜드커넥트 3가지 수익화 흐름을 모두 다룹니다.',
|
||||
},
|
||||
{
|
||||
q: '업데이트는 얼마나 자주 되나요?',
|
||||
a: '월 1~2회 주요 업데이트. 구매자 전용 Notion 페이지에서 변경 이력과 최신 파일을 제공합니다. 구매 후 12개월간 무료.',
|
||||
},
|
||||
{
|
||||
q: '환불이 되나요?',
|
||||
a: '전자상거래법상 디지털 콘텐츠는 제공 시작 후 환불이 제한됩니다. 구매 전 샘플 미리보기를 충분히 확인해주세요. 파일 손상·전달 불량은 즉시 재전달 또는 환불됩니다.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function BlogServicePage() {
|
||||
const [agreeOpen, setAgreeOpen] = useState(false);
|
||||
const [openFaq, setOpenFaq] = useState<number | null>(0);
|
||||
|
||||
return (
|
||||
<div className="min-h-full bg-white">
|
||||
{/* HERO */}
|
||||
<section className="relative overflow-hidden px-6 py-20 lg:px-14 lg:py-28 bg-gradient-to-br from-blue-50 via-white to-sky-50">
|
||||
<div className="relative max-w-5xl mx-auto">
|
||||
<p className="font-mono text-xs text-blue-700/70 tracking-[0.25em] uppercase mb-6">
|
||||
Blog Automation Pack
|
||||
</p>
|
||||
<h1
|
||||
className="text-[2.4rem] md:text-[3.2rem] lg:text-[4rem] font-extrabold leading-[1.08] tracking-tight text-slate-900 mb-6"
|
||||
style={{ wordBreak: 'keep-all' }}
|
||||
>
|
||||
매일 글쓰기 고민,
|
||||
<br />
|
||||
<span className="text-blue-700">AI에게 맡기세요.</span>
|
||||
</h1>
|
||||
<p
|
||||
className="text-slate-600 text-lg md:text-xl leading-relaxed mb-4 max-w-2xl"
|
||||
style={{ wordBreak: 'keep-all' }}
|
||||
>
|
||||
쿠팡파트너스 · 네이버 애드포스트 · 브랜드커넥트 수익을
|
||||
<br />
|
||||
<span className="text-slate-900 font-semibold">자동화하는 프롬프트 · 구조 · 샘플 세트</span>.
|
||||
</p>
|
||||
<div className="inline-flex items-center gap-3 bg-white border border-blue-200 rounded-xl px-5 py-3 mb-8 shadow-sm">
|
||||
<span className="text-3xl font-extrabold text-blue-700 font-mono">₩29,000</span>
|
||||
<span className="text-xs text-slate-500">한 번 결제 · 12개월 무료 업데이트</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<button
|
||||
onClick={() => setAgreeOpen(true)}
|
||||
className="inline-flex items-center gap-2 bg-blue-700 hover:bg-blue-600 text-white px-8 py-4 rounded-xl font-bold text-sm transition-colors shadow-lg shadow-blue-500/30"
|
||||
>
|
||||
구매하기 →
|
||||
</button>
|
||||
<a
|
||||
href="#sample"
|
||||
className="inline-flex items-center gap-2 border border-slate-300 hover:border-blue-500 text-slate-700 hover:text-blue-700 px-8 py-4 rounded-xl font-semibold text-sm transition-all"
|
||||
>
|
||||
샘플 미리보기
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* PAIN POINTS */}
|
||||
<section className="px-6 py-16 lg:px-14 bg-white">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2
|
||||
className="text-2xl md:text-3xl font-extrabold text-slate-900 mb-10 text-center"
|
||||
style={{ wordBreak: 'keep-all' }}
|
||||
>
|
||||
이런 분들을 위한 팩입니다.
|
||||
</h2>
|
||||
<div className="grid sm:grid-cols-3 gap-5">
|
||||
{[
|
||||
{ icon: '🕐', title: '매일 1시간+', desc: '글 소재·구성에 시간 다 쓰고 수익은 제자리' },
|
||||
{ icon: '📉', title: '수익화 6개월+', desc: '블로그 키워놓고도 수익 구조가 안 잡힘' },
|
||||
{ icon: '🤖', title: 'AI 글은 어색', desc: 'ChatGPT 그대로 복붙하면 바로 들통' },
|
||||
].map((p) => (
|
||||
<div key={p.title} className="border border-slate-200 rounded-2xl p-6 bg-slate-50/50">
|
||||
<div className="text-3xl mb-3">{p.icon}</div>
|
||||
<h3 className="font-extrabold text-slate-900 mb-2">{p.title}</h3>
|
||||
<p className="text-sm text-slate-600 leading-relaxed" style={{ wordBreak: 'keep-all' }}>
|
||||
{p.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* PACK CONTENT */}
|
||||
<section id="sample" className="px-6 py-20 lg:px-14 bg-slate-50">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<p className="font-mono text-xs text-blue-700/70 tracking-widest uppercase mb-2">
|
||||
Pack Contents
|
||||
</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-slate-900 mb-10">
|
||||
구성품 4종
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 gap-5">
|
||||
{PACK_ITEMS.map((it) => (
|
||||
<div
|
||||
key={it.title}
|
||||
className="flex gap-4 bg-white border border-slate-200 rounded-2xl p-6 hover:border-blue-400 transition-colors"
|
||||
>
|
||||
<div className="text-3xl flex-shrink-0">{it.icon}</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1.5">
|
||||
<h3 className="font-extrabold text-slate-900">{it.title}</h3>
|
||||
<span className="text-[10px] font-mono text-blue-700 bg-blue-100 px-1.5 py-0.5 rounded">
|
||||
{it.meta}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-slate-600 leading-relaxed" style={{ wordBreak: 'keep-all' }}>
|
||||
{it.desc}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Sample preview */}
|
||||
<div className="mt-12 bg-white border-2 border-dashed border-blue-300 rounded-2xl p-8 relative overflow-hidden">
|
||||
<span className="absolute top-4 right-4 text-[10px] font-bold text-blue-700 bg-blue-100 px-2 py-1 rounded">
|
||||
샘플 미리보기
|
||||
</span>
|
||||
<h4 className="font-extrabold text-slate-900 mb-3">프롬프트 예시 · 상품 리뷰 글 자동 생성</h4>
|
||||
<pre className="text-xs font-mono text-slate-700 bg-slate-50 rounded-lg p-4 overflow-x-auto leading-relaxed">{`당신은 [카테고리] 전문 블로거입니다.
|
||||
아래 상품의 [핵심 장점 3개]와 [주의점 1개]를 기반으로
|
||||
C-Rank 알고리즘에 최적화된 1,200자 리뷰 글을 작성하세요.
|
||||
|
||||
[구조]
|
||||
1. 후킹 도입 (공감형 질문)
|
||||
2. 상품 요약 (스펙 표)
|
||||
3. 실사용 관점 장점·단점
|
||||
4. 대안 비교 (쿠팡 링크 삽입 지점: {LINK})
|
||||
5. 결론 + 재질문 유도
|
||||
|
||||
[톤앤매너] 친근한 존댓말, 광고 느낌 최소화 ...`}</pre>
|
||||
<p className="text-xs text-slate-500 mt-4">
|
||||
실제 팩에는 카테고리별 45종의 프롬프트와 최적화 파라미터가 포함됩니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ */}
|
||||
<section className="px-6 py-20 lg:px-14 bg-white">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-slate-900 mb-8 text-center">
|
||||
자주 묻는 질문
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{FAQS.map((f, i) => (
|
||||
<div key={i} className="border border-slate-200 rounded-xl overflow-hidden">
|
||||
<button
|
||||
onClick={() => setOpenFaq(openFaq === i ? null : i)}
|
||||
className="w-full flex items-center justify-between px-5 py-4 text-left hover:bg-slate-50 transition-colors"
|
||||
>
|
||||
<span className="font-bold text-slate-900 text-sm">{f.q}</span>
|
||||
<span className={`text-blue-700 text-xl transition-transform ${openFaq === i ? 'rotate-45' : ''}`}>
|
||||
+
|
||||
</span>
|
||||
</button>
|
||||
{openFaq === i && (
|
||||
<div className="px-5 pb-5 text-sm text-slate-600 leading-relaxed" style={{ wordBreak: 'keep-all' }}>
|
||||
{f.a}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FINAL CTA */}
|
||||
<section className="px-6 py-20 lg:px-14 bg-gradient-to-br from-blue-700 to-blue-900">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl md:text-4xl font-extrabold text-white mb-4" style={{ wordBreak: 'keep-all' }}>
|
||||
오늘부터 블로그 수익 자동화.
|
||||
</h2>
|
||||
<p className="text-blue-100 text-lg mb-8">₩29,000 한 번 결제 · 평생 업데이트</p>
|
||||
<button
|
||||
onClick={() => setAgreeOpen(true)}
|
||||
className="inline-flex items-center gap-2 bg-white text-blue-700 hover:bg-blue-50 px-10 py-4 rounded-xl font-extrabold text-base transition-colors shadow-xl"
|
||||
>
|
||||
지금 구매하기 →
|
||||
</button>
|
||||
<p className="text-blue-200/80 text-xs mt-6">
|
||||
<Link href="/legal/refund" className="underline hover:text-white">환불 정책</Link>
|
||||
{' · '}
|
||||
<Link href="/legal/terms" className="underline hover:text-white">이용약관</Link>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<PurchaseAgreementModal
|
||||
isOpen={agreeOpen}
|
||||
onClose={() => setAgreeOpen(false)}
|
||||
productName="블로그 자동화 솔루션 팩"
|
||||
price="₩29,000"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '로또 번호 추천',
|
||||
description:
|
||||
'1,100+회차 빅데이터 기반 로또 번호 분석. 핫/콜드 번호 통계, 몬테카를로 시뮬레이션으로 매주 최적 번호 조합을 제공합니다. 월 900원부터 구독.',
|
||||
keywords: [
|
||||
'로또 번호 추천',
|
||||
'로또 번호 분석',
|
||||
'로또 빅데이터',
|
||||
'로또 통계',
|
||||
'로또 번호 생성',
|
||||
'핫넘버 콜드넘버',
|
||||
],
|
||||
openGraph: {
|
||||
title: '로또 번호 추천 서비스 | 쟁승메이드',
|
||||
description:
|
||||
'1,100+회차 데이터 분석 · 월 900원 구독 · 이메일/텔레그램 자동 발송.',
|
||||
url: 'https://jaengseung-made.com/services/lotto',
|
||||
},
|
||||
};
|
||||
|
||||
export default function LottoLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
// PG 심사 정책상 로또 관련 서비스 비공개 처리
|
||||
export default function LottoPage() {
|
||||
redirect('/');
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
interface PersonalPattern {
|
||||
total_analyzed: number;
|
||||
number_frequency: Record<string, number>;
|
||||
top_picks: number[];
|
||||
least_picks: number[];
|
||||
pattern: {
|
||||
avg_odd_count: number;
|
||||
avg_sum: number;
|
||||
avg_range: number;
|
||||
consecutive_rate: number;
|
||||
zone_avg: Record<string, number>;
|
||||
};
|
||||
vs_draw_avg: {
|
||||
odd_diff: number;
|
||||
sum_diff: number;
|
||||
odd_tendency: string;
|
||||
sum_tendency: string;
|
||||
};
|
||||
}
|
||||
|
||||
function getBallStyle(n: number) {
|
||||
if (n <= 10) return { bg: 'linear-gradient(145deg,#fde68a,#fbbf24,#d97706)', text: '#78350f' };
|
||||
if (n <= 20) return { bg: 'linear-gradient(145deg,#93c5fd,#3b82f6,#1d4ed8)', text: '#fff' };
|
||||
if (n <= 30) return { bg: 'linear-gradient(145deg,#fca5a5,#ef4444,#b91c1c)', text: '#fff' };
|
||||
if (n <= 40) return { bg: 'linear-gradient(145deg,#d1d5db,#9ca3af,#4b5563)', text: '#fff' };
|
||||
return { bg: 'linear-gradient(145deg,#86efac,#22c55e,#15803d)', text: '#fff' };
|
||||
}
|
||||
|
||||
function SmallBall({ n, size = 30, freq }: { n: number; size?: number; freq?: number }) {
|
||||
const { bg, text } = getBallStyle(n);
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3 }}>
|
||||
<div style={{
|
||||
width: size, height: size, borderRadius: '50%', background: bg,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
fontSize: size * 0.35, fontWeight: 900, color: text, flexShrink: 0,
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,.3)',
|
||||
}}>{n}</div>
|
||||
{freq !== undefined && <div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.55rem', fontFamily: "'JetBrains Mono',monospace" }}>{freq}회</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ZoneBar({ label, value, max }: { label: string; value: number; max: number }) {
|
||||
const pct = max > 0 ? (value / max) * 100 : 0;
|
||||
return (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '.6rem' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.4)', fontSize: '.65rem', minWidth: 44, fontFamily: "'JetBrains Mono',monospace" }}>{label}</div>
|
||||
<div style={{ flex: 1, height: 8, background: 'rgba(255,255,255,.06)', borderRadius: 4, overflow: 'hidden' }}>
|
||||
<div style={{ height: '100%', width: `${pct}%`, background: 'linear-gradient(90deg,#fbbf24,#f97316)', borderRadius: 4, transition: 'width 1s ease' }} />
|
||||
</div>
|
||||
<div style={{ color: '#fbbf24', fontSize: '.65rem', minWidth: 24, textAlign: 'right', fontFamily: "'JetBrains Mono',monospace" }}>{value.toFixed(1)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PatternTab() {
|
||||
const [data, setData] = useState<PersonalPattern | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/lotto/analysis/personal').then(r => r.json())
|
||||
.then(d => {
|
||||
if (d?.error) { setError(d.error === 'NAS_TIMEOUT' ? 'NAS 서버 응답 시간 초과.' : '패턴 분석을 불러오지 못했습니다.'); return; }
|
||||
setData(d);
|
||||
})
|
||||
.catch(() => setError('패턴 분석을 불러오지 못했습니다.'))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
if (loading) return (
|
||||
<div style={{ textAlign: 'center', padding: '4rem 0' }}>
|
||||
<div style={{ width: 36, height: 36, borderRadius: '50%', border: '3px solid rgba(251,191,36,.15)', borderTop: '3px solid #fbbf24', animation: 'spin .8s linear infinite', margin: '0 auto 1rem' }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
if (error) return <div style={{ textAlign: 'center', padding: '4rem 0', color: '#f87171', fontSize: '.85rem' }}>{error}</div>;
|
||||
|
||||
if (!data || data.total_analyzed === 0) return (
|
||||
<div style={{ textAlign: 'center', padding: '5rem 1rem' }}>
|
||||
<div style={{ fontSize: '3rem', marginBottom: '1rem' }}>📊</div>
|
||||
<div style={{ color: 'rgba(255,255,255,.4)', fontSize: '.9rem', marginBottom: '.5rem' }}>아직 분석할 데이터가 없습니다</div>
|
||||
<div style={{ color: 'rgba(255,255,255,.2)', fontSize: '.75rem' }}>번호 생성 탭에서 번호를 추천받으면 패턴이 쌓입니다</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const zoneMax = Math.max(...Object.values(data.pattern.zone_avg));
|
||||
const tendencyColor = (tendency: string) =>
|
||||
tendency.includes('고') || tendency.includes('홀수') ? '#f87171' : tendency.includes('저') || tendency.includes('짝수') ? '#60a5fa' : '#4ade80';
|
||||
|
||||
return (
|
||||
<div style={{ animation: 'slideUp .4s ease forwards' }}>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', marginBottom: '1.5rem', flexWrap: 'wrap' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: '.6rem', color: 'rgba(251,191,36,.5)', letterSpacing: '.12em', marginBottom: '.3rem' }}>PERSONAL PATTERN ANALYSIS</div>
|
||||
<h2 style={{ color: '#fff', fontSize: '1.3rem', fontWeight: 900, margin: 0 }}>내 번호 선택 패턴</h2>
|
||||
</div>
|
||||
<div style={{ background: 'rgba(251,191,36,.08)', border: '1px solid rgba(251,191,36,.2)', borderRadius: '.75rem', padding: '.5rem 1rem', marginLeft: 'auto' }}>
|
||||
<span style={{ color: 'rgba(255,255,255,.4)', fontSize: '.65rem' }}>총 분석 </span>
|
||||
<span style={{ color: '#fbbf24', fontWeight: 900, fontFamily: "'JetBrains Mono',monospace" }}>{data.total_analyzed}</span>
|
||||
<span style={{ color: 'rgba(255,255,255,.4)', fontSize: '.65rem' }}>회</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(280px,1fr))', gap: '1rem' }}>
|
||||
|
||||
{/* 자주 선택한 번호 */}
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1.25rem' }}>
|
||||
<div style={{ color: '#fbbf24', fontSize: '.72rem', fontWeight: 700, marginBottom: '.75rem' }}>⭐ 자주 선택한 번호 TOP 10</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '.5rem' }}>
|
||||
{data.top_picks.map(n => (
|
||||
<SmallBall key={n} n={n} size={34} freq={data.number_frequency[String(n)] ?? 0} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 한 번도 안 쓴 번호 */}
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1.25rem' }}>
|
||||
<div style={{ color: '#60a5fa', fontSize: '.72rem', fontWeight: 700, marginBottom: '.75rem' }}>💤 거의 안 쓴 번호</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '.5rem' }}>
|
||||
{data.least_picks.map(n => <SmallBall key={n} n={n} size={34} />)}
|
||||
</div>
|
||||
<div style={{ color: 'rgba(255,255,255,.25)', fontSize: '.65rem', marginTop: '.75rem' }}>이 번호들도 가끔 포함해보세요</div>
|
||||
</div>
|
||||
|
||||
{/* 패턴 지표 */}
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1.25rem' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.5)', fontSize: '.72rem', fontWeight: 700, marginBottom: '.75rem' }}>📐 선택 패턴 지표</div>
|
||||
{[
|
||||
{ label: '평균 홀수 개수', value: data.pattern.avg_odd_count.toFixed(1) + '개', ref: '역대 평균 3.0개', refColor: 'rgba(255,255,255,.2)' },
|
||||
{ label: '평균 합계', value: data.pattern.avg_sum.toFixed(0), ref: '역대 평균 138', refColor: 'rgba(255,255,255,.2)' },
|
||||
{ label: '평균 범위(최대-최소)', value: data.pattern.avg_range.toFixed(1), ref: '', refColor: '' },
|
||||
{ label: '연속번호 포함률', value: `${(data.pattern.consecutive_rate * 100).toFixed(0)}%`, ref: '', refColor: '' },
|
||||
].map(({ label, value, ref }) => (
|
||||
<div key={label} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', padding: '.45rem 0', borderBottom: '1px solid rgba(255,255,255,.05)' }}>
|
||||
<span style={{ color: 'rgba(255,255,255,.35)', fontSize: '.68rem' }}>{label}</span>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<div style={{ color: '#fff', fontWeight: 700, fontSize: '.8rem', fontFamily: "'JetBrains Mono',monospace" }}>{value}</div>
|
||||
{ref && <div style={{ color: 'rgba(255,255,255,.2)', fontSize: '.58rem' }}>{ref}</div>}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 구간별 선택 분포 */}
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1.25rem' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.5)', fontSize: '.72rem', fontWeight: 700, marginBottom: '.75rem' }}>🎯 구간별 선택 분포</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '.5rem' }}>
|
||||
{Object.entries(data.pattern.zone_avg).map(([zone, val]) => (
|
||||
<ZoneBar key={zone} label={zone} value={val} max={zoneMax} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 역대 당첨과 비교 */}
|
||||
<div style={{ gridColumn: '1/-1', background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1.25rem' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.5)', fontSize: '.72rem', fontWeight: 700, marginBottom: '1rem' }}>⚖️ 역대 당첨 평균과 비교</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(200px,1fr))', gap: '1rem' }}>
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', borderRadius: '.75rem', padding: '1rem', textAlign: 'center' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.65rem', marginBottom: '.4rem' }}>홀수 선택 경향</div>
|
||||
<div style={{ color: tendencyColor(data.vs_draw_avg.odd_tendency), fontSize: '1.1rem', fontWeight: 900 }}>{data.vs_draw_avg.odd_tendency}</div>
|
||||
<div style={{ color: 'rgba(255,255,255,.25)', fontSize: '.65rem', marginTop: '.3rem', fontFamily: "'JetBrains Mono',monospace" }}>
|
||||
당첨 평균 대비 {data.vs_draw_avg.odd_diff > 0 ? '+' : ''}{data.vs_draw_avg.odd_diff.toFixed(1)}개
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', borderRadius: '.75rem', padding: '1rem', textAlign: 'center' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.65rem', marginBottom: '.4rem' }}>합계 선택 경향</div>
|
||||
<div style={{ color: tendencyColor(data.vs_draw_avg.sum_tendency), fontSize: '1.1rem', fontWeight: 900 }}>{data.vs_draw_avg.sum_tendency}</div>
|
||||
<div style={{ color: 'rgba(255,255,255,.25)', fontSize: '.65rem', marginTop: '.3rem', fontFamily: "'JetBrains Mono',monospace" }}>
|
||||
당첨 평균 대비 {data.vs_draw_avg.sum_diff > 0 ? '+' : ''}{data.vs_draw_avg.sum_diff.toFixed(1)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
interface PurchaseRecord {
|
||||
id: number;
|
||||
draw_no: number;
|
||||
amount: number;
|
||||
sets: number;
|
||||
prize: number;
|
||||
note: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface PurchaseStats {
|
||||
total_records: number;
|
||||
total_invested: number;
|
||||
total_prize: number;
|
||||
net: number;
|
||||
return_rate: number;
|
||||
prize_count: number;
|
||||
max_prize: number;
|
||||
}
|
||||
|
||||
function StatCard({ label, value, sub, color }: { label: string; value: string; sub?: string; color?: string }) {
|
||||
return (
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '.75rem', padding: '1rem', textAlign: 'center' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.35)', fontSize: '.65rem', marginBottom: '.4rem' }}>{label}</div>
|
||||
<div style={{ color: color ?? '#fff', fontSize: '1.3rem', fontWeight: 900, fontFamily: "'JetBrains Mono',monospace", lineHeight: 1.1 }}>{value}</div>
|
||||
{sub && <div style={{ color: 'rgba(255,255,255,.25)', fontSize: '.6rem', marginTop: '.2rem' }}>{sub}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PurchaseTab() {
|
||||
const [records, setRecords] = useState<PurchaseRecord[]>([]);
|
||||
const [stats, setStats] = useState<PurchaseStats | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
const [editPrize, setEditPrize] = useState('');
|
||||
const [editNote, setEditNote] = useState('');
|
||||
const [showAdd, setShowAdd] = useState(false);
|
||||
const [addForm, setAddForm] = useState({ draw_no: '', amount: '5000', sets: '5', prize: '0', note: '' });
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const load = async () => {
|
||||
try {
|
||||
const [recRes, statRes] = await Promise.all([
|
||||
fetch('/api/lotto/purchase').then(r => r.json()),
|
||||
fetch('/api/lotto/purchase/stats').then(r => r.json()),
|
||||
]);
|
||||
if (recRes?.error || statRes?.error) throw new Error(recRes?.error ?? statRes?.error);
|
||||
setRecords(recRes.records ?? []);
|
||||
setStats(statRes);
|
||||
} catch { /* 에러 시 빈 상태 유지 */ } finally { setLoading(false); }
|
||||
};
|
||||
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
const handleAdd = async () => {
|
||||
if (!addForm.draw_no) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
await fetch('/api/lotto/purchase', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
draw_no: parseInt(addForm.draw_no),
|
||||
amount: parseInt(addForm.amount),
|
||||
sets: parseInt(addForm.sets),
|
||||
prize: parseInt(addForm.prize),
|
||||
note: addForm.note,
|
||||
}),
|
||||
});
|
||||
setShowAdd(false);
|
||||
setAddForm({ draw_no: '', amount: '5000', sets: '5', prize: '0', note: '' });
|
||||
await load();
|
||||
} finally { setSaving(false); }
|
||||
};
|
||||
|
||||
const handleUpdate = async (id: number) => {
|
||||
setSaving(true);
|
||||
try {
|
||||
await fetch(`/api/lotto/purchase/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ prize: parseInt(editPrize) || 0, note: editNote }),
|
||||
});
|
||||
setEditingId(null);
|
||||
await load();
|
||||
} finally { setSaving(false); }
|
||||
};
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
if (!confirm('삭제하시겠습니까?')) return;
|
||||
await fetch(`/api/lotto/purchase/${id}`, { method: 'DELETE' });
|
||||
await load();
|
||||
};
|
||||
|
||||
const inputStyle: React.CSSProperties = {
|
||||
background: 'rgba(255,255,255,.06)', border: '1px solid rgba(255,255,255,.12)',
|
||||
borderRadius: '.4rem', padding: '.35rem .65rem', color: '#fff', fontSize: '.78rem', width: '100%',
|
||||
outline: 'none',
|
||||
};
|
||||
|
||||
if (loading) return (
|
||||
<div style={{ textAlign: 'center', padding: '4rem 0' }}>
|
||||
<div style={{ width: 36, height: 36, borderRadius: '50%', border: '3px solid rgba(251,191,36,.15)', borderTop: '3px solid #fbbf24', animation: 'spin .8s linear infinite', margin: '0 auto 1rem' }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ animation: 'slideUp .4s ease forwards' }}>
|
||||
|
||||
{/* 통계 카드 */}
|
||||
{stats && (
|
||||
<div style={{ marginBottom: '1.5rem' }}>
|
||||
<div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: '.6rem', color: 'rgba(251,191,36,.5)', letterSpacing: '.12em', marginBottom: '.75rem' }}>INVESTMENT STATS</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(120px,1fr))', gap: '.6rem' }}>
|
||||
<StatCard label="총 구매금액" value={`${(stats.total_invested / 10000).toFixed(1)}만원`} sub={`${stats.total_records}회 구매`} />
|
||||
<StatCard label="총 당첨금" value={`${(stats.total_prize / 10000).toFixed(1)}만원`} sub={`${stats.prize_count}건 당첨`} color="#4ade80" />
|
||||
<StatCard
|
||||
label="순손익"
|
||||
value={`${stats.net >= 0 ? '+' : ''}${(stats.net / 10000).toFixed(1)}만원`}
|
||||
sub={`회수율 ${stats.return_rate.toFixed(1)}%`}
|
||||
color={stats.net >= 0 ? '#4ade80' : '#f87171'}
|
||||
/>
|
||||
<StatCard label="최대 당첨금" value={stats.max_prize > 0 ? `${stats.max_prize.toLocaleString()}원` : '-'} color="#fbbf24" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 구매 기록 테이블 */}
|
||||
<div style={{ background: 'rgba(255,255,255,.02)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', overflow: 'hidden' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '1rem 1.25rem', borderBottom: '1px solid rgba(255,255,255,.06)' }}>
|
||||
<div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: '.65rem', color: 'rgba(255,255,255,.4)', letterSpacing: '.1em' }}>PURCHASE HISTORY</div>
|
||||
<button
|
||||
onClick={() => setShowAdd(!showAdd)}
|
||||
style={{
|
||||
background: 'rgba(251,191,36,.1)', border: '1px solid rgba(251,191,36,.25)',
|
||||
color: '#fbbf24', borderRadius: '.5rem', padding: '.3rem .75rem', fontSize: '.72rem', cursor: 'pointer', fontWeight: 700,
|
||||
}}>
|
||||
+ 구매 추가
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 추가 폼 */}
|
||||
{showAdd && (
|
||||
<div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid rgba(255,255,255,.06)', background: 'rgba(251,191,36,.04)' }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(100px,1fr))', gap: '.5rem', marginBottom: '.75rem' }}>
|
||||
<div>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.62rem', marginBottom: '.25rem' }}>회차 *</div>
|
||||
<input style={inputStyle} placeholder="1181" value={addForm.draw_no} onChange={e => setAddForm(p => ({ ...p, draw_no: e.target.value }))} />
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.62rem', marginBottom: '.25rem' }}>구매금액</div>
|
||||
<input style={inputStyle} placeholder="5000" value={addForm.amount} onChange={e => setAddForm(p => ({ ...p, amount: e.target.value }))} />
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.62rem', marginBottom: '.25rem' }}>세트수</div>
|
||||
<input style={inputStyle} placeholder="5" value={addForm.sets} onChange={e => setAddForm(p => ({ ...p, sets: e.target.value }))} />
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.62rem', marginBottom: '.25rem' }}>당첨금</div>
|
||||
<input style={inputStyle} placeholder="0" value={addForm.prize} onChange={e => setAddForm(p => ({ ...p, prize: e.target.value }))} />
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.62rem', marginBottom: '.25rem' }}>메모</div>
|
||||
<input style={inputStyle} placeholder="5등 1개" value={addForm.note} onChange={e => setAddForm(p => ({ ...p, note: e.target.value }))} />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '.5rem' }}>
|
||||
<button onClick={handleAdd} disabled={saving || !addForm.draw_no}
|
||||
style={{ background: '#fbbf24', color: '#020c1e', border: 'none', borderRadius: '.5rem', padding: '.4rem 1rem', fontSize: '.75rem', fontWeight: 700, cursor: saving ? 'not-allowed' : 'pointer' }}>
|
||||
{saving ? '저장 중...' : '저장'}
|
||||
</button>
|
||||
<button onClick={() => setShowAdd(false)}
|
||||
style={{ background: 'rgba(255,255,255,.06)', color: 'rgba(255,255,255,.4)', border: '1px solid rgba(255,255,255,.1)', borderRadius: '.5rem', padding: '.4rem 1rem', fontSize: '.75rem', cursor: 'pointer' }}>
|
||||
취소
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 레코드 목록 */}
|
||||
{records.length === 0 ? (
|
||||
<div style={{ padding: '3rem', textAlign: 'center', color: 'rgba(255,255,255,.2)', fontSize: '.8rem' }}>
|
||||
구매 기록이 없습니다. 구매 후 기록을 추가해보세요.
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ overflowX: 'auto' }}>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '.75rem' }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: '1px solid rgba(255,255,255,.06)' }}>
|
||||
{['회차', '구매금액', '세트', '당첨금', '손익', '메모', ''].map(h => (
|
||||
<th key={h} style={{ padding: '.6rem 1rem', color: 'rgba(255,255,255,.3)', fontWeight: 600, textAlign: 'left', whiteSpace: 'nowrap' }}>{h}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{records.map(rec => {
|
||||
const net = rec.prize - rec.amount;
|
||||
const isEditing = editingId === rec.id;
|
||||
return (
|
||||
<tr key={rec.id} style={{ borderBottom: '1px solid rgba(255,255,255,.04)', transition: 'background .15s' }}>
|
||||
<td style={{ padding: '.7rem 1rem', color: '#fbbf24', fontWeight: 700, fontFamily: "'JetBrains Mono',monospace" }}>{rec.draw_no}회</td>
|
||||
<td style={{ padding: '.7rem 1rem', color: 'rgba(255,255,255,.6)' }}>{rec.amount.toLocaleString()}원</td>
|
||||
<td style={{ padding: '.7rem 1rem', color: 'rgba(255,255,255,.4)' }}>{rec.sets}세트</td>
|
||||
<td style={{ padding: '.7rem 1rem' }}>
|
||||
{isEditing ? (
|
||||
<input style={{ ...inputStyle, width: 80 }} value={editPrize} onChange={e => setEditPrize(e.target.value)} />
|
||||
) : (
|
||||
<span style={{ color: rec.prize > 0 ? '#4ade80' : 'rgba(255,255,255,.3)' }}>
|
||||
{rec.prize > 0 ? `${rec.prize.toLocaleString()}원` : '-'}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td style={{ padding: '.7rem 1rem', color: net > 0 ? '#4ade80' : net < 0 ? '#f87171' : 'rgba(255,255,255,.3)', fontFamily: "'JetBrains Mono',monospace", fontWeight: 700 }}>
|
||||
{net > 0 ? '+' : ''}{net.toLocaleString()}
|
||||
</td>
|
||||
<td style={{ padding: '.7rem 1rem' }}>
|
||||
{isEditing ? (
|
||||
<input style={{ ...inputStyle, width: 100 }} value={editNote} onChange={e => setEditNote(e.target.value)} />
|
||||
) : (
|
||||
<span style={{ color: 'rgba(255,255,255,.4)' }}>{rec.note || '-'}</span>
|
||||
)}
|
||||
</td>
|
||||
<td style={{ padding: '.7rem 1rem', whiteSpace: 'nowrap' }}>
|
||||
{isEditing ? (
|
||||
<div style={{ display: 'flex', gap: '.3rem' }}>
|
||||
<button onClick={() => handleUpdate(rec.id)} disabled={saving}
|
||||
style={{ background: '#4ade80', color: '#020c1e', border: 'none', borderRadius: '.35rem', padding: '.25rem .6rem', fontSize: '.65rem', fontWeight: 700, cursor: 'pointer' }}>저장</button>
|
||||
<button onClick={() => setEditingId(null)}
|
||||
style={{ background: 'rgba(255,255,255,.08)', color: 'rgba(255,255,255,.4)', border: 'none', borderRadius: '.35rem', padding: '.25rem .6rem', fontSize: '.65rem', cursor: 'pointer' }}>취소</button>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: 'flex', gap: '.3rem' }}>
|
||||
<button onClick={() => { setEditingId(rec.id); setEditPrize(String(rec.prize)); setEditNote(rec.note); }}
|
||||
style={{ background: 'rgba(251,191,36,.1)', color: '#fbbf24', border: '1px solid rgba(251,191,36,.2)', borderRadius: '.35rem', padding: '.25rem .6rem', fontSize: '.65rem', cursor: 'pointer' }}>수정</button>
|
||||
<button onClick={() => handleDelete(rec.id)}
|
||||
style={{ background: 'rgba(239,68,68,.1)', color: '#f87171', border: '1px solid rgba(239,68,68,.2)', borderRadius: '.35rem', padding: '.25rem .6rem', fontSize: '.65rem', cursor: 'pointer' }}>삭제</button>
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
// ─── Types ───────────────────────────────────────────────────────────────────
|
||||
interface ReportData {
|
||||
target_drw_no: number;
|
||||
based_on_draw: number;
|
||||
generated_at: string;
|
||||
hot_numbers: number[];
|
||||
cold_numbers: number[];
|
||||
overdue_numbers: number[];
|
||||
recent_pattern: {
|
||||
last3_numbers: number[];
|
||||
triple_appear: number[];
|
||||
recent_sum_avg: number;
|
||||
recent_odd_avg: number;
|
||||
};
|
||||
recommended_sets: Array<{
|
||||
strategy: string;
|
||||
numbers: number[];
|
||||
description: string;
|
||||
}>;
|
||||
confidence_score: number;
|
||||
confidence_factors: {
|
||||
data_volume: number;
|
||||
pattern_consistency: number;
|
||||
recent_trend: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface HistoryItem { drw_no: number; generated_at: string; }
|
||||
|
||||
function getBallStyle(n: number) {
|
||||
if (n <= 10) return { bg: 'linear-gradient(145deg,#fde68a,#fbbf24,#d97706)', text: '#78350f' };
|
||||
if (n <= 20) return { bg: 'linear-gradient(145deg,#93c5fd,#3b82f6,#1d4ed8)', text: '#fff' };
|
||||
if (n <= 30) return { bg: 'linear-gradient(145deg,#fca5a5,#ef4444,#b91c1c)', text: '#fff' };
|
||||
if (n <= 40) return { bg: 'linear-gradient(145deg,#d1d5db,#9ca3af,#4b5563)', text: '#fff' };
|
||||
return { bg: 'linear-gradient(145deg,#86efac,#22c55e,#15803d)', text: '#fff' };
|
||||
}
|
||||
|
||||
function SmallBall({ n, size = 32 }: { n: number; size?: number }) {
|
||||
const { bg, text } = getBallStyle(n);
|
||||
return (
|
||||
<div style={{
|
||||
width: size, height: size, borderRadius: '50%', background: bg,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
fontSize: size * 0.35, fontWeight: 900, color: text, flexShrink: 0,
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,.3)',
|
||||
}}>{n}</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ConfidenceBar({ label, value }: { label: string; value: number }) {
|
||||
const color = value >= 85 ? '#4ade80' : value >= 70 ? '#fbbf24' : '#f87171';
|
||||
return (
|
||||
<div style={{ marginBottom: '.6rem' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '.25rem' }}>
|
||||
<span style={{ color: 'rgba(255,255,255,.4)', fontSize: '.7rem' }}>{label}</span>
|
||||
<span style={{ color, fontSize: '.7rem', fontWeight: 700, fontFamily: "'JetBrains Mono',monospace" }}>{value}</span>
|
||||
</div>
|
||||
<div style={{ height: 5, background: 'rgba(255,255,255,.07)', borderRadius: 3, overflow: 'hidden' }}>
|
||||
<div style={{ height: '100%', width: `${value}%`, background: color, borderRadius: 3, transition: 'width 1s ease' }} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ReportTab() {
|
||||
const [report, setReport] = useState<ReportData | null>(null);
|
||||
const [history, setHistory] = useState<HistoryItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const [copiedIdx, setCopiedIdx] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
fetch('/api/lotto/report/latest').then(r => r.json()),
|
||||
fetch('/api/lotto/report/history?limit=10').then(r => r.json()),
|
||||
]).then(([rep, hist]) => {
|
||||
if (rep?.error) {
|
||||
setError(rep.error === 'NAS_TIMEOUT'
|
||||
? 'NAS 서버 응답 시간 초과. 잠시 후 다시 시도해주세요.'
|
||||
: '리포트를 불러오지 못했습니다. (' + rep.error + ')');
|
||||
return;
|
||||
}
|
||||
setReport(rep);
|
||||
setHistory(hist?.reports ?? []);
|
||||
}).catch(() => setError('리포트를 불러오지 못했습니다.'))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const copyNumbers = (numbers: number[], idx: number) => {
|
||||
navigator.clipboard.writeText(numbers.join(', '));
|
||||
setCopiedIdx(idx);
|
||||
setTimeout(() => setCopiedIdx(null), 1500);
|
||||
};
|
||||
|
||||
if (loading) return (
|
||||
<div style={{ textAlign: 'center', padding: '4rem 0' }}>
|
||||
<div style={{ width: 36, height: 36, borderRadius: '50%', border: '3px solid rgba(251,191,36,.15)', borderTop: '3px solid #fbbf24', animation: 'spin .8s linear infinite', margin: '0 auto 1rem' }} />
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.75rem' }}>리포트 불러오는 중...</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (error) return (
|
||||
<div style={{ textAlign: 'center', padding: '4rem 0', color: '#f87171', fontSize: '.85rem' }}>{error}</div>
|
||||
);
|
||||
|
||||
if (!report || !report.confidence_factors || !report.recommended_sets) return null;
|
||||
|
||||
const strategyColors = ['#fbbf24', '#60a5fa', '#a78bfa'];
|
||||
|
||||
return (
|
||||
<div style={{ animation: 'slideUp .4s ease forwards' }}>
|
||||
|
||||
{/* 헤더 */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1.5rem', flexWrap: 'wrap', gap: '.75rem' }}>
|
||||
<div>
|
||||
<div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: '.6rem', color: 'rgba(251,191,36,.5)', letterSpacing: '.15em', marginBottom: '.3rem' }}>WEEKLY ATTACK REPORT</div>
|
||||
<h2 style={{ color: '#fff', fontSize: '1.4rem', fontWeight: 900, margin: 0 }}>
|
||||
제{report.target_drw_no}회 공략 리포트
|
||||
</h2>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.72rem', marginTop: '.2rem' }}>
|
||||
{report.based_on_draw}회까지 데이터 기반 · {new Date(report.generated_at).toLocaleDateString('ko-KR')} 생성
|
||||
</div>
|
||||
</div>
|
||||
{/* 신뢰도 점수 */}
|
||||
<div style={{
|
||||
background: 'rgba(251,191,36,.08)', border: '1px solid rgba(251,191,36,.2)',
|
||||
borderRadius: '1rem', padding: '.75rem 1.25rem', textAlign: 'center',
|
||||
}}>
|
||||
<div style={{ color: 'rgba(251,191,36,.5)', fontSize: '.6rem', fontFamily: "'JetBrains Mono',monospace", letterSpacing: '.1em' }}>CONFIDENCE</div>
|
||||
<div style={{ color: '#fbbf24', fontSize: '2rem', fontWeight: 900, lineHeight: 1.1, fontFamily: "'JetBrains Mono',monospace" }}>{report.confidence_score}</div>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.6rem' }}>/100</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(280px,1fr))', gap: '1rem', marginBottom: '1.5rem' }}>
|
||||
|
||||
{/* 추천 번호 세트 */}
|
||||
<div style={{ gridColumn: '1/-1', background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1.25rem' }}>
|
||||
<div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: '.6rem', color: 'rgba(251,191,36,.5)', letterSpacing: '.12em', marginBottom: '1rem' }}>RECOMMENDED SETS</div>
|
||||
<div style={{ display: 'grid', gap: '.75rem' }}>
|
||||
{report.recommended_sets.map((set, i) => (
|
||||
<div key={i} style={{
|
||||
background: `rgba(${i === 0 ? '251,191,36' : i === 1 ? '96,165,250' : '167,139,250'},.05)`,
|
||||
border: `1px solid rgba(${i === 0 ? '251,191,36' : i === 1 ? '96,165,250' : '167,139,250'},.15)`,
|
||||
borderRadius: '.75rem', padding: '1rem',
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '.6rem', flexWrap: 'wrap', gap: '.5rem' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '.5rem' }}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: strategyColors[i] }} />
|
||||
<span style={{ color: strategyColors[i], fontSize: '.72rem', fontWeight: 700 }}>{set.strategy}</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => copyNumbers(set.numbers, i)}
|
||||
style={{
|
||||
background: 'rgba(255,255,255,.06)', border: '1px solid rgba(255,255,255,.1)',
|
||||
color: copiedIdx === i ? '#4ade80' : 'rgba(255,255,255,.4)',
|
||||
borderRadius: '.4rem', padding: '.2rem .6rem', fontSize: '.65rem', cursor: 'pointer',
|
||||
}}>
|
||||
{copiedIdx === i ? '✓ 복사됨' : '복사'}
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '.4rem', flexWrap: 'wrap', marginBottom: '.5rem' }}>
|
||||
{set.numbers.map(n => <SmallBall key={n} n={n} size={36} />)}
|
||||
</div>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.68rem' }}>{set.description}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 핫/콜드/미출현 */}
|
||||
{[
|
||||
{ label: '🔥 최근 과출현', numbers: report.hot_numbers, color: '#f87171', desc: '최근 10회 2회 이상 출현' },
|
||||
{ label: '❄️ 저빈도 번호', numbers: report.cold_numbers, color: '#60a5fa', desc: '역대 출현 빈도 하위' },
|
||||
{ label: '⏳ 장기 미출현', numbers: report.overdue_numbers, color: '#a78bfa', desc: '가장 오래 미출현 번호' },
|
||||
].map(({ label, numbers, color, desc }) => (
|
||||
<div key={label} style={{ background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1rem' }}>
|
||||
<div style={{ color, fontSize: '.75rem', fontWeight: 700, marginBottom: '.3rem' }}>{label}</div>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.65rem', marginBottom: '.75rem' }}>{desc}</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '.35rem' }}>
|
||||
{numbers.map(n => <SmallBall key={n} n={n} size={30} />)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* 최근 패턴 */}
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1rem' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.5)', fontSize: '.72rem', fontWeight: 700, marginBottom: '.75rem' }}>📊 최근 패턴</div>
|
||||
{[
|
||||
{ label: '최근 10회 합계 평균', value: report.recent_pattern.recent_sum_avg.toFixed(1) },
|
||||
{ label: '최근 10회 홀수 평균', value: report.recent_pattern.recent_odd_avg.toFixed(1) + '개' },
|
||||
].map(({ label, value }) => (
|
||||
<div key={label} style={{ display: 'flex', justifyContent: 'space-between', padding: '.4rem 0', borderBottom: '1px solid rgba(255,255,255,.05)' }}>
|
||||
<span style={{ color: 'rgba(255,255,255,.3)', fontSize: '.7rem' }}>{label}</span>
|
||||
<span style={{ color: '#fbbf24', fontSize: '.7rem', fontWeight: 700, fontFamily: "'JetBrains Mono',monospace" }}>{value}</span>
|
||||
</div>
|
||||
))}
|
||||
{report.recent_pattern.triple_appear.length > 0 && (
|
||||
<div style={{ marginTop: '.75rem' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.3)', fontSize: '.65rem', marginBottom: '.4rem' }}>직전 3회 연속 출현</div>
|
||||
<div style={{ display: 'flex', gap: '.3rem' }}>
|
||||
{report.recent_pattern.triple_appear.map(n => <SmallBall key={n} n={n} size={28} />)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 신뢰도 상세 */}
|
||||
<div style={{ background: 'rgba(255,255,255,.03)', border: '1px solid rgba(255,255,255,.07)', borderRadius: '1rem', padding: '1rem' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.5)', fontSize: '.72rem', fontWeight: 700, marginBottom: '.75rem' }}>🎯 신뢰도 분석</div>
|
||||
<ConfidenceBar label="데이터 충분도" value={report.confidence_factors.data_volume} />
|
||||
<ConfidenceBar label="패턴 안정성" value={report.confidence_factors.pattern_consistency} />
|
||||
<ConfidenceBar label="최근 트렌드" value={report.confidence_factors.recent_trend} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 이전 리포트 목록 */}
|
||||
{history.length > 0 && (
|
||||
<div style={{ background: 'rgba(255,255,255,.02)', border: '1px solid rgba(255,255,255,.05)', borderRadius: '1rem', padding: '1rem' }}>
|
||||
<div style={{ color: 'rgba(255,255,255,.4)', fontSize: '.7rem', fontWeight: 700, marginBottom: '.75rem', fontFamily: "'JetBrains Mono',monospace", letterSpacing: '.08em' }}>REPORT HISTORY</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '.4rem' }}>
|
||||
{history.map(h => (
|
||||
<button key={h.drw_no}
|
||||
style={{
|
||||
background: h.drw_no === report.target_drw_no ? 'rgba(251,191,36,.15)' : 'rgba(255,255,255,.05)',
|
||||
border: `1px solid ${h.drw_no === report.target_drw_no ? 'rgba(251,191,36,.4)' : 'rgba(255,255,255,.1)'}`,
|
||||
color: h.drw_no === report.target_drw_no ? '#fbbf24' : 'rgba(255,255,255,.4)',
|
||||
borderRadius: '.5rem', padding: '.3rem .65rem', fontSize: '.68rem', cursor: 'pointer',
|
||||
}}>
|
||||
{h.drw_no}회
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
28
app/services/music/layout.tsx
Normal file
28
app/services/music/layout.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'AI 음악 마스터 구조 팩 | Suno · MV · 유튜브 쇼츠',
|
||||
description:
|
||||
'7년차 개발자가 설계한 4단계 AI 음악 제작 공정. Suno 프롬프트 조합법 + MV 비디오 생성 워크플로우 + 저작권 가이드 + 템플릿 PDF + 샘플 프로젝트. 입문 ₩39k / 프로 ₩99k / 마스터 ₩149k.',
|
||||
keywords: [
|
||||
'AI 음악 만들기',
|
||||
'Suno 프롬프트',
|
||||
'AI 뮤직비디오',
|
||||
'AI 커버곡',
|
||||
'유튜브 쇼츠 음악',
|
||||
'AI 작곡',
|
||||
'크리에이터 이코노미',
|
||||
'Lyria 프롬프트',
|
||||
'Runway AI 비디오',
|
||||
],
|
||||
openGraph: {
|
||||
title: 'AI 음악 마스터 구조 팩 | 쟁승메이드',
|
||||
description:
|
||||
'네 사연을 노래로. 쇼츠까지 한 번에. 4단계 AI 음악 공정 · Suno Pro 검증 · 평생 업데이트.',
|
||||
url: 'https://jaengseung-made.com/services/music',
|
||||
},
|
||||
};
|
||||
|
||||
export default function MusicLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
483
app/services/music/page.tsx
Normal file
483
app/services/music/page.tsx
Normal file
@@ -0,0 +1,483 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import PurchaseAgreementModal from '../../components/PurchaseAgreementModal';
|
||||
|
||||
type Tier = 'starter' | 'pro' | 'master';
|
||||
|
||||
const TIERS: Record<Tier, { name: string; price: string; priceNum: string; desc: string; features: string[]; highlight?: boolean }> = {
|
||||
starter: {
|
||||
name: '입문',
|
||||
price: '₩39,000',
|
||||
priceNum: '39,000',
|
||||
desc: '첫 AI 음악을 위한 필수 구성',
|
||||
features: [
|
||||
'Suno 프롬프트 조합법 20종',
|
||||
'기본 가사 최적화 템플릿',
|
||||
'구조 템플릿 PDF 40p',
|
||||
'저작권 가이드 기본판',
|
||||
'12개월 무료 업데이트',
|
||||
],
|
||||
},
|
||||
pro: {
|
||||
name: '프로',
|
||||
price: '₩99,000',
|
||||
priceNum: '99,000',
|
||||
desc: '쇼츠 업로드까지 완성하는 풀세트',
|
||||
highlight: true,
|
||||
features: [
|
||||
'입문 전체 포함',
|
||||
'고급 편집법 (Stems 분리 · 마스터링 프롬프트)',
|
||||
'MV 비디오 생성 워크플로우 (Runway/Luma/Pika)',
|
||||
'샘플 프로젝트 1개 (.prj · 영상 포함)',
|
||||
'이메일/페이지 1:1 Q&A 1회 (30일 이내)',
|
||||
'유튜브 SEO 템플릿',
|
||||
],
|
||||
},
|
||||
master: {
|
||||
name: '마스터',
|
||||
price: '₩149,000',
|
||||
priceNum: '149,000',
|
||||
desc: '여러 장르·포맷을 커버하는 마스터피스',
|
||||
features: [
|
||||
'프로 전체 포함',
|
||||
'샘플 프로젝트 다수 (장르별 3종)',
|
||||
'우선 업데이트 · 베타 기능 선공개',
|
||||
'저작권 가이드 심화판 + 상업 이용 체크리스트',
|
||||
'제작 레시피 영상 가이드',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const PROCESS = [
|
||||
{
|
||||
num: '01',
|
||||
title: '크리에이티브 디렉팅',
|
||||
subtitle: 'Concept & Lyrics',
|
||||
customer: '원하는 키워드 3개 또는 사연 제공',
|
||||
value: 'ChatGPT·Claude로 Suno가 이해하는 가사·스타일 태그로 변환',
|
||||
result: 'AI 최적화 가사 · 메타데이터 시트',
|
||||
color: 'from-violet-500 to-fuchsia-500',
|
||||
},
|
||||
{
|
||||
num: '02',
|
||||
title: '오디오 엔지니어링',
|
||||
subtitle: 'Music Generation',
|
||||
customer: '결과물 확인 · 방향 피드백',
|
||||
value: 'Suno Custom Mode로 가사 배치·파트·보컬·악기 세밀 조정. 가장 높은 퀄리티가 나올 때까지 프롬프트 깎기',
|
||||
result: '고품질 완곡 (Full Track, 스템 분리본)',
|
||||
color: 'from-fuchsia-500 to-pink-500',
|
||||
},
|
||||
{
|
||||
num: '03',
|
||||
title: '비주얼 마스터링',
|
||||
subtitle: 'AI MV Generation',
|
||||
customer: '-',
|
||||
value: 'Midjourney · Runway · Luma로 음악 분위기에 맞는 이미지·영상 생성. 비트와 가사에 맞춘 싱크 설계',
|
||||
result: '쇼츠(9:16) 또는 유튜브(16:9) 고화질 영상',
|
||||
color: 'from-sky-500 to-cyan-500',
|
||||
},
|
||||
{
|
||||
num: '04',
|
||||
title: '퍼블리싱 가이드',
|
||||
subtitle: 'Viral Optimization',
|
||||
customer: '유튜브 업로드',
|
||||
value: '제목·해시태그·설명란(SEO) AI 최적화 템플릿 제공',
|
||||
result: '즉시 업로드 가능한 유튜브 배포 패키지',
|
||||
color: 'from-cyan-500 to-emerald-500',
|
||||
},
|
||||
];
|
||||
|
||||
const FAQS = [
|
||||
{
|
||||
q: 'Suno 유료 플랜 가입이 꼭 필요한가요?',
|
||||
a: 'Suno 무료 플랜은 상업적 이용이 제한됩니다. 본인 결과물을 유튜브·SNS에 업로드해 수익화하려면 Suno Pro 이상 권장. 팩 구매 후 가입 전 플랜 선택 가이드가 포함됩니다.',
|
||||
},
|
||||
{
|
||||
q: '제가 만든 결과물의 상업 이용·저작권은?',
|
||||
a: '결과물의 상업권은 고객이 가입한 AI 서비스의 이용약관을 따릅니다. 팩에는 Suno·Runway·Luma 각 서비스의 최신 약관 요약과 상업 이용 체크리스트가 포함되어 있습니다. (법률 자문이 아닌 참고용 가이드입니다.)',
|
||||
},
|
||||
{
|
||||
q: '결과물 품질을 보장하나요?',
|
||||
a: 'AI 생성물은 모델 버전·프롬프트 입력에 따라 달라지므로 결과물 자체를 보장하지 않습니다. 다만 팩은 동일 프롬프트로 반복 가능한 고품질 구간을 설계하는 방법을 제공합니다. 샘플 쇼츠·프로젝트로 품질 기대치를 사전 확인하세요.',
|
||||
},
|
||||
{
|
||||
q: '환불이 가능한가요?',
|
||||
a: '전자상거래법 제17조 제2항 제5호에 따라 디지털 콘텐츠는 제공 시작 후 청약철회가 제한됩니다. 무료 샘플로 사전 확인을 제공하므로 충분히 검토 후 구매해주세요. 파일 손상·전달 불량 등 회사 귀책은 즉시 재전달 또는 환불됩니다.',
|
||||
},
|
||||
{
|
||||
q: '업데이트는 어떻게 받나요?',
|
||||
a: '구매자 전용 Notion 페이지에서 변경 이력과 최신 파일을 제공. 12개월간 무료 업데이트가 기본, 마스터는 우선 업데이트·베타 선공개가 포함됩니다.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function MusicServicePage() {
|
||||
const [selectedTier, setSelectedTier] = useState<Tier | null>(null);
|
||||
const [openFaq, setOpenFaq] = useState<number | null>(0);
|
||||
|
||||
return (
|
||||
<div className="min-h-full bg-slate-950 text-white">
|
||||
{/* HERO */}
|
||||
<section
|
||||
className="relative overflow-hidden px-6 py-24 lg:px-14 lg:py-32"
|
||||
style={{
|
||||
background:
|
||||
'radial-gradient(circle at 25% 20%, #2e1065 0%, #020617 55%), radial-gradient(circle at 80% 80%, #164e63 0%, transparent 50%)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.05] pointer-events-none mix-blend-overlay"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.5'/></svg>\")",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="relative max-w-5xl mx-auto">
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<span className="inline-flex h-2 w-2 rounded-full bg-violet-400 animate-pulse" />
|
||||
<span className="font-mono text-xs text-violet-300/80 tracking-[0.25em] uppercase">
|
||||
AI Music Pack · v1
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className="text-[2.8rem] md:text-[4rem] lg:text-[5.5rem] font-extrabold leading-[1.02] tracking-tight mb-6"
|
||||
style={{ wordBreak: 'keep-all' }}
|
||||
>
|
||||
<span className="text-white">네 사연을 노래로.</span>
|
||||
<br />
|
||||
<span className="bg-gradient-to-r from-violet-300 via-pink-200 to-cyan-300 bg-clip-text text-transparent">
|
||||
쇼츠까지 한 번에.
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className="text-slate-300 text-lg md:text-xl leading-relaxed mb-3 max-w-2xl"
|
||||
style={{ wordBreak: 'keep-all' }}
|
||||
>
|
||||
AI로 음악을 뽑는 게 아니라, <span className="text-white font-semibold">고품질 결과물을 빠르게</span> 뽑는 법을 팝니다.
|
||||
</p>
|
||||
<p className="text-slate-400 text-base mb-10 max-w-2xl">
|
||||
7년차 개발자가 설계한 <span className="text-white">4단계 AI 음악 공정</span> · Suno Pro 검증.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-3 mb-10">
|
||||
<a
|
||||
href="#pricing"
|
||||
className="inline-flex items-center gap-2 bg-violet-600 hover:bg-violet-500 text-white px-8 py-4 rounded-xl font-bold text-sm transition-colors shadow-[0_0_40px_rgba(139,92,246,0.45)]"
|
||||
>
|
||||
팩 둘러보기
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="#samples"
|
||||
className="inline-flex items-center gap-2 border border-white/20 hover:border-white/50 text-white/90 hover:text-white px-8 py-4 rounded-xl font-semibold text-sm transition-all"
|
||||
>
|
||||
▶ 샘플 쇼츠 보기
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-5 text-xs text-slate-400">
|
||||
<span className="flex items-center gap-1.5">✅ 평생 업데이트</span>
|
||||
<span className="flex items-center gap-1.5">✅ 즉시 다운로드</span>
|
||||
<span className="flex items-center gap-1.5">✅ Suno Pro 검증 샘플</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom waveform */}
|
||||
<div className="absolute bottom-0 left-0 right-0 h-32 opacity-40 pointer-events-none">
|
||||
<svg viewBox="0 0 1200 120" preserveAspectRatio="none" className="w-full h-full">
|
||||
<path
|
||||
d="M0,60 Q150,10 300,60 T600,60 T900,60 T1200,60 L1200,120 L0,120 Z"
|
||||
fill="url(#wg)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="wg" x1="0%" x2="100%">
|
||||
<stop offset="0%" stopColor="#a78bfa" stopOpacity="0.5" />
|
||||
<stop offset="50%" stopColor="#22d3ee" stopOpacity="0.3" />
|
||||
<stop offset="100%" stopColor="#a78bfa" stopOpacity="0.5" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* BEFORE / AFTER */}
|
||||
<section className="px-6 py-20 lg:px-14 bg-slate-950 border-t border-white/5">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<p className="font-mono text-xs text-violet-300/70 tracking-widest uppercase mb-2 text-center">
|
||||
Before vs After
|
||||
</p>
|
||||
<h2 className="text-3xl md:text-4xl font-extrabold text-center mb-12">
|
||||
AI 음악, 왜 다들 어렵다고 할까요?
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 gap-5">
|
||||
<div className="border border-rose-500/20 bg-rose-500/5 rounded-2xl p-8">
|
||||
<div className="text-4xl mb-3">😵</div>
|
||||
<h3 className="font-extrabold text-rose-300 mb-3 text-lg">Before · 대충 뽑은 결과</h3>
|
||||
<ul className="space-y-2 text-sm text-slate-300 leading-relaxed">
|
||||
<li>• Suno 10번 돌렸는데 다 별로…</li>
|
||||
<li>• 가사가 이상하게 붙음</li>
|
||||
<li>• 영상 만들려니 뭐부터 할지 모름</li>
|
||||
<li>• 유튜브 올려도 조회수 0</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="border border-emerald-500/30 bg-emerald-500/5 rounded-2xl p-8 shadow-[0_0_40px_rgba(16,185,129,0.15)]">
|
||||
<div className="text-4xl mb-3">🎯</div>
|
||||
<h3 className="font-extrabold text-emerald-300 mb-3 text-lg">After · 구조를 쓴 결과</h3>
|
||||
<ul className="space-y-2 text-sm text-slate-200 leading-relaxed">
|
||||
<li>• 프롬프트 1번으로 원하는 무드 적중</li>
|
||||
<li>• 30분 만에 쇼츠까지 완성</li>
|
||||
<li>• 저작권·상업 이용 안전 체크</li>
|
||||
<li>• SEO 템플릿으로 노출 최적화</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* PROCESS — 4 STEPS */}
|
||||
<section className="px-6 py-24 lg:px-14 bg-gradient-to-b from-slate-950 to-[#0b0530]">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<p className="font-mono text-xs text-violet-300/70 tracking-widest uppercase mb-2">
|
||||
Process Architecture
|
||||
</p>
|
||||
<h2 className="text-3xl md:text-4xl font-extrabold mb-4" style={{ wordBreak: 'keep-all' }}>
|
||||
컨셉 → 음악 → 비주얼 → 퍼블리싱,
|
||||
</h2>
|
||||
<p className="text-slate-400 text-lg mb-16">한 번에 이어지는 4단계 공정 설계도.</p>
|
||||
|
||||
<div className="space-y-6">
|
||||
{PROCESS.map((step) => (
|
||||
<div
|
||||
key={step.num}
|
||||
className="group relative border border-white/10 hover:border-violet-400/50 rounded-3xl p-8 md:p-10 bg-white/[0.02] backdrop-blur transition-all"
|
||||
>
|
||||
<div className="flex flex-col md:flex-row md:items-start gap-6">
|
||||
<div className="flex-shrink-0">
|
||||
<div
|
||||
className={`inline-flex items-center justify-center w-20 h-20 rounded-2xl bg-gradient-to-br ${step.color} font-extrabold text-2xl text-white shadow-lg`}
|
||||
>
|
||||
{step.num}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-mono text-[11px] text-violet-300/60 uppercase tracking-widest mb-1">
|
||||
{step.subtitle}
|
||||
</p>
|
||||
<h3 className="text-2xl md:text-3xl font-extrabold text-white mb-4">
|
||||
{step.title}
|
||||
</h3>
|
||||
<dl className="grid sm:grid-cols-3 gap-4 text-sm">
|
||||
<div>
|
||||
<dt className="text-slate-500 text-xs uppercase font-bold mb-1">고객 역할</dt>
|
||||
<dd className="text-slate-300 leading-relaxed">{step.customer}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-slate-500 text-xs uppercase font-bold mb-1">나의 가치</dt>
|
||||
<dd className="text-slate-300 leading-relaxed">{step.value}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-slate-500 text-xs uppercase font-bold mb-1">결과물</dt>
|
||||
<dd className="text-white font-semibold leading-relaxed">{step.result}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* SAMPLES */}
|
||||
<section id="samples" className="px-6 py-20 lg:px-14 bg-[#0b0530]">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<p className="font-mono text-xs text-violet-300/70 tracking-widest uppercase mb-2">
|
||||
Sample Showcase
|
||||
</p>
|
||||
<h2 className="text-3xl md:text-4xl font-extrabold mb-3">
|
||||
이 쇼츠, 이 팩으로 만들었어요.
|
||||
</h2>
|
||||
<p className="text-slate-400 mb-10">30분 만에 나온 결과물을 직접 들어보세요.</p>
|
||||
|
||||
<div className="grid sm:grid-cols-3 gap-4">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="aspect-[9/16] rounded-2xl border border-white/10 bg-gradient-to-br from-violet-900/40 to-slate-900 flex items-center justify-center relative overflow-hidden group cursor-pointer"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-violet-500/10 to-cyan-500/10 group-hover:from-violet-500/20 group-hover:to-cyan-500/20 transition-all" />
|
||||
<div className="relative text-center p-6">
|
||||
<div className="text-5xl mb-3">🎬</div>
|
||||
<p className="text-xs text-slate-400 font-mono">Sample {i}</p>
|
||||
<p className="text-sm text-slate-200 mt-1">준비 중</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mt-6 text-center">
|
||||
유튜브 쇼츠 임베드로 교체 예정. 샘플 오디오는 구매 전 전체 듣기가 가능하도록 제공됩니다.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* PRICING */}
|
||||
<section id="pricing" className="px-6 py-24 lg:px-14 bg-slate-950">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<p className="font-mono text-xs text-violet-300/70 tracking-widest uppercase mb-2 text-center">
|
||||
Pricing
|
||||
</p>
|
||||
<h2 className="text-3xl md:text-4xl font-extrabold text-center mb-4">
|
||||
3개 티어, 내 목표에 맞게.
|
||||
</h2>
|
||||
<p className="text-center text-slate-400 mb-14">한 번 결제로 평생 업데이트.</p>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-5 items-stretch">
|
||||
{(Object.keys(TIERS) as Tier[]).map((key) => {
|
||||
const t = TIERS[key];
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
className={`relative rounded-3xl p-8 flex flex-col border transition-all ${
|
||||
t.highlight
|
||||
? 'border-violet-400 bg-gradient-to-br from-violet-900/40 to-slate-900 shadow-[0_0_60px_rgba(139,92,246,0.35)] md:scale-[1.03] md:-translate-y-2'
|
||||
: 'border-white/10 bg-white/[0.02] hover:border-white/30'
|
||||
}`}
|
||||
>
|
||||
{t.highlight && (
|
||||
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
||||
<span className="inline-flex items-center gap-1 bg-gradient-to-r from-violet-500 to-pink-500 text-white text-[10px] font-extrabold px-3 py-1.5 rounded-full uppercase tracking-wider">
|
||||
🔥 80%가 선택
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<h3 className="font-extrabold text-2xl mb-1">{t.name}</h3>
|
||||
<p className="text-sm text-slate-400 mb-6">{t.desc}</p>
|
||||
<div className="mb-6">
|
||||
<span className="text-4xl font-extrabold font-mono">{t.price}</span>
|
||||
<span className="text-xs text-slate-500 ml-2">1회 결제</span>
|
||||
</div>
|
||||
<ul className="space-y-3 text-sm text-slate-200 mb-8 flex-1">
|
||||
{t.features.map((f) => (
|
||||
<li key={f} className="flex gap-2.5">
|
||||
<svg className="w-4 h-4 text-emerald-400 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<span className="leading-relaxed">{f}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button
|
||||
onClick={() => setSelectedTier(key)}
|
||||
className={`w-full py-4 rounded-xl font-extrabold text-sm transition-colors ${
|
||||
t.highlight
|
||||
? 'bg-violet-500 hover:bg-violet-400 text-white'
|
||||
: 'bg-white/10 hover:bg-white/20 text-white'
|
||||
}`}
|
||||
>
|
||||
{t.name} 구매하기 →
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 text-center mt-8">
|
||||
구매 전 <Link href="/legal/refund" className="underline hover:text-white">환불 정책</Link>을 반드시 확인해주세요.
|
||||
디지털 콘텐츠 특성상 제공 시작 후 청약철회가 제한됩니다.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* B2B */}
|
||||
<section className="px-6 py-16 lg:px-14 bg-gradient-to-br from-slate-900 to-slate-950 border-y border-white/5">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-center gap-6 border border-amber-500/30 bg-amber-500/5 rounded-2xl p-8">
|
||||
<div className="flex-1">
|
||||
<p className="font-mono text-xs text-amber-300/80 tracking-widest uppercase mb-2">
|
||||
For Business
|
||||
</p>
|
||||
<h3 className="text-2xl font-extrabold text-white mb-2">
|
||||
📣 내 가게 전용 BGM, 저작권 걱정 없이.
|
||||
</h3>
|
||||
<p className="text-sm text-slate-300 leading-relaxed">
|
||||
카페 · 쇼핑몰 · 인스타 릴스 · 틱톡 셀러용 전용 BGM 제작.
|
||||
1분 만에 브랜드에 맞는 음악을 뽑아드립니다.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/contact?service=bgm"
|
||||
className="inline-flex items-center gap-2 bg-amber-500 hover:bg-amber-400 text-slate-900 px-6 py-3 rounded-xl font-extrabold text-sm transition-colors"
|
||||
>
|
||||
B2B 문의 →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ */}
|
||||
<section className="px-6 py-20 lg:px-14 bg-slate-950">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-3xl md:text-4xl font-extrabold text-center mb-10">
|
||||
자주 묻는 질문
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{FAQS.map((f, i) => (
|
||||
<div key={i} className="border border-white/10 rounded-xl overflow-hidden bg-white/[0.02]">
|
||||
<button
|
||||
onClick={() => setOpenFaq(openFaq === i ? null : i)}
|
||||
className="w-full flex items-center justify-between px-5 py-4 text-left hover:bg-white/5 transition-colors"
|
||||
>
|
||||
<span className="font-bold text-white text-sm">{f.q}</span>
|
||||
<span className={`text-violet-400 text-xl transition-transform ${openFaq === i ? 'rotate-45' : ''}`}>
|
||||
+
|
||||
</span>
|
||||
</button>
|
||||
{openFaq === i && (
|
||||
<div className="px-5 pb-5 text-sm text-slate-300 leading-relaxed" style={{ wordBreak: 'keep-all' }}>
|
||||
{f.a}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FINAL CTA */}
|
||||
<section className="px-6 py-20 lg:px-14 bg-gradient-to-br from-violet-900 via-slate-950 to-[#0b0530]">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl md:text-5xl font-extrabold text-white mb-4 leading-tight" style={{ wordBreak: 'keep-all' }}>
|
||||
오늘 밤,
|
||||
<br />
|
||||
<span className="bg-gradient-to-r from-violet-300 via-pink-200 to-cyan-300 bg-clip-text text-transparent">
|
||||
첫 쇼츠를 업로드하세요.
|
||||
</span>
|
||||
</h2>
|
||||
<p className="text-slate-400 text-lg mb-10">
|
||||
₩39,000부터 · 평생 업데이트 · 즉시 다운로드
|
||||
</p>
|
||||
<a
|
||||
href="#pricing"
|
||||
className="inline-flex items-center gap-2 bg-violet-600 hover:bg-violet-500 text-white px-12 py-5 rounded-xl font-extrabold text-base transition-colors shadow-[0_0_60px_rgba(139,92,246,0.5)]"
|
||||
>
|
||||
팩 선택하기 →
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{selectedTier && (
|
||||
<PurchaseAgreementModal
|
||||
isOpen={!!selectedTier}
|
||||
onClose={() => setSelectedTier(null)}
|
||||
productName={`AI 음악 마스터 팩 · ${TIERS[selectedTier].name}`}
|
||||
price={TIERS[selectedTier].price}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'ChatGPT·Claude 프롬프트 엔지니어링 | 업무 AI 자동화',
|
||||
description:
|
||||
'ChatGPT, Claude, Gemini를 제대로 활용하는 맞춤형 프롬프트 설계. 이메일·보고서·코드리뷰·고객응대 업무를 AI로 3~5배 빠르게. 이미지 생성 프롬프트 패키지 12,900원, 자소서 첨삭 프롬프트 9,900원.',
|
||||
keywords: [
|
||||
'프롬프트 엔지니어링',
|
||||
'ChatGPT 프롬프트 만들기',
|
||||
'Claude 프롬프트 최적화',
|
||||
'AI 업무 자동화 프롬프트',
|
||||
'ChatGPT 활용법',
|
||||
'이미지 생성 프롬프트',
|
||||
'Midjourney 프롬프트',
|
||||
'자소서 AI 첨삭',
|
||||
'이력서 AI 교정',
|
||||
'프롬프트 패키지',
|
||||
'AI 프롬프트 구매',
|
||||
],
|
||||
openGraph: {
|
||||
title: 'ChatGPT·Claude 프롬프트 엔지니어링 | 쟁승메이드',
|
||||
description:
|
||||
'업무 특화 AI 프롬프트 설계. 이미지 생성·자소서 첨삭 패키지 즉시 구매 가능. 9,900원~.',
|
||||
url: 'https://jaengseung-made.com/services/prompt',
|
||||
},
|
||||
};
|
||||
|
||||
export default function PromptLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
@@ -1,656 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import ContactModal from '../../components/ContactModal';
|
||||
import PaymentButton from '../../components/PaymentButton';
|
||||
import { trackCTAClick } from '../../../lib/gtag';
|
||||
const KAKAO_CHANNEL_URL = process.env.NEXT_PUBLIC_KAKAO_CHANNEL_URL ?? null;
|
||||
|
||||
function useScrollReveal() {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: '0px 0px -40px 0px' }
|
||||
);
|
||||
el.querySelectorAll('.reveal').forEach((child) => observer.observe(child));
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
return ref;
|
||||
}
|
||||
|
||||
const CHECKLIST = [
|
||||
'주로 어떤 AI 도구를 사용하는지 (ChatGPT / Claude / Gemini)',
|
||||
'자동화하고 싶은 업무 유형 (이메일 / 보고서 / 코드 등)',
|
||||
'현재 프롬프트 사용 방식 및 불만족스러운 점',
|
||||
'필요한 프롬프트 수량 (단건 / 패키지 / 팀 전체)',
|
||||
'납품 후 사용 가이드 및 1:1 교육 포함 여부 확인',
|
||||
];
|
||||
|
||||
// ─── 프리미엄 상품 ───
|
||||
const premiumProducts = [
|
||||
{
|
||||
id: 'image_gen',
|
||||
badge: 'IMAGE GENERATION',
|
||||
badgeColor: '#e879f9',
|
||||
bgFrom: '#1a0533',
|
||||
bgTo: '#2d1054',
|
||||
accentColor: '#d946ef',
|
||||
accentBg: 'rgba(217,70,239,0.12)',
|
||||
accentBorder: 'rgba(217,70,239,0.3)',
|
||||
title: 'AI 이미지 생성 마스터 프롬프트 패키지',
|
||||
subtitle: 'Midjourney · DALL-E 3 · Stable Diffusion 전용',
|
||||
price: '45,000원',
|
||||
salePrice: '12,900원',
|
||||
discountRate: '72% OFF',
|
||||
saleLabel: '런칭 기념 특가',
|
||||
priceNote: '/ 패키지 (즉시 다운로드)',
|
||||
desc: '수천 장의 이미지 생성 실험을 통해 검증된, 업종별·스타일별 고품질 프롬프트 50종 세트. 단순 키워드 나열이 아닌 구도·조명·분위기·카메라·후처리까지 세밀하게 설계된 전문가급 프롬프트입니다.',
|
||||
features: [
|
||||
{ label: '50종 프롬프트 라이브러리', desc: '상업용 · 마케팅 · SNS 콘텐츠 · 제품 사진 · 인물 포트레이트 · 배경 · 로고 컨셉 · 인테리어 등 카테고리별 구성' },
|
||||
{ label: '구도·조명·후처리 공식', desc: '주제(Subject) → 환경(Environment) → 스타일(Style) → 조명(Lighting) → 카메라(Camera) → 후처리(Post) 6단계 구조 적용' },
|
||||
{ label: '네거티브 프롬프트 포함', desc: '흐림·왜곡·불필요한 텍스트·비현실적 요소를 제거하는 부정 키워드까지 최적화하여 실패 확률 최소화' },
|
||||
{ label: 'Midjourney 파라미터 완전 가이드', desc: '--ar / --v / --style / --chaos / --no 등 핵심 파라미터 활용법과 상황별 추천값 포함' },
|
||||
{ label: '한국어 → 영어 변환 치트시트', desc: '자주 쓰는 한국어 표현을 AI가 잘 이해하는 영어 표현으로 매핑한 빠른 참고 시트' },
|
||||
{ label: '업종별 특화 세트 5개', desc: '카페/음식 · 패션 · 부동산 인테리어 · 교육/강의 · 뷰티/헬스 분야 특화 프롬프트 세트별 제공' },
|
||||
{ label: '활용 예시 이미지 50장', desc: '각 프롬프트로 실제 생성한 결과물 예시 이미지 포함 (PDF 가이드북 형태 제공)' },
|
||||
{ label: '무제한 재사용 가능', desc: '구매 후 본인 사업·작업에 무제한 활용 가능. 상업적 이용 허용' },
|
||||
],
|
||||
promptPreview: {
|
||||
title: '프롬프트 예시 — 프리미엄 카페 음료 사진',
|
||||
content: `A professional product photograph of a single iced caramel latte in a tall clear glass,
|
||||
placed on a rustic wooden cafe table. The drink features layers of espresso, milk,
|
||||
and golden caramel syrup with ice cubes.
|
||||
|
||||
Lighting: soft natural window light from the left, warm golden hour tone,
|
||||
subtle rim lighting highlighting condensation droplets on the glass.
|
||||
|
||||
Camera: Canon EOS R5, 85mm f/1.8 lens, shallow depth of field,
|
||||
foreground blur with coffee beans and a sprig of dried lavender.
|
||||
|
||||
Style: editorial food photography, Pinterest aesthetic, warm muted tones,
|
||||
slightly desaturated with lifted shadows.
|
||||
|
||||
Post-processing: film grain texture, subtle vignette,
|
||||
color grade with warm highlights and cool shadows.
|
||||
|
||||
--ar 4:5 --v 6.1 --style raw --q 2
|
||||
|
||||
Negative: text, watermark, multiple cups, cartoon, illustration,
|
||||
overexposed, blurry, plastic look, artificial lighting`,
|
||||
},
|
||||
cta: '패키지 구매 문의 →',
|
||||
productId: 'prompt_image_gen',
|
||||
},
|
||||
{
|
||||
id: 'resume',
|
||||
badge: 'CAREER COACHING',
|
||||
badgeColor: '#34d399',
|
||||
bgFrom: '#052e16',
|
||||
bgTo: '#064e3b',
|
||||
accentColor: '#10b981',
|
||||
accentBg: 'rgba(16,185,129,0.12)',
|
||||
accentBorder: 'rgba(16,185,129,0.3)',
|
||||
title: 'AI 자소서·이력서 첨삭 마스터 프롬프트',
|
||||
subtitle: 'ChatGPT · Claude 전용 · 대기업 HR 기준 적용',
|
||||
price: '35,000원',
|
||||
salePrice: '9,900원',
|
||||
discountRate: '72% OFF',
|
||||
saleLabel: '런칭 기념 특가',
|
||||
priceNote: '/ 패키지 (즉시 다운로드)',
|
||||
desc: '대기업 현직 개발자의 실전 경험과 수십 명의 신입/경력 지원자 첨삭 경험을 바탕으로 설계한 자소서·이력서 최적화 프롬프트 세트. 합격률을 높이는 구체적인 표현과 구조로 AI가 전문 컨설턴트처럼 첨삭하도록 만들어드립니다.',
|
||||
features: [
|
||||
{ label: '자기소개서 7가지 유형별 프롬프트', desc: '지원동기 · 성장과정 · 강점/약점 · 직무역량 · 팀워크 경험 · 위기극복 · 입사 후 포부 — 각 항목에 최적화된 별도 프롬프트 제공' },
|
||||
{ label: 'STAR 기법 자동 적용', desc: '상황(Situation) → 과제(Task) → 행동(Action) → 결과(Result) 구조로 경험을 임팩트 있게 재구성하는 프롬프트 포함' },
|
||||
{ label: '이력서 불릿포인트 최적화', desc: '단순 업무 나열이 아닌 "무엇을 했고, 어떤 방법으로, 어떤 결과를 냈는지" 3단 구조 + 수치화로 강점을 극대화하는 첨삭 프롬프트' },
|
||||
{ label: 'ATS 키워드 최적화', desc: '채용 공고의 키워드를 분석하여 자소서에 자연스럽게 녹여내는 ATS(지원자 추적 시스템) 통과 최적화 프롬프트' },
|
||||
{ label: '업종/직무별 맞춤 톤 설정', desc: 'IT · 금융 · 제조 · 마케팅 · 공공기관 등 업종별, 신입/경력별 적합한 문체와 표현 스타일로 자동 조정' },
|
||||
{ label: '약점을 강점으로 전환하는 프롬프트', desc: '공백기 · 전공 불일치 · 낮은 학점 · 짧은 재직기간 등 불리한 스펙을 긍정적으로 표현하는 전략적 첨삭 프롬프트' },
|
||||
{ label: '면접 질문 예측 & 답변 준비', desc: '작성된 자소서를 기반으로 예상 면접 질문을 생성하고, 모범 답변 구조를 잡아주는 면접 대비 프롬프트 포함' },
|
||||
{ label: '실제 첨삭 Before/After 5사례', desc: '실제로 사용하여 개선된 자소서 전후 비교 예시 5가지를 PDF로 제공 (직무별 다양한 케이스)' },
|
||||
],
|
||||
promptPreview: {
|
||||
title: '프롬프트 예시 — 지원동기 항목 첨삭',
|
||||
content: `당신은 15년 경력의 대기업 HR 수석 컨설턴트입니다.
|
||||
수백 명의 합격 자소서를 분석한 전문가 관점에서 다음 자소서를 첨삭해주세요.
|
||||
|
||||
[첨삭 기준]
|
||||
1. STAR 기법(상황-과제-행동-결과) 구조가 명확한가?
|
||||
2. 지원 동기가 회사의 사업 방향/가치와 구체적으로 연결되는가?
|
||||
3. "열정", "성장" 등 추상적 단어 대신 구체적 경험과 수치가 있는가?
|
||||
4. 첫 문장이 면접관의 시선을 끄는 Hook으로 시작하는가?
|
||||
5. 지원 직무에서 필요한 역량이 자연스럽게 드러나는가?
|
||||
|
||||
[첨삭 방식]
|
||||
① 현재 자소서의 강점 2가지 (구체적 근거 포함)
|
||||
② 치명적 약점 3가지와 개선 방향
|
||||
③ 표현이 약한 문장 3개를 지목하여 강화 버전으로 재작성
|
||||
④ 전체 구조 리뉴얼 버전 (300자 이내로 압축한 임팩트 버전)
|
||||
⑤ 이 자소서로 예상되는 면접 질문 2가지
|
||||
|
||||
[지원 정보]
|
||||
- 회사/직무: [입력]
|
||||
- 채용 공고 키워드: [입력]
|
||||
|
||||
[첨삭할 자소서]
|
||||
[여기에 자소서 붙여넣기]`,
|
||||
},
|
||||
cta: '패키지 구매 문의 →',
|
||||
productId: 'prompt_resume',
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
badge: 'BUSINESS EMAIL',
|
||||
badgeColor: '#60a5fa',
|
||||
bgFrom: '#0c1a3a',
|
||||
bgTo: '#1e3a6e',
|
||||
accentColor: '#3b82f6',
|
||||
accentBg: 'rgba(59,130,246,0.12)',
|
||||
accentBorder: 'rgba(59,130,246,0.3)',
|
||||
title: '비즈니스 이메일 마스터 프롬프트 패키지',
|
||||
subtitle: 'ChatGPT · Claude 전용 · 상황별 40종 템플릿',
|
||||
price: '32,000원',
|
||||
salePrice: '10,900원',
|
||||
discountRate: '66% OFF',
|
||||
saleLabel: '런칭 기념 특가',
|
||||
priceNote: '/ 패키지 (즉시 다운로드)',
|
||||
desc: '신규 파트너 제안부터 거절 메일, 클레임 응대, 계약 협상까지 — 실무에서 반복 사용하는 비즈니스 이메일 상황 40가지를 AI가 전문 비서처럼 작성하도록 설계한 완성형 프롬프트 패키지입니다.',
|
||||
features: [
|
||||
{ label: '40가지 상황별 이메일 프롬프트', desc: '제안·문의·거절·사과·팔로업·계약·협상·내부 보고 등 비즈니스 전 상황 커버' },
|
||||
{ label: '상대방 직급별 톤 자동 조정', desc: '대표·임원·실무자·외부 파트너·고객별로 적합한 경어와 표현 강도를 자동으로 조정하는 프롬프트' },
|
||||
{ label: '영어 이메일 동시 출력', desc: '한국어 초안 작성 후 비즈니스 영어 버전으로 즉시 변환하는 이중 출력 프롬프트 포함' },
|
||||
{ label: '클레임·컴플레인 전문 대응 세트', desc: '감정적 고객·거래처 이메일을 분석하고 상황별 최적 응대 메일을 생성하는 CS 전문 프롬프트 7종' },
|
||||
{ label: '이메일 후속 조치 자동화', desc: '1차 연락 후 답장 없을 때의 팔로업, 회신 독촉, 미팅 확정 등 타임라인별 후속 이메일 시리즈 프롬프트' },
|
||||
{ label: '제목 라인 A/B 테스트 생성', desc: '오픈율을 높이는 이메일 제목 5가지 변형을 자동 생성하는 헤드라인 최적화 프롬프트' },
|
||||
{ label: '실전 예시 40쌍 (Before/After)', desc: '평범한 이메일 → 임팩트 있는 전문 이메일로 변환된 실전 예시 40쌍 PDF 가이드북 포함' },
|
||||
],
|
||||
promptPreview: {
|
||||
title: '프롬프트 예시 — 신규 파트너십 제안 이메일',
|
||||
content: `당신은 10년 경력의 B2B 영업 전문가이자 비즈니스 라이터입니다.
|
||||
아래 정보를 바탕으로 상대방이 반드시 읽고 싶어지는 파트너십 제안 이메일을 작성해주세요.
|
||||
|
||||
[작성 원칙]
|
||||
1. 첫 문장에서 상대방의 이익/관심사를 직접 언급 (내 소개 X)
|
||||
2. 제안 핵심을 3줄 이내로 압축 (바쁜 담당자가 5초 내 파악)
|
||||
3. 구체적 수치나 레퍼런스로 신뢰도 구축
|
||||
4. 명확한 CTA 1개만 포함 (회의 일정 링크 OR 짧은 통화 요청)
|
||||
5. 전체 300자 이내, 첨부 자료는 1개 이하
|
||||
|
||||
[제안 정보]
|
||||
- 우리 회사/서비스: [입력]
|
||||
- 제안 대상 회사: [입력]
|
||||
- 협업으로 상대방이 얻는 이익: [입력]
|
||||
- 기존 레퍼런스/실적: [입력]
|
||||
- 원하는 다음 액션: [입력]
|
||||
|
||||
[출력 형식]
|
||||
① 이메일 제목 3가지 옵션 (각각 다른 각도)
|
||||
② 본문 이메일 (원칙 준수)
|
||||
③ 수신자가 거절할 가능성이 높은 이유와 예방 팁`,
|
||||
},
|
||||
cta: '패키지 구매 문의 →',
|
||||
productId: 'prompt_email',
|
||||
},
|
||||
{
|
||||
id: 'marketing',
|
||||
badge: 'MARKETING COPY',
|
||||
badgeColor: '#fb923c',
|
||||
bgFrom: '#1c0a00',
|
||||
bgTo: '#431407',
|
||||
accentColor: '#f97316',
|
||||
accentBg: 'rgba(249,115,22,0.12)',
|
||||
accentBorder: 'rgba(249,115,22,0.3)',
|
||||
title: '마케팅 카피라이팅 마스터 프롬프트',
|
||||
subtitle: 'SNS · 광고 · 상세페이지 · 유튜브 전용 35종',
|
||||
price: '38,000원',
|
||||
salePrice: '12,900원',
|
||||
discountRate: '66% OFF',
|
||||
saleLabel: '런칭 기념 특가',
|
||||
priceNote: '/ 패키지 (즉시 다운로드)',
|
||||
desc: '인스타그램·유튜브·쿠팡·스마트스토어·카카오 채널까지 — 각 플랫폼의 알고리즘과 소비자 심리를 반영하여 클릭률·전환율을 극대화하는 마케팅 카피 전문 프롬프트 35종 세트입니다.',
|
||||
features: [
|
||||
{ label: 'SNS 플랫폼별 최적화 카피 세트', desc: '인스타그램 피드/릴스·유튜브 제목·스레드·카카오 비즈메시지 각 채널 알고리즘 특성을 반영한 별도 프롬프트' },
|
||||
{ label: '감성 카피 ↔ 이성 카피 전환', desc: '같은 상품을 감성 스토리텔링 방식과 스펙·기능 중심 이성 방식으로 각각 작성하여 A/B 테스트용 카피 쌍 생성' },
|
||||
{ label: '상세페이지 전환율 최적화 구조', desc: '훅(Hook) → 문제 공감 → 해결책 제시 → 사회적 증거 → CTA의 5단계 전환 공식을 자동 적용하는 상세페이지 카피 프롬프트' },
|
||||
{ label: '제품 소개글 10초 요약 공식', desc: '핵심 USP(독보적 강점)를 10초 안에 전달하는 엘리베이터 피치형 짧은 소개글 자동 생성 프롬프트' },
|
||||
{ label: '유튜브/숏폼 썸네일 제목 생성기', desc: '조회수를 높이는 클릭베이트형 제목과 커뮤니티 공감형 제목을 구분하여 생성하는 유튜브 최적화 프롬프트' },
|
||||
{ label: '리뷰·후기 마케팅 카피 변환', desc: '고객 후기·댓글을 분석하여 그 안의 핵심 감동 포인트를 광고 카피로 변환하는 소셜 프루프 카피 프롬프트' },
|
||||
{ label: '업종별 금지 표현 자동 필터', desc: '식품·의료·금융·부동산 등 규제 업종의 법적 주의 표현을 사전에 체크하고 대안 표현을 제시하는 컴플라이언스 프롬프트' },
|
||||
],
|
||||
promptPreview: {
|
||||
title: '프롬프트 예시 — 인스타그램 상품 소개 릴스 스크립트',
|
||||
content: `당신은 팔로워 50만 명의 인스타그램 쇼핑 인플루언서이자
|
||||
마케팅 전환율 전문가입니다.
|
||||
다음 상품 정보를 바탕으로 구매 욕구를 자극하는 릴스 스크립트를 작성해주세요.
|
||||
|
||||
[스크립트 구조 — 60초 이내]
|
||||
00~03초: 즉각 멈추게 하는 훅 문장 (의문형 OR 공감형 OR 충격 통계)
|
||||
04~10초: "이게 뭔데?" 궁금증 유발 — 상품 첫 노출
|
||||
11~30초: 핵심 기능 3가지를 BEFORE/AFTER 방식으로 시연
|
||||
31~45초: 소셜 프루프 (실제 고객 반응 또는 수치)
|
||||
46~55초: 한정 혜택/가격 공개
|
||||
56~60초: 명확한 CTA (링크 인 바이오, DM, 댓글 단어)
|
||||
|
||||
[상품 정보]
|
||||
- 상품명/카테고리: [입력]
|
||||
- 핵심 기능/차별점: [입력]
|
||||
- 타겟 고객: [입력]
|
||||
- 가격/혜택: [입력]
|
||||
|
||||
[출력]
|
||||
① 릴스 스크립트 본문
|
||||
② 자막용 텍스트 (10자 이내 임팩트 문구 5개)
|
||||
③ 해시태그 20개 (도달 최적화)`,
|
||||
},
|
||||
cta: '패키지 구매 문의 →',
|
||||
productId: 'prompt_marketing',
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
badge: 'BUSINESS REPORT',
|
||||
badgeColor: '#a78bfa',
|
||||
bgFrom: '#13082b',
|
||||
bgTo: '#1e1148',
|
||||
accentColor: '#8b5cf6',
|
||||
accentBg: 'rgba(139,92,246,0.12)',
|
||||
accentBorder: 'rgba(139,92,246,0.3)',
|
||||
title: '업무 보고서·기획서 자동화 프롬프트 패키지',
|
||||
subtitle: 'ChatGPT · Claude 전용 · 직장인 필수 30종',
|
||||
price: '30,000원',
|
||||
salePrice: '10,900원',
|
||||
discountRate: '64% OFF',
|
||||
saleLabel: '런칭 기념 특가',
|
||||
priceNote: '/ 패키지 (즉시 다운로드)',
|
||||
desc: '주간 업무 보고부터 임원 발표용 기획서, 투자 제안서, 회의록 요약까지 — 직장인이 매주 반복 작성하는 문서를 AI가 체계적으로 작성하도록 설계한 업무 자동화 프롬프트 30종 세트입니다.',
|
||||
features: [
|
||||
{ label: '주간·월간 업무 보고서 자동화', desc: '팀원별 진행 현황, 완료 사항, 이슈, 다음 주 계획을 표 형식으로 정리하는 구조화된 보고서 생성 프롬프트' },
|
||||
{ label: '임원 보고용 1페이지 요약 공식', desc: '"Bottom Line Up Front" 원칙으로 핵심 결론 → 근거 → 요청사항 순서의 임원 친화적 요약 프롬프트' },
|
||||
{ label: '신사업 기획서 뼈대 자동 생성', desc: '시장 분석 → 목표 설정 → 실행 방안 → 예산 계획 → KPI 설정의 5단계 기획서 프레임워크 자동 구성 프롬프트' },
|
||||
{ label: '회의록 → 액션 아이템 변환', desc: '날것의 회의 내용을 입력하면 결정사항·담당자·기한·후속 과제로 즉시 분류하는 회의록 구조화 프롬프트' },
|
||||
{ label: '데이터 분석 결과 스토리텔링', desc: '숫자·표·그래프 데이터를 경영진이 이해하기 쉬운 인사이트 내러티브로 변환하는 데이터 보고 프롬프트' },
|
||||
{ label: 'RFP·제안서 경쟁력 강화', desc: '발주사의 선정 기준에 맞춰 제안서의 차별점·강점을 부각시키고 약점을 보완하는 제안서 최적화 프롬프트' },
|
||||
{ label: '프레젠테이션 슬라이드 목차 자동 설계', desc: '보고 목적과 청중에 맞는 슬라이드 구성 순서, 각 슬라이드 핵심 메시지를 자동으로 설계해주는 프롬프트' },
|
||||
],
|
||||
promptPreview: {
|
||||
title: '프롬프트 예시 — 주간 업무 보고서 자동 작성',
|
||||
content: `당신은 5년 경력의 기업 커뮤니케이션 전문가이자
|
||||
비즈니스 라이터입니다.
|
||||
아래 업무 내용을 바탕으로 팀장에게 보고할 주간 업무 보고서를 작성해주세요.
|
||||
|
||||
[보고서 작성 원칙]
|
||||
1. 첫 줄에 이번 주 가장 중요한 성과 1가지를 먼저 명시
|
||||
2. 수치로 표현 가능한 모든 항목은 반드시 수치화
|
||||
3. 이슈는 "문제 상황 → 조치 내용 → 현재 상태" 3단 구조로 기술
|
||||
4. 다음 주 계획은 담당자·기한과 함께 표 형식으로 정리
|
||||
5. 전체 A4 1장 이내, 핵심 위주
|
||||
|
||||
[업무 내용 입력]
|
||||
- 이번 주 완료 업무: [입력]
|
||||
- 진행 중인 업무: [입력]
|
||||
- 발생한 이슈/리스크: [입력]
|
||||
- 다음 주 계획: [입력]
|
||||
- 요청 사항 (상급자에게): [입력]
|
||||
|
||||
[출력 형식]
|
||||
① 이번 주 핵심 성과 (1~2줄 요약)
|
||||
② 완료 업무 목록 (수치 포함)
|
||||
③ 진행 중 업무 + 달성률
|
||||
④ 이슈 및 대응 현황
|
||||
⑤ 다음 주 계획 (담당·기한 포함 표)
|
||||
⑥ 협조 요청 사항`,
|
||||
},
|
||||
cta: '패키지 구매 문의 →',
|
||||
productId: 'prompt_report',
|
||||
},
|
||||
];
|
||||
|
||||
const useCases = [
|
||||
{ label: '이메일 작성', desc: '고객사별, 상황별 최적화된 비즈니스 이메일 프롬프트' },
|
||||
{ label: '보고서·기획서', desc: '회사 내부 보고서, 제안서, 기획서 자동 작성용 프롬프트' },
|
||||
{ label: '고객 응대', desc: 'CS 상담, FAQ 응답, 컴플레인 처리를 위한 프롬프트' },
|
||||
{ label: '마케팅 카피', desc: '제품 소개글, 광고 카피, SNS 콘텐츠 생성 프롬프트' },
|
||||
{ label: '개발 보조', desc: '코드 리뷰, 버그 설명, 문서화를 위한 개발자 전용 프롬프트' },
|
||||
{ label: '학습·요약', desc: '문서 요약, 핵심 추출, 번역 최적화 프롬프트' },
|
||||
];
|
||||
|
||||
const plans = [
|
||||
{
|
||||
name: '단건 설계',
|
||||
price: '30,000원',
|
||||
period: '/ 건',
|
||||
desc: '특정 업무 1건 프롬프트 설계',
|
||||
features: ['요구사항 분석 및 인터뷰', '목적별 프롬프트 1개 설계', 'ChatGPT / Claude 최적화', '수정 1회 포함', '사용 가이드 문서 제공'],
|
||||
highlight: false,
|
||||
productId: 'prompt_single',
|
||||
},
|
||||
{
|
||||
name: '비즈니스 패키지',
|
||||
price: '99,000원',
|
||||
period: '/ 패키지',
|
||||
desc: '업무 유형별 5개 프롬프트 세트',
|
||||
features: ['업무 분석 심층 인터뷰', '5개 프롬프트 맞춤 설계', '용도별 프롬프트 라이브러리', '수정 3회 포함', '활용 방법 1:1 교육 (30분)', '1개월 내 추가 조정 가능'],
|
||||
highlight: true,
|
||||
productId: 'prompt_business',
|
||||
},
|
||||
{
|
||||
name: '팀/기업 패키지',
|
||||
price: '249,000원~',
|
||||
period: '/ 세트',
|
||||
desc: '부서·팀 전체 프롬프트 시스템 구축',
|
||||
features: ['팀 업무 프로세스 전체 분석', '10개 이상 프롬프트 설계', '팀 공유 프롬프트 라이브러리', '사내 가이드 문서 작성', '전 직원 교육 자료 제공', '3개월 내 업데이트 지원'],
|
||||
highlight: false,
|
||||
productId: 'prompt_team',
|
||||
},
|
||||
];
|
||||
|
||||
const examples = [
|
||||
{
|
||||
type: '회의록 요약',
|
||||
before: '회의 내용을 요약해줘',
|
||||
after: '다음 회의록을 분석하여: 1) 핵심 결정사항 3가지, 2) 담당자별 Action Item, 3) 다음 회의 전 완료해야 할 사항을 불릿 형식으로 정리해줘. 회의록: [내용]',
|
||||
improvement: '구조화된 출력 · 역할 분리 · 명확한 포맷',
|
||||
},
|
||||
{
|
||||
type: '코드 리뷰',
|
||||
before: '이 코드 리뷰해줘',
|
||||
after: '시니어 백엔드 개발자 관점에서 다음 코드를 리뷰해줘: 1) 버그 및 잠재적 오류, 2) 성능 개선 포인트, 3) 클린코드 관점에서의 개선사항을 각각 심각도(High/Medium/Low)와 함께 알려줘. 코드: [코드]',
|
||||
improvement: '페르소나 설정 · 심각도 기준 · 다각도 분석',
|
||||
},
|
||||
];
|
||||
|
||||
export default function PromptPage() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalService, setModalService] = useState('프롬프트 엔지니어링');
|
||||
const containerRef = useScrollReveal();
|
||||
|
||||
const openModal = (service: string) => {
|
||||
trackCTAClick(service, '/services/prompt');
|
||||
setModalService(service);
|
||||
setModalOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="min-h-full bg-[#f0f5ff]">
|
||||
<style>{`
|
||||
.prompt-card {
|
||||
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.prompt-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 20px 40px -12px rgba(0,0,0,0.15);
|
||||
}
|
||||
`}</style>
|
||||
<ContactModal
|
||||
isOpen={modalOpen}
|
||||
onClose={() => setModalOpen(false)}
|
||||
service={modalService}
|
||||
checklist={CHECKLIST}
|
||||
accentColor="text-violet-400"
|
||||
headerFrom="#0d0a2e"
|
||||
headerTo="#1a0f5c"
|
||||
/>
|
||||
|
||||
{/* ─── Hero ─── */}
|
||||
<div className="relative overflow-hidden bg-[#0d0a2e] px-6 py-14 lg:px-12" style={{ backgroundImage: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 40px)' }}>
|
||||
|
||||
<div className="relative max-w-3xl mx-auto">
|
||||
<Link href="/" className="inline-flex items-center gap-1.5 text-violet-300/60 hover:text-violet-300 text-sm mb-8 transition">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /></svg>
|
||||
홈으로
|
||||
</Link>
|
||||
<p className="text-violet-400 text-xs font-bold uppercase tracking-widest mb-4 font-mono">프롬프트 엔지니어링 · AI 활용 극대화</p>
|
||||
<h1 className="text-4xl md:text-5xl font-extrabold text-white mb-4 tracking-tight leading-tight">
|
||||
AI를 제대로<br />
|
||||
100% 활용하기
|
||||
</h1>
|
||||
<p className="text-violet-100/50 text-base md:text-lg leading-relaxed max-w-xl mx-auto mb-6">
|
||||
ChatGPT·Claude를 쓰는데 결과가 항상 애매하신가요?<br />
|
||||
업무에 딱 맞는 프롬프트를 전문 설계하여 AI를 제대로 활용하도록 도와드립니다.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2 justify-center">
|
||||
<div className="inline-flex items-center gap-2 bg-violet-400/10 border border-violet-400/20 text-violet-300 text-xs font-medium px-4 py-2 rounded-full">
|
||||
<span className="text-green-400">↑</span> 업무 효율 평균 3~5배 향상
|
||||
</div>
|
||||
<div className="inline-flex items-center gap-2 bg-white/5 border border-white/10 text-white/50 text-xs font-medium px-4 py-2 rounded-full">
|
||||
ChatGPT · Claude · Gemini 전용 최적화
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 프리미엄 상품 ─── */}
|
||||
<div className="px-6 py-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<div className="inline-flex items-center gap-2 bg-fuchsia-500/10 border border-fuchsia-500/30 text-fuchsia-400 text-xs font-extrabold px-4 py-1.5 rounded-full uppercase tracking-widest mb-4">
|
||||
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="currentColor"><path d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" /></svg>
|
||||
PREMIUM PRODUCTS
|
||||
</div>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">바로 쓸 수 있는 프리미엄 프롬프트</h2>
|
||||
<p className="text-slate-500 text-sm mt-2">전문가가 직접 설계하고 검증한 완성형 프롬프트 패키지 — 구매 즉시 사용 가능</p>
|
||||
</div>
|
||||
<div className="grid lg:grid-cols-2 gap-6">
|
||||
{premiumProducts.map((product, idx) => (
|
||||
<div
|
||||
key={product.id}
|
||||
className={`rounded-2xl overflow-hidden border prompt-card reveal reveal-d${(idx % 4) + 1}`}
|
||||
style={{ borderColor: product.accentBorder, background: `linear-gradient(135deg, ${product.bgFrom}, ${product.bgTo})` }}
|
||||
>
|
||||
{/* 헤더 */}
|
||||
<div className="p-6 border-b" style={{ borderColor: product.accentBorder }}>
|
||||
<div className="flex items-start justify-between gap-3 mb-3">
|
||||
<span
|
||||
className="text-xs font-extrabold px-3 py-1 rounded-full uppercase tracking-widest"
|
||||
style={{ color: product.accentColor, background: product.accentBg, border: `1px solid ${product.accentBorder}` }}
|
||||
>
|
||||
{product.badge}
|
||||
</span>
|
||||
<div className="text-right">
|
||||
{/* 할인 배지 */}
|
||||
<div className="flex items-center justify-end gap-2 mb-1">
|
||||
<span className="text-xs font-extrabold bg-red-500 text-white px-2 py-0.5 rounded-md animate-pulse">
|
||||
{product.discountRate}
|
||||
</span>
|
||||
<span className="text-xs font-bold" style={{ color: product.accentColor }}>
|
||||
{product.saleLabel}
|
||||
</span>
|
||||
</div>
|
||||
{/* 원가 취소선 */}
|
||||
<div className="text-sm line-through opacity-40 text-right text-white mb-0.5">{product.price}</div>
|
||||
{/* 세일가 */}
|
||||
<div className="text-2xl font-extrabold text-white">{product.salePrice}</div>
|
||||
<div className="text-xs" style={{ color: product.accentColor + '99' }}>{product.priceNote}</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-lg font-extrabold text-white mb-1 leading-snug">{product.title}</h3>
|
||||
<p className="text-xs mb-3" style={{ color: product.accentColor + 'aa' }}>{product.subtitle}</p>
|
||||
<p className="text-sm leading-relaxed" style={{ color: 'rgba(255,255,255,0.55)' }}>{product.desc}</p>
|
||||
</div>
|
||||
{/* 기능 목록 */}
|
||||
<div className="p-6 border-b" style={{ borderColor: product.accentBorder }}>
|
||||
<p className="text-xs font-bold uppercase tracking-widest mb-3" style={{ color: product.accentColor + 'cc' }}>포함 내용</p>
|
||||
<ul className="space-y-2.5">
|
||||
{product.features.map((f, i) => (
|
||||
<li key={i} className="flex items-start gap-2.5">
|
||||
<div className="w-4 h-4 rounded-full flex items-center justify-center flex-shrink-0 mt-0.5" style={{ background: product.accentBg, border: `1px solid ${product.accentBorder}` }}>
|
||||
<div className="w-1.5 h-1.5 rounded-full" style={{ background: product.accentColor }} />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs font-bold text-white">{f.label}</span>
|
||||
<span className="text-xs ml-1.5" style={{ color: 'rgba(255,255,255,0.45)' }}>— {f.desc}</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{/* 프롬프트 미리보기 */}
|
||||
<div className="p-6 border-b" style={{ borderColor: product.accentBorder }}>
|
||||
<p className="text-xs font-bold uppercase tracking-widest mb-3" style={{ color: product.accentColor + 'cc' }}>
|
||||
{product.promptPreview.title}
|
||||
</p>
|
||||
<div
|
||||
className="rounded-xl p-4 font-mono text-xs leading-relaxed overflow-auto"
|
||||
style={{ background: 'rgba(0,0,0,0.35)', color: 'rgba(255,255,255,0.6)', border: `1px solid ${product.accentBorder}`, whiteSpace: 'pre-line', maxHeight: '180px' }}
|
||||
>
|
||||
{product.promptPreview.content}
|
||||
</div>
|
||||
</div>
|
||||
{/* CTA */}
|
||||
<div className="p-6">
|
||||
<PaymentButton
|
||||
productId={product.productId}
|
||||
className="flex items-center justify-center gap-2 w-full py-3.5 rounded-xl text-sm font-extrabold transition-all hover:opacity-90"
|
||||
style={{ background: product.accentColor, color: product.bgFrom }}
|
||||
>
|
||||
지금 구매하기 →
|
||||
</PaymentButton>
|
||||
<p className="text-center text-xs mt-2" style={{ color: 'rgba(255,255,255,0.3)' }}>
|
||||
결제 즉시 다운로드 · 로그인 필요
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── Before/After ─── */}
|
||||
<div className="px-6 py-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-violet-600 text-xs font-bold uppercase tracking-widest mb-2">BEFORE vs AFTER</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">이런 차이가 납니다</h2>
|
||||
</div>
|
||||
<div className="space-y-5">
|
||||
{examples.map((ex, idx) => (
|
||||
<div key={ex.type} className={`bg-white rounded-2xl border border-[#dbe8ff] overflow-hidden reveal reveal-d${idx + 1}`}>
|
||||
<div className="bg-[#04102b] px-5 py-3 flex items-center justify-between">
|
||||
<span className="text-white/60 text-xs font-semibold font-mono">{ex.type} 예시</span>
|
||||
<span className="bg-violet-400/20 border border-violet-400/30 text-violet-300 text-xs px-3 py-1 rounded-full">{ex.improvement}</span>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-2 divide-y md:divide-y-0 md:divide-x divide-[#dbe8ff]">
|
||||
<div className="p-5">
|
||||
<div className="inline-block bg-red-50 border border-red-200 text-red-600 text-xs font-bold px-2 py-0.5 rounded-md mb-3">일반 프롬프트</div>
|
||||
<div className="bg-slate-50 rounded-xl px-4 py-3 font-mono text-sm text-slate-600 border border-slate-200">“{ex.before}”</div>
|
||||
<div className="mt-3 text-xs text-red-500 flex items-center gap-1.5">
|
||||
<svg className="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" /></svg>
|
||||
모호한 지시 → 불완전한 결과
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<div className="inline-block bg-violet-50 border border-violet-200 text-violet-700 text-xs font-bold px-2 py-0.5 rounded-md mb-3">최적화 프롬프트</div>
|
||||
<div className="bg-violet-50 rounded-xl px-4 py-3 font-mono text-sm text-slate-700 border border-violet-100 leading-relaxed">“{ex.after}”</div>
|
||||
<div className="mt-3 text-xs text-violet-600 flex items-center gap-1.5">
|
||||
<svg className="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" /></svg>
|
||||
명확한 구조 → 바로 쓸 수 있는 결과
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 활용 분야 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<p className="text-violet-600 text-xs font-bold uppercase tracking-widest mb-2">USE CASES</p>
|
||||
<h2 className="text-2xl md:text-3xl font-extrabold text-[#04102b]">활용 분야</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{useCases.map((uc, i) => (
|
||||
<div key={uc.label} className={`bg-white rounded-2xl border border-[#dbe8ff] p-5 hover:border-violet-200 transition-all duration-300 reveal reveal-d${(i % 3) + 1}`}>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-violet-50 border border-violet-200 flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-violet-600 font-extrabold text-xs">{String(i + 1).padStart(2, '0')}</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-[#04102b] text-sm mb-1">{uc.label}</h3>
|
||||
<p className="text-slate-500 text-xs leading-relaxed">{uc.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 요금제 ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="text-center mb-8 reveal">
|
||||
<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]">요금제</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-3 gap-5">
|
||||
{plans.map((plan, idx) => (
|
||||
<div key={plan.name} className={`rounded-2xl border p-6 relative flex flex-col reveal reveal-d${idx + 1} ${
|
||||
plan.highlight
|
||||
? 'bg-gradient-to-br from-[#0d0a2e] to-[#1a0f5c] border-violet-400/30 shadow-2xl shadow-violet-900/20 scale-105'
|
||||
: 'bg-white border-[#dbe8ff]'
|
||||
}`}>
|
||||
{plan.highlight && (
|
||||
<div className="absolute -top-3.5 left-1/2 -translate-x-1/2 bg-violet-400 text-[#0d0a2e] text-xs font-extrabold px-4 py-1 rounded-full tracking-wide">추천</div>
|
||||
)}
|
||||
<div className={`text-xs font-bold mb-2 tracking-wide ${plan.highlight ? 'text-violet-400' : 'text-slate-400'}`}>{plan.name.toUpperCase()}</div>
|
||||
<div className="flex items-baseline gap-1 mb-1">
|
||||
<span className={`text-3xl font-extrabold ${plan.highlight ? 'text-white' : 'text-[#04102b]'}`}>{plan.price}</span>
|
||||
<span className={`text-sm ${plan.highlight ? 'text-violet-300/50' : 'text-slate-400'}`}>{plan.period}</span>
|
||||
</div>
|
||||
<p className={`text-xs mb-5 ${plan.highlight ? 'text-violet-300/50' : 'text-slate-400'}`}>{plan.desc}</p>
|
||||
<ul className="space-y-2 mb-6 flex-1">
|
||||
{plan.features.map((f) => (
|
||||
<li key={f} className={`flex items-start gap-2 text-xs ${plan.highlight ? 'text-violet-100/80' : 'text-slate-600'}`}>
|
||||
<div className={`w-4 h-4 rounded-full flex items-center justify-center flex-shrink-0 mt-0.5 ${plan.highlight ? 'bg-violet-400/20 border border-violet-400/40' : 'bg-[#f0f5ff] border border-[#dbe8ff]'}`}>
|
||||
<div className={`w-1.5 h-1.5 rounded-full ${plan.highlight ? 'bg-violet-400' : 'bg-[#1a56db]'}`} />
|
||||
</div>
|
||||
{f}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button
|
||||
onClick={() => openModal(`프롬프트 엔지니어링 - ${plan.name}`)}
|
||||
className={`block w-full text-center py-3 rounded-xl text-sm font-bold transition ${
|
||||
plan.highlight ? 'bg-violet-400 text-[#0d0a2e] hover:bg-violet-300' : 'bg-[#04102b] text-white hover:bg-[#0a1f5c]'
|
||||
}`}
|
||||
>
|
||||
무료 상담 신청
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── CTA ─── */}
|
||||
<div className="px-6 pb-12 lg:px-12">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="bg-gradient-to-r from-[#0d0a2e] to-[#1a0f5c] rounded-2xl border border-violet-400/20 p-8 text-center reveal">
|
||||
<p className="text-violet-400 text-xs font-bold uppercase tracking-widest mb-2">GET STARTED</p>
|
||||
<h3 className="text-white text-2xl font-extrabold mb-2">AI를 제대로 활용하고 싶다면</h3>
|
||||
<p className="text-violet-100/40 text-sm mb-6">업무 분석 인터뷰 → 맞춤 설계 → 가이드 제공</p>
|
||||
<button
|
||||
onClick={() => openModal('프롬프트 엔지니어링')}
|
||||
className="inline-flex items-center gap-2 bg-violet-400 hover:bg-violet-300 text-[#0d0a2e] px-8 py-3 rounded-xl font-extrabold text-sm transition-all shadow-lg shadow-violet-900/30"
|
||||
>
|
||||
무료 상담 신청 →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '주식 자동 매매 프로그램',
|
||||
description:
|
||||
'NAS 서버에서 직접 운영 중인 주식 자동 매매 시스템. RSI·MACD·볼린저밴드 기반 매매 신호를 텔레그램으로 수신하고 자동 매수·매도합니다. 키움·한국투자 연동.',
|
||||
keywords: [
|
||||
'주식 자동 매매',
|
||||
'알고트레이딩',
|
||||
'주식 자동화',
|
||||
'텔레그램 주식 알림',
|
||||
'키움 자동매매',
|
||||
'주식 프로그램',
|
||||
'RSI 매매',
|
||||
],
|
||||
openGraph: {
|
||||
title: '주식 자동 매매 프로그램 | 쟁승메이드',
|
||||
description:
|
||||
'직접 운영 중인 알고트레이딩 시스템. 텔레그램 연동 · 자동 매수매도 · 설치 49,000원~.',
|
||||
url: 'https://jaengseung-made.com/services/stock',
|
||||
},
|
||||
};
|
||||
|
||||
export default function StockLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
// 토스페이먼츠 심사 정책상 판매 불가 상품으로 분류 — 비공개 처리
|
||||
export default function StockPage() {
|
||||
redirect('/');
|
||||
}
|
||||
@@ -23,6 +23,7 @@ export const metadata: Metadata = {
|
||||
'소상공인·스타트업 홈페이지 제작. 템플릿 없이 직접 개발, SEO 포함, 20만원~.',
|
||||
url: 'https://jaengseung-made.com/services/website',
|
||||
},
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
export default function WebsiteLayout({ children }: { children: React.ReactNode }) {
|
||||
|
||||
Reference in New Issue
Block a user