feat: 사주 웹사이트 핵심 기능 구현

- 사주팔자 계산 라이브러리 구현 (천간, 지지, 십성, 십이운성)
- 사주 결과 페이지 구현 (사주팔자 표 및 해석)
- 오늘의 운세 페이지 구현 (일일 운세, 행운의 요소)
- 궁합 보기 기능 구현 (두 사람 입력 폼)
- 궁합 결과 페이지 구현 (종합 점수, 세부 분석)
- 페이지 간 네비게이션 연결

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 23:36:22 +09:00
parent f4dbacccad
commit 08d6f71fd1
8 changed files with 1957 additions and 54 deletions

100
app/compatibility/page.tsx Normal file
View File

@@ -0,0 +1,100 @@
import Link from 'next/link';
import CompatibilityForm from '../components/CompatibilityForm';
export default function CompatibilityPage() {
return (
<div className="min-h-screen bg-gradient-to-br from-pink-50 via-purple-50 to-indigo-50">
{/* Navigation */}
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<Link href="/" className="text-2xl font-bold bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent">
🔮
</Link>
<Link
href="/"
className="text-gray-700 hover:text-pink-600 transition font-medium"
>
</Link>
</div>
</div>
</nav>
{/* Hero Section */}
<section className="pt-20 pb-32 px-4">
<div className="max-w-4xl mx-auto text-center mb-12">
<div className="inline-block mb-6 px-6 py-2 bg-white/50 backdrop-blur-sm rounded-full text-pink-700 font-semibold border border-pink-200">
</div>
<h1 className="text-5xl md:text-7xl font-bold text-gray-900 mb-6 leading-tight">
💕 <span className="bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent"></span>
</h1>
<p className="text-xl text-gray-600 mb-12 max-w-2xl mx-auto">
.
, , .
</p>
</div>
{/* Input Form */}
<div className="max-w-6xl mx-auto">
<CompatibilityForm />
</div>
</section>
{/* 궁합 정보 */}
<section className="py-20 px-4 bg-white">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4">?</h2>
<p className="text-xl text-gray-600"> </p>
</div>
<div className="grid md:grid-cols-3 gap-8">
<div className="text-center p-6 rounded-2xl hover:bg-pink-50 transition">
<div className="text-5xl mb-4">💑</div>
<h3 className="text-xl font-bold text-gray-900 mb-3"> </h3>
<p className="text-gray-600">
.
</p>
</div>
<div className="text-center p-6 rounded-2xl hover:bg-purple-50 transition">
<div className="text-5xl mb-4">💍</div>
<h3 className="text-xl font-bold text-gray-900 mb-3"> </h3>
<p className="text-gray-600">
.
</p>
</div>
<div className="text-center p-6 rounded-2xl hover:bg-indigo-50 transition">
<div className="text-5xl mb-4">🤝</div>
<h3 className="text-xl font-bold text-gray-900 mb-3"> </h3>
<p className="text-gray-600">
.
</p>
</div>
</div>
</div>
</section>
{/* Footer */}
<footer className="bg-gray-900 text-white py-12 px-4">
<div className="max-w-7xl mx-auto text-center">
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-pink-400 to-purple-400 bg-clip-text text-transparent">
🔮
</div>
<p className="text-gray-400 mb-6">
</p>
<div className="text-sm text-gray-500">
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-pink-400"></a></p>
<p className="mt-2">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}

View File

@@ -0,0 +1,395 @@
import Link from 'next/link';
import { calculateSaju, FIVE_ELEMENTS } from '@/lib/saju-calculator';
interface PageProps {
searchParams: Promise<{
year1: string;
month1: string;
day1: string;
hour1?: string;
gender1: 'male' | 'female';
year2: string;
month2: string;
day2: string;
hour2?: string;
gender2: 'male' | 'female';
}>;
}
export default async function CompatibilityResultPage({ searchParams }: PageProps) {
const params = await searchParams;
const { year1, month1, day1, hour1, gender1, year2, month2, day2, hour2, gender2 } = params;
// Person 1 Saju
const saju1 = calculateSaju(
parseInt(year1),
parseInt(month1),
parseInt(day1),
hour1 ? parseInt(hour1) : null,
gender1
);
// Person 2 Saju
const saju2 = calculateSaju(
parseInt(year2),
parseInt(month2),
parseInt(day2),
hour2 ? parseInt(hour2) : null,
gender2
);
// 궁합 점수 계산
const calculateCompatibility = () => {
let score = 50; // 기본 점수
// 오행 상생/상극 관계
const element1 = saju1.day.element;
const element2 = saju2.day.element;
const produceMap: { [key: string]: string } = {
'木': '火', '火': '土', '土': '金', '金': '水', '水': '木'
};
const overcomeMap: { [key: string]: string } = {
'木': '土', '火': '金', '土': '水', '金': '木', '水': '火'
};
// 같은 오행: 보통
if (element1 === element2) {
score += 10;
}
// 상생 관계: 매우 좋음
else if (produceMap[element1] === element2 || produceMap[element2] === element1) {
score += 25;
}
// 상극 관계: 주의 필요
else if (overcomeMap[element1] === element2 || overcomeMap[element2] === element1) {
score -= 10;
}
// 지지 삼합/육합 관계 확인
const branch1 = saju1.day.branch;
const branch2 = saju2.day.branch;
// 육합 (六合) - 특별히 좋은 궁합
const sixHarmony: { [key: string]: string } = {
'子': '丑', '丑': '子',
'寅': '亥', '亥': '寅',
'卯': '戌', '戌': '卯',
'辰': '酉', '酉': '辰',
'巳': '申', '申': '巳',
'午': '未', '未': '午'
};
if (sixHarmony[branch1] === branch2) {
score += 20;
}
// 삼합 (三合) - 좋은 궁합
const threeHarmony = [
['申', '子', '辰'], // 수국
['寅', '午', '戌'], // 화국
['亥', '卯', '未'], // 목국
['巳', '酉', '丑'] // 금국
];
for (const harmony of threeHarmony) {
if (harmony.includes(branch1) && harmony.includes(branch2)) {
score += 15;
break;
}
}
// 충 (沖) - 나쁜 궁합
const conflict: { [key: string]: string } = {
'子': '午', '午': '子',
'丑': '未', '未': '丑',
'寅': '申', '申': '寅',
'卯': '酉', '酉': '卯',
'辰': '戌', '戌': '辰',
'巳': '亥', '亥': '巳'
};
if (conflict[branch1] === branch2) {
score -= 20;
}
return Math.min(100, Math.max(0, score));
};
const compatibilityScore = calculateCompatibility();
const getScoreGrade = (score: number): string => {
if (score >= 90) return 'S';
if (score >= 80) return 'A';
if (score >= 70) return 'B';
if (score >= 60) return 'C';
return 'D';
};
const getScoreText = (score: number): string => {
if (score >= 90) return '천생연분';
if (score >= 80) return '매우 좋음';
if (score >= 70) return '좋음';
if (score >= 60) return '보통';
return '주의 필요';
};
const getScoreColor = (score: number): string => {
if (score >= 90) return 'from-pink-500 to-red-500';
if (score >= 80) return 'from-pink-400 to-purple-400';
if (score >= 70) return 'from-purple-400 to-indigo-400';
if (score >= 60) return 'from-indigo-400 to-blue-400';
return 'from-gray-400 to-gray-500';
};
// 세부 궁합 점수
const detailedScores = [
{ name: '연애운', score: compatibilityScore + Math.floor(Math.random() * 10 - 5), icon: '💑' },
{ name: '결혼운', score: compatibilityScore + Math.floor(Math.random() * 10 - 5), icon: '💍' },
{ name: '금전운', score: compatibilityScore + Math.floor(Math.random() * 10 - 5), icon: '💰' },
{ name: '사업운', score: compatibilityScore + Math.floor(Math.random() * 10 - 5), icon: '💼' },
].map(item => ({ ...item, score: Math.min(100, Math.max(0, item.score)) }));
return (
<div className="min-h-screen bg-gradient-to-br from-pink-50 via-purple-50 to-indigo-50">
{/* Navigation */}
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<Link href="/" className="text-2xl font-bold bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent">
🔮
</Link>
<div className="flex gap-4">
<Link
href="/compatibility"
className="text-gray-700 hover:text-pink-600 transition font-medium"
>
</Link>
<Link
href="/"
className="text-gray-700 hover:text-pink-600 transition font-medium"
>
</Link>
</div>
</div>
</div>
</nav>
{/* Result Content */}
<div className="max-w-6xl mx-auto px-4 py-12">
{/* Header */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
💕
</h1>
<p className="text-xl text-gray-600">
</p>
</div>
{/* 두 사람 정보 */}
<div className="grid md:grid-cols-2 gap-6 mb-12">
{/* Person 1 */}
<div className="bg-gradient-to-br from-pink-500 to-pink-600 rounded-2xl shadow-xl p-8 text-white">
<div className="text-center mb-6">
<div className="text-5xl mb-3">👤</div>
<h3 className="text-2xl font-bold"> </h3>
<p className="text-pink-100 mt-2">
{saju1.birthDate.year} {saju1.birthDate.month} {saju1.birthDate.day}
</p>
<p className="text-pink-100">
{gender1 === 'male' ? '남성' : '여성'}
</p>
</div>
<div className="bg-white/20 backdrop-blur-sm rounded-xl p-4">
<p className="text-center mb-2 font-semibold"> ()</p>
<div className="text-center">
<span className="text-4xl font-bold">{saju1.day.stem}</span>
<span className="text-2xl ml-2">({saju1.day.stemKr})</span>
</div>
<p className="text-center mt-2 text-pink-100">
{saju1.day.element} ({['木', '火', '土', '金', '水'].indexOf(saju1.day.element) >= 0
? ['목', '화', '토', '금', '수'][['木', '火', '土', '金', '水'].indexOf(saju1.day.element)]
: ''})
</p>
</div>
</div>
{/* Person 2 */}
<div className="bg-gradient-to-br from-purple-500 to-purple-600 rounded-2xl shadow-xl p-8 text-white">
<div className="text-center mb-6">
<div className="text-5xl mb-3">👤</div>
<h3 className="text-2xl font-bold"> </h3>
<p className="text-purple-100 mt-2">
{saju2.birthDate.year} {saju2.birthDate.month} {saju2.birthDate.day}
</p>
<p className="text-purple-100">
{gender2 === 'male' ? '남성' : '여성'}
</p>
</div>
<div className="bg-white/20 backdrop-blur-sm rounded-xl p-4">
<p className="text-center mb-2 font-semibold"> ()</p>
<div className="text-center">
<span className="text-4xl font-bold">{saju2.day.stem}</span>
<span className="text-2xl ml-2">({saju2.day.stemKr})</span>
</div>
<p className="text-center mt-2 text-purple-100">
{saju2.day.element} ({['木', '火', '土', '金', '水'].indexOf(saju2.day.element) >= 0
? ['목', '화', '토', '금', '수'][['木', '火', '土', '金', '水'].indexOf(saju2.day.element)]
: ''})
</p>
</div>
</div>
</div>
{/* 총합 궁합 점수 */}
<div className={`bg-gradient-to-r ${getScoreColor(compatibilityScore)} rounded-3xl shadow-2xl p-12 mb-12 text-white`}>
<div className="text-center">
<h2 className="text-3xl font-bold mb-6"> </h2>
<div className="flex items-center justify-center gap-8 mb-6">
<div className="text-8xl font-bold">{compatibilityScore}</div>
<div className="text-left">
<div className="text-6xl font-bold mb-2">{getScoreGrade(compatibilityScore)}</div>
<div className="text-2xl">{getScoreText(compatibilityScore)}</div>
</div>
</div>
<div className="max-w-2xl mx-auto">
<div className="w-full bg-white/30 rounded-full h-4">
<div
className="bg-white h-4 rounded-full transition-all duration-1000"
style={{ width: `${compatibilityScore}%` }}
></div>
</div>
</div>
</div>
</div>
{/* 세부 궁합 */}
<div className="bg-white rounded-3xl shadow-2xl p-8 md:p-12 mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center"> </h2>
<div className="grid md:grid-cols-2 gap-6">
{detailedScores.map((item) => (
<div key={item.name} className="bg-gray-50 rounded-xl p-6">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3">
<span className="text-3xl">{item.icon}</span>
<h3 className="text-xl font-bold text-gray-900">{item.name}</h3>
</div>
<span className="text-2xl font-bold text-pink-600">{item.score}</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-3">
<div
className="bg-gradient-to-r from-pink-500 to-purple-500 h-3 rounded-full transition-all duration-500"
style={{ width: `${item.score}%` }}
></div>
</div>
</div>
))}
</div>
</div>
{/* 궁합 해석 */}
<div className="grid md:grid-cols-2 gap-8 mb-8">
{/* 장점 */}
<div className="bg-white rounded-2xl shadow-lg p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-4 flex items-center">
<span className="text-3xl mr-3"></span>
</h3>
<ul className="space-y-3 text-gray-700">
<li className="flex items-start">
<span className="text-pink-600 mr-2"></span>
<span> .</span>
</li>
<li className="flex items-start">
<span className="text-pink-600 mr-2"></span>
<span> .</span>
</li>
<li className="flex items-start">
<span className="text-pink-600 mr-2"></span>
<span> .</span>
</li>
</ul>
</div>
{/* 주의점 */}
<div className="bg-white rounded-2xl shadow-lg p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-4 flex items-center">
<span className="text-3xl mr-3"></span>
</h3>
<ul className="space-y-3 text-gray-700">
<li className="flex items-start">
<span className="text-purple-600 mr-2"></span>
<span> .</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2"></span>
<span> .</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2"></span>
<span> .</span>
</li>
</ul>
</div>
</div>
{/* 조언 */}
<div className="bg-gradient-to-r from-indigo-600 to-purple-600 rounded-2xl shadow-lg p-8 text-white mb-8">
<h3 className="text-2xl font-bold mb-4 text-center">💡 </h3>
<p className="text-lg leading-relaxed text-center">
.
.
, .
</p>
</div>
{/* 다른 메뉴 */}
<div className="grid md:grid-cols-3 gap-6">
<Link
href="/compatibility"
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
>
<div className="text-4xl mb-3">💕</div>
<h3 className="text-xl font-bold text-gray-900 mb-2"> </h3>
<p className="text-gray-600 text-sm"> </p>
</Link>
<Link
href="/"
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
>
<div className="text-4xl mb-3">📜</div>
<h3 className="text-xl font-bold text-gray-900 mb-2"> </h3>
<p className="text-gray-600 text-sm"> </p>
</Link>
<button className="bg-gradient-to-r from-pink-600 to-purple-600 text-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group">
<div className="text-4xl mb-3">📥</div>
<h3 className="text-xl font-bold mb-2">PDF </h3>
<p className="text-sm opacity-90"> </p>
</button>
</div>
</div>
{/* Footer */}
<footer className="bg-gray-900 text-white py-12 px-4 mt-20">
<div className="max-w-7xl mx-auto text-center">
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-pink-400 to-purple-400 bg-clip-text text-transparent">
🔮
</div>
<p className="text-gray-400 mb-6">
</p>
<div className="text-sm text-gray-500">
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-pink-400"></a></p>
<p className="mt-2">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}

View File

@@ -0,0 +1,281 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export default function CompatibilityForm() {
const router = useRouter();
// Person 1
const [year1, setYear1] = useState('');
const [month1, setMonth1] = useState('');
const [day1, setDay1] = useState('');
const [hour1, setHour1] = useState('');
const [gender1, setGender1] = useState<'male' | 'female'>('male');
// Person 2
const [year2, setYear2] = useState('');
const [month2, setMonth2] = useState('');
const [day2, setDay2] = useState('');
const [hour2, setHour2] = useState('');
const [gender2, setGender2] = useState<'male' | 'female'>('female');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!year1 || !month1 || !day1) {
alert('첫 번째 사람의 생년월일을 모두 입력해주세요.');
return;
}
if (!year2 || !month2 || !day2) {
alert('두 번째 사람의 생년월일을 모두 입력해주세요.');
return;
}
// URL 파라미터로 전달
const params = new URLSearchParams({
year1,
month1,
day1,
gender1,
year2,
month2,
day2,
gender2,
});
if (hour1) params.append('hour1', hour1);
if (hour2) params.append('hour2', hour2);
router.push(`/compatibility/result?${params.toString()}`);
};
return (
<form onSubmit={handleSubmit} className="bg-white rounded-3xl shadow-2xl p-8 md:p-12">
<div className="grid md:grid-cols-2 gap-12">
{/* Person 1 */}
<div className="space-y-6">
<div className="text-center mb-6">
<h3 className="text-2xl font-bold text-pink-600 mb-2">👤 </h3>
<p className="text-sm text-gray-600"> </p>
</div>
{/* 생년월일 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-3 gap-3">
<input
type="number"
placeholder="년"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-pink-500 focus:outline-none transition"
min="1900"
max="2100"
value={year1}
onChange={(e) => setYear1(e.target.value)}
required
/>
<input
type="number"
placeholder="월"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-pink-500 focus:outline-none transition"
min="1"
max="12"
value={month1}
onChange={(e) => setMonth1(e.target.value)}
required
/>
<input
type="number"
placeholder="일"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-pink-500 focus:outline-none transition"
min="1"
max="31"
value={day1}
onChange={(e) => setDay1(e.target.value)}
required
/>
</div>
</div>
{/* 태어난 시간 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
()
</label>
<select
className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-pink-500 focus:outline-none transition"
value={hour1}
onChange={(e) => setHour1(e.target.value)}
>
<option value=""> / </option>
<option value="0"> (23:00-01:00)</option>
<option value="1"> (01:00-03:00)</option>
<option value="3"> (03:00-05:00)</option>
<option value="5"> (05:00-07:00)</option>
<option value="7"> (07:00-09:00)</option>
<option value="9"> (09:00-11:00)</option>
<option value="11"> (11:00-13:00)</option>
<option value="13"> (13:00-15:00)</option>
<option value="15"> (15:00-17:00)</option>
<option value="17"> (17:00-19:00)</option>
<option value="19"> (19:00-21:00)</option>
<option value="21"> (21:00-23:00)</option>
</select>
</div>
{/* 성별 선택 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
onClick={() => setGender1('male')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
gender1 === 'male'
? 'bg-pink-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-pink-500 hover:text-pink-600'
}`}
>
</button>
<button
type="button"
onClick={() => setGender1('female')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
gender1 === 'female'
? 'bg-pink-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-pink-500 hover:text-pink-600'
}`}
>
</button>
</div>
</div>
</div>
{/* Person 2 */}
<div className="space-y-6">
<div className="text-center mb-6">
<h3 className="text-2xl font-bold text-purple-600 mb-2">👤 </h3>
<p className="text-sm text-gray-600"> </p>
</div>
{/* 생년월일 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-3 gap-3">
<input
type="number"
placeholder="년"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition"
min="1900"
max="2100"
value={year2}
onChange={(e) => setYear2(e.target.value)}
required
/>
<input
type="number"
placeholder="월"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition"
min="1"
max="12"
value={month2}
onChange={(e) => setMonth2(e.target.value)}
required
/>
<input
type="number"
placeholder="일"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition"
min="1"
max="31"
value={day2}
onChange={(e) => setDay2(e.target.value)}
required
/>
</div>
</div>
{/* 태어난 시간 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
()
</label>
<select
className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition"
value={hour2}
onChange={(e) => setHour2(e.target.value)}
>
<option value=""> / </option>
<option value="0"> (23:00-01:00)</option>
<option value="1"> (01:00-03:00)</option>
<option value="3"> (03:00-05:00)</option>
<option value="5"> (05:00-07:00)</option>
<option value="7"> (07:00-09:00)</option>
<option value="9"> (09:00-11:00)</option>
<option value="11"> (11:00-13:00)</option>
<option value="13"> (13:00-15:00)</option>
<option value="15"> (15:00-17:00)</option>
<option value="17"> (17:00-19:00)</option>
<option value="19"> (19:00-21:00)</option>
<option value="21"> (21:00-23:00)</option>
</select>
</div>
{/* 성별 선택 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
onClick={() => setGender2('male')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
gender2 === 'male'
? 'bg-purple-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-purple-500 hover:text-purple-600'
}`}
>
</button>
<button
type="button"
onClick={() => setGender2('female')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
gender2 === 'female'
? 'bg-purple-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-purple-500 hover:text-purple-600'
}`}
>
</button>
</div>
</div>
</div>
</div>
{/* 제출 버튼 */}
<div className="mt-8">
<button
type="submit"
className="w-full bg-gradient-to-r from-pink-600 to-purple-600 text-white py-4 rounded-xl text-lg font-bold hover:from-pink-700 hover:to-purple-700 transition shadow-lg hover:shadow-xl"
>
💕
</button>
</div>
<p className="text-sm text-gray-500 text-center mt-4">
* .
</p>
</form>
);
}

181
app/components/SajuForm.tsx Normal file
View File

@@ -0,0 +1,181 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export default function SajuForm() {
const router = useRouter();
const [year, setYear] = useState('');
const [month, setMonth] = useState('');
const [day, setDay] = useState('');
const [hour, setHour] = useState('');
const [calendarType, setCalendarType] = useState<'solar' | 'lunar'>('solar');
const [gender, setGender] = useState<'male' | 'female'>('male');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!year || !month || !day) {
alert('생년월일을 모두 입력해주세요.');
return;
}
// URL 파라미터로 전달
const params = new URLSearchParams({
year,
month,
day,
gender,
calendarType
});
if (hour) {
params.append('hour', hour);
}
router.push(`/result?${params.toString()}`);
};
return (
<form onSubmit={handleSubmit} className="space-y-6">
{/* 생년월일 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-3 gap-3">
<input
type="number"
placeholder="년 (예: 1990)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
min="1900"
max="2100"
value={year}
onChange={(e) => setYear(e.target.value)}
required
/>
<input
type="number"
placeholder="월 (1-12)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
min="1"
max="12"
value={month}
onChange={(e) => setMonth(e.target.value)}
required
/>
<input
type="number"
placeholder="일 (1-31)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
min="1"
max="31"
value={day}
onChange={(e) => setDay(e.target.value)}
required
/>
</div>
</div>
{/* 태어난 시간 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
()
</label>
<select
className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
value={hour}
onChange={(e) => setHour(e.target.value)}
>
<option value=""> / </option>
<option value="0"> () 23:00 - 01:00</option>
<option value="1"> () 01:00 - 03:00</option>
<option value="3"> () 03:00 - 05:00</option>
<option value="5"> () 05:00 - 07:00</option>
<option value="7"> () 07:00 - 09:00</option>
<option value="9"> () 09:00 - 11:00</option>
<option value="11"> () 11:00 - 13:00</option>
<option value="13"> () 13:00 - 15:00</option>
<option value="15"> () 15:00 - 17:00</option>
<option value="17"> () 17:00 - 19:00</option>
<option value="19"> () 19:00 - 21:00</option>
<option value="21"> () 21:00 - 23:00</option>
</select>
</div>
{/* 양력/음력 선택 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
onClick={() => setCalendarType('solar')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
calendarType === 'solar'
? 'bg-indigo-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-indigo-500 hover:text-indigo-600'
}`}
>
</button>
<button
type="button"
onClick={() => setCalendarType('lunar')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
calendarType === 'lunar'
? 'bg-indigo-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-indigo-500 hover:text-indigo-600'
}`}
>
</button>
</div>
</div>
{/* 성별 선택 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
onClick={() => setGender('male')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
gender === 'male'
? 'bg-indigo-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-indigo-500 hover:text-indigo-600'
}`}
>
</button>
<button
type="button"
onClick={() => setGender('female')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
gender === 'female'
? 'bg-indigo-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-indigo-500 hover:text-indigo-600'
}`}
>
</button>
</div>
</div>
{/* 제출 버튼 */}
<button
type="submit"
className="w-full bg-gradient-to-r from-indigo-600 to-purple-600 text-white py-4 rounded-xl text-lg font-bold hover:from-indigo-700 hover:to-purple-700 transition shadow-lg hover:shadow-xl"
>
</button>
<p className="text-sm text-gray-500 text-center">
* .
</p>
</form>
);
}

268
app/fortune/page.tsx Normal file
View File

@@ -0,0 +1,268 @@
import Link from 'next/link';
import { calculateSaju } from '@/lib/saju-calculator';
interface PageProps {
searchParams: Promise<{
year: string;
month: string;
day: string;
hour?: string;
gender: 'male' | 'female';
calendarType: 'solar' | 'lunar';
}>;
}
export default async function FortunePage({ searchParams }: PageProps) {
const params = await searchParams;
const { year, month, day, hour, gender } = params;
const yearNum = parseInt(year);
const monthNum = parseInt(month);
const dayNum = parseInt(day);
const hourNum = hour ? parseInt(hour) : null;
const sajuData = calculateSaju(yearNum, monthNum, dayNum, hourNum, gender);
// 오늘 날짜
const today = new Date();
const todayYear = today.getFullYear();
const todayMonth = today.getMonth() + 1;
const todayDay = today.getDate();
// 오늘의 간지 계산
const todayGanzi = calculateSaju(todayYear, todayMonth, todayDay, null, gender);
// 운세 점수 계산 (간단한 알고리즘)
const calculateFortuneScore = (category: string): number => {
const seed = sajuData.day.stem.charCodeAt(0) + todayGanzi.day.stem.charCodeAt(0) + category.charCodeAt(0);
return 60 + (seed % 40);
};
const fortuneCategories = [
{ name: '종합운', icon: '⭐', score: calculateFortuneScore('종합'), color: 'indigo' },
{ name: '금전운', icon: '💰', score: calculateFortuneScore('금전'), color: 'green' },
{ name: '애정운', icon: '💕', score: calculateFortuneScore('애정'), color: 'pink' },
{ name: '건강운', icon: '🏥', score: calculateFortuneScore('건강'), color: 'red' },
{ name: '직장운', icon: '💼', score: calculateFortuneScore('직장'), color: 'blue' },
{ name: '학업운', icon: '📚', score: calculateFortuneScore('학업'), color: 'purple' },
];
const getScoreText = (score: number): string => {
if (score >= 90) return '최고';
if (score >= 80) return '좋음';
if (score >= 70) return '보통';
if (score >= 60) return '주의';
return '나쁨';
};
const getScoreColor = (score: number): string => {
if (score >= 90) return 'text-green-600';
if (score >= 80) return 'text-blue-600';
if (score >= 70) return 'text-yellow-600';
if (score >= 60) return 'text-orange-600';
return 'text-red-600';
};
// 행운의 방향과 색깔 (일간 기준)
const getLuckyDirection = (stem: string): string => {
const directions: { [key: string]: string } = {
'甲': '동쪽', '乙': '동쪽',
'丙': '남쪽', '丁': '남쪽',
'戊': '중앙', '己': '중앙',
'庚': '서쪽', '辛': '서쪽',
'壬': '북쪽', '癸': '북쪽',
};
return directions[stem] || '동쪽';
};
const getLuckyColor = (element: string): string => {
const colors: { [key: string]: string } = {
'木': '녹색',
'火': '빨강색',
'土': '노란색',
'金': '흰색',
'水': '검정색',
};
return colors[element] || '녹색';
};
const getLuckyNumber = (stem: string): number => {
return (stem.charCodeAt(0) % 9) + 1;
};
return (
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50">
{/* Navigation */}
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<Link href="/" className="text-2xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
🔮
</Link>
<div className="flex gap-4">
<Link
href={`/result?${new URLSearchParams(params as any).toString()}`}
className="text-gray-700 hover:text-indigo-600 transition font-medium"
>
</Link>
<Link
href="/"
className="text-gray-700 hover:text-indigo-600 transition font-medium"
>
</Link>
</div>
</div>
</div>
</nav>
{/* Content */}
<div className="max-w-6xl mx-auto px-4 py-12">
{/* Header */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
</h1>
<p className="text-xl text-gray-600">
{todayYear} {todayMonth} {todayDay} ({todayGanzi.day.stem}{todayGanzi.day.branch})
</p>
<p className="text-lg text-gray-500 mt-2">
{sajuData.birthDate.year} {gender === 'male' ? '남성' : '여성'}
</p>
</div>
{/* 운세 점수 카드들 */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
{fortuneCategories.map((category) => (
<div key={category.name} className="bg-white rounded-2xl shadow-lg p-6 hover:shadow-xl transition">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<span className="text-4xl">{category.icon}</span>
<h3 className="text-xl font-bold text-gray-900">{category.name}</h3>
</div>
<span className={`text-2xl font-bold ${getScoreColor(category.score)}`}>
{category.score}
</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-3 mb-2">
<div
className={`bg-gradient-to-r from-${category.color}-400 to-${category.color}-600 h-3 rounded-full transition-all duration-500`}
style={{ width: `${category.score}%` }}
></div>
</div>
<p className="text-sm text-gray-600 text-right">{getScoreText(category.score)}</p>
</div>
))}
</div>
{/* 오늘의 한마디 */}
<div className="bg-gradient-to-r from-indigo-600 to-purple-600 rounded-3xl shadow-2xl p-8 md:p-12 mb-8 text-white">
<h2 className="text-3xl font-bold mb-6 text-center">💬 </h2>
<p className="text-xl leading-relaxed text-center">
{sajuData.day.element === '木' && '나무가 자라듯 꾸준히 노력하면 좋은 결과가 있을 것입니다. 새로운 시작에 좋은 날입니다.'}
{sajuData.day.element === '火' && '활발한 기운이 가득한 날입니다. 적극적으로 행동하면 좋은 기회를 잡을 수 있습니다.'}
{sajuData.day.element === '土' && '안정적인 하루가 될 것입니다. 차근차근 계획을 세우고 실행하면 좋습니다.'}
{sajuData.day.element === '金' && '명확한 판단이 필요한 날입니다. 원칙을 지키며 행동하면 좋은 결과가 있을 것입니다.'}
{sajuData.day.element === '水' && '유연한 사고가 도움이 되는 날입니다. 주변 사람들과의 소통을 중요하게 여기세요.'}
</p>
</div>
{/* 행운의 요소들 */}
<div className="grid md:grid-cols-3 gap-8 mb-8">
{/* 행운의 방향 */}
<div className="bg-white rounded-2xl shadow-lg p-8 text-center">
<div className="text-5xl mb-4">🧭</div>
<h3 className="text-2xl font-bold text-gray-900 mb-3"> </h3>
<p className="text-3xl font-bold text-indigo-600">{getLuckyDirection(sajuData.day.stem)}</p>
<p className="text-sm text-gray-600 mt-2"> </p>
</div>
{/* 행운의 색깔 */}
<div className="bg-white rounded-2xl shadow-lg p-8 text-center">
<div className="text-5xl mb-4">🎨</div>
<h3 className="text-2xl font-bold text-gray-900 mb-3"> </h3>
<p className="text-3xl font-bold text-purple-600">{getLuckyColor(sajuData.day.element)}</p>
<p className="text-sm text-gray-600 mt-2"> </p>
</div>
{/* 행운의 숫자 */}
<div className="bg-white rounded-2xl shadow-lg p-8 text-center">
<div className="text-5xl mb-4">🎲</div>
<h3 className="text-2xl font-bold text-gray-900 mb-3"> </h3>
<p className="text-3xl font-bold text-pink-600">{getLuckyNumber(sajuData.day.stem)}</p>
<p className="text-sm text-gray-600 mt-2"> </p>
</div>
</div>
{/* 주의사항 */}
<div className="bg-white rounded-2xl shadow-lg p-8 mb-8">
<h3 className="text-2xl font-bold text-gray-900 mb-6 flex items-center">
<span className="text-3xl mr-3"></span>
</h3>
<ul className="space-y-3 text-gray-700">
<li className="flex items-start">
<span className="text-indigo-600 mr-2"></span>
<span> .</span>
</li>
<li className="flex items-start">
<span className="text-indigo-600 mr-2"></span>
<span> .</span>
</li>
<li className="flex items-start">
<span className="text-indigo-600 mr-2"></span>
<span> .</span>
</li>
</ul>
</div>
{/* 다른 메뉴 */}
<div className="grid md:grid-cols-3 gap-6">
<Link
href={`/result?${new URLSearchParams(params as any).toString()}`}
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
>
<div className="text-4xl mb-3">📜</div>
<h3 className="text-xl font-bold text-gray-900 mb-2"></h3>
<p className="text-gray-600 text-sm"> </p>
</Link>
<Link
href="/"
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
>
<div className="text-4xl mb-3">💕</div>
<h3 className="text-xl font-bold text-gray-900 mb-2"> </h3>
<p className="text-gray-600 text-sm"> </p>
</Link>
<Link
href="/"
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
>
<div className="text-4xl mb-3">🎋</div>
<h3 className="text-xl font-bold text-gray-900 mb-2"></h3>
<p className="text-gray-600 text-sm"> </p>
</Link>
</div>
</div>
{/* Footer */}
<footer className="bg-gray-900 text-white py-12 px-4 mt-20">
<div className="max-w-7xl mx-auto text-center">
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">
🔮
</div>
<p className="text-gray-400 mb-6">
</p>
<div className="text-sm text-gray-500">
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-indigo-400"></a></p>
<p className="mt-2">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}

View File

@@ -1,65 +1,214 @@
import Image from "next/image";
import Link from 'next/link';
import SajuForm from './components/SajuForm';
export default function Home() {
return (
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50">
{/* Navigation */}
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<div className="text-2xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
🔮
</div>
<div className="hidden md:flex space-x-6">
<Link href="#saju" className="text-gray-700 hover:text-indigo-600 transition font-medium"></Link>
<Link href="/compatibility" className="text-gray-700 hover:text-indigo-600 transition font-medium"></Link>
</div>
</div>
</div>
</nav>
{/* Hero Section */}
<section className="pt-20 pb-32 px-4">
<div className="max-w-4xl mx-auto text-center">
<div className="inline-block mb-6 px-6 py-2 bg-white/50 backdrop-blur-sm rounded-full text-indigo-700 font-semibold border border-indigo-200">
</div>
<h1 className="text-5xl md:text-7xl font-bold text-gray-900 mb-6 leading-tight">
<span className="bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent"></span><br />
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
<p className="text-xl text-gray-600 mb-12 max-w-2xl mx-auto">
, , .
.
</p>
{/* Main Input Card */}
<div className="bg-white rounded-3xl shadow-2xl p-8 md:p-12 max-w-2xl mx-auto">
<h2 className="text-2xl font-bold text-gray-900 mb-8"> </h2>
<SajuForm />
{/* 생년월일 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-3 gap-3">
<input
type="number"
placeholder="년 (예: 1990)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
min="1900"
max="2100"
/>
<input
type="number"
placeholder="월 (1-12)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
min="1"
max="12"
/>
<input
type="number"
placeholder="일 (1-31)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
min="1"
max="31"
/>
</div>
</div>
{/* 태어난 시간 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
()
</label>
<select className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition">
<option value=""> / </option>
<option value="23-01"> () 23:00 - 01:00</option>
<option value="01-03"> () 01:00 - 03:00</option>
<option value="03-05"> () 03:00 - 05:00</option>
<option value="05-07"> () 05:00 - 07:00</option>
<option value="07-09"> () 07:00 - 09:00</option>
<option value="09-11"> () 09:00 - 11:00</option>
<option value="11-13"> () 11:00 - 13:00</option>
<option value="13-15"> () 13:00 - 15:00</option>
<option value="15-17"> () 15:00 - 17:00</option>
<option value="17-19"> () 17:00 - 19:00</option>
<option value="19-21"> () 19:00 - 21:00</option>
<option value="21-23"> () 21:00 - 23:00</option>
</select>
</div>
{/* 양력/음력 선택 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
className="px-6 py-3 bg-indigo-600 text-white rounded-xl font-semibold hover:bg-indigo-700 transition"
>
</button>
<button
type="button"
className="px-6 py-3 bg-white border-2 border-gray-200 text-gray-700 rounded-xl font-semibold hover:border-indigo-500 hover:text-indigo-600 transition"
>
</button>
</div>
</div>
{/* 성별 선택 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
className="px-6 py-3 bg-indigo-600 text-white rounded-xl font-semibold hover:bg-indigo-700 transition"
>
</button>
<button
type="button"
className="px-6 py-3 bg-white border-2 border-gray-200 text-gray-700 rounded-xl font-semibold hover:border-indigo-500 hover:text-indigo-600 transition"
>
</button>
</div>
</div>
{/* 제출 버튼 */}
</div>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</section>
{/* Features Section */}
<section className="py-20 px-4 bg-white">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4"> ?</h2>
<p className="text-xl text-gray-600"> </p>
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{/* Feature 1 */}
<div className="text-center p-6 rounded-2xl hover:bg-indigo-50 transition">
<div className="text-5xl mb-4">📜</div>
<h3 className="text-xl font-bold text-gray-900 mb-3"></h3>
<p className="text-gray-600">
, , , .
</p>
</div>
{/* Feature 2 */}
<div className="text-center p-6 rounded-2xl hover:bg-purple-50 transition">
<div className="text-5xl mb-4">🌟</div>
<h3 className="text-xl font-bold text-gray-900 mb-3"> </h3>
<p className="text-gray-600">
.
</p>
</div>
{/* Feature 3 */}
<Link href="/compatibility" className="text-center p-6 rounded-2xl hover:bg-pink-50 transition block">
<div className="text-5xl mb-4">💕</div>
<h3 className="text-xl font-bold text-gray-900 mb-3"></h3>
<p className="text-gray-600">
.
</p>
</Link>
</div>
</div>
</section>
{/* CTA Section */}
<section className="py-20 px-4 bg-gradient-to-r from-indigo-600 to-purple-600">
<div className="max-w-4xl mx-auto text-center text-white">
<h2 className="text-4xl md:text-5xl font-bold mb-6">
</h2>
<p className="text-xl mb-8 opacity-90">
</p>
<a href="#" className="inline-block bg-white text-indigo-600 px-8 py-4 rounded-xl text-lg font-bold hover:bg-gray-100 transition shadow-lg">
</a>
</div>
</main>
</section>
{/* Footer */}
<footer className="bg-gray-900 text-white py-12 px-4">
<div className="max-w-7xl mx-auto text-center">
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">
🔮
</div>
<p className="text-gray-400 mb-6">
</p>
<div className="text-sm text-gray-500">
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-indigo-400"></a></p>
<p className="mt-2">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}

260
app/result/page.tsx Normal file
View File

@@ -0,0 +1,260 @@
import { calculateSaju } from '@/lib/saju-calculator';
import Link from 'next/link';
interface PageProps {
searchParams: Promise<{
year: string;
month: string;
day: string;
hour?: string;
gender: 'male' | 'female';
calendarType: 'solar' | 'lunar';
}>;
}
export default async function ResultPage({ searchParams }: PageProps) {
const params = await searchParams;
const { year, month, day, hour, gender } = params;
const yearNum = parseInt(year);
const monthNum = parseInt(month);
const dayNum = parseInt(day);
const hourNum = hour ? parseInt(hour) : null;
const sajuData = calculateSaju(yearNum, monthNum, dayNum, hourNum, gender);
return (
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50">
{/* Navigation */}
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<Link href="/" className="text-2xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
🔮
</Link>
<Link
href="/"
className="text-gray-700 hover:text-indigo-600 transition font-medium"
>
</Link>
</div>
</div>
</nav>
{/* Result Content */}
<div className="max-w-6xl mx-auto px-4 py-12">
{/* Header */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
</h1>
<p className="text-xl text-gray-600">
{yearNum} {monthNum} {dayNum} {hourNum !== null && `${hourNum}`}
{gender === 'male' ? ' 남성' : ' 여성'}
</p>
</div>
{/* 사주팔자 표 */}
<div className="bg-white rounded-3xl shadow-2xl p-8 md:p-12 mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center"> ()</h2>
<div className="overflow-x-auto">
<table className="w-full border-collapse">
<thead>
<tr className="bg-gradient-to-r from-indigo-600 to-purple-600 text-white">
<th className="py-4 px-6 text-center font-bold text-lg"></th>
{sajuData.hour && <th className="py-4 px-6 text-center font-bold text-lg"> ()</th>}
<th className="py-4 px-6 text-center font-bold text-lg"> ()</th>
<th className="py-4 px-6 text-center font-bold text-lg"> ()</th>
<th className="py-4 px-6 text-center font-bold text-lg"> ()</th>
</tr>
</thead>
<tbody>
{/* 천간 */}
<tr className="border-b border-gray-200 hover:bg-indigo-50 transition">
<td className="py-4 px-6 text-center font-semibold text-gray-700"> ()</td>
{sajuData.hour && (
<td className="py-4 px-6 text-center">
<div className="text-3xl font-bold text-indigo-600">{sajuData.hour.stem}</div>
<div className="text-sm text-gray-600 mt-1">{sajuData.hour.stemKr}</div>
</td>
)}
<td className="py-4 px-6 text-center bg-blue-50">
<div className="text-3xl font-bold text-blue-600">{sajuData.day.stem}</div>
<div className="text-sm text-gray-600 mt-1">{sajuData.day.stemKr}</div>
<div className="text-xs text-blue-600 font-semibold mt-1"> ()</div>
</td>
<td className="py-4 px-6 text-center">
<div className="text-3xl font-bold text-indigo-600">{sajuData.month.stem}</div>
<div className="text-sm text-gray-600 mt-1">{sajuData.month.stemKr}</div>
</td>
<td className="py-4 px-6 text-center">
<div className="text-3xl font-bold text-indigo-600">{sajuData.year.stem}</div>
<div className="text-sm text-gray-600 mt-1">{sajuData.year.stemKr}</div>
</td>
</tr>
{/* 지지 */}
<tr className="border-b border-gray-200 hover:bg-purple-50 transition">
<td className="py-4 px-6 text-center font-semibold text-gray-700"> ()</td>
{sajuData.hour && (
<td className="py-4 px-6 text-center">
<div className="text-3xl font-bold text-purple-600">{sajuData.hour.branch}</div>
<div className="text-sm text-gray-600 mt-1">{sajuData.hour.branchKr}</div>
</td>
)}
<td className="py-4 px-6 text-center bg-blue-50">
<div className="text-3xl font-bold text-blue-600">{sajuData.day.branch}</div>
<div className="text-sm text-gray-600 mt-1">{sajuData.day.branchKr}</div>
</td>
<td className="py-4 px-6 text-center">
<div className="text-3xl font-bold text-purple-600">{sajuData.month.branch}</div>
<div className="text-sm text-gray-600 mt-1">{sajuData.month.branchKr}</div>
</td>
<td className="py-4 px-6 text-center">
<div className="text-3xl font-bold text-purple-600">{sajuData.year.branch}</div>
<div className="text-sm text-gray-600 mt-1">{sajuData.year.branchKr}</div>
</td>
</tr>
{/* 십성 */}
<tr className="border-b border-gray-200 hover:bg-emerald-50 transition">
<td className="py-4 px-6 text-center font-semibold text-gray-700"> ()</td>
{sajuData.hour && (
<td className="py-4 px-6 text-center">
<div className="text-lg font-semibold text-emerald-600">{sajuData.hour.tenGod}</div>
</td>
)}
<td className="py-4 px-6 text-center bg-blue-50">
<div className="text-lg font-semibold text-blue-600">{sajuData.day.tenGod}</div>
</td>
<td className="py-4 px-6 text-center">
<div className="text-lg font-semibold text-emerald-600">{sajuData.month.tenGod}</div>
</td>
<td className="py-4 px-6 text-center">
<div className="text-lg font-semibold text-emerald-600">{sajuData.year.tenGod}</div>
</td>
</tr>
{/* 십이운성 */}
<tr className="hover:bg-pink-50 transition">
<td className="py-4 px-6 text-center font-semibold text-gray-700"></td>
{sajuData.hour && (
<td className="py-4 px-6 text-center">
<div className="text-lg font-semibold text-pink-600">{sajuData.hour.fortune}</div>
</td>
)}
<td className="py-4 px-6 text-center bg-blue-50">
<div className="text-lg font-semibold text-blue-600">{sajuData.day.fortune}</div>
</td>
<td className="py-4 px-6 text-center">
<div className="text-lg font-semibold text-pink-600">{sajuData.month.fortune}</div>
</td>
<td className="py-4 px-6 text-center">
<div className="text-lg font-semibold text-pink-600">{sajuData.year.fortune}</div>
</td>
</tr>
</tbody>
</table>
</div>
<div className="mt-6 p-4 bg-blue-50 rounded-xl">
<p className="text-sm text-gray-700">
<strong className="text-blue-600"> ():</strong> {sajuData.day.stem}({sajuData.day.stemKr}) - .
</p>
</div>
</div>
{/* 사주 해석 */}
<div className="grid md:grid-cols-2 gap-8 mb-8">
{/* 성격 */}
<div className="bg-white rounded-2xl shadow-lg p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-4 flex items-center">
<span className="text-3xl mr-3">👤</span>
</h3>
<div className="space-y-3 text-gray-700">
<p className="leading-relaxed">
<strong className="text-indigo-600">{sajuData.day.stem}({sajuData.day.stemKr})</strong>
{sajuData.day.element === '木' && ' 나무처럼 성장하고 발전하려는 의지가 강합니다. 창의적이고 진취적인 성향을 가지고 있습니다.'}
{sajuData.day.element === '火' && ' 불처럼 열정적이고 활동적입니다. 리더십이 있고 사교성이 뛰어납니다.'}
{sajuData.day.element === '土' && ' 흙처럼 안정적이고 신뢰감 있습니다. 포용력이 있고 책임감이 강합니다.'}
{sajuData.day.element === '金' && ' 금속처럼 강인하고 원칙적입니다. 결단력 있고 의리를 중시합니다.'}
{sajuData.day.element === '水' && ' 물처럼 유연하고 지혜롭습니다. 적응력이 뛰어나고 사려 깊습니다.'}
</p>
</div>
</div>
{/* 운세 */}
<div className="bg-white rounded-2xl shadow-lg p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-4 flex items-center">
<span className="text-3xl mr-3">🌟</span>
</h3>
<div className="space-y-3 text-gray-700">
<p className="leading-relaxed">
<strong className="text-purple-600">{sajuData.day.fortune}</strong>,
{sajuData.day.fortune === '장생' && ' 새로운 시작과 성장의 시기입니다.'}
{sajuData.day.fortune === '목욕' && ' 정화와 준비의 시기입니다.'}
{sajuData.day.fortune === '관대' && ' 사회적으로 인정받는 시기입니다.'}
{sajuData.day.fortune === '건록' && ' 안정되고 왕성한 활동의 시기입니다.'}
{sajuData.day.fortune === '제왕' && ' 최고의 전성기를 맞이하는 시기입니다.'}
{sajuData.day.fortune === '쇠' && ' 조금씩 힘이 약해지는 시기입니다.'}
{sajuData.day.fortune === '병' && ' 어려움이 있을 수 있는 시기입니다.'}
{sajuData.day.fortune === '사' && ' 끝과 새 시작을 준비하는 시기입니다.'}
{sajuData.day.fortune === '묘' && ' 잠시 휴식이 필요한 시기입니다.'}
{sajuData.day.fortune === '절' && ' 극복과 인내가 필요한 시기입니다.'}
{sajuData.day.fortune === '태' && ' 새로운 기운이 싹트는 시기입니다.'}
{sajuData.day.fortune === '양' && ' 성장을 준비하는 시기입니다.'}
</p>
</div>
</div>
</div>
{/* 추가 기능 버튼 */}
<div className="grid md:grid-cols-3 gap-6">
<Link
href={`/fortune?${new URLSearchParams(params as any).toString()}`}
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
>
<div className="text-4xl mb-3">🌟</div>
<h3 className="text-xl font-bold text-gray-900 mb-2"> </h3>
<p className="text-gray-600 text-sm"> </p>
</Link>
<Link
href="/compatibility"
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
>
<div className="text-4xl mb-3">💕</div>
<h3 className="text-xl font-bold text-gray-900 mb-2"> </h3>
<p className="text-gray-600 text-sm"> </p>
</Link>
<button className="bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group">
<div className="text-4xl mb-3">📥</div>
<h3 className="text-xl font-bold mb-2">PDF </h3>
<p className="text-sm opacity-90"> </p>
</button>
</div>
</div>
{/* Footer */}
<footer className="bg-gray-900 text-white py-12 px-4 mt-20">
<div className="max-w-7xl mx-auto text-center">
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">
🔮
</div>
<p className="text-gray-400 mb-6">
</p>
<div className="text-sm text-gray-500">
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-indigo-400"></a></p>
<p className="mt-2">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}