Tap the button to start your random journey
{selectedRegion.name} 여행 코스
상세 일정
{item.plan}
import React, { useState, useEffect, useRef } from 'react'; import { MapPin, Notebook, Save, X, Calendar, Wallet, Navigation, Trash2, Target, Plus, Camera, Image as ImageIcon, Cloud, Sun, CloudRain, Thermometer } from 'lucide-react'; const App = () => { // 앱 상태 관리 const [budget, setBudget] = useState(300000); const [duration, setDuration] = useState(2); const [isThrowing, setIsThrowing] = useState(false); const [isImpact, setIsImpact] = useState(false); const [dartPath, setDartPath] = useState({ x: 50, y: 50 }); const [selectedRegion, setSelectedRegion] = useState(null); const [showRecommendation, setShowRecommendation] = useState(false); const [showDiary, setShowDiary] = useState(false); const [diaries, setDiaries] = useState([]); const [newDiary, setNewDiary] = useState({ title: '', date: '', budget: '', content: '', emotion: '😊', image: null }); const [weather, setWeather] = useState(null); const [detailedSchedule, setDetailedSchedule] = useState([]); const [isLoadingInfo, setIsLoadingInfo] = useState(false); const fileInputRef = useRef(null); const resultRef = useRef(null); const apiKey = ""; // API Key const colors = { primary: '#7CC1E7', secondary: '#ACAD79', background: '#FFFFFF', text: '#333333' }; const regions = [ { name: '수도권', x: 42, y: 25 }, { name: '강원도', x: 68, y: 20 }, { name: '충청북도', x: 58, y: 40 }, { name: '충청남도', x: 38, y: 48 }, { name: '경상북도', x: 74, y: 52 }, { name: '경상남도', x: 64, y: 78 }, { name: '전라북도', x: 42, y: 68 }, { name: '전라남도', x: 36, y: 92 }, { name: '제주도', x: 38, y: 122 }, ]; const fetchWithRetry = async (url, options, retries = 5, backoff = 1000) => { try { const res = await fetch(url, options); if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`); return await res.json(); } catch (error) { if (retries > 0) { await new Promise(resolve => setTimeout(resolve, backoff)); return fetchWithRetry(url, options, retries - 1, backoff * 2); } throw error; } }; const fetchTravelInfo = async (regionName, days) => { setIsLoadingInfo(true); setWeather("정보 확인 중..."); setDetailedSchedule([]); try { const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`; const combinedPrompt = `${regionName}의 현재 날씨와 ${days}일간의 상세 여행 일정을 알려줘. JSON 형식: { "weather": "상태/기온", "schedule": [{"day": 1, "plan": "내용"}] }`; const result = await fetchWithRetry(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: combinedPrompt }] }], tools: [{ "google_search": {} }], generationConfig: { responseMimeType: "application/json", responseSchema: { type: "OBJECT", properties: { weather: { type: "STRING" }, schedule: { type: "ARRAY", items: { type: "OBJECT", properties: { day: { type: "NUMBER" }, plan: { type: "STRING" } }, required: ["day", "plan"] } } }, required: ["weather", "schedule"] } } }) }); if (result.candidates?.[0]?.content?.parts?.[0]?.text) { const parsedData = JSON.parse(result.candidates[0].content.parts[0].text); setWeather(parsedData.weather); setDetailedSchedule(parsedData.schedule); } } catch (error) { setWeather("정보를 불러올 수 없습니다."); setDetailedSchedule([{ day: 1, plan: "오류가 발생했습니다." }]); } finally { setIsLoadingInfo(false); } }; const handleThrowDart = () => { if (isThrowing) return; setIsThrowing(true); setIsImpact(false); setSelectedRegion(null); setShowRecommendation(false); const randomIndex = Math.floor(Math.random() * regions.length); const target = regions[randomIndex]; setDartPath({ x: target.x, y: target.y }); setTimeout(() => { setIsImpact(true); setTimeout(() => setIsImpact(false), 150); setSelectedRegion(target); setIsThrowing(false); setShowRecommendation(true); fetchTravelInfo(target.name, duration); // 결과창으로 부드럽게 스크롤 setTimeout(() => { resultRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 100); }, 800); }; const handleImageUpload = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => setNewDiary({ ...newDiary, image: reader.result }); reader.readAsDataURL(file); } }; const saveDiary = () => { if (!newDiary.title || !newDiary.date) return; setDiaries([{ ...newDiary, id: Date.now() }, ...diaries]); setNewDiary({ title: '', date: '', budget: '', content: '', emotion: '😊', image: null }); setShowDiary(false); }; return (
Tap the button to start your random journey
{item.plan}