공공데이터를 미리 많이 넣어두면 서비스 품질이 좋아지지 않을까?
라고 생각했다. 실제로도 일리가 있다.
ZIP:ON은 계약 전 부동산 위험을 진단하는 서비스다. 사용자가 주소를 입력하면 실거래가, 공시가격, 건축물대장, 지역 통계 등을 바탕으로 “이 매물을 계약하기 전에 무엇을 확인해야 하는지” 보여주는 것이 목표다.
그러려면 당연히 데이터가 많을수록 좋을 것 같았다.
전국 실거래가를 적재하고, R-ONE 통계를 저장하고, V-World 공시가격을 snapshot으로 남기고, 건축물대장, 주택도시보증공사 등 부동산 관련 OPEN API가 많으니 모두 적재하고 가치있는것을 연결하면 어떤 주소가 들어와도 풍부한 분석을 제공할 수 있을 것처럼 보였다.
하지만 실제로 구현해보니 며칠동안 적재를 위한 호출을 해야했다.
그래서 어디까지 적재하는 것이 서비스 가치에 비해 합리적인가? 라는 생각이 들 수밖에 없었다…
실거래가와 통계는 대량 적재에 잘 맞았다
처음에는 분위기가 나쁘지 않았다.
실거래가 fact는 146,415건까지 적재되었고, R-ONE 통계 데이터도 대량 저장이 가능했다.
이런 데이터는 batch 처리와 잘 맞는다. 지역 코드와 기간을 기준으로 요청 범위를 나눌 수 있고, 결과를 fact table에 누적하기도 쉽다.
지역 코드 선택
→ 기간 선택
→ 외부 API 호출
→ 중복 제거
→ fact table 저장
→ 수집 이력 기록이 단계까지만 보면 전국 단위 적재도 충분히 가능해 보인다.
문제는 공시가격과 건축물대장으로 넘어가면서 드러났다.
데이터는 있어도 주소와 연결되지 않으면 쓸 수 없다
부동산 데이터에서 중요한 것은 단순한 row 수가 아니다.
핵심은 사용자가 입력한 주소와 외부 데이터가 정확히 연결되는지다.
여기서 PNU가 중요해진다.
PNU는 특정 필지를 식별하는 코드다. 공시가격이나 건축물대장 같은 데이터는 PNU가 있어야 강하게 연결된다.
하지만 현실의 주소 데이터는 깔끔하지 않다.
사용자는 도로명주소를 입력할 수 있다. 실거래가 데이터에는 법정동명과 지번은 있지만 PNU가 없을 수 있다. 건축물대장에서는 여러 후보가 나올 수도 있다.
결국 필요한 것은 단순한 성공/실패가 아니었다.
정확히 연결됨
건축물대장에서 파생됨
주소 검색으로 파생됨
실거래가에서 복원됨
복수 후보 존재
지역 단위까지만 연결
미해결이런 연결 상태를 관리하지 않으면 데이터는 많이 쌓여 있어도 서비스에서 제대로 쓰기 어렵다.
공공데이터 서비스에서 중요한 것은 “많이 가지고 있는가”가 아니라 “사용자의 입력과 얼마나 신뢰성 있게 연결되는가”였다.
Redis를 지도 캐시로 쓰면 되지 않을까?
중간에 이런 생각도 했다.
사용자가 보고 있는 지도 영역만 Redis에 캐싱하면 되지 않을까?
예를 들어 사용자가 관악구 일부를 보고 있다면, 해당 viewport 안의 실거래가나 공시가격 후보만 Redis에 올려두는 방식이다. 지도 이동이나 확대/축소가 많으니 Redis를 쓰면 반응성이 좋아질 것 같았다.
이 생각 자체는 틀리지 않다.
Redis는 지도 화면의 짧은 응답 캐시로는 쓸 수 있다.
현재 지도 bounds
줌 레벨
필터 조건
매물 유형
최근 조회 결과이런 조합을 key로 잡고 짧은 TTL을 주면, 같은 영역을 반복해서 볼 때 API나 DB 부하를 줄일 수 있다.
하지만 이 방식은 ZIP:ON의 핵심 데이터 전략이 되기 어렵다.
이유는 지도 캐시와 진단 근거의 목적이 다르기 때문이다.
지도는 사용자의 움직임에 따라 계속 바뀐다.
조금만 이동해도 bounds가 바뀌고, 줌 레벨이 달라지면 필요한 데이터 밀도도 달라진다. 같은 지역이라도 넓게 볼 때는 집계가 필요하고, 좁게 볼 때는 개별 건물이나 거래 정보가 필요하다.
즉 지도용 데이터는 “빠르게 보여주기 위한 데이터”다.
반면 ZIP:ON의 진단 데이터는 “왜 이런 위험 판단이 나왔는지 설명하기 위한 근거”다. 이 데이터는 수집 시점, 원천, 연결 방식, 실패 사유가 남아야 한다.
Redis에 viewport 결과를 캐싱할 수는 있다.
하지만 그것이 공시가격 snapshot이나 건축물대장 연결 결과를 대체할 수는 없다.
정리하면 Redis의 역할은 이 정도가 맞다.
Redis
→ 지도 조회 결과 캐시
→ 중복 API 호출 방지
→ batch lock
→ rate limit 제어
→ 짧은 TTL의 임시 상태
MySQL
→ 진단 근거 저장
→ 공시가격 snapshot
→ 건축물대장 연결 이력
→ PNU 후보 상태
→ 실패 사유와 수집 시점Redis는 속도를 위한 장치이지, 판단 근거의 원본 저장소는 아니다.
전국 전체 적재는 제품 검증에 비해 무거웠다
관악구 건축물대장 batch를 돌려보니 대략적인 감이 왔다.
대상: 679건
성공: 573건
복수 후보: 105건
미조회: 1건
저장 snapshot: 1,137건
소요 시간: 약 7분679건에 7분이면 1건당 약 0.6초다.
처음에는 아주 느리다고 생각하지 않았다. 하지만 후보 universe가 4만 건만 되어도 단순 계산으로 6~7시간이 걸린다. 여기에 API 제한, 네트워크 지연, 실패 재시도, 복수 후보 검토까지 들어가면 전국 전체 백필은 금방 운영 문제가 된다.
이때 질문이 바뀌었다.
어떻게 하면 전부 넣을 수 있을까?가 아니라,
전부 넣는 것이 지금 제품 단계에서 맞는가?가 되었다.
프로토타입에서 중요한 것은 전국 coverage가 아니었다
ZIP:ON이 검증해야 하는 것은 전국 모든 주소를 완벽히 분석하는 능력이 아니다.
핵심은 사용자가 주소를 입력했을 때 다음 흐름이 납득 가능하게 이어지는지다.
주소 정제
→ PNU 후보 생성
→ 공시가격 또는 대체 근거 조회
→ 실거래가 비교
→ 지역 통계 해석
→ 위험도 판단
→ 추가 확인사항 안내이 흐름을 검증하는 데 전국 전체 데이터가 반드시 필요한 것은 아니다.
오히려 지금 단계에서는 최신성이 높고 설명력이 있는 데이터를 먼저 제공하는 편이 낫다.
그래서 전략을 바꿨다.
전국 전체 적재
→ 하지 않는다
지역별 최신순 500건
→ 우선 적재한다
사용자가 직접 입력한 주소
→ 필요할 때 on-demand 조회한다
PNU가 확정된 후보
→ 공시가격을 조회한다
PNU가 불확실한 후보
→ 보류 상태로 남기고 대체 근거를 제공한다이건 단순한 축소가 아니다.
제품 검증에 필요한 데이터와, 나중에 확장할 데이터를 분리한 것이다.
위험도와 데이터 연결 상태는 분리해야 한다
이번 작업에서 가장 중요했던 판단은 이것이다.
데이터가 부족하다고 해서 곧바로 위험한 것은 아니다.
공시가격을 못 찾았다고 해서 진단이 실패한 것도 아니다.
예를 들어 PNU가 확정되지 않았더라도 제공할 수 있는 근거는 있다.
유사 실거래가
법정동 단위 통계
시군구 단위 시장 흐름
R-ONE 지표
건축물대장 후보 상태
직접 확인이 필요한 항목따라서 사용자에게는 두 가지를 분리해서 보여줘야 한다.
위험도
→ 확보된 근거로 판단한 계약 리스크
데이터 연결 상태
→ 어떤 데이터가 연결되었고, 무엇이 불확실한지“공시가격 확인 불가”는 “위험함”과 같은 말이 아니다.
그건 데이터 연결 상태의 문제다.
반대로 데이터가 많이 연결되었다고 해서 무조건 안전한 것도 아니다. 연결된 데이터 안에서 보증금 비율이 과도하거나, 최근 거래 대비 가격이 이상하면 위험 신호가 될 수 있다.
이 둘을 분리해야 서비스가 정직해진다.
결론: 많이 저장하는 것보다 설명 가능하게 저장해야 한다
처음에는 데이터를 많이 넣으면 좋은 서비스가 될 것이라고 생각했다.
하지만 실제로는 그렇지 않았다.
공공데이터 기반 서비스에서 중요한 것은 데이터 양보다 데이터 책임이었다.
이 데이터는 어디서 왔는가?
언제 수집했는가?
어떤 주소와 연결되었는가?
연결 신뢰도는 어느 정도인가?
실패했다면 왜 실패했는가?
사용자에게 어떤 근거로 보여줄 것인가?이 질문에 답하지 못하면 데이터가 많아질수록 오히려 서비스는 불안정해진다.
ZIP:ON의 데이터 전략은 그래서 이렇게 바뀌었다.
많이 저장하기
→ 설명 가능한 근거를 저장하기
전국 전체 적재
→ 지역별 최신순 500건 우선 적재
모든 주소 즉시 분석
→ 대표 데이터 선적재 + on-demand 조회
API 성공/실패
→ READY, SUCCESS, HOLD, SKIPPED, FAILED 상태 관리
PNU 없으면 진단 실패
→ PNU 연결 상태와 위험도 판단 분리
Redis에 데이터 저장
→ Redis는 지도 캐시와 제어용, MySQL은 근거 저장용공공데이터를 사용하는 서비스에서 “전부 저장”은 매력적인 목표처럼 보인다.
하지만 프로토타입에서 더 중요한 것은 완벽한 전국 coverage가 아니었다.
중요한 것은 사용자가 납득할 수 있는 최신 근거를 제공하고, 그 근거의 한계도 정직하게 보여주는 구조였다.
결국 이번 작업의 결론은 단순하다.
데이터를 얼마나 많이 가지고 있는가보다,
그 데이터를 얼마나 책임 있게 설명할 수 있는가가 더 중요하다.