diff --git a/app/compatibility/result/page.tsx b/app/compatibility/result/page.tsx
index 288d443..bd92ce0 100644
--- a/app/compatibility/result/page.tsx
+++ b/app/compatibility/result/page.tsx
@@ -1,6 +1,7 @@
import Link from 'next/link';
import { calculateSaju, FIVE_ELEMENTS } from '@/lib/saju-calculator';
import PDFButton from '../../components/PDFButton';
+import ShareButtons from '../../components/ShareButtons';
interface PageProps {
searchParams: Promise<{
@@ -349,7 +350,7 @@ export default async function CompatibilityResultPage({ searchParams }: PageProp
{/* 다른 메뉴 */}
-
diff --git a/app/components/CompatibilityForm.tsx b/app/components/CompatibilityForm.tsx
index 19a9152..dcebe70 100644
--- a/app/components/CompatibilityForm.tsx
+++ b/app/components/CompatibilityForm.tsx
@@ -2,6 +2,7 @@
import { useState } from 'react';
import { useRouter } from 'next/navigation';
+import { lunarToSolar } from '@/lib/lunar-utils';
export default function CompatibilityForm() {
const router = useRouter();
@@ -12,6 +13,8 @@ export default function CompatibilityForm() {
const [day1, setDay1] = useState('');
const [hour1, setHour1] = useState('');
const [gender1, setGender1] = useState<'male' | 'female'>('male');
+ const [calendarType1, setCalendarType1] = useState<'solar' | 'lunar'>('solar');
+ const [isLeapMonth1, setIsLeapMonth1] = useState(false);
// Person 2
const [year2, setYear2] = useState('');
@@ -19,6 +22,8 @@ export default function CompatibilityForm() {
const [day2, setDay2] = useState('');
const [hour2, setHour2] = useState('');
const [gender2, setGender2] = useState<'male' | 'female'>('female');
+ const [calendarType2, setCalendarType2] = useState<'solar' | 'lunar'>('solar');
+ const [isLeapMonth2, setIsLeapMonth2] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
@@ -33,15 +38,33 @@ export default function CompatibilityForm() {
return;
}
+ let finalYear1 = year1, finalMonth1 = month1, finalDay1 = day1;
+ let finalYear2 = year2, finalMonth2 = month2, finalDay2 = day2;
+
+ // 음력인 경우 양력으로 변환
+ if (calendarType1 === 'lunar') {
+ const solar = lunarToSolar(parseInt(year1), parseInt(month1), parseInt(day1), isLeapMonth1);
+ finalYear1 = solar.year.toString();
+ finalMonth1 = solar.month.toString();
+ finalDay1 = solar.day.toString();
+ }
+
+ if (calendarType2 === 'lunar') {
+ const solar = lunarToSolar(parseInt(year2), parseInt(month2), parseInt(day2), isLeapMonth2);
+ finalYear2 = solar.year.toString();
+ finalMonth2 = solar.month.toString();
+ finalDay2 = solar.day.toString();
+ }
+
// URL 파라미터로 전달
const params = new URLSearchParams({
- year1,
- month1,
- day1,
+ year1: finalYear1,
+ month1: finalMonth1,
+ day1: finalDay1,
gender1,
- year2,
- month2,
- day2,
+ year2: finalYear2,
+ month2: finalMonth2,
+ day2: finalDay2,
gender2,
});
@@ -126,6 +149,50 @@ export default function CompatibilityForm() {
+ {/* 양력/음력 선택 */}
+
+
+
+
+
+
+ {calendarType1 === 'lunar' && (
+
+
+
+ )}
+
+
{/* 성별 선택 */}
+ {/* 양력/음력 선택 */}
+
+
+
+
+
+
+ {calendarType2 === 'lunar' && (
+
+
+
+ )}
+
+
{/* 성별 선택 */}
+ {calendarType === 'lunar' && (
+
+
+
+ )}
{/* 성별 선택 */}
diff --git a/app/components/ShareButtons.tsx b/app/components/ShareButtons.tsx
new file mode 100644
index 0000000..fbdd718
--- /dev/null
+++ b/app/components/ShareButtons.tsx
@@ -0,0 +1,162 @@
+'use client';
+
+import { useState } from 'react';
+
+interface ShareButtonsProps {
+ title: string;
+ description: string;
+ url?: string;
+}
+
+export default function ShareButtons({ title, description, url }: ShareButtonsProps) {
+ const [showShareMenu, setShowShareMenu] = useState(false);
+ const shareUrl = url || (typeof window !== 'undefined' ? window.location.href : '');
+
+ const handleKakaoShare = () => {
+ if (typeof window !== 'undefined' && (window as any).Kakao) {
+ (window as any).Kakao.Share.sendDefault({
+ objectType: 'feed',
+ content: {
+ title: title,
+ description: description,
+ imageUrl: 'https://developers.kakao.com/assets/img/about/logos/kakaolink/kakaolink_btn_medium.png',
+ link: {
+ mobileWebUrl: shareUrl,
+ webUrl: shareUrl,
+ },
+ },
+ buttons: [
+ {
+ title: '자세히 보기',
+ link: {
+ mobileWebUrl: shareUrl,
+ webUrl: shareUrl,
+ },
+ },
+ ],
+ });
+ } else {
+ alert('카카오톡 공유 기능을 사용할 수 없습니다.');
+ }
+ };
+
+ const handleFacebookShare = () => {
+ const facebookUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`;
+ window.open(facebookUrl, '_blank', 'width=600,height=400');
+ };
+
+ const handleTwitterShare = () => {
+ const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(shareUrl)}`;
+ window.open(twitterUrl, '_blank', 'width=600,height=400');
+ };
+
+ const handleCopyLink = async () => {
+ try {
+ await navigator.clipboard.writeText(shareUrl);
+ alert('링크가 복사되었습니다!');
+ setShowShareMenu(false);
+ } catch (err) {
+ alert('링크 복사에 실패했습니다.');
+ }
+ };
+
+ const handleNativeShare = async () => {
+ if (navigator.share) {
+ try {
+ await navigator.share({
+ title: title,
+ text: description,
+ url: shareUrl,
+ });
+ setShowShareMenu(false);
+ } catch (err) {
+ console.log('Share cancelled or failed', err);
+ }
+ } else {
+ setShowShareMenu(true);
+ }
+ };
+
+ return (
+
+
+
+ {/* 공유 메뉴 (모바일에서 네이티브 공유가 안 될 때) */}
+ {showShareMenu && (
+ <>
+ {/* 배경 오버레이 */}
+
setShowShareMenu(false)}
+ >
+
+ {/* 공유 메뉴 */}
+
+
+
공유하기
+
+
+
+
+ {/* 카카오톡 */}
+
+
+ {/* 페이스북 */}
+
+
+ {/* 트위터 */}
+
+
+ {/* 링크 복사 */}
+
+
+
+ >
+ )}
+
+ );
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index f7fa87e..0d0a747 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -13,8 +13,8 @@ const geistMono = Geist_Mono({
});
export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
+ title: "사주보기 - 무료 사주팔자, 운세, 궁합, 토정비결",
+ description: "생년월일로 무료로 사주팔자, 오늘의 운세, 궁합, 토정비결을 확인하세요. 쟁승메이드가 제공하는 정확한 사주 서비스입니다.",
};
export default function RootLayout({
@@ -23,7 +23,24 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+
+
+
+
+
diff --git a/app/result/page.tsx b/app/result/page.tsx
index 73b873c..cda1b7c 100644
--- a/app/result/page.tsx
+++ b/app/result/page.tsx
@@ -1,6 +1,8 @@
import { calculateSaju } from '@/lib/saju-calculator';
import Link from 'next/link';
import PDFButton from '../components/PDFButton';
+import ShareButtons from '../components/ShareButtons';
+import { calculateDaeun, getCurrentDaeun, getDaeunDescription } from '@/lib/daeun-calculator';
interface PageProps {
searchParams: Promise<{
@@ -24,6 +26,18 @@ export default async function ResultPage({ searchParams }: PageProps) {
const sajuData = calculateSaju(yearNum, monthNum, dayNum, hourNum, gender);
+ // 대운 계산
+ const daeunList = calculateDaeun(
+ yearNum,
+ monthNum,
+ dayNum,
+ gender,
+ sajuData.month.stem,
+ sajuData.month.branch
+ );
+ const currentYear = new Date().getFullYear();
+ const currentDaeun = getCurrentDaeun(daeunList, currentYear);
+
return (
{/* Navigation */}
@@ -213,8 +227,85 @@ export default async function ResultPage({ searchParams }: PageProps) {
+ {/* 대운 (大運) */}
+
+
+ 🔄 대운 (大運) - 10년 주기 운세
+
+
+ {/* 현재 대운 */}
+ {currentDaeun && (
+
+
현재 대운
+
+
+ {currentDaeun.stem}{currentDaeun.branch}
+
+
+ {currentDaeun.stemKr}{currentDaeun.branchKr}
+
+
+ {currentDaeun.age}세 ~ {currentDaeun.age + 9}세 ({currentDaeun.startYear}년 ~ {currentDaeun.endYear}년)
+
+
+
+ {getDaeunDescription(currentDaeun, sajuData.day.stem)}
+
+
+ )}
+
+ {/* 전체 대운 목록 */}
+
+ {daeunList.map((daeun, index) => {
+ const isCurrent = currentDaeun &&
+ daeun.startYear === currentDaeun.startYear &&
+ daeun.endYear === currentDaeun.endYear;
+
+ return (
+
+
+
+ {daeun.stem}{daeun.branch}
+
+
+ {daeun.stemKr}{daeun.branchKr}
+
+
+ {daeun.age}세 ~ {daeun.age + 9}세
+
+
+ {daeun.startYear} ~ {daeun.endYear}
+
+ {isCurrent && (
+
+
+ 현재
+
+
+ )}
+
+
+ );
+ })}
+
+
+
+
+ 대운(大運): 10년 단위로 변화하는 큰 운의 흐름입니다.
+ 각 대운마다 삶의 방향과 환경이 달라질 수 있으므로, 현재 대운의 특성을 이해하고 활용하는 것이 중요합니다.
+
+
+
+
{/* 추가 기능 버튼 */}
-
diff --git a/app/tojeong/result/page.tsx b/app/tojeong/result/page.tsx
index ce72b29..c9966d7 100644
--- a/app/tojeong/result/page.tsx
+++ b/app/tojeong/result/page.tsx
@@ -1,6 +1,7 @@
import Link from 'next/link';
import { calculateSaju } from '@/lib/saju-calculator';
import PDFButton from '../../components/PDFButton';
+import ShareButtons from '../../components/ShareButtons';
interface PageProps {
searchParams: Promise<{
@@ -284,7 +285,7 @@ export default async function TojeongResultPage({ searchParams }: PageProps) {
{/* 다른 메뉴 */}
-
diff --git a/lib/daeun-calculator.ts b/lib/daeun-calculator.ts
new file mode 100644
index 0000000..be49e45
--- /dev/null
+++ b/lib/daeun-calculator.ts
@@ -0,0 +1,135 @@
+import { HEAVENLY_STEMS, EARTHLY_BRANCHES, HEAVENLY_STEMS_KR, EARTHLY_BRANCHES_KR } from './saju-calculator';
+
+/**
+ * 대운 (大運) 정보
+ */
+export interface DaeunPillar {
+ age: number; // 시작 나이
+ startYear: number; // 시작 년도
+ endYear: number; // 끝 년도
+ stem: string; // 천간
+ branch: string; // 지지
+ stemKr: string; // 천간 한글
+ branchKr: string; // 지지 한글
+}
+
+/**
+ * 대운 계산
+ * @param birthYear 생년
+ * @param birthMonth 생월
+ * @param birthDay 생일
+ * @param gender 성별
+ * @param monthStem 월주 천간 인덱스
+ * @param monthBranch 월주 지지 인덱스
+ * @returns 대운 배열 (10년 단위)
+ */
+export function calculateDaeun(
+ birthYear: number,
+ birthMonth: number,
+ birthDay: number,
+ gender: 'male' | 'female',
+ monthStem: string,
+ monthBranch: string
+): DaeunPillar[] {
+ const monthStemIndex = HEAVENLY_STEMS.indexOf(monthStem as any);
+ const monthBranchIndex = EARTHLY_BRANCHES.indexOf(monthBranch as any);
+
+ if (monthStemIndex === -1 || monthBranchIndex === -1) {
+ return [];
+ }
+
+ // 양남음녀(陽男陰女)는 순행, 음남양녀(陰男陽女)는 역행
+ const yearStemIndex = (birthYear - 1900 + 6) % 10;
+ const isYangYear = yearStemIndex % 2 === 0; // 양년
+
+ let isForward: boolean;
+ if (gender === 'male') {
+ isForward = isYangYear; // 양남: 순행, 음남: 역행
+ } else {
+ isForward = !isYangYear; // 양녀: 역행, 음녀: 순행
+ }
+
+ // 대운 시작 나이 계산 (간단화: 평균 8세로 설정)
+ // 실제로는 절입일부터 생일까지의 일수를 계산해야 하지만 복잡하므로 단순화
+ const startAge = 8;
+
+ const daeunList: DaeunPillar[] = [];
+
+ for (let i = 0; i < 8; i++) {
+ const age = startAge + (i * 10);
+ const startYear = birthYear + age;
+ const endYear = startYear + 9;
+
+ let stemIndex: number;
+ let branchIndex: number;
+
+ if (isForward) {
+ // 순행: 월주에서 증가
+ stemIndex = (monthStemIndex + i + 1) % 10;
+ branchIndex = (monthBranchIndex + i + 1) % 12;
+ } else {
+ // 역행: 월주에서 감소
+ stemIndex = (monthStemIndex - i - 1 + 100) % 10;
+ branchIndex = (monthBranchIndex - i - 1 + 120) % 12;
+ }
+
+ daeunList.push({
+ age,
+ startYear,
+ endYear,
+ stem: HEAVENLY_STEMS[stemIndex],
+ branch: EARTHLY_BRANCHES[branchIndex],
+ stemKr: HEAVENLY_STEMS_KR[stemIndex],
+ branchKr: EARTHLY_BRANCHES_KR[branchIndex]
+ });
+ }
+
+ return daeunList;
+}
+
+/**
+ * 현재 대운 찾기
+ * @param daeunList 대운 목록
+ * @param currentYear 현재 년도
+ */
+export function getCurrentDaeun(daeunList: DaeunPillar[], currentYear: number): DaeunPillar | null {
+ for (const daeun of daeunList) {
+ if (currentYear >= daeun.startYear && currentYear <= daeun.endYear) {
+ return daeun;
+ }
+ }
+ return null;
+}
+
+/**
+ * 대운 해석
+ * @param daeun 대운 정보
+ * @param dayStem 일간
+ */
+export function getDaeunDescription(daeun: DaeunPillar, dayStem: string): string {
+ const age = daeun.age;
+ const ganzi = `${daeun.stem}${daeun.branch}`;
+
+ let description = `${age}세부터 ${age + 9}세까지의 10년은 ${daeun.stemKr}${daeun.branchKr}(${ganzi}) 대운입니다. `;
+
+ // 대운 천간과 일간의 관계에 따른 기본 해석
+ const stemIndex = HEAVENLY_STEMS.indexOf(daeun.stem as any);
+
+ if (age < 20) {
+ description += '청소년기로 학업과 기초를 다지는 시기입니다. ';
+ } else if (age < 40) {
+ description += '성장과 발전의 시기로 사회활동이 왕성한 때입니다. ';
+ } else if (age < 60) {
+ description += '안정과 성숙의 시기로 경험이 쌓이는 때입니다. ';
+ } else {
+ description += '원숙한 시기로 인생의 지혜를 나누는 때입니다. ';
+ }
+
+ if (stemIndex % 2 === 0) {
+ description += '적극적이고 외향적인 활동이 유리합니다.';
+ } else {
+ description += '차분하고 내실을 다지는 것이 좋습니다.';
+ }
+
+ return description;
+}
diff --git a/lib/lunar-utils.ts b/lib/lunar-utils.ts
new file mode 100644
index 0000000..ee9ce51
--- /dev/null
+++ b/lib/lunar-utils.ts
@@ -0,0 +1,97 @@
+/**
+ * 음력-양력 변환 유틸리티
+ */
+
+interface LunarDate {
+ year: number;
+ month: number;
+ day: number;
+ isLeap: boolean;
+}
+
+interface SolarDate {
+ year: number;
+ month: number;
+ day: number;
+}
+
+/**
+ * 음력을 양력으로 변환
+ * @param lunarYear 음력 년
+ * @param lunarMonth 음력 월
+ * @param lunarDay 음력 일
+ * @param isLeapMonth 윤달 여부
+ */
+export function lunarToSolar(
+ lunarYear: number,
+ lunarMonth: number,
+ lunarDay: number,
+ isLeapMonth: boolean = false
+): SolarDate {
+ try {
+ const lunar = require('lunar-calendar');
+ const result = lunar.lunarToSolar(lunarYear, lunarMonth, lunarDay, isLeapMonth);
+
+ return {
+ year: result.year,
+ month: result.month,
+ day: result.day
+ };
+ } catch (error) {
+ console.error('음력 변환 오류:', error);
+ // 변환 실패시 입력값 그대로 반환
+ return {
+ year: lunarYear,
+ month: lunarMonth,
+ day: lunarDay
+ };
+ }
+}
+
+/**
+ * 양력을 음력으로 변환
+ * @param solarYear 양력 년
+ * @param solarMonth 양력 월
+ * @param solarDay 양력 일
+ */
+export function solarToLunar(
+ solarYear: number,
+ solarMonth: number,
+ solarDay: number
+): LunarDate {
+ try {
+ const lunar = require('lunar-calendar');
+ const result = lunar.solarToLunar(solarYear, solarMonth, solarDay);
+
+ return {
+ year: result.year,
+ month: result.month,
+ day: result.day,
+ isLeap: result.isLeap || false
+ };
+ } catch (error) {
+ console.error('양력 변환 오류:', error);
+ // 변환 실패시 입력값 그대로 반환
+ return {
+ year: solarYear,
+ month: solarMonth,
+ day: solarDay,
+ isLeap: false
+ };
+ }
+}
+
+/**
+ * 음력 날짜를 문자열로 변환
+ */
+export function formatLunarDate(lunar: LunarDate): string {
+ const leapText = lunar.isLeap ? '윤' : '';
+ return `음력 ${lunar.year}년 ${leapText}${lunar.month}월 ${lunar.day}일`;
+}
+
+/**
+ * 양력 날짜를 문자열로 변환
+ */
+export function formatSolarDate(solar: SolarDate): string {
+ return `양력 ${solar.year}년 ${solar.month}월 ${solar.day}일`;
+}
diff --git a/package-lock.json b/package-lock.json
index 5fe7912..225bb50 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"html2canvas": "^1.4.1",
"jspdf": "^4.1.0",
+ "lunar-calendar": "^0.1.4",
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3"
@@ -4977,6 +4978,14 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lunar-calendar": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/lunar-calendar/-/lunar-calendar-0.1.4.tgz",
+ "integrity": "sha512-5r87vbg5yg56z/jkf3A+Ur+ZggUTiJw1VATT9P7RELQgWcTNhfJ+OLkNYroSna6r65bMqyaAgapo9vRN40L75A==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
diff --git a/package.json b/package.json
index 03899ad..fd5f774 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"html2canvas": "^1.4.1",
"jspdf": "^4.1.0",
+ "lunar-calendar": "^0.1.4",
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3"