MicroAI Paygate
An x402-style payment gateway for AI requests. Sign EIP-712, get a signed receipt, verify the signature client-side.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
법정 상한요율 기준으로 부동산 중개보수(복비)를 바로 계산해주는 공개용 웹 앱입니다. 기존 "애플부동산 정산 시스템"(apple-realty-settlement)과는 완전히 별도의 프로젝트입니다. 정산 시스템은 로그인이 필요한 사무소 내부용이고, 이 앱은 로그인 없이 누구나 쓰는 공개 계산기예요. • Next.js 14.2.35 (App Router) + TypeScript + Tailwind CSS • 별도 DB/로그인 없음 (계산은 전부 브라우저에서 즉시 처리) • 예외 1건: app/api/base-rate(서버리스 라우트) — 전월세 전환율 계산기가 쓰는 한국은행 기준금리를 실시간으로 대신 조회해주는 얇은 프록시. DB는 아니지만 첫 서버 API/환경변수라 아래 "2." 참고 • Vercel Hobby 플랜으로 배포 예정 환경변수 — ECOS_API_KEY (전월세 전환율 계산기용, 선택사항이지만 강력 권장)查看完整 README ↗
법정 상한요율 기준으로 부동산 중개보수(복비)를 바로 계산해주는 공개용 웹 앱입니다. 기존 "애플부동산 정산 시스템"(apple-realty-settlement)과는 완전히 별도의 프로젝트입니다. 정산 시스템은 로그인이 필요한 사무소 내부용이고, 이 앱은 로그인 없이 누구나 쓰는 공개 계산기예요.
app/api/base-rate(서버리스 라우트) — 전월세 전환율 계산기가 쓰는 한국은행
기준금리를 실시간으로 대신 조회해주는 얇은 프록시. DB는 아니지만 첫 서버 API/환경변수라
아래 "2." 참고npm install
cp .env.local.example .env.local # ECOS_API_KEY 채워넣기 (아래 참고)
npm run dev # http://localhost:3000
ECOS_API_KEY (전월세 전환율 계산기용, 선택사항이지만 강력 권장)전월세 전환율(3.6) 계산기는 한국은행 ECOS Open API로 기준금리를 실시간 조회해서 법정 전환율 상한을 자동 계산합니다. 키가 없어도 앱은 정상 작동하지만(마지막으로 확인해둔 값으로 대체), 형이 매번 직접 기준금리를 확인해줄 필요가 없으려면 키를 발급받아 등록하는 걸 권장합니다.
OpenAPI →
인증키 신청 → 활용 목적 간단히 기재(예: "개인 프로젝트 부동산 계산기 개발")마이페이지 → 인증키 관리에서 확인).env.local에 ECOS_API_KEY=발급받은키 추가ECOS_API_KEY 이름으로 등록 → 재배포. 이걸 안 하면 프로덕션에서도 폴백값(수동 확인값)으로
동작함(앱이 죽지는 않지만 실시간 갱신이 안 됨).계산 로직만 빠르게 검증하려면:
npm run verify:calc
이 스크립트는 매매/전세/월세/오피스텔/토지·상가 등 대표 케이스를 계산해 콘솔에 출력합니다.
(참고: npm run build로 실제 프로덕션 빌드도 정상적으로 되는 것을 확인했습니다.)
3.2/3.4(부대비용·일할계산·Cap Rate) 로직은 별도로 npm run verify:calc2로 검증합니다.
lib/calc.ts에 모두 있습니다. 공인중개사법 시행규칙 제20조 및 서울특별시 주택중개보수 조례
(2021.12.30 시행, 국토교통부 표준 개정안과 동일)를 기준으로 했습니다.
⚠️ 주의: 일부 지자체는 조례가 다를 수 있습니다(대부분 동일하지만 100% 보장은 아님). 앱 하단에 "참고용 안내"라는 문구를 넣어뒀고, 실제 거래에서는 최종적으로 개업공인중개사와 협의해서 결정한다는 점을 명시했습니다. 계산기가 법적 자문이 아니라는 걸 분명히 해두는 게 나중에 분쟁 소지를 줄여줍니다.
기존 정산 시스템과 같은 방식입니다.
xchanz-tech/calc-realty, Public 또는 Private 상관없음)git init
git add .
git commit -m "초기 커밋: 복비 계산기"
git branch -M main
git remote add origin https://github.com/xchanz-tech/calc-realty.git
git push -u origin main
.env.local 없이도 배포·동작은 됩니다. 다만 전월세 전환율 계산기의
기준금리 실시간 갱신을 쓰려면 위 "2."의 ECOS_API_KEY를 Vercel 환경변수에 등록하세요)calc-realty.vercel.app 같은 주소가 생깁니다. 커스텀 도메인을 쓰고 싶으면 Vercel
프로젝트 설정 → Domains에서 추가하면 됩니다.토스 개발자센터(https://developers-apps-in-toss.toss.im) 기준으로 확인한 최신 절차입니다.
npm install @apps-in-toss/web-framework
npx ait init
granite.config.ts에 appName/앱 이름/색상/아이콘을 콘솔에 등록한 값과 동일하게 맞춰줍니다.인앱 광고 수익 = 노출 수 × eCPM ÷ 1,000. 즉 트래픽이 어느 정도 쌓여야 의미 있는 수익이 나옵니다. 초기에는 배너 광고 위주로 시작하고, 사용자가 결과를 다 본 뒤 "결과 복사하기"를 누르는 시점 등에 전면형 광고를 자연스럽게 배치하면 좋습니다(단, 서비스 흐름을 막으면 정책 위반이니 주의).
lib/jeonseConversion.ts의 CURRENT_BASE_RATE_PERCENT 최신값으로 갱신할 것.WonInput 한글 단위 힌트, 거래유형 선택
평탄화(매매/전세/월세), 탭 라벨 정리 전부 반영.granite.config.ts 세팅 및 앱인토스 SDK 연동 (디자인 리스타일 완료됐으니 착수 가능)components/BrokerageFeeCalculator.tsx로 분리, components/ui.tsx에 공용 입력 컴포넌트
(WonInput/SegButton) 추출. lib/prorate.ts에 일할계산 로직 신규 작성(금액 ÷ 해당월 총일수 ×
거주일수, 장기수선충당금은 기본 제외·옵션으로 포함 가능). app/page.tsx는 두 계산기를 탭으로
전환하는 셸로 재작성(components/ProrateCalculator.tsx 신규).npx tsc --noEmit 클린 통과 확인. npm run build는 마운트 폴더 특성상 타임아웃되어 스킵(비정상은
아님 — 다음 세션에서 로컬 터미널로 재확인 권장).울산 세션 이후 원격 히스토리가 강제 갱신되어 있어(로컬 재클론으로 동기화) 이어서 진행. 마스터플랜 문서의 남은 계산기 3종 + 실무 옵션 + 공유 기능을 전부 구현·배포했습니다.
components/MovingCostCalculator.tsx, lib/movingCost.ts) —
매매: 취득세(주택수·조정대상지역·가격구간별 1calcBrokerageFee를 그대로 호출해 중개보수를 합산.BrokerageFeeCalculator.tsx 수정) —
실무 모드에서 공동중개(단타 50%/양타 100%) + RS 분배율(기본 70%) 슬라이더 노출, 사무소 수령액/개인
수령액을 세전 기준으로 분리 표시.components/CapRateCalculator.tsx,
lib/capRate.ts) — 연간 순수익률 = [(월세×12−대출이자×12)] / [매매가−보증금−대출금] × 100, 권리금
수수료는 협의 수수료(5~10%) 구간별 예상액.components/ReceiptCard.tsx,
components/ShareReceiptButton.tsx, 신규 의존성 html2canvas) — 결과를 영수증 카드 DOM으로 렌더링 후
캡처해 PNG로 저장, Web Share API 지원 환경(대부분 모바일)에서는 바로 공유, 미지원 환경은 다운로드로
폴백. 하단에 "RealtyTools by Retools · Powered by RealtyBook" 워터마크 고정. 3.1/3.2 결과 화면에 공통
적용(3.4는 분석 지표 성격이라 영수증 카드 미적용).npx tsc --noEmit + /tmp 격리 npm run build 클린 통과 확인 후 배포, 배포마다 라이브
캐시버스팅으로 재확인 완료.html2canvas 추가로 로컬 개발 서버를 계속 쓰려면 npm install 재실행 필요(Vercel 배포는 자동 설치).계산기 5종(3.1~3.5) 배포 완료 보고를 받고 마스터플랜 문서 최신본을 다시 읽어 명칭 변경 사항을 확인했고(위 6장에 백로그로 반영), 아래 실테스트를 진행함.
/tmp 격리 환경에서 Linux용 npm install 후 진행): lib/prorate.ts,
lib/capRate.ts(+calcPremiumFee), lib/movingCost.ts(매매/전월세 양쪽) 전부 정상 입력값으로 직접
호출해 결과값을 손계산과 대조 — 전부 공식과 일치. scripts/verify-calc.mjs(3.1 회귀 테스트)의
기존 import 경로 오타(./lib/calc.ts→../lib/calc.ts)를 발견해 수정하고 재실행, 9개 테스트 케이스
전부 마스터플랜 요율표와 일치 확인.calc-realty.vercel.app
프로덕션 페이지 자체는 정상 로드되고 4개 탭·모드스위치 등 DOM에 전부 존재하는 것까지는 확인했으나,
이번 세션의 브라우저 자동화 도구(스크린샷/요소탐색/클릭)가 이 페이지에서 반응하지 않는 문제가
있었음(신규 탭에서도 재현, React 이벤트 핸들러를 직접 호출해도 탭 전환이 DOM에 반영 안 됨).
콘솔 에러는 전혀 없었고, 백그라운드 탭 렌더링 스로틀링 등 도구 쪽 문제일 가능성이 높지만 완전히
배제하진 못함 — 다음 세션(또는 형이 직접 폰/PC로) 탭 전환·입력·영수증카드 저장 버튼을 한 번
수동으로 눌러서 확인하는 걸 권장.ShareReceiptButton.tsx가 navigator.canShare()가
true를 반환하면 무조건 navigator.share()(OS 네이티브 공유시트)를 호출했는데, Windows
Chrome은 파일 공유 자체는 지원한다고 응답하면서도(canShare=true) 실제 공유 대상 앱을 찾는
단계에서 깨지는 경우가 있음 — 이건 모바일 공유시트를 위한 API라 데스크톱에서 신뢰하기 어려움.
수정: navigator.maxTouchPoints > 0(터치 기기)일 때만 공유 시도, PC 등 그 외 환경은 바로
다운로드로 처리. 공유 시도 중 오류(취소 제외) 시에도 다운로드로 자동 폴백하도록 보강.truncate 클래스였음. 처음엔
화면은 멀쩡한데 다운로드 이미지만 잘린다고 판단해 카드 바깥 overflow-hidden 제거 +
html2canvas 캡처 높이 명시(windowHeight)로 고쳤는데, 이후 형이 실제로는 화면(계산기
상태 미리보기)에서도 똑같이 잘리고 있다고 알려줘서 재조사함. 진짜 원인은 CTA 바 중간
텍스트("공인중개사 스마트 정산장부") span에 걸려있던 Tailwind truncate
(overflow:hidden+text-overflow:ellipsis+nowrap) 클래스 — 이 overflow:hidden이
화면·캡처 이미지 양쪽 모두에서 그 줄의 한글 받침을 살짝 잘라내고 있었음(짧은 고정 문구라
애초에 말줄임표가 필요 없는데 방어적으로 넣어둔 게 화근). truncate 제거하고
whitespace-nowrap만 남김, 정렬도 다시 items-center로 되돌려 로고·텍스트 눈높이를 맞춤.
(이전에 시도했던 overflow-hidden 제거/windowHeight 명시는 부작용은 없어 그대로 유지.)ReceiptCard.tsx) 디자인 개선 — PM 제안 3안 중 "2안(바이럴 후킹 CTA 바형)"
채택. 하단의 밋밋한 회색 영문 텍스트를 Core Navy(#0A2540) 풀블리드 바로 교체하고
rb-mark-white.png 심볼 + "리얼티북 | 공인중개사 1초 정산장부 (2인 평생 무료) · retools.kr →"
카피를 넣어 카카오톡 공유 시 바이럴 유입 CTA 역할을 하도록 함. 상단에는 신규 subtitle prop을
추가해 "[매물유형·거래유형 · 금액 · YYYY.MM 기준]" 형태의 기준정보 태그를 노출 —
BrokerageFeeCalculator/ 양쪽에 적용.형이 Claude Design 툴(claude.ai/design)에서 CTA 바를 새로 시안 작업해 링크로 전달 →
Pro 탭 → 요소 클릭 → Debug JSON(raw inline style)으로 정확한 스펙을 뽑아 ReceiptCard.tsx에
그대로 이식. 이후 실사용 중 발견된 문제를 두 차례 더 고쳐 최종적으로 정적 이미지 기반 CTA로
정착함.
linear-gradient(120deg, #08203a 0%,#0d3b57 55%,#15719b 100%)), 아이콘 23px, "리얼티북"(16px/extrabold/흰색),
구분선(1px/흰색 28%), "공인중개사 1초 정산장부"(15px/bold/하늘색), "(2인 평생 무료)"(14px/
extrabold/골드), 하단 골드 캡슐 CTA(radius 999px, box-shadow 포함)까지 전부 Debug JSON 값
그대로 Tailwind로 옮김. (커밋 7f16f53)flex-wrap이 폭마다 제각각 다른 지점에서 줄바꿈돼 화면마다 다르게
보였음. 브랜드 줄/혜택 줄을 처음부터 고정된 2줄 구조로 나눠 폭과 무관하게 항상 같은 모양이
되도록 수정. (커밋 e99cf49)public/images/receipt-cta.png로 저장, ReceiptCard.tsx의 CTA 영역을 텍스트 조립 대신
<img> 한 장(w-full + 이미지 실제 비율로 aspect-[] 고정 + rounded-b-[11px])으로 교체.
이제 카드 폭이 얼마든 이미지가 원본 비율 그대로 스케일되므로 줄바꿈 걱정 자체가 사라짐.
(커밋 800d731)public/images/receipt-cta-v2.png로 교체, aspect-[73/23]로 갱신. 같은 카드 폭에서도 줄당
세로 해상도가 훨씬 넉넉해 선명도가 크게 개선됨(형 확인: "잘나온다"). (커밋 b0c2495)public/images/receipt-cta.png(구 2줄형)는 이제 미사용이지만, 작업 루트 폴더
정책상 이미 쓴 파일은 삭제·rename이 안 돼 그대로 남아있음. 실제 렌더링에는 영향 없음.형이 제미나이 PM 권고안대로 Claude Design에서 직접 시안(전체 4탭 리디자인)을 떠서 전달 →
안전하게 시험해볼 수 있도록 design-preview 브랜치를 따로 만들어 이식 → 확인 후 main 병합·배포까지
한 세션에 끝냄.
design-preview 브랜치, 이후 main에 병합) — 마스터플랜 4장 팔레트
(navy #0A2540 / cobalt #0066FF / cyan #00D2FF / mist)와 Sora+IBM Plex Sans KR 폰트를
tailwind.config.js/app/layout.tsx에 토큰으로 추가. 신규 공용 컴포넌트
components/ResultCard.tsx(네이비 배경+시안 강조 헤드라인+행목록+하이라이트 서브박스)를 만들어
4개 계산기(복비/일할/부대비용/상가수익률) 결과 섹션을 전부 이걸로 교체. app/page.tsx 헤더를
"부동산 계산기"(부제 "복비·일할정산·취득세·수익률", v1.0 배지)로 전면 재설계하고, 상단에
[일반고객용]/[공인중개사 실무용] 글로벌 모드 스위치 + 4개 아이콘 카드 탭 네비게이션 신설.
components/ui.tsx의 버튼/인풋 강조색도 구 브랜드색(#14607F)에서 코발트로 통일.
일할계산기는 목업엔 매도인/매수인 분리 계산 UX가 있었지만, 기존 단일 합계 계산 로직은 그대로
두고 시각적 스타일만 이식(기능 확장은 범위 밖으로 판단).components/Modal.tsx
신규)로 분리 (복비·부대비용 계산기 적용).ReceiptCard.tsx).px-4로 일관 적용돼 있음을 확인, 추가 수정
불필요(점검 과정에서 남아있던 구 브랜드색 잔여분도 코발트로 마저 통일).design-preview → main 병합·배포 — 두 브랜치가 커밋 1개 차이(선형)라 충돌 없이 병합,
push 후 Vercel 자동 배포로 calc-realty.vercel.app에 반영 완료(형이 실제 화면 캡처로 확인).app/page.tsx). 상단 모드 스위치 자체는 "중개사 어필용"으로 유지, 복비 탭 안의 실무용
전용 필드(공동중개 단타/양타 + RS 정산비율 슬라이더 → 내 실수령액)는 그대로 유지.public/sw.js가 stale-while-revalidate 방식이라 서비스워커가
캐시해둔 이전 화면을 먼저 보여준 것으로 판단. 강력 새로고침(Ctrl+Shift+R) 또는
DevTools → Application → Service Workers → Unregister로 해결 안내함.apple-realty-settlement의 NavBar.tsx에 "🧮 복비 계산기"
새 탭 링크 추가 — 중개사가 계약 등록/조회 중에도 옆 탭으로 바로 이 계산기를 열어 쓸 수
있게 함(계산 로직을 그 앱 안에 넣는 게 아니라 이미 배포된 이 공개 계산기로 링크 연결만).
상세 내역은 apple-realty-settlement/CLAUDE.md 참고.npx tsc --noEmit 통과 확인 후 배포. npm run build는 이번에도 샌드박스
네트워크 제약(Google Fonts fetch 타임아웃)으로 끝까지 못 돌렸음 — 로직 변경이 없어 타입체크로
충분하다고 판단했고, 실제 배포 후 형이 프로덕션에서 직접 확인 완료.Claude Design에서 PM 권고사항 반영한 "부동산 계산기 v1.1" 목업을 형이 스크린샷으로 전달 → 디자인 캔버스 원본(claude.ai/design) 확인해 스크린샷이 전체 스코프임을 검증 → 형 요청대로 상단 입력폼 UI만 적용하고 전월세 전환율(5%) 계산기는 이번 작업에서 제외(법정 기준금리 기반 전환율 확인 전까지 보류, 이전에도 요율표류는 사용자 확인 없이 하드코딩하지 않는다는 원칙을 그대로 적용). 하단 결과 카드/복사·영수증 버튼/파트너 배너/푸터는 목업과 프로덕션 코드가 이미 정확히 일치함을 코드 비교로 확인해 손대지 않음.
components/ui.tsx, components/BrokerageFeeCalculator.tsx,
app/page.tsx 원본 전체를 design-v1.1-backup/*.orig.txt에 보관(형 요청 — "원복할 수
있으니 오리지널 코드 남겨두고"). 필요 시 이 파일 내용으로 그대로 되돌리면 됨.components/ui.tsx — WonInput 한글 단위 힌트 추가. 신규 formatKoreanUnit() 함수로
입력값을 "n억 n천만원" 형태로 변환해 입력창 아래 코발트색 보조 텍스트로 실시간 표시(예:
500,000,000원 입력 시 "5억원"). 숫자 폰트를 Sora 대응 font-sora로 키워 가독성 강화. 모든
계산기가 공유하는 컴포넌트라 복비/일할/부대비용/상가수익률/평수 계산기 전체에 자동 반영.components/BrokerageFeeCalculator.tsx — 거래유형 선택 평탄화. 기존
"매매·교환 / 임대차(전세·월세)" → "전세 / 월세" 2단계 선택을 매매·전세·월세 3버튼 단일
SegButton으로 합침(내부적으로 dealType+isMonthly 상태는 그대로 유지, UI만 평탄화).
매물 유형 라벨 순서를 매물유형→거래유형에서 거래유형→매물유형으로 바꾸고 "토지·상가"를
"상가·토지"로 재정렬(목업 반영).app/page.tsx — 헤더/모드스위치/탭 그리드 리스타일. 헤더를 네이비 카드+로고에서
라이트 배경 위 "부동산 계산기 v1.1" 타이틀 + 골드 그라디언트 "FREE" 뱃지로 교체, 부제
텍스트 갱신. 모드 스위치([일반고객용]/[공인중개사 실무용])를 헤더 카드에서 분리해 별도
흰색 카드로. 탭 그리드를 4개(grid-cols-4)에서 5개(grid-cols-3, 신규 평수·평단가 탭
포함)로 확장하고 라벨을 "복비 계산/잔금일 일할/취득세·부대/상가·수익률/평수·평단가"로
정리(전월세·5% 탭은 포함하지 않음).lib/pyeong.ts(1평 = 400/121㎡ ≒ 3.3058㎡ 기준 평↔㎡
환산, 평당가/㎡당가 계산, 순수 산술이라 법정 요율 이슈 없음) + components/PyeongCalculator.tsx
(총 금액 + 평/㎡ 단위 전환 입력 → ResultCard로 평당가·㎡당가·환산면적 표시, 결과 텍스트
복사 지원). 기존 CapRateCalculator.tsx 패턴(모드 prop 없음, ResultCard 단일 사용) 그대로
따름.npx tsc --noEmit -p tsconfig.json 클린 통과 확인(exit 0). npm run build는 이번에도
마운트 환경 특성상 스킵 — 로직 자체가 순수 산술 변환/UI 스타일 변경뿐이라 타입체크로 충분하다고
판단.git push 안 함 — 형 확인 후 커밋/푸시 예정. 커밋 시 design-v1.1-backup/도 함께
올려서 부산 쪽에서도 원본을 참고할 수 있게 할 것(용량 작음, 텍스트 3개 파일).형이 "MAX 플랜으로 바뀌었으니 토큰 걱정 말고 전월세 전환율 계산기도 법정 전환율 확인해서 반영해줘"라고 해서, 위에서 보류했던 3.6을 같은 세션에서 이어서 구현·완료함.
lib/jeonseConversion.ts(기준금리 상수 CURRENT_BASE_RATE_PERCENT를
확인일자 주석과 함께 하드코딩 — 복비 요율표와 동일하게 "사용자 확인 없이 임의 추정 금지"
원칙 적용, 대신 기준금리는 공개된 한국은행 발표값이라 매번 재검색으로 갱신 가능) +
components/JeonseConversionCalculator.tsx(보증금↔월세 전환 계산기 + 계약갱신 5% 상한
계산기, 두 섹션을 한 컴포넌트에 — CapRateCalculator.tsx가 Cap Rate+권리금을 한 컴포넌트에
담은 것과 같은 패턴). app/page.tsx에 "전월세·5%" 탭(🔄)으로 연결, 총 6개 탭
(grid-cols-3 2줄)으로 확장.npx tsc --noEmit -p tsconfig.json 클린 통과(exit 0).git push 전 — 위 v1.1 리디자인 작업과 함께 한 번에 커밋 예정.12-1에서 기준금리를 상수로 하드코딩해뒀는데, 형이 "MAX 플랜이니 매번 사람이 확인 안 해도 되게 자동 갱신으로 만들자"고 해서 실시간 연동으로 업그레이드함. 하드코딩/정기 수동확인/실시간 API 3가지 옵션을 제시했고 형이 실시간 API 연동을 선택.
app/api/base-rate/route.ts — 한국은행 ECOS Open API(통계표
722Y001, 항목코드 0101000 = 한국은행 기준금리, 일별 시계열)를 서버에서 대신 호출.
ECOS_API_KEY 환경변수 필요(README "2." 참고). 6시간 캐시(revalidate = 21600)로 ECOS
호출을 아낌. 키가 없거나 호출이 실패하면 lib/jeonseConversion.ts의
FALLBACK_BASE_RATE_PERCENT(2026-07-16 결정, 2.75%)로 조용히 대체하고 stale: true를
내려줌 — 실패해도 계산기가 멈추지 않는 우아한 저하(graceful degradation) 설계.lib/jeonseConversion.ts 리팩터: CURRENT_BASE_RATE_PERCENT → FALLBACK_BASE_RATE_PERCENT로
이름을 바꿔 "이건 폴백용일 뿐, 실제로는 API가 실시간 값을 준다"는 의미를 명확히 함.
calcConversion()이 currentBaseRatePercent를 인자로 받아 법정 상한 판정에 실시간 값을
쓰도록 변경.components/JeonseConversionCalculator.tsx: 마운트 시 /api/base-rate를 fetch해서
기준금리를 받아오고, 전환율 슬라이더 기본값을 실시간 법정 상한으로 자동 세팅(사용자가 슬라이더를
직접 만지면 그 선택을 존중하고 덮어쓰지 않음 — "법정 상한으로" 버튼으로 언제든 복귀 가능).
화면에 "한국은행 실시간 확인, YYYY-MM-DD 기준" 또는 실패 시 "실시간 확인 실패 — 마지막
확인값 사용 중"을 안내 문구로 표시해 사용자가 데이터 신선도를 알 수 있게 함..env.local.example 신규 추가 + README "2."에 ECOS 인증키 발급·Vercel 환경변수 등록
절차 문서화. ECOS_API_KEY는 .gitignore의 .env.local 패턴에 걸리지 않으므로 예시 파일
자체는 정상 커밋됨(빈 값).npx tsc --noEmit -p tsconfig.json 클린 통과(exit 0).ECOS_API_KEY는 형 계정으로 직접 발급받아야 하는 값이라 Claude가 대신
가입할 수 없음. https://ecos.bok.or.kr/api/ 에서 무료 가입 후 Vercel 프로젝트 환경변수에
등록해주면 그 이후로는 완전 자동 — 형이 다시 신경 쓸 일 없음. 키 등록 전까지는 폴백값(2.75%,
2026-07-16 기준)으로 계속 정상 동작함.형이 ECOS에서 인증키를 실제로 발급받아 전달해줌(A44NSDI3DHSZJ1XNOXS7). curl로 직접 호출
테스트하는 과정에서 버그 발견: app/api/base-rate가 180일 범위를 조회하면서 요청 건수를
100건으로 잡아뒀는데, 이 통계는 일별로 값이 나와서 180일치가 실제로는 150~190건 정도 됨 →
100건만 받으면 오래된 쪽부터 잘려서 "가장 최근 로우 = 최신값"이라는 가정이 깨지고 약 5개월
전 데이터(2026-03-22, 2.5%)를 최신값으로 잘못 읽는 버그가 있었음. 요청 건수를 500으로
올려서 수정하고, curl로 재검증(178건 전부 수신, 최신값 2026-08-16자 2.75% 정상 확인).
로컬 .env.local에 발급받은 키를 등록해 로컬 개발 환경에서도 바로 테스트 가능하게 함
(.env.local은 .gitignore에 포함돼 있어 커밋되지 않음).
.env.local은 로컬 전용이라, 프로덕션에 반영하려면
Vercel 프로젝트 → Settings → Environment Variables에 같은 키(ECOS_API_KEY =
A44NSDI3DHSZJ1XNOXS7)를 형이 직접 등록해야 함(Claude는 Vercel 계정 접근 권한이 없음).
등록 후 재배포하면 그 순간부터 실시간 기준금리 자동 반영 시작.npx tsc --noEmit -p tsconfig.json 클린 통과.형이 "calc-realty를 구글 플레이스토어에 단독 앱으로 출시" 요청 (TWA/Bubblewrap 기반), 6가지 조건:
패키지명 kr.retools.realtycalc, 앱이름 "부동산 계산기 (by 리얼티북)", 호스팅
https://calc-realty.vercel.app, 512×512 아이콘, assetlinks.json, 서명 .aab.
Cowork 샌드박스에서 한 것:
keytool로 서명 키스토어 생성(RSA 2048bit, alias realtycalc, 유효기간 10,000일/~27년).
이 키는 앱을 한 번 출시한 뒤로는 절대 바뀌면 안 됨 — Play Store 업데이트마다 계속 재사용.public/.well-known/assetlinks.json 생성·커밋·배포 (앱↔웹사이트 소유권 증명, SHA256 지문 포함).
배포 확인: https://calc-realty.vercel.app/.well-known/assetlinks.jsonTwaManifestJson 스키마를 직접 읽어서 twa-manifest.json을 손으로 작성
(대화형 bubblewrap init 마법사 대신 — 패키지명/앱이름/색상/아이콘 URL/서명키 정보 전부 포함)..aab 빌드는 완료 못 함: Bubblewrap이 처음 실행 시 JDK 17 + Android SDK를
자동 다운로드하는데, 샌드박스의 매 명령 호출이 별도 프로세스라 다운로드가 이어받기 없이 매번
처음부터 다시 받아짐 → 시간 내에 못 끝남. 키스토어·twa-manifest.json·빌드 순서 안내를 만들어서
형 PC(로컬 Windows)로 넘김.형 PC(로컬)에서 실제 빌드 진행하며 만난 문제 3개와 해결법 (다음에 또 나올 수 있으니 기록):
Get-Content/Set-Content로 twa-manifest.json을 손보거나,
콘솔에 한글을 직접 붙여넣으면 부동산 → 遺?숈궛 식으로 깨짐). 원인: Windows PowerShell 5.1이
BOM 없는 UTF-8 파일을 시스템 코드페이지(한국어 Windows는 CP949)로 잘못 추측해서 읽음.
해결: 파일을 쓸 때는 Base64로 인코딩해서 콘솔에 붙여넣고
[System.IO.File]::WriteAllBytes(...)로 바이트 그대로 저장(콘솔 인코딩을 아예 안 거침),
읽어서 확인할 땐 Get-Content ... -Encoding UTF8 명시. Node.js(bubblewrap 본체)는 콘솔
인코딩과 무관하게 파일을 정확히 읽으므로 빌드 자체엔 문제 없음.Could not reserve enough space for 1572864KB object heap (메모리 16GB에
여유 3GB 있어도 발생 — 물리 메모리 문제가 아니라 JVM이 요청한 만큼의 연속된 가상주소 공간을
못 잡는 문제). 해결: 프로젝트 루트의 gradle.properties에서 기존
org.gradle.jvmargs=-Xmx1536m 줄을 지우고
org.gradle.jvmargs=-Xmx1024m -XX:MaxMetaspaceSize=256m + org.gradle.daemon=false 추가.keystore password was incorrect): PASSWORD_DO_NOT_LOSE.txt 파일
내용이 GENERATED_KEYSTORE_PASSWORD=실제비밀번호 형식인데, = 앞부분(라벨)까지 통째로
비밀번호로 입력해서 발생. = 뒤의 값만 Key Store/Key 비밀번호 둘 다에 입력해야 함
(이 키스토어는 storepass=keypass로 동일하게 생성해뒀음).최종 결과: app-release-bundle.aab + app-release-signed.apk 로컬 빌드 성공
(C:\android-build\). 키스토어·비밀번호는 형이 구글 드라이브
(G:\내 드라이브\Work\리툴스\android-signing, 본인 PC마다 드라이브 문자는 다를 수 있음)에
백업 완료. assetlinks.json은 git push 완료, 프로덕션에서 라이브 확인함(커밋 cee23bc).
SHA256 인증서 지문(Play Console "앱 서명" 메뉴에서 대조용):
F5:3C:B5:D9:28:CB:01:93:08:EC:E8:D6:AD:10:FE:B9:E9:66:F1:06:21:DE:D0:04:9C:C8:E4:74:A0:B1:6B:4F
아직 안 한 것: Play Console에 app-release-bundle.aab 업로드 + 스토어 등록(스크린샷, 설명,
개인정보처리방침 URL 등) + 심사 제출 — 전부 형이 직접 해야 하는 단계(Claude는 Play Console
계정 접근 권한 없음). 이후 앱 업데이트할 때 다시 빌드해야 하면, 이 CLAUDE.md의
"Android(TWA) 앱 빌드/업데이트 방법" 섹션에 재사용 가능한 전체 커맨드가 있음(부산 등 다른 PC에서도
그대로 사용 가능하도록 정리해둠).
위 13절에 빌드 폴더가 C:\android-signing 으로 적혀 있었다. 그런 폴더는 없다.
이 잘못된 이름이 총괄 지시판(지시 029 4-1)에도 옮겨 적혀, 개발팀이 없는 폴더의 접근 권한을
요청했다가 거절당하고 그것을 「권한이 안 나온다」로 보고하는 헛수고가 한 라운드 있었다.
두 곳을 구분해 적는다.
| 무엇 | 어디 |
|---|---|
| TWA 빌드 프로젝트 (build.gradle, twa-manifest.json, 키스토어, 서명 스크립트) | C:\android-build\ |
| 키스토어 백업 뿐 (빌드 프로젝트 없음) | 구글 드라이브 Work\리툴스\android-signing\ |
번들에 서명이 붙지 않던 원인: app\build.gradle 의 buildTypes.release 에 minifyEnabled
만 있고 signingConfigs 블록이 어디에도 없다. 그래서 Gradle 이 서명 없는 .aab 와
app-release-unsigned.apk 를 낸다. 8월에 올린 app-release-bundle.aab 도 서명 블록이 없었고
Play Console 이 거부했다.
build.gradle 을 고치지 않는다 (총괄 판정, 지시 030 2-6). signingConfigs 를 넣어도
bubblewrap update 가 파일을 다시 만들면서 지운다. 지워지는 고침은 고침이 아니다.
대신 C:\android-build\서명하기.ps1 을 쓴다. 빌드가 끝난 뒤 이 스크립트를 실행하면
app\build\outputs\bundle\release\app-release.aab 에 릴리스 키로 서명을 붙여
C:\android-build\app-release-signed-bundle.aab 를 만든다. Play Console 에는 이 파일을
올린다. 빌드 산출물을 그대로 올리면 안 된다.
스크립트가 하는 일:
PASSWORD_DO_NOT_LOSE.txt 에서 직접 읽는다. 사람이 옮겨 치지 않는다.
1판에서 손으로 입력했다가 오타로 keystore password was incorrect 가 났다. 23자에 대소문자가
섞여 있고 구분 기호가 없어 틀리기 쉽다.jarsigner -verify 를 돌린다.META-INF/MANIFEST.MF · META-INF/*.SF · 인증서(.RSA/.DSA/.EC)
세 종류를 세고, 하나라도 없으면 소리내어 실패하고 종료 코드 1 을 낸다.
jarsigner -verify 만으로는 모자란다. 서명이 없는 파일에도 0 을 돌려주고 「jar is unsigned」
라고만 적는 판이 있다. 이번에 사람이 번들을 열어 잡은 것을 스크립트가 잡게 한 것이다.
실측: 서명본 468항목(MANIFEST 1 · SF 1 · 인증서 1) / 미서명본 465항목(셋 다 0).재빌드는 웹 내용이 바뀔 때 필요한 것이 아니다. TWA 는 웹사이트를 띄우는 껍데기라 번들 안에 웹 내용이 없다. 재빌드가 필요한 때는 시작 주소·패키지명·아이콘·versionCode 가 바뀔 때다. 그리고 재빌드할 때마다 서명이 다시 빠지므로 서명하기.ps1 을 다시 돌려야 한다.
업로드 값: 패키지 kr.retools.realtycalc · versionName 1.0.0 · versionCode 2.
앱 이름이 두 곳에 적혀 있고, 실제로 컴파일되는 것은 app\build.gradle 쪽이다.
| 파일 | 무엇 |
|---|---|
twa-manifest.json 의 "name" | bubblewrap 이 읽는 원본 |
app\build.gradle 의 twaManifest.name | Gradle 이 resValue "string", "appName" 으로 굽는 값 |
bubblewrap update 를 돌리면 앞의 것에서 뒤의 것이 다시 만들어지지만, 그것을 돌리지 않고
파일만 고치면 앞쪽만 새 이름이 되고 앱에는 옛 이름이 박힌다. 이름을 바꿀 때는 두 곳을
같이 고친다. 스플래시 배경색(backgroundColor)도 같은 성질이다.
빌드가 서지 않는 이유가 둘이고 둘 다 저장소 밖에 있다.
JAVA_HOME 이 없다. gradlew.bat 을 그냥 돌리면
ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH 로 선다.
이 PC 의 자바는 bubblewrap 이 %USERPROFILE%\.bubblewrap 아래에 받아 둔 것이라 PATH 에 없다.
Gradle 은 JRE 가 아니라 JDK 가 필요하다 — java.exe 옆에 javac.exe 가 있어야 한다.local.properties 가 없으면 안드로이드 SDK 를 못 찾는다. 저장소에 들어가지 않는 파일이라
PC 를 바꾸면 또 없다. 이 PC 의 SDK 는 C:\Users\xchan\.bubblewrap\android_sdk 이고
C:\android-build\local.properties 에 sdk.dir 로 적어 두었다.그래서 C:\android-build\빌드하기.ps1 을 만들었다. 서명하기.ps1 이 jarsigner 를 찾는 것과
같은 방법으로 JDK 를 찾아 그 창에서만 JAVA_HOME 을 잡고 빌드를 돌린다. 대표가 환경변수를
손볼 것이 없다. 빌드가 끝나면 이어서 서명할지 물어본다.
cd C:\android-build
.\빌드하기.ps1
2026-09-05 에 실제로 난 일: 이름을 고치고 재빌드를 돌렸는데 위 1번(JAVA_HOME)으로 빌드가 서지 않았고, 그 사실을 모른 채 8월에 만든 옛 번들에 서명이 다시 붙었다. 서명 확인은 통과했다. 서명은 정말로 붙었기 때문이다. 옛 이름이 박힌 번들이 Play Console 에 올라갈 뻔했다.
서명이 붙었는지만 보고 무엇에 붙었는지는 안 본 것이 문제였다. 그래서 서명하기.ps1 이 서명하기
전에 둘을 더 본다.
app\build.gradle 보다 새것인가. 옛것이면 재빌드가 안 돈 것이므로 서명하지 않고 멈춘다.twa-manifest.json 의 이름과 같은가. 다르면 서명하지 않고 멈춘다.★ 확인은 「됐는가」가 아니라 「무엇이 됐는가」를 보아야 한다.
/calc 서브패스 임베드 배포xchanz-tech에서 GitHub Organization **retools-labs**로 이전됨.
GitHub: https://github.com/retools-labs/calc-realty (기존 URL은 GitHub이 자동 리다이렉트).realtybook.retools.kr/calc 신설을 위해 같은 저장소를 가리키는 **별도의 두 번째 Vercel
프로젝트 calc-realty-embed**를 만들고, 거기에만 NEXT_PUBLIC_BASE_PATH=/calc 환경변수를
설정함. next.config.js가 이 값을 읽어 Next.js basePath로 적용하도록 수정(basePath = process.env.NEXT_PUBLIC_BASE_PATH || "").
기존 calc-realty.vercel.app(루트, 안드로이드 TWA와 연결) 배포는 이 환경변수가 없어 그대로
루트로 동작 — 절대 영향받지 않음.lib/basePath.ts의 BASE_PATH 상수를, Next.js basePath가 자동으로 못 붙이는
절대경로 참조(순수 <img src="/...">, fetch("/..."), navigator.serviceWorker.register("/sw.js"))
세 군데(app/page.tsx, components/ReceiptCard.tsx, components/JeonseConversionCalculator.tsx,
components/InstallPrompt.tsx)에 수동으로 붙여서 /calc 배포에서도 아이콘·API·서비스워커가
정상 작동하도록 수정함. 앞으로 절대경로(/로 시작)로 뭔가를 참조하는 코드를 추가할 때는
반드시 BASE_PATH를 앞에 붙일 것 — 안 그러면 /calc 배포에서만 조용히 깨진다(루트 배포는
BASE_PATH가 빈 문자열이라 티가 안 남).realtybook.retools.kr)의 vercel.json이 /calc/:path*를
calc-realty-embed.vercel.app으로 rewrite(프록시)해서 최종적으로 realtybook.retools.kr/calc가
뜨는 구조. retools.kr 메인 페이지("무엇을 만들고 있나요" 섹션 RealtyBook 카드)에서도 이제
realtybook.retools.kr로 직접 링크됨.MovingCostCalculator