import React, { useState, useEffect, useRef } from 'react';
import { PiggyBank, Utensils, ArrowLeft, RefreshCw, Wallet, ChefHat } from 'lucide-react';
// --- API 설정 및 헬퍼 함수 ---
const apiKey = ""; // 실행 환경에서 주입됨
const generateContent = async (prompt, systemInstruction) => {
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`;
const payload = {
contents: [{ parts: [{ text: prompt }] }],
systemInstruction: { parts: [{ text: systemInstruction }] },
};
const attemptFetch = async (retries = 5, delay = 1000) => {
try {
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
return data.candidates?.[0]?.content?.parts?.[0]?.text || "결과를 불러올 수 없습니다.";
} catch (error) {
if (retries === 0) throw error;
await new Promise((resolve) => setTimeout(resolve, delay));
return attemptFetch(retries - 1, delay * 2);
}
};
return attemptFetch();
};
// --- 랜덤 저축 멘트 리스트 ---
const quotes = [
"딱 {amt}원만 넣어볼까요? 습관적으로 마시는 카페 라떼 한 잔 대신 시원한 물 한 잔으로 건강과 돈을 모두 챙겨봐요.",
"오늘의 저축액은 {amt}원. 무심코 집어 들던 마이쮸나 젤리 하나만 포기해도 당신의 통장은 웃게 됩니다. 티끌모아 태산!",
"{amt}원 저축 도전! 배달 음식의 '배달 팁'만 아껴도 이만큼이 모여요. 오늘은 직접 픽업하러 가는 건 어떨까요?",
"오늘은 {amt}원을 파킹통장에 넣어보세요. 다이소에서 '그냥 예뻐서' 샀던 소품들이 사실은 통장 구멍의 주범일지 몰라요.",
"{amt}원 저축! 가까운 거리는 버스 대신 걸으면서 '공짜 유산소 운동'을 했다고 생각하면 기분이 더 좋아질 거예요.",
"오늘은 {amt}원 세이브. 세일한다고 산 '한 철용' 저가 의류보다, 차곡차곡 모인 이 자본이 당신의 미래를 더 빛나게 할 거예요.",
"강의실 가기 전 {amt}원 저축! 학교 자판기 음료수 대신 텀블러를 챙기는 것만으로도 '갓생'에 한 걸음 더 다가갈 수 있어요.",
"오늘은 {amt}원 저축 어때요? 고칼로리 편의점 도시락보다 가벼운 식단으로 바디 관리와 자산 관리를 동시에 시작하세요.",
"딱 {amt}원만 파킹통장으로! 스마트폰 케이스를 바꿀까 고민하던 그 마음을 숫자로 바꿔보세요.",
"오늘의 미션: {amt}원 저축. 야식 한 번만 참아도 내일 아침의 몸무게와 통장 잔고가 모두 가벼워질 거예요.",
"{amt}원 저축하세요! 유행하는 두쫀쿠 대신 건강한 차 한잔으로 타협해 보는 건 어떨까요?",
"{amt}원 저축! 혹시 보지도 않으면서 매달 결제되는 구독 서비스가 있진 않나요? 지금 정리하면 매달 이만큼이 공짜로 생겨요.",
"오늘은 {amt}원을 넣어보세요. 비 올 때 무심코 샀던 편의점 간식 값이에요.",
"{amt}원 저축 도전! 이미 화장대에 가득한 비슷한 색깔의 립밤이나 화장품, 더 늘리지 않아도 충분히 아름다워요."
];
const getSavingsQuote = (amount) => {
if (amount <= 5000 && Math.random() < 0.3) {
return `오늘은 ${amount.toLocaleString()}원을 저축해 보세요! 편의점 음료수의 유혹만 참아도 충분히 가능한 금액이에요.`;
}
const q = quotes[Math.floor(Math.random() * quotes.length)];
return q.replace(/{amt}/g, amount.toLocaleString());
};
// --- 컴포넌트 ---
export default function App() {
const [view, setView] = useState('home'); // 'home', 'savings', 'recipe'
const [maxAmount, setMaxAmount] = useState(5000);
const [savedAmount, setSavedAmount] = useState(null);
const [quote, setQuote] = useState(null);
const [ingredients, setIngredients] = useState("");
const [recipeResult, setRecipeResult] = useState(null);
const [loading, setLoading] = useState(false);
// 로딩 초기화 및 뷰 전환 헬퍼
const navigateTo = (target) => {
setView(target);
setLoading(false);
setSavedAmount(null);
setQuote(null);
setRecipeResult(null);
};
const handleRandomSave = () => {
setLoading(true);
setSavedAmount(null);
// 로딩 물채우기 애니메이션을 보여주기 위한 딜레이 (2.5초)
setTimeout(() => {
const minAmount = 500;
// 100원 단위로 랜덤 추출
const steps = Math.floor((maxAmount - minAmount) / 100);
const randomStep = Math.floor(Math.random() * (steps + 1));
const amount = minAmount + (randomStep * 100);
setSavedAmount(amount);
setQuote(getSavingsQuote(amount));
setLoading(false);
}, 2500);
};
const handleRecipeRecommend = async () => {
if (!ingredients.trim()) return;
setLoading(true);
setRecipeResult(null);
const systemInstruction = `너는 사용자의 냉장고 속에 남은 식재료를 분석하여 가장 현실적이고 맛있는 식단을 제안하는 '스마트 홈셰프'야. 사용자의 목표는 남은 재료를 버리지 않고 활용하는 것이며, 요리 과정은 최대한 간단해야 해.
[핵심 원칙]
1. 선택적 조합: 입력된 모든 재료를 한꺼번에 다 쓸 필요는 없어. 궁합이 맞는 재료끼리 묶어서 1~3가지의 서로 다른 식단 옵션을 제안해줘.
2. 기본 양념 허용: 간장, 설탕, 소금, 식용유, 마늘, 고추장 등 일반적인 가정집에 있을 법한 기본 양념은 재료 목록에 없어도 사용하는 것으로 간주해.
3. 난이도 조절: 1인 가구나 대학생도 쉽게 따라 할 수 있는 간단한 조리법 위주로 생각할 것.
4. 설명 추가: 단순히 메뉴 이름만 말하지 말고, 왜 그 재료들을 선택했는지 한 줄 설명을 덧붙여줘.
[입출력 예시]
입력: [두부, 김치, 스팸, 양파]
추천 1: 스팸 김치 짜글이 (스팸의 짭짤함과 김치의 산미가 어우러져 밥 한 공기 뚝딱 할 수 있는 최고의 메뉴예요.)
추천 2: 두부 김치 두루치기 (두부를 데치고 볶은 김치를 곁들이면 건강하면서도 든든한 한 끼가 됩니다.)
입력: [토마토, 계란, 낙지, 쭈꾸미, 고추, 칼국수면]
추천 1: 토마토 달걀 볶음 (토달볶) (재료가 없을 때 가장 가볍고 영양가 있게 즐길 수 있는 중식 스타일의 한 끼예요.)
추천 2: 매콤 쭈꾸미 비빔 칼국수 (쭈꾸미와 고추를 볶아 매콤한 양념을 만든 뒤, 삶은 칼국수 면을 비벼 먹으면 별미예요.)
추천 3: 낙지 연포탕 스타일 칼국수 (낙지와 고추를 넣어 시원하고 칼칼한 국물을 낸 뒤 칼국수 면을 넣어 끓여보세요.)`;
const prompt = `냉장고 재료: [${ingredients}]\n이 재료들로 만들 수 있는 간단하고 맛있는 무지출 식단을 추천해줘.`;
try {
const result = await generateContent(prompt, systemInstruction);
setRecipeResult(result);
} catch (error) {
setRecipeResult("오류가 발생했습니다. 잠시 후 다시 시도해주세요.");
} finally {
setLoading(false);
}
};
return (
{/* CSS Styles for Water Loading Animation and Custom Slider */}
{/* Header */}
{/* --- HOME VIEW --- */}
{view === 'home' && (
navigateTo('savings')}>
랜덤 저축 챌린지
"오늘은 커피 대신 파킹통장으로!"
소소한 충동소비를 막아주는 자동 저축 퀘스트
navigateTo('recipe')}>
무지출 데이 식단 추천
외식이나 배달은 그만!
냉장고 속 재료로 뚝딱 만드는 스마트 홈셰프
)}
{/* --- SAVINGS VIEW --- */}
{view === 'savings' && (
랜덤 저축 챌린지
{!loading && !savedAmount && (
최대 저축 금액 한도 설정
{maxAmount.toLocaleString()}원
setMaxAmount(Number(e.target.value))}
className="range-slider"
/>
500원
10,000원
)}
{loading && (
)}
{savedAmount && !loading && (
오늘의 짠테크 미션 완료!
{savedAmount.toLocaleString()}원
)}
)}
{/* --- RECIPE VIEW --- */}
{view === 'recipe' && (
냉파 홈셰프
{!loading && !recipeResult && (
)}
{loading && (
)}
{recipeResult && !loading && (
)}
)}
);
}