🔗 GitHub Repository:MING-beta/fire-calculator 구경하기
👉 실제 서비스 써보기:FIRE Tribe 계산기 접속하기
![[바이브 코딩 실전 8단계] 디테일이 완성도를 만든다: 플로팅 메뉴, PDF 캡처, 프로필 저장 2 image 223](https://i0.wp.com/dopaming.blog/wp-content/uploads/2026/09/image-223.png?resize=696%2C864&ssl=1)
계산기의 코어 엔진과 차트, 타임라인까지 모두 완성되었습니다. 하지만 사용자가 이 앱을 ‘진짜 유용한 서비스’로 느끼게 하려면 2%의 디테일이 더 필요합니다.
수많은 부가 기능을 깔끔하게 숨겨줄 플로팅 메뉴(Speed Dial), 공들여 만든 내 은퇴 계획을 폰에 저장할 수 있는 이미지/PDF 내보내기, 그리고 복잡한 입력값들을 시나리오별로 저장해 두는 프로필(State) 관리 기능입니다.
이번 8단계에서는 이 부가 기능들을 바이브 코딩으로 어떻게 우아하게 엮어냈는지 fire.html과 script.js의 코드를 통해 살펴봅니다.
1. 복잡함을 숨기는 미학: Speed Dial 플로팅 메뉴
기능이 많아지면 화면이 버튼으로 가득 차서 모바일 사용성이 크게 떨어집니다. 그래서 화면 우측 하단에 떠 있는 동그란 버튼(Speed Dial)을 기획했습니다.
💬 나의 피드백 프롬프트
“화면 우측 하단에 떠 있는 플로팅 액션 버튼(Speed Dial,
#speed-dial)을 만들어 줘.메인 버튼을 누르면 위쪽으로 4개의 서브 버튼(그래프, 고물가 스트레스, 몬테카를로, 프로필 관리)이 애니메이션과 함께 나타나야 해.
바깥쪽 영역을 클릭하면 메뉴가 자동으로 닫히게 JS 이벤트 리스너도 달아줘.”
AI는 fire.html에 SVG 아이콘을 포함한 세련된 마크업을 생성했고[cite: 2], script.js에는 완벽한 토글 로직을 붙여주었습니다.
// script.js 에서 발췌: Speed Dial 제어 로직
const speedDial = document.getElementById('speed-dial');[cite: 3]
const speedDialMainBtn = document.getElementById('speed-dial-main-btn');[cite: 3]
if (speedDial && speedDialMainBtn) {
// 메인 버튼 클릭 시 메뉴 토글
speedDialMainBtn.addEventListener('click', (e) => {
e.stopPropagation();[cite: 3]
speedDial.classList.toggle('active');[cite: 3]
});
// 메뉴 바깥쪽 영역 클릭 시 닫기
document.addEventListener('click', (e) => {
if (!speedDial.contains(e.target)) {[cite: 3]
speedDial.classList.remove('active');[cite: 3]
}
});
}프론트엔드 초심자들이 자주 놓치는 ‘외부 클릭 감지(Outside Click)’ 로직을 AI가 알아서 꼼꼼하게 처리해 준 점이 돋보입니다.
2. 캡처는 앱의 기본 소양: html2canvas & jsPDF 연동
파이어족 커뮤니티에 내 계획을 자랑하거나 엑셀 대신 보관하려면 PDF나 이미지 저장 기능이 필수입니다. 하지만 DOM 요소 전체를 이미지로 뜨는 작업은 브라우저 렌더링 엔진의 특성상 꽤 까다롭습니다.
💬 나의 피드백 프롬프트
“결과 대시보드(
#export-dashboard-btn)는 PNG 이미지로, 앱 전체 화면(#export-pdf-btn)은 PDF로 저장하는 기능을 구현해 줘.CDN으로
html2canvas와jspdf,FileSaver.js를 불러와서 사용해.특히, 모바일 카카오톡이나 인스타그램 ‘인앱 브라우저’에서는 다운로드가 막히는 경우가 많으니까, 이때는 캡처된 이미지를 화면 팝업으로 띄워서 ‘꾹 눌러서 저장’하라고 안내문을 보여주는 예외 처리(Fallback)를 빡세게 해 줘.”
AI가 작성한 PDF 내보내기 로직은 기대 이상이었습니다. 현재 테마(다크/라이트)에 맞춰 배경색(bgColor)을 동적으로 감지하고 적용하는 디테일을 스스로 챙겼습니다.
// script.js 에서 발췌: 인앱 브라우저 예외 처리 로직의 일부
const ua = navigator.userAgent || navigator.vendor || window.opera;[cite: 3]
const isInAppBrowser = /kakao|instagram|line|everytime|naver/i.test(ua);[cite: 3]
if (isInAppBrowser) {
// 인앱 브라우저 다운로드 차단 우회: 화면에 이미지 오버레이 띄우기
showToast("인앱 브라우저(카카오/인스타 등)에서는 PDF 파일 저장이 원활하지 않습니다.<br><br>화면 우측 하단 또는 상단의 <b>'⠇(메뉴)'</b> 버튼을 눌러<br><b>'다른 브라우저(크롬/사파리)로 열기'</b>를 선택한 후 다시 시도해주세요.");[cite: 3]
return;[cite: 3]
}모바일 특유의 브라우저 파편화 문제까지 정규식으로 감지하고 사용자에게 우회 방법을 친절하게 안내하는 UX 처리까지 해냈습니다.
3. 입력 지옥에서 구원하다: 프로필(State) 저장 시스템
입력할 변수가 20개가 넘어가다 보니, 새로고침 할 때마다 날아가는 데이터를 다시 입력하는 것은 고역이었습니다. 데이터를 브라우저의 localStorage에 JSON 형태로 통째로 저장하고 불러오는 ‘프로필 관리’ 시스템을 도입했습니다[cite: 2, 3].
💬 나의 피드백 프롬프트
“현재 입력된 모든 슬라이더 값, 토글 상태, 생애 이벤트 배열 등을 하나의 객체(State)로 묶어서
localStorage에 저장하는 시스템을 구축해 줘.
getStateObject(): 현재 화면의 모든 입력값을 JSON 객체로 추출.applyState(state): JSON 객체를 받아서 화면의 슬라이더와 폼 값들을 복원.- 신규 유저가 처음 앱을 켰을 때 참고할 수 있도록 ‘공격적 투자자’, ‘안정적 근로자’, ‘늦깎이 파이어족’ 같은 기본 샘플 프로필 3개를 로컬스토리지에 초기화해 줘.”
AI는 방대한 ID 값들을 매핑하여 상태를 추출(getStateObject)하고 복원(applyState)하는 지루한 코드를 불평 없이 수백 줄 작성해 냈습니다. 특히 신규 유저를 위한 초기 샘플 데이터 주입 로직은 앱의 온보딩 경험을 극도로 끌어올렸습니다.
// script.js 에서 발췌: 샘플 프로필 자동 주입 로직
const samples = {
'sample_aggressive': {
name: "🔥 공격적 투자자 (30대)",
state: {
calcMode: "age", currentAge: "30", monthlyExpense: "250", monthlySavings: "400", currentSavings: "5000",
returnRate: "12.0", inflationRate: "2.5", lifeExpectancy: "90",
useAssetAlloc: true, assetStockAmt: "4000", assetStockRet: "15.0", // ...
}
},
// ... 'sample_balanced', 'sample_late' 생략 ...
};[cite: 3]💡 8단계 회고: 노가다는 AI에게, 기획은 인간이
DOM 요소의 값을 읽고, 객체로 묶고, 로컬 스토리지에 넣고, 다시 꺼내서 각 input.value에 집어넣는 코드는 개발자에게 가장 귀찮고 오타가 나기 쉬운 단순 노동(노가다)입니다. 바이브 코딩 시대에는 “어떤 항목들을 저장하고 싶은지”, “저장/불러오기 UX 흐름은 어때야 하는지”만 명확히 정의하면, 이런 반복 작업은 AI가 순식간에 처리해 줍니다.
🚀 다음 단계 예고
이제 계산기는 시각화, 부가 기능, 편의성까지 완벽합니다. 하지만 2008년 서브프라임 모기지 사태나 최근의 인플레이션 쇼크처럼 “주식 시장이 붕괴하면 내 은퇴 계획은 어떻게 될까?”라는 두려움이 남습니다.
다음 [9단계]에서는 1,000번의 난수 시뮬레이션을 돌려 내 파이어 플랜의 파산 확률을 검증하는 끝판왕 기능, ‘몬테카를로(Monte Carlo) 스트레스 테스트’를 바이브 코딩으로 어떻게 구현했는지 집중 해부합니다!



