diff --git a/.env b/.env index b6734a4..0d0fce6 100644 --- a/.env +++ b/.env @@ -1,2 +1 @@ -VITE_NCP_API_KEY_ID=pvngw60boi -VITE_NCP_API_KEY=pQerQ8RH43mXRUitA0yZS0fv8az2y3AR2NAJ1GR3 +VITE_KAKAO_REST_API_KEY=f5171fb698d216fdffae9815161287c6 \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..0d0fce6 --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +VITE_KAKAO_REST_API_KEY=f5171fb698d216fdffae9815161287c6 \ No newline at end of file diff --git a/index.html b/index.html index ff23cde..9de86df 100644 --- a/index.html +++ b/index.html @@ -5,21 +5,20 @@ 산재보험 가이드 - - -
- + + - + + + +
+ - \ No newline at end of file + diff --git a/package.json b/package.json index 18c6f4d..bed81b4 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "@svgr/rollup": "^8.1.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", - "@types/navermaps": "^3.9.1", "@types/node": "^24.8.1", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", diff --git a/src/App.tsx b/src/App.tsx index 8938227..7c553c8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,8 @@ import "./App.css"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import { FormDataProvider } from "./contexts/FormDataContext"; import { LocalizationProvider } from "./contexts/LocalizationContext"; +import usePageTracking from "./hooks/usePageTracking"; + import HomePage from "./pages/HomePage/HomePage"; import CasePage from "./pages/CasePage/CasePage"; import CaseDetailPage from "./pages/CasePage/CaseDetailPage"; @@ -33,12 +35,17 @@ import SplashPage from "./pages/SplashPage/SplashPage"; // import EditPage from "./pages/MyPage/EditPage"; // import DataPage from "./pages/MyPage/DataPage"; // import LoginPage from "./pages/LoginPage/LoginPage"; +function PageTrackerWrapper() { + usePageTracking(); // Router 내부에서 호출됨 + return null; +} function App() { return ( + } /> } /> diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..bc39f99 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,8 @@ +//usePageTracking.ts 고려 +declare global { + interface Window { + gtag?: (...args: any[]) => void; + } +} + +export {}; \ No newline at end of file diff --git a/src/hooks/usePageTracking.ts b/src/hooks/usePageTracking.ts new file mode 100644 index 0000000..97665bb --- /dev/null +++ b/src/hooks/usePageTracking.ts @@ -0,0 +1,15 @@ +//SPA 페이지 이동 추적 코드 +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +export default function usePageTracking() { + const location = useLocation(); + + useEffect(() => { + if (typeof window.gtag !== "undefined") { + window.gtag("event", "page_view", { + page_path: location.pathname + location.search, + }); + } + }, [location]); +} \ No newline at end of file diff --git a/src/pages/Medi-carePage/Medi-address-1.tsx b/src/pages/Medi-carePage/Medi-address-1.tsx index 9a222f1..1ffc5f2 100644 --- a/src/pages/Medi-carePage/Medi-address-1.tsx +++ b/src/pages/Medi-carePage/Medi-address-1.tsx @@ -5,31 +5,34 @@ import "./Medi-address-1.css"; import { useState, useCallback } from "react"; import { useNavigate, useLocation } from "react-router-dom"; -// 네이버 지오코딩 API 응답 타입 정의 +// 카카오 주소 검색 응답 타입 interface Address { roadAddress: string; jibunAddress: string; - englishAddress: string; + englishAddress?: string; } interface GeocodeResponse { - status: string; - meta: { - totalCount: number; - page: number; - count: number; - }; - addresses: Address[]; + documents: { + address_name: string; + road_address: { + address_name: string; + } | null; + address: { + address_name: string; + } | null; + }[]; } const MediAddress1 = () => { const navigate = useNavigate(); const location = useLocation(); const { t } = useLocalization(); - const { returnPath, returnToStep, fieldName } = location.state || { - returnPath: '/medicare-guide-flow', + + const { returnPath, returnToStep, fieldName } = location.state || { + returnPath: "/medicare-guide-flow", returnToStep: 1, - fieldName: 'address' + fieldName: "address", }; const [searchQuery, setSearchQuery] = useState(""); @@ -38,7 +41,7 @@ const MediAddress1 = () => { const [isAddressConfirmed, setIsAddressConfirmed] = useState(false); const [detailAddress, setDetailAddress] = useState(""); - // API 호출 함수 + // 카카오 지오코딩 const searchAddress = useCallback(async (query: string) => { if (!query.trim()) { setAddresses([]); @@ -46,49 +49,58 @@ const MediAddress1 = () => { } try { - // 프록시 경로로 변경 const response = await fetch( - `/api/geocode?query=${encodeURIComponent(query)}`, + `https://dapi.kakao.com/v2/local/search/address.json?query=${encodeURIComponent( + query + )}`, { - method: 'GET', + method: "GET", headers: { - 'Accept': 'application/json' - } + Authorization: `KakaoAK ${ + import.meta.env.VITE_KAKAO_REST_API_KEY + }`, + }, } ); const data: GeocodeResponse = await response.json(); - - if (data.status === 'OK' && data.addresses) { - setAddresses(data.addresses); + + if (data.documents && data.documents.length > 0) { + const parsed = data.documents.map((doc) => ({ + roadAddress: doc.road_address?.address_name ?? "", + jibunAddress: doc.address?.address_name ?? "", + englishAddress: "", + })); + + setAddresses(parsed); } else { setAddresses([]); } } catch (error) { - console.error('주소 검색 실패:', error); + console.error("주소 검색 실패:", error); setAddresses([]); } }, []); - // 입력 핸들러 (디바운싱 적용) + // 입력 디바운스 + let debounceTimer: number | undefined; const handleInputChange = (e: React.ChangeEvent) => { const value = e.target.value; setSearchQuery(value); - - // 간단한 디바운싱 (500ms 후 검색) - const timeoutId = setTimeout(() => { + + if (debounceTimer) clearTimeout(debounceTimer); + + debounceTimer = window.setTimeout(() => { searchAddress(value); }, 500); - - return () => clearTimeout(timeoutId); }; - // 주소 선택 핸들러 + // 주소 선택 const handleSelectAddress = (address: Address) => { setSelectedAddress(address); }; - // 주소 입력칸 클릭 핸들러 (주소 확정 후 다시 검색) + // 주소 확정 후 다시 클릭하면 초기화 const handleAddressInputClick = () => { if (isAddressConfirmed) { setIsAddressConfirmed(false); @@ -98,93 +110,102 @@ const MediAddress1 = () => { } }; + // 다음 버튼 const handleNext = () => { if (!isAddressConfirmed && selectedAddress) { - // 첫 번째 확인: 주소 확정 setSearchQuery(selectedAddress.roadAddress); setAddresses([]); setIsAddressConfirmed(true); } else if (isAddressConfirmed) { - // 두 번째 확인: 최종 완료 - 원래 페이지로 돌아가기 - const fullAddress = detailAddress + const fullAddress = detailAddress ? `${searchQuery} ${detailAddress}` : searchQuery; - + navigate(returnPath, { state: { [fieldName]: fullAddress, - returnToStep: returnToStep // step 정보 전달 - } + returnToStep: returnToStep, + }, }); } }; const handleBack = () => { - // 뒤로가기 로직 - 원래 페이지로 돌아가기 navigate(returnPath); }; - // 상세 주소 입력 핸들러 - const handleDetailAddressChange = (e: React.ChangeEvent) => { + const handleDetailAddressChange = ( + e: React.ChangeEvent + ) => { setDetailAddress(e.target.value); }; return (
-
+
+
-

{t('applyEnterAddr')}

+

{t("applyEnterAddr")}

+
-
- - {/* 주소 검색 결과 - 주소 확정 전에만 표시 */} + + {/* 검색 결과 */} {!isAddressConfirmed && (
{addresses.map((address, index) => ( -
handleSelectAddress(address)} > -
{address.roadAddress}
-
{address.jibunAddress}
+
+ {address.roadAddress} +
+
+ {address.jibunAddress} +
))}
)} - {/* 상세 주소 입력 - 주소 확정 후에만 표시 */} + {/* 상세 주소 입력 */} {isAddressConfirmed && (
-
)}
+
- +
); }; -export default MediAddress1; \ No newline at end of file +export default MediAddress1;