From a273423b3f763725515c8b852cb5b3746e3dda14 Mon Sep 17 00:00:00 2001 From: zzzryt Date: Fri, 5 Dec 2025 18:09:58 +0900 Subject: [PATCH 1/3] =?UTF-8?q?(#195):=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=ED=86=A1=20=EA=B3=B5=EC=9C=A0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/shared/lib/useKakaoShare.ts | 70 +++++++++++++++++++ .../shared/ui/SharedButtonContainer.tsx | 25 +++++++ src/features/tourList/ui/TourInfoCard.tsx | 26 +++---- 3 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 src/features/shared/lib/useKakaoShare.ts create mode 100644 src/features/shared/ui/SharedButtonContainer.tsx diff --git a/src/features/shared/lib/useKakaoShare.ts b/src/features/shared/lib/useKakaoShare.ts new file mode 100644 index 0000000..a6ca628 --- /dev/null +++ b/src/features/shared/lib/useKakaoShare.ts @@ -0,0 +1,70 @@ +import { tourQueries } from '@/entities/tour'; +import { useQuery } from '@tanstack/react-query'; +import { useEffect } from 'react'; + +declare global { + interface Window { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Kakao: any; + } +} + +interface UseKakaoShareParams { + contentId: string; + link: string; +} + +const useKakaoShare = ({ contentId, link }: UseKakaoShareParams) => { + const { data } = useQuery({ + ...tourQueries.detailCommon(contentId), + }); + + useEffect(() => { + // SDK 로드 + const script = document.createElement('script'); + script.src = 'https://t1.kakaocdn.net/kakao_js_sdk/2.7.0/kakao.min.js'; + script.async = true; + document.body.appendChild(script); + + return () => { + document.body.removeChild(script); + }; + }, []); + + const shareKakao = () => { + if (window.Kakao) { + const kakao = window.Kakao; + if (!kakao.isInitialized()) { + kakao.init(import.meta.env.VITE_KAKAO_MAP_KEY); + } + window.Kakao.Share.sendDefault({ + objectType: 'feed', + content: { + title: data?.title ?? '여행지 추천', + description: data?.overview ?? '멋진 여행지를 확인해보세요!', + imageUrl: + data?.firstimage || + data?.firstimage2 || + 'https://developers.kakao.com/assets/img/about/logos/kakaolink/kakaolink_btn_medium.png', + link: { + mobileWebUrl: link, + webUrl: link, + }, + }, + buttons: [ + { + title: '여행 시작하기', + link: { + mobileWebUrl: link, + webUrl: link, + }, + }, + ], + }); + } + }; + + return { shareKakao }; +}; + +export default useKakaoShare; diff --git a/src/features/shared/ui/SharedButtonContainer.tsx b/src/features/shared/ui/SharedButtonContainer.tsx new file mode 100644 index 0000000..9901c42 --- /dev/null +++ b/src/features/shared/ui/SharedButtonContainer.tsx @@ -0,0 +1,25 @@ +/* eslint-disable */ +import { commonSVG } from '@/assets'; +import useKakaoShare from '../lib/useKakaoShare'; + +interface SharedButtonContainerProps { + contentId: string; +} + +export default function SharedButtonContainer({ + contentId, +}: SharedButtonContainerProps) { + const link = window.location.href; + const { shareKakao } = useKakaoShare({ contentId, link }); + + return ( + + ); +} diff --git a/src/features/tourList/ui/TourInfoCard.tsx b/src/features/tourList/ui/TourInfoCard.tsx index b02804f..219b261 100644 --- a/src/features/tourList/ui/TourInfoCard.tsx +++ b/src/features/tourList/ui/TourInfoCard.tsx @@ -1,5 +1,4 @@ import { Suspense } from 'react'; -import { commonSVG } from '@/assets'; import { TourCardImages } from '@/features/tourList'; import { SkeletonCard, StartTripButton } from '@/features/tour'; @@ -13,6 +12,7 @@ import { } from '@/shared'; import type { TourItem } from '@/entities/tour'; +import SharedButtonContainer from '@/features/shared/ui/SharedButtonContainer'; interface TourInfoCardProps { tourInfo: TourItem; } @@ -40,9 +40,7 @@ export default function TourInfoCard({ tourInfo }: TourInfoCardProps) {