Files
saju-web/app/components/ShareButtons.tsx
gahusb d513c063cf feat: 절기 정밀화, AI 해석 추가, 공유 기능 개선
절기 날짜 정밀화:
- solarlunar 라이브러리 추가
- 천문학적 계산 기반 정확한 절기 날짜 계산
- 각 절기별 정확한 날짜 범위 검색
- 폴백 메커니즘으로 안정성 확보

AI 상세 해석 시스템:
- ai-interpretation.ts 라이브러리 생성
- 일간 기반 성격 분석 (10개 천간별 상세 해석)
- 오행 균형 분석 및 점수 계산
- 십성 기반 다차원 분석
  - 직업 운세 (오행 + 십성 조합)
  - 대인 관계 (십성 기반)
  - 재물 운세 (정재/편재 분석)
  - 건강 운세 (오행 균형)
- 맞춤형 조언 생성

결과 페이지 AI 해석 섹션:
- 오행 균형 시각화 (막대 그래프)
- 장점/주의할 점 구분 표시
- 4가지 운세 카드 (직업/대인/재물/건강)
- AI 조언 그리드 레이아웃
- 전문가 상담 권장 안내

공유 기능 개선:
- localhost 감지 로직 추가
- localhost인 경우:
  - 카카오톡: 텍스트 형식으로 사주 정보 공유
  - 링크 복사: 사주 정보 텍스트 복사
  - 사용자에게 개발 환경임을 안내
- 배포 환경: 기존대로 URL 공유
- 더 나은 사용자 경험 제공

기술 개선:
- solarlunar 라이브러리 (정밀 절기 계산)
- 타입 안전성 강화
- 모듈화된 해석 로직
- 성능 최적화

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 00:26:12 +09:00

192 lines
6.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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 : '');
// localhost 체크
const isLocalhost = shareUrl.includes('localhost') || shareUrl.includes('127.0.0.1');
const handleKakaoShare = () => {
if (isLocalhost) {
// localhost인 경우 텍스트로 공유
const shareText = `${title}\n\n${description}\n\n🔮 사주보기 - 쟁승메이드\n(배포 후 링크가 제공됩니다)`;
if (typeof window !== 'undefined' && (window as any).Kakao) {
(window as any).Kakao.Share.sendDefault({
objectType: 'text',
text: shareText,
link: {
mobileWebUrl: 'https://jaengseung-made.com',
webUrl: 'https://jaengseung-made.com',
},
});
} else {
alert('카카오톡 공유 기능을 사용할 수 없습니다.');
}
} else {
// 배포된 URL인 경우 정상 공유
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 {
if (isLocalhost) {
// localhost인 경우 텍스트 정보 복사
const shareText = `${title}\n\n${description}\n\n🔮 사주보기 - 쟁승메이드\n(배포 후 링크가 제공됩니다)`;
await navigator.clipboard.writeText(shareText);
alert('사주 정보가 복사되었습니다!\n(개발 환경이므로 URL 대신 텍스트 정보가 복사됩니다)');
} else {
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 (
<div className="relative">
<button
onClick={handleNativeShare}
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group w-full"
>
<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>
</button>
{/* 공유 메뉴 (모바일에서 네이티브 공유가 안 될 때) */}
{showShareMenu && (
<>
{/* 배경 오버레이 */}
<div
className="fixed inset-0 bg-black bg-opacity-50 z-40"
onClick={() => setShowShareMenu(false)}
></div>
{/* 공유 메뉴 */}
<div className="fixed bottom-0 left-0 right-0 bg-white rounded-t-3xl shadow-2xl z-50 p-6 animate-slide-up">
<div className="flex justify-between items-center mb-6">
<h3 className="text-xl font-bold text-gray-900"></h3>
<button
onClick={() => setShowShareMenu(false)}
className="text-gray-500 hover:text-gray-700 text-2xl"
>
</button>
</div>
<div className="grid grid-cols-4 gap-4 mb-4">
{/* 카카오톡 */}
<button
onClick={handleKakaoShare}
className="flex flex-col items-center gap-2 p-3 rounded-xl hover:bg-gray-50 transition"
>
<div className="w-12 h-12 bg-yellow-400 rounded-full flex items-center justify-center text-2xl">
💬
</div>
<span className="text-xs text-gray-700"></span>
</button>
{/* 페이스북 */}
<button
onClick={handleFacebookShare}
className="flex flex-col items-center gap-2 p-3 rounded-xl hover:bg-gray-50 transition"
>
<div className="w-12 h-12 bg-blue-600 rounded-full flex items-center justify-center text-2xl text-white">
f
</div>
<span className="text-xs text-gray-700"></span>
</button>
{/* 트위터 */}
<button
onClick={handleTwitterShare}
className="flex flex-col items-center gap-2 p-3 rounded-xl hover:bg-gray-50 transition"
>
<div className="w-12 h-12 bg-blue-400 rounded-full flex items-center justify-center text-2xl text-white">
𝕏
</div>
<span className="text-xs text-gray-700"></span>
</button>
{/* 링크 복사 */}
<button
onClick={handleCopyLink}
className="flex flex-col items-center gap-2 p-3 rounded-xl hover:bg-gray-50 transition"
>
<div className="w-12 h-12 bg-gray-600 rounded-full flex items-center justify-center text-2xl text-white">
🔗
</div>
<span className="text-xs text-gray-700"> </span>
</button>
</div>
</div>
</>
)}
</div>
);
}