import React, { useState, useMemo, useEffect } from 'react'; // Mock Icons using simple SVG or text for lucide-react (since we can't import it) const Check = () => ; const Info = () => ; const Send = () => ; const Home = () => // FIX: Closing tag was incorrect. Changed to const Settings = () => const Package = () => const CalculatorIcon = () => ; // --- UI Component Mocks for Single File --- const Card = ({ children, className = '' }) => (
{children}
); const Label = ({ children, htmlFor, className = '' }) => ( ); const Input = ({ type = 'text', value, onChange, id, className = '', ...props }) => ( ); const Button = ({ children, onClick, className = '', disabled = false }) => ( ); const Checkbox = ({ id, checked, onCheckedChange }) => ( onCheckedChange(e.target.checked)} className="w-5 h-5 text-blue-600 bg-gray-100 border-gray-300 rounded-md focus:ring-blue-500 transition duration-150 transform scale-100 hover:scale-105" /> ); const Select = ({ value, onValueChange, placeholder, children }) => (
); const SelectItem = ({ value, children, disabled = false }) => ( ); const Modal = ({ isOpen, onClose, title, children }) => { if (!isOpen) return null; return (

{title}

{children}
); }; // --- End UI Component Mocks --- // Utility function to format number to Korean currency style const formatCurrency = (amount) => { if (amount === null || isNaN(amount)) return '0 원'; return `${Math.round(amount).toLocaleString('ko-KR')}`; }; // Main Application Component export default function App() { // 견적 관련 상태 const [area, setArea] = useState(30); // 면적 (평) const [structure, setStructure] = useState("경량목구조"); // 구조 방식 const [finish, setFinish] = useState("기본형"); // 마감 등급 const [performance, setPerformance] = useState("저에너지 하우스"); // 성능 수준 const [exterior, setExterior] = useState("세라믹사이딩"); // 외장 마감 const [roof, setRoof] = useState("아스팔트슁글"); // 지붕 마감 const [ventilation, setVentilation] = useState("국산 열회수환기장치"); // 환기 장치 const [insulation, setInsulation] = useState("글라스울"); // 단열재 const [wallFinish, setWallFinish] = useState("벽지"); // 내부 벽 마감 const [floorFinish, setFloorFinish] = useState("강마루"); // 내부 바닥 마감 const [shadingSystem, setShadingSystem] = useState(""); // 전동 외부 차양 시스템 const [options, setOptions] = useState({ attic: false }); // 다락방 const [optionalItems, setOptionalItems] = useState({ pointWall: false, openCeiling: false }); // 특화 옵션 // 상담 신청 관련 상태 const [clientName, setClientName] = useState(''); const [clientPhone, setClientPhone] = useState(''); const [clientEmail, setClientEmail] = useState(''); const [isModalOpen, setIsModalOpen] = useState(false); const [modalContent, setModalContent] = useState(''); const [phoneError, setPhoneError] = useState(''); // 구조 방식 변경 핸들러: 단열재 및 성능 수준 연동 로직 포함 const handleStructureChange = (val) => { setStructure(val); // 단열재 옵션 강제 재설정 if (val === "철근콘크리트") { setInsulation("EPS"); } else if (val === "중목구조" || val === "경량목구조") { setInsulation("글라스울"); } }; // 성능 수준 변경 핸들러: 차양 시스템 초기화 const handlePerformanceChange = (val) => { setPerformance(val); if (val !== "패시브인증주택") { setShadingSystem(""); } } // 전화번호 입력 시 숫자만 허용하도록 유효성 검사 useEffect(() => { const cleanedPhone = clientPhone.replace(/[^0-9]/g, ''); if (clientPhone && clientPhone !== cleanedPhone) { setPhoneError('숫자만 입력해 주세요.'); } else { setPhoneError(''); } }, [clientPhone]); // --- 핵심 견적 계산 로직 (상세 Breakdown 반환) --- const detailedEstimate = useMemo(() => { if (area <= 0 || isNaN(area)) { return { totalCost: null, costPerPyeong: null, breakdown: [] }; } let totalCost = 0; const roofAreaPyeong = area * 1.2; // 지붕 면적 가정 const breakdown = []; // 1. 기본 구조 및 마감 비용 (평당 가격 기준, 단위: 원) let basePricePerPyeong = 0; let baseCostDetails = []; switch (structure) { case "경량목구조": basePricePerPyeong = 7700000; break; case "중목구조": basePricePerPyeong = 8200000; break; case "철근콘크리트": basePricePerPyeong = 9000000; break; default: return { totalCost: null, costPerPyeong: null, breakdown: [] }; } // 1-1. 기본 구조 비용 const structuralBaseCost = basePricePerPyeong * area; totalCost += structuralBaseCost; baseCostDetails.push({ item: `${structure} (기본단가: ${formatCurrency(basePricePerPyeong)}/평)`, cost: structuralBaseCost }); // 1-2. 마감 등급 추가 let finishPremium = 0; if (finish === "고급형") { finishPremium = 1000000 * area; totalCost += finishPremium; baseCostDetails.push({ item: "마감 등급: 고급형 (+100만원/평)", cost: finishPremium }); } else { baseCostDetails.push({ item: "마감 등급: 기본형", cost: 0 }); } breakdown.push({ category: "1. 기본 구조 및 마감 (면적: " + area + "평)", details: baseCostDetails, subTotal: structuralBaseCost + finishPremium }); // 2. 단열 및 에너지 성능 옵션 (평당/총액) let insulationDetails = []; let insulationTotal = 0; let insulationPremiumPerPyeong = 0; switch (insulation) { case "EPS": break; // 철콘 기본 case "글라스울": break; // 목구조 기본 case "분사식 미네랄울": insulationPremiumPerPyeong = 500000; break; case "셀룰로오스": insulationPremiumPerPyeong = 1200000; break; case "경질 우레탄폼": insulationPremiumPerPyeong = 800000; break; } if (insulationPremiumPerPyeong > 0) { insulationTotal = insulationPremiumPerPyeong * area; totalCost += insulationTotal; insulationDetails.push({ item: `단열재: ${insulation} (+${formatCurrency(insulationPremiumPerPyeong)}/평)`, cost: insulationTotal }); } else { insulationDetails.push({ item: `단열재: ${insulation} (기본 사양)`, cost: 0 }); } // 환기 장치 비용 추가 let ventilationCost = 0; if (ventilation === "유럽산 열회수환기장치") ventilationCost = 3000000; if (ventilationCost > 0) { insulationTotal += ventilationCost; totalCost += ventilationCost; insulationDetails.push({ item: "열회수 환기 장치: 유럽산 (+300만원)", cost: ventilationCost }); } else { insulationDetails.push({ item: "열회수 환기 장치: 국산 (기본)", cost: 0 }); } breakdown.push({ category: "2. 단열 및 에너지 성능", details: insulationDetails, subTotal: insulationTotal }); // 3. 외부 마감 및 지붕 let exteriorDetails = []; let exteriorTotal = 0; // 3-1. 외장재 변경 비용 let exteriorPremiumPerPyeong = 0; if (exterior === "벽돌타일") exteriorPremiumPerPyeong = 700000; if (exterior === "시멘트사이딩") exteriorPremiumPerPyeong = -500000; if (exteriorPremiumPerPyeong !== 0) { const cost = exteriorPremiumPerPyeong * area; exteriorTotal += cost; totalCost += cost; exteriorDetails.push({ item: `외장재: ${exterior} (${exteriorPremiumPerPyeong > 0 ? '+' : ''}${formatCurrency(exteriorPremiumPerPyeong)}/평)`, cost: cost }); } else { exteriorDetails.push({ item: `외장재: ${exterior} (기본 사양)`, cost: 0 }); } // 3-2. 지붕 마감 변경 비용 let roofPremiumPerPyeong = 0; if (roof === "징크") roofPremiumPerPyeong = 900000; if (roof === "기와") roofPremiumPerPyeong = 600000; if (roofPremiumPerPyeong > 0) { const cost = roofPremiumPerPyeong * roofAreaPyeong; exteriorTotal += cost; totalCost += cost; exteriorDetails.push({ item: `지붕: ${roof} (+${formatCurrency(roofPremiumPerPyeong)}/지붕 평당 ${roofAreaPyeong.toFixed(1)}평)`, cost: cost }); } else { exteriorDetails.push({ item: `지붕: ${roof} (기본 사양)`, cost: 0 }); } breakdown.push({ category: "3. 외부 마감 및 지붕 (지붕면적: " + roofAreaPyeong.toFixed(1) + "평)", details: exteriorDetails, subTotal: exteriorTotal }); // 4. 내부 마감 let interiorDetails = []; let interiorTotal = 0; // 4-1. 내부 벽 마감 if (wallFinish === "도장") { const cost = 400000 * area; interiorTotal += cost; totalCost += cost; interiorDetails.push({ item: "내부 벽: 도장 마감 (+40만원/평)", cost: cost }); } else { interiorDetails.push({ item: `내부 벽: ${wallFinish} (기본)`, cost: 0 }); } // 4-2. 내부 바닥 마감 let floorPremium = 0; if (floorFinish === "원목마루") floorPremium = 700000; if (floorFinish === "타일") floorPremium = 100000; if (floorPremium > 0) { const cost = floorPremium * area; interiorTotal += cost; totalCost += cost; interiorDetails.push({ item: `내부 바닥: ${floorFinish} (+${formatCurrency(floorPremium)}/평)`, cost: cost }); } else { interiorDetails.push({ item: `내부 바닥: ${floorFinish} (기본)`, cost: 0 }); } breakdown.push({ category: "4. 내부 마감", details: interiorDetails, subTotal: interiorTotal }); // 5. 기타 공간 및 특화 옵션 (총액) let optionalDetails = []; let optionalTotal = 0; if (options.attic) { optionalTotal += 15000000; optionalDetails.push({ item: "다락방 추가", cost: 15000000 }); } if (optionalItems.pointWall) { optionalTotal += 2000000; optionalDetails.push({ item: "특화 디자인 포인트 월", cost: 2000000 }); } if (optionalItems.openCeiling) { optionalTotal += 5000000; optionalDetails.push({ item: "오픈 천장", cost: 5000000 }); } // 5-1. 차양 시스템 (패시브인증주택일 경우에만) if (performance === "패시브인증주택" && shadingSystem) { let shadingCost = 0; if (shadingSystem === "루버식 블라인드 EVB") { shadingCost = 8000000; optionalDetails.push({ item: "전동 외부 차양: 루버식 블라인드 EVB", cost: shadingCost }); } else if (shadingSystem === "롤링 셔터") { shadingCost = 10000000; optionalDetails.push({ item: "전동 외부 차양: 롤링 셔터", cost: shadingCost }); } optionalTotal += shadingCost; } totalCost += optionalTotal; if (optionalDetails.length > 0) { breakdown.push({ category: "5. 기타 공간 및 특화 옵션", details: optionalDetails, subTotal: optionalTotal }); } // 6. 성능 수준에 따른 최종 조정 (비율 증가) let performanceAdjustment = 0; if (performance === "패시브인증주택") { performanceAdjustment = totalCost * 0.08; // 8% 증가 totalCost += performanceAdjustment; breakdown.push({ category: "6. 최종 성능 수준 조정", details: [{ item: "패시브인증주택을 위한 최종 8% 조정 (고성능 창호 등)", cost: performanceAdjustment }], subTotal: performanceAdjustment }); } // 최종 금액 및 평당 단가 계산 const finalRoundedCost = Math.round(totalCost / 100000) * 100000; // 10만원 단위로 반올림 const finalCostPerPyeong = finalRoundedCost / area; // 최종 반환 객체 return { totalCost: finalRoundedCost, costPerPyeong: finalCostPerPyeong, breakdown }; }, [area, structure, finish, performance, exterior, roof, ventilation, insulation, wallFinish, floorFinish, options, optionalItems, shadingSystem]); const { totalCost, costPerPyeong, breakdown } = detailedEstimate; // 유효성 검사 const isConsultationValid = clientName && clientPhone && clientEmail && totalCost !== null && !phoneError; // 상담 신청 핸들러 const handleConsultationRequest = () => { // 유효성 검사 if (!clientName || !clientPhone || !clientEmail) { setModalContent('이름, 연락처, 이메일 주소는 모두 필수 입력 항목입니다.'); setIsModalOpen(true); return; } if (phoneError) { setModalContent('연락처는 숫자만 입력 가능합니다. 다시 확인해주세요.'); setIsModalOpen(true); return; } // 메일 본문 내용 생성 const specifications = { '총 예상 건축비 (VAT 별도)': formatCurrency(totalCost) + ' 원', '평당 최종 건축비 (VAT 별도)': formatCurrency(costPerPyeong) + ' 원', '건축 면적': area + ' 평', '구조 방식': structure, '마감 등급': finish, '성능 수준': performance, '주요 단열재': insulation, '열회수 환기 장치': ventilation, '외장 마감': exterior, '지붕 마감': roof, '내부 벽 마감': wallFinish, '내부 바닥 마감': floorFinish, '전동 외부 차양': performance === '패시브인증주택' ? (shadingSystem || '미선택') : '해당 없음', '다락방 추가': options.attic ? '예' : '아니오', '특화 디자인 포인트 월': optionalItems.pointWall ? '예' : '아니오', '오픈 천장': optionalItems.openCeiling ? '예' : '아니오', }; const details = Object.entries(specifications) .map(([key, value]) => `* ${key}: ${value}`) .join('\n'); // 요청하신 이메일 주소로 변경: MY.HOME.PLACE@GMAIL.COM const emailReceiver = 'MY.HOME.PLACE@GMAIL.COM'; const emailSubject = `[주택 건축 상세 견적 기반 상담 신청] ${clientName}님 (${area}평)`; const emailBody = ` ${clientName}님, 안녕하세요. 상세 견적 계산 내역을 기반으로 상담을 신청합니다. *--- 고객 정보 ---* 성함: ${clientName} 연락처: ${clientPhone} 이메일: ${clientEmail} *--- 견적 요청 스펙 ---* ${details} 편하신 상담 가능 일정을 회신해 주시면 감사하겠습니다. `; const mailtoLink = `mailto:${emailReceiver}?subject=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(emailBody.trim())}`; // 클라이언트 메일 앱 실행 window.location.href = mailtoLink; // 모달을 사용하여 사용자에게 안내 setModalContent(`상담 이메일 (${emailReceiver}) 작성이 시작됩니다. 메일 앱을 확인해 주세요.\n\n[주의: 실제 발송은 메일 앱에서 '보내기'를 누르셔야 완료됩니다.]`); setIsModalOpen(true); }; // 옵션 체크박스 컴포넌트 const OptionCheckbox = ({ id, label, cost, checked, onChange }) => (
onChange(!checked)} > {cost}
); // 섹션 타이틀 컴포넌트 const SectionTitle = ({ icon, title, description }) => (

{icon} {title}

{description}

); // 셀렉트 그룹 컴포넌트 const SelectGroup = ({ label, value, onValueChange, children, hint }) => (
); // 단열재 SelectGroup을 구조 방식에 따라 다르게 렌더링 const renderInsulationSelect = () => { if (structure === "철근콘크리트") { return ( EPS (기본) 경질 우레탄폼 (+80만원/평) ); } else if (structure === "경량목구조" || structure === "중목구조") { const glassWoolLabel = structure === "중목구조" ? "글라스울(지붕), 미네랄울(벽) (기본)" : "글라스울 (기본)"; return ( {glassWoolLabel} 분사식 미네랄울 (+50만원/평) 셀룰로오스 (+120만원/평) ); } else { return (
구조 방식을 먼저 선택해주세요.
) } } // 상세 견적 내역 렌더링 컴포넌트 const EstimateDetail = ({ breakdown }) => { if (!breakdown || breakdown.length === 0 || breakdown.every(b => b.details.length === 0)) { return (
건축 면적을 입력하고 옵션을 선택해주세요.
); } return (
{breakdown.filter(b => b.details.length > 0).map((section, sectionIndex) => (

{section.category}

    {section.details.map((detail, detailIndex) => (
  • {detail.item} {formatCurrency(detail.cost)} 원
  • ))}
{section.subTotal > 0 && (
소계 {formatCurrency(section.subTotal)} 원
)}
))}
); }; return (
{/* 헤더 */}

주택 건축 상세 견적 시뮬레이터

내집 마련의 꿈, 전문가와 함께 합리적인 예산을 설계하세요.

{/* 메인 컨텐츠 영역: PC에서 2열 레이아웃 */}
{/* 1. 좌측: 옵션 선택 영역 (PC: 2/3 너비) */}
{/* 1. 기본 정보 및 구조 선택 */} } title="1. 기본 정보 및 구조 선택" description="건축 면적을 입력하고, 건물의 뼈대가 되는 구조 방식과 마감 등급을 선택합니다." />
{/* 건축 면적 */}
setArea(Number(e.target.value))} className="text-lg font-bold" /> {area > 0 &&

약 {Math.round(area * 3.30578 * 10) / 10} m²

}
{/* 구조 방식 */} 경량목구조 (기본) 중목구조 (+50만원/평) 철근콘크리트 (+130만원/평) {/* 마감 등급 */} 기본형 고급형 (+100만원/평)
{/* 2. 성능 및 단열 상세 설정 */} } title="2. 성능 및 단열 상세 설정" description="주택의 에너지 효율과 단열 사양을 결정합니다. 고성능 창호 및 열회수 환기 장치가 기본 포함됩니다." />
{/* 성능 수준 */} 저에너지 하우스 (기본) 패시브인증주택 (+8% 최종 비용 조정) {/* 조건부 설명 문구 */} {performance === "패시브인증주택" && (
한국패시브건축협회 인증을 위한 최고 성능 사양입니다. (외부 전동 차양 시스템 필수 선택)
)}
{/* 단열재 선택 항목 (구조 방식에 따라 조건부 렌더링) */} {renderInsulationSelect()} {/* 환기 장치 */} 국산 (기본) 유럽산 (+300만원)
{/* '패시브인증주택' 선택 시 '전동 외부 차양 시스템' 조건부 렌더링 */} {performance === "패시브인증주택" && ( 차양 시스템 선택 루버식 블라인드 EVB (+800만원) 롤링 셔터 (+1,000만원) )}
{/* 3. 마감재 옵션 */} } title="3. 마감재 옵션" description="건물의 외부 및 내부 디자인을 완성하는 마감재를 선택합니다." />
{/* 외장 마감 */} 세라믹사이딩 (기본) 벽돌타일 (+70만원/평) 시멘트사이딩 (-50만원/평) {/* 지붕 마감 */} 아스팔트슁글 (기본) 기와 (+60만원/지붕 평당) 징크 (+90만원/지붕 평당) {/* 내부 벽 마감 */} 벽지 (기본) 도장 (+40만원/평) {/* 내부 바닥 마감 */} 강마루 (기본) 타일 (+10만원/평) 원목마루 (+70만원/평)
{/* 4. 기타 공간 및 특화 옵션 */} } title="4. 기타 공간 및 특화 옵션" description="추가 공간이나 디자인적 요소를 위한 옵션들을 선택합니다." />
setOptions(prev => ({ ...prev, attic: checked }))} /> setOptionalItems(prev => ({ ...prev, pointWall: checked }))} /> setOptionalItems(prev => ({ ...prev, openCeiling: checked }))} />
{/* 2. 우측: 견적 결과 및 상담 신청 영역 (PC: 1/3 너비) */}
{/* 최종 견적 결과 카드 */}

최종 예상 견적 결과

{totalCost !== null && totalCost > 0 ? ( <>
총 건축 면적 {area} 평
평당 건축비 (VAT 별도) {formatCurrency(costPerPyeong)}
총 예상 건축비 (VAT 별도) {formatCurrency(totalCost)}

* 상기 금액은 VAT가 별도이며, 대지 구입 비용, 토목/부대 공사 비용, 설계비용 등은 포함되지 않은 순수 건축비 견적입니다.

상세 견적 내역 (Breakdown)

) : (
건축 면적을 1평 이상 입력하고 옵션을 선택해 주세요.
)}
{/* 상담 신청 카드 */}

견적 기반 상담 신청

setClientName(e.target.value)} />
setClientPhone(e.target.value)} /> {phoneError &&

{phoneError}

}
setClientEmail(e.target.value)} />

상담 요청 시, 설정된 견적 내역이 메일 본문으로 자동 생성됩니다.

{/* 알림 모달 */} setIsModalOpen(false)} title="안내">

{modalContent}

); }