문제
부동산실록은 원래 PHP로 된 부동산 계산기 사이트였어요. 값을 넣고 계산하면 PHP 엔드포인트로 보내고, 서버가 결과 HTML을 통째로 돌려주는 구조였어요. 화면은 Bootstrap 4와 jQuery 3였고요. 기존 계산기 56개를 하나씩 분석하는 데서 시작했어요.
집을 고르는 사람에게는 계산기만으로 부족했어요. 그런데 실거래가 같은 공공데이터는 신고 지연, 해제 거래, 직거래 이상치 때문에 그대로 믿기 어려웠어요.
“국토부 API는 신고지연·해제거래·직거래 이상치로 실시간성/정확성이 낮고…”
목표
- 계산기를 50개 넘게 갖추고, 세율이 바뀌면 자동으로 갱신한다.
- 검색에 잘 걸리도록 서버에서 렌더링하고, 모바일과 다크 모드를 지원한다.
- 실거래가, 주소, 건축물 정보를 모으되, 믿을 수 있는지 대조해서 보여 준다.
- 커뮤니티와 기사로 다시 찾아올 이유를 만든다.
풀어 간 과정
처음 시도한 방법, 막힌 지점, 방향을 바꾼 이유를 시간 순서로 적었어요.
2026년 3월
원래 사이트를 계산기 단위로 뜯어보기
원래 사이트의 계산기 56개를 입력 필드와 PHP 요청 구조까지 하나씩 문서로 남겼어요. 반응형 웹 하나로 다시 만들고, 옛 사이트에서는 위키와 세율표만 옮기기로 정했어요. 옛 기능마다 무엇으로 대신할지 표로 맞춰 보다가, CSV 내보내기와 사용량 통계가 빠진 것도 찾아 되살렸어요.
2026년 5월
공공데이터는 서로 대조해서
실거래가, 주소, 지오코딩 API를 비교해 보니 어느 하나로는 다 채울 수 없었어요. 지오코더에는 건물을 계속 가리키는 ID가 없고, 실거래가에는 정규화된 주소 키가 없었거든요. 그래서 도로명주소와 건축물대장으로 보강하고, 실거래가와 도로명주소는 건물관리번호가 정확히 같을 때만 잇는 교차 검증을 넣었어요.
2026년 7월
로드맵을 다시 보며 빈 곳 찾기
기능 로드맵을 점검하면서 데이터 품질을 다루는 층이 아직 없다는 걸 적어 두었어요. 국토부 데이터를 그대로 믿기 어려운 이유도 이 문서에 정리했어요.
지금의 구조
공공데이터를 모아 검증하고, 계산기와 지도로 보여 주는 순서예요.
- 1
국토부 실거래가를 모으고, 도로명주소와 건축물대장으로 보강하고, 행정안전부 건물 도형을 들여와요.
- 2
수집할 때마다 기록을 남기고, 실거래가와 도로명주소는 서로 대조해 검증해요.
- 3
청약 공고는 출처 5곳을 우선순위에 따라 합치고 중복을 지워요.
- 4
세법을 반영한 계산 엔진이 Decimal로 계산해요. 계산기는 66종이에요.
- 5
단지 요약은 AI가 쓰고, 입력값의 해시로 7일 동안 캐시해요.
- 6
기사는 LLM이 초안을 쓰고, 점수 기준을 넘은 것만 자동으로 발행돼요.


핵심 결정
기사는 점수 기준을 통과한 초안만 자동 발행
- 이유
- LLM에게는 초안만 맡기고, 발행과 재시도, 중복 방지는 결정적인 코드가 맡게 했어요. 도입부 점수와 내용 점수가 모두 7점 이상이고 검토 경고가 없어야 자동 발행되고, 못 넘은 초안은 보류돼요.
- 다른 선택지
- 모든 초안을 사람이 승인. 지금도 설정 하나로 이 방식으로 바꿀 수 있어요.
- 감수한 것
- 점수가 LLM의 자기 평가라 치우칠 수 있어요.
AI 요약은 캐시하고, 실패하면 규칙 기반으로
- 이유
- 페이지를 열 때마다 LLM을 부르면 느리고, AI 서버가 멈추면 페이지도 비어요. 입력값이 같으면 7일 동안 같은 요약을 쓰고, AI 서버가 응답하지 않으면 규칙으로 만든 요약을 보여 줘요.
- 다른 선택지
- 페이지를 볼 때마다 LLM을 직접 호출
- 감수한 것
- 최대 7일 지난 요약이 보일 수 있고, 대체 요약은 덜 풍부해요.
지도는 많이 그리기보다 빠르게
- 이유
- 전국 단지를 한 번에 그리면 브라우저가 버티지 못해요. 화면 안의 전체 단지 수는 클러스터로 보존하고, 개별 마커는 확대했을 때만 그려요.
- 다른 선택지
- 모든 단지 마커를 한 번에 그리기
- 감수한 것
- 넓게 볼 때는 개별 단지를 바로 누를 수 없어요.
어려웠던 문제
겪었거나 배포 전에 막은 문제를 어떻게 알아챘고, 무엇을 의심해 원인을 찾았는지, 다시 일어나지 않게 무엇을 바꿨는지 기록했어요.
보유세 전년 대비 증감이 늘 0원이던 문제
당시 계산 엔진 58개를 법령과 하나씩 대조하는 감사에서 P0 버그 4건을 찾았어요. 그중 보유세 통합 계산기는 전년 대비 증감이 항상 0원이었고 세부담상한도 없었어요. 상속세 장애인공제의 기대여명을 80세로 어림한 문제와, 임대소득세 경비율을 사례 하나에서 가져온 문제도 함께 나왔어요.
왜 어려웠나
0원은 그럴듯한 값이라 오류처럼 보이지 않았어요. 게다가 전년 세액이 0으로 고정돼 있어서, 버그가 있든 없든 화면 결과가 똑같았어요. 법을 옆에 두고 계산식을 한 줄씩 대조하지 않으면 찾을 수 없는 종류였어요.
추적 과정
1. 증감 필드가 실제로 비교를 할까?
전년 세액에서 전년 세액 자신을 빼고 있었어요.
2. 전년 값은 어디서 올까?
0으로 고정돼 있었고, 값을 입력받는 경로 자체가 없었어요.
3. 법과 대조하면 무엇이 빠졌을까?
지방세법 제122조의 세부담상한이 구현돼 있지 않았어요.
4. 다른 계산기도 같은 종류의 문제가 있을까?
같은 방식으로 상속세, 임대소득세, 양도세에서 P0를 더 찾았어요. 양도세는 화면에서 받는 입력 8개가 계산에 쓰이지 않고 있었어요.
원인
보유세 엔진이 전년 세액을 입력받지 않고 0으로 고정한 채, 증감을 '전년 세액 빼기 전년 세액'으로 계산했어요.
해결
전년 세액을 입력으로 받고, 1세대 1주택은 105%, 그 밖에는 130%를 세부담상한으로 적용해요. 증감은 올해 세액에서 전년 세액을 빼요. 같은 감사에서 상속세는 통계청 기대여명표와 선형 보간으로, 임대소득세는 단순경비율과 기준경비율을 나누고 간주임대료를 계산하게 고쳤어요.
고치기 전
previous_year_total = 0
"year_over_year_change": previous_year_total - previous_year_total,고친 뒤
previous_year_total = self._to_int(self.inputs.get("previous_year_tax", 0))
tax_burden_cap_rate = 1.05 if is_one_household_one_home else 1.30
if previous_year_total > 0:
tax_burden_cap_amount = self.floor_to_won(previous_year_total * tax_burden_cap_rate)
if total_holding_tax > tax_burden_cap_amount:
tax_burden_cap_applied = True
total_holding_tax = tax_burden_cap_amount
"year_over_year_change": total_holding_tax - previous_year_total,검증
- 보유세 테스트 3개 (전년 세액을 넣으면 상한 적용, API 별칭 입력, 전년 미입력 시 증감은 총액)
- 상속세 6개, 임대소득세 5개, 양도세 6개 테스트 추가
- 감사 전체 테스트 302개 통과
재발 방지
- 코드에 박혀 있던 세율마다 이름 붙은 상수와 법령 출처를 달았어요.
- 이번 범위 밖 항목은 P3로 따로 적어, 다음 감사에서 이어 보게 했어요.
- 주택 세부담상한을 공시가격 구간별로 더 나누는 일은 다음 감사 과제로 남아 있어요.
남긴 원칙
그럴듯한 0원이 가장 위험해요. 세금 계산은 법 조항과 한 줄씩 대조해야 해요.
배포 뒤 열어 둔 탭이 오류 화면에 갇히던 문제
배포 전에 열어 둔 탭에서 계산기, 커뮤니티, 부동산, 마이페이지로 이동하면 오류 화면에 갇혔고, '다시 시도'를 눌러도 돌아오지 않았어요. 배포 직후 모든 계산기에서 재현됐어요.
왜 어려웠나
새로 연 탭에서는 재현되지 않았어요. 오류 화면은 일반적인 문구로 원인을 가렸고, 화면의 '다시 시도'로는 이미 사라진 파일을 다시 받아 올 수 없었어요.
추적 과정
1. API가 문제일까?
네 화면 묶음에서 동시에 났어요. 서버보다 브라우저의 파일 로딩을 먼저 의심했어요.
2. 무엇이 사라졌을까?
배포로 컨테이너가 바뀌면서 이전 빌드의 지연 로딩 청크 파일이 모두 사라졌어요.
3. 어떻게 되살릴 수 있을까?
열려 있는 탭이 새 빌드를 받는 방법은 전체 새로고침뿐이었어요.
4. 고쳐졌는지 어떻게 확인할까?
로컬 프로덕션 빌드에 헤드리스 브라우저로 청크 404를 일부러 넣었어요. 고치기 전에는 오류 화면에 갇히고, 고친 뒤에는 한 번 새로고침된 뒤 정상으로 떴어요.
원인
네 곳의 오류 경계(error.tsx)가 오류를 기록만 하고, 청크 로딩 오류인지 구분하지 않았어요.
- 1이전 빌드의 파일 목록을 들고 열려 있음이전 컨테이너
- 2배포, 새 컨테이너, 이전 청크 삭제
- 3화면 이동, 이전 청크 요청404
- 4청크 로딩 오류, 오류 화면에 갇힘
- 5(고친 뒤) 한 번 새로고침해 새 빌드를 받음새 청크 응답
해결
청크 로딩 오류면 한 번만 새로고침해요. 마지막 새로고침 시각을 세션 저장소에 남겨 30초 안에는 다시 하지 않고, 저장소를 쓸 수 없으면 반복을 막을 방법이 없으니 자동 새로고침을 아예 하지 않아요.
고치기 전
useEffect(() => {
console.error("Calculator error:", error);
}, [error]);고친 뒤
useEffect(() => {
if (reloadOnChunkLoadError(error)) return;
console.error("Calculator error:", error);
}, [error]);
// chunk-reload.ts 핵심
try { lastReloadAt = Number(sessionStorage.getItem(KEY)) || 0; }
catch { return false; } // 저장소가 없으면 반복을 막을 수 없으니 새로고침하지 않아요
if (Date.now() - lastReloadAt < 30_000) return false;
sessionStorage.setItem(KEY, String(Date.now()));
window.location.reload();
return true;검증
- 단위 테스트 8개 (이름과 메시지로 감지, 무관한 오류 무시, 한 번만 새로고침, 쿨다운 전후, 저장소 없음)
- 헤드리스 브라우저로 청크 404를 넣어 고치기 전후를 비교
- 프론트엔드 테스트 1,332개, 타입 검사, 린트, 빌드 통과
재발 방지
- 30초 쿨다운으로 새로고침이 반복되지 않게 막아요.
- 세션 저장소를 쓸 수 없는 환경에서는 자동 새로고침을 하지 않아요.
남긴 원칙
반복을 막을 장치가 없으면, 자동 복구를 하지 않는 편이 안전해요.
결과
- 계산기
- 66종 (원래 사이트는 56종)
- 계산기 감사
- P0 버그 4건 수정, 테스트 302건 통과
- 단지 좌표
- 190,857개 중 173,602개 확보 (91%)
- 백엔드 테스트
- 테스트 함수 1,027개
- 계산할 때마다 페이지를 통째로 다시 받던 구조에서, 입력은 화면에서 받고 계산은 API가 맡는 구조로 바뀌었어요.
- 계산기, 실거래가 지도, 청약 공고, 부동산 위키와 커뮤니티를 한 사이트에서 써요.
