흐름은 이렇습니다
1. 기존 구조가 왜 나왔는지
2. 그 구조가 어떤 점에서는 합리적이었는지
3. 그런데 실제 사용자 화면에서 어떤 오해가 생겼는지
4. 그래서 무엇을 분리했는지
5. 개선 후 어떤 판단 흐름이 되었는지위험한 매물과 판단이 부족한 매물을 분리하기
ZIP:ON은 사용자가 계약을 고민하는 특정 매물에 대해 “이 매물을 계약해도 되는지 판단하기 전에 무엇을 확인해야 하는가”를 알려주는 서비스다.
그래서 위험도 산정 로직을 만들 때 처음부터 가장 중요했던 것은 점수의 정교함보다 잘못 확신하지 않는 것이었다. 부동산 계약에는 보증금이라는 큰돈이 걸려 있고, 서비스가 “안전하다”고 말하는 순간 사용자는 그 말을 실제 판단 근거로 받아들일 수 있다.
이 때문에 기존 구조는 꽤 보수적으로 설계되었다.
| 설계 선택 | 당시 의도 |
|---|---|
| AI가 최종 점수를 결정하지 않음 | 같은 입력에는 같은 결과가 나와야 하고, 판단 근거를 추적할 수 있어야 함 |
| 백엔드가 항목별 점수와 가중치로 최종 등급 계산 | AI 응답이 흔들려도 서비스의 최종 판단은 통제 가능해야 함 |
| 등기부, 선순위 임차인, 보증보험은 0점 가중치로 분리 | 공공데이터만으로 확정하기 어려운 항목을 무리하게 점수화하지 않기 위함 |
| 데이터가 부족하면 보수적으로 처리 | 근거가 부족한데 “안전”처럼 보이는 것을 막기 위함 |
이 구조는 멍청한 설계가 아니었다. MVP 단계에서는 오히려 안전한 선택이었다.
문제는 사용자가 보는 화면에서 이 의도가 충분히 드러나지 않았다는 점이다.
기존 구조의 핵심 문제
기존 위험도 산정에는 여러 층이 있었다.
| 구분 | 역할 |
|---|---|
riskAssessment.totalRiskScore | 정확 주소 진단의 공식 위험 점수 |
| rule-based fallback | AI 비활성 또는 실패 시 사용하는 기본 산정 로직 |
| 보증금/월세 비율 계산기 | 보증금이 가격 대비 높은지 문장으로 설명 |
| 관심 부동산 리포트 점수 | 저장된 진단 이력 또는 리포트 근거 상태를 화면에 표시 |
내부적으로는 역할이 나뉘어 있었지만, 사용자가 보는 결과에서는 다음 네 가지가 섞여 보일 수 있었다.
| 실제 의미 | 사용자에게 보일 수 있는 표현 | 문제 |
|---|---|---|
| 실제 위험 신호가 높음 | 위험 등급 높음 | 정상 |
| 자료가 부족함 | 추가 확인 필요, 보류 | 위험한 것인지 판단 불가인지 헷갈림 |
| 직접 확인이 필요함 | 체크리스트, 확인 필요 | 총점보다 약하게 보일 수 있음 |
| 리포트 근거가 부족함 | 대시보드 점수 낮음 | 위험 점수로 오해될 수 있음 |
사용자는 내부 구조를 모른다. 화면에 D등급, 추가 확인 필요, 건물 유형 확인 불가, 자료 부족이 함께 보이면 이렇게 받아들일 수 있다.
“이게 진짜 위험하다는 건가, 아니면 시스템이 판단을 못 했다는 건가?”
이 질문에 명확히 답하지 못하는 것이 기존 구조의 가장 큰 문제였다.
문제 1. 위험도와 자료 부족이 같은 점수에 섞였다
기존 계산은 대략 이런 형태였다.
totalRiskScore = 실제 위험 점수 + 불확실성 벌점이 방식은 나름의 이유가 있다. 자료가 부족한데 위험도를 낮게 보여주면 사용자가 안전하다고 오해할 수 있기 때문이다.
하지만 실제 위험과 자료 부족은 다르다.
| 케이스 | 의미 | 사용자가 알아야 할 것 |
|---|---|---|
| 보증금이 매매가의 90% 이상 | 실제 위험이 높음 | 계약 자체가 위험할 수 있음 |
| 매매가·공시가격·실거래가 근거 없음 | 판단 근거 부족 | 위험하다고 단정할 수 없지만, 판단 보류가 필요함 |
둘 다 높은 점수로만 보이면 사용자는 차이를 알 수 없다.
그래서 개선 방향은 단순하다.
| 기존 | 개선 |
|---|---|
totalRiskScore 하나에 위험과 불확실성을 합산 | 위험도, 근거 충족도, 판정 상태를 분리 |
| 자료 부족을 점수 벌점으로 표현 | 자료 부족은 evidenceQuality와 decisionStatus로 표현 |
| 높은 점수의 이유가 불명확 | “위험해서 높은지”, “근거가 부족한지” 구분 가능 |
개선 후에는 결과가 이렇게 나뉘어야 한다.
| 필드 | 의미 |
|---|---|
riskScore | 실제 위험 신호 |
evidenceQualityScore | 판단 근거가 얼마나 충분한지 |
decisionStatus | 진단 가능, 추가 확인 필요, 판단 보류 등 |
manualCheckBlockers | 계약 전 직접 확인해야 하는 항목 |
예를 들어 같은 “조심해야 하는 매물”이라도 문구가 달라져야 한다.
| 상황 | 사용자 문구 |
|---|---|
| 실제 위험 높음 | “보증금이 확인 가능한 가격 기준 대비 높은 편입니다.” |
| 자료 부족 | “현재 자료만으로는 위험도를 단정하기 어렵습니다.” |
| 직접 확인 필요 | “등기부 권리관계 확인 전까지 계약 안전 여부는 확정할 수 없습니다.” |
문제 2. 보증금 비율이 최종 점수에 충분히 반영되지 않았다
전세·월세 위험진단에서 가장 중요한 질문은 결국 이것이다.
보증금이 집값 대비 과한가?
기존에도 보증금 비율 계산기는 있었다. 매매가 대비 보증금 비율, 공시가격 대비 보증금 비율을 보고 주의 또는 고위험을 판단하는 기준도 있었다.
| 기준 | 의미 |
|---|---|
| 매매가 대비 60% 이상 | 주의 |
| 매매가 대비 80% 이상 | 고위험 |
| 공시가격 대비 80% 이상 | 주의 |
| 공시가격 대비 100% 이상 | 고위험 |
문제는 이 계산 결과가 DEPOSIT_TO_VALUE_RISK의 실제 점수에 직접 연결되지 않을 수 있다는 점이었다.
기존 fallback은 가격 근거가 하나라도 있으면 보증금 위험을 중간 점수로 두는 식의 구조를 가질 수 있었다.
가격 근거 있음 → DEPOSIT_TO_VALUE_RISK = 50점
가격 근거 없음 → DATA_MISSING이것도 초기에는 현실적인 선택이었다. 외부 API 데이터 품질이 일정하지 않고, 매매가·공시가격·시장 signal의 신뢰도가 케이스마다 다르기 때문에, MVP에서는 “일단 중간 위험으로 두고 문장 설명에서 보완하자”는 판단이 가능하다.
하지만 이 방식은 서비스가 위험도를 설득해야 하는 단계에서는 부족하다.
| 매매가 | 보증금 | 비율 | 실제 위험 |
|---|---|---|---|
| 5억 | 2억 | 40% | 낮음 |
| 5억 | 4.7억 | 94% | 매우 높음 |
둘 다 “가격 근거 있음”이라는 이유로 비슷한 점수를 받으면 위험도 산정의 설득력이 떨어진다.
따라서 보증금 비율 계산은 문장 요약에만 쓰일 것이 아니라 최종 위험 점수에 직접 반영되어야 한다.
| 기존 | 개선 |
|---|---|
| 가격 근거가 있으면 일단 50점 | 보증금/매매가, 보증금/공시가격 비율로 점수화 |
| 계산기는 설명 문장 중심 | 계산 결과를 DEPOSIT_TO_VALUE_RISK에 직접 연결 |
| 자료 없음은 중간 위험처럼 보일 수 있음 | 자료 없음은 위험 점수가 아니라 근거 부족으로 처리 |
추천 방향은 다음과 같다.
| 조건 | 위험도 처리 |
|---|---|
| 보증금 / 매매가 < 60% | 낮은 위험 |
| 60% 이상 | 주의 |
| 80% 이상 | 고위험 |
| 90% 이상 | 매우 고위험 |
| 가격 근거 없음 | 위험 50점 고정 금지, 판단 근거 부족으로 처리 |
핵심은 ,, 자료가 없다는 것은 “중간 정도로 위험하다”가 아니라 “판단할 근거가 부족하다”는 뜻이라는 점.
문제 3. 직접 확인 항목이 총점 뒤에 숨을 수 있었다
기존 템플릿에서 등기부, 선순위 임차인, 보증보험 가능 여부는 0.00 가중치였다.
| 항목 | 0점 가중치로 둔 이유 |
|---|---|
| 등기부 권리관계 | 공공데이터만으로 권리관계를 확정하기 어려움 |
| 선순위 임차인 | 특히 다가구·단독주택에서 자동 확인이 어려움 |
| 보증보험 가능 여부 | 보증기관 기준과 개별 조건 확인 필요 |
| 계약 체크리스트 | 사용자가 계약 전 직접 확인해야 함 |
이 설계 자체는 맞다. 자동으로 확정할 수 없는 항목을 점수화하면 더 위험하다.
하지만 0점 가중치가 화면에서 “중요하지 않다”처럼 보이면 문제가 된다.
| 개발자의 의도 | 사용자의 오해 |
|---|---|
| 자동 점수화하지 않는다 | 총점에 영향이 없으니 덜 중요한 항목인가? |
| 직접 확인해야 한다 | 참고사항 정도인가? |
| 점수에는 넣지 않는다 | 위험도 낮으면 계약해도 되는 것 아닌가? |
따라서 이 항목들은 총점에 넣기보다 판정 gate로 써야 한다.
| 상태 | 점수 처리 | 화면 처리 |
|---|---|---|
| 등기부 미확인 | riskScore에 직접 가산하지 않음 | “등기부 확인 전까지 계약 안전 여부 확정 불가” |
| 보증보험 미확인 | riskScore에 직접 가산하지 않음 | “보증보험 가능 여부 확인 필요” |
| 선순위 임차인 미확인 | riskScore에 직접 가산하지 않음 | 다가구·단독주택이면 강한 경고 |
즉 개선 방향은 이렇다.
0점 가중치 항목 = 점수 제외
0점 가중치 항목 = 화면 영향 없음이 아니라,
0점 가중치 항목 = 점수 제외
0점 가중치 항목 = 판정 상태와 경고 문구에 강하게 반영이어야 한다.
문제 4. 관심 부동산 리포트의 점수와 위험 점수가 섞일 수 있었다
정확 주소 진단과 관심 부동산 리포트는 비슷해 보이지만 내부 흐름이 다르다.
| 구분 | 흐름 |
|---|---|
| 정확 주소 진단 | 입력 주소와 보증금 조건으로 즉시 위험도 산정 |
| 관심 부동산 리포트 | 저장된 진단 이력을 주소로 다시 찾아 붙임 |
관심 리포트는 주소 문자열로 진단 이력을 찾는 구조를 가질 수 있다.
LOWER(address) = #{addressValue}
OR LOWER(COALESCE(normalized_address, '')) = #{addressValue}이 방식도 초기에는 간단하고 빠르다. 하지만 주소는 시스템 join key로 쓰기에는 불안정하다.
| 같은 매물인데 달라질 수 있는 값 |
|---|
| 서울시 / 서울특별시 |
| 도로명주소 / 지번주소 |
| 건물명 포함 여부 |
| 동·호수 포함 여부 |
| 공백, 괄호, 특수문자 |
| 정규화 주소 포맷 |
그래서 관심 리포트와 진단 이력은 문자열 주소보다 안정적인 식별자로 연결해야 한다.
| 우선순위 | 연결 기준 |
|---|---|
| 1 | diagnosis_history_id 직접 저장 |
| 2 | PNU + 동/호수 |
| 3 | 도로명주소 관리번호 + 상세주소 hash |
| 4 | normalized address hash |
| 5 | 기존 문자열 주소 fallback |
또한 관심 리포트의 대시보드 점수가 실제 위험 점수가 아니라 리포트 준비도에 가까운 값이라면, 용어를 분리해야 한다.
| 잘못 보일 수 있는 표현 | 개선 표현 |
|---|---|
| 대시보드 점수 | 리포트 완성도 |
| 분석 점수 | 근거 충족도 |
| AI 점수 | 분석 신뢰도 |
| 위험 점수와 같은 위치의 리포트 점수 | 위험도와 별도 섹션으로 분리 |
개선 후 구조
결국 개선의 핵심은 더 복잡한 점수를 만드는 것이 아니다.
사용자가 계약 전 판단을 할 때 필요한 질문에 맞춰 결과를 나누는 것이다.
| 사용자의 질문 | 시스템이 제공해야 하는 값 |
|---|---|
| 이 매물 자체가 위험한가? | riskScore, riskGrade |
| 그 판단을 믿을 만큼 근거가 충분한가? | evidenceQualityScore, evidenceGrade |
| 계약 전에 내가 직접 확인해야 할 것은 무엇인가? | decisionStatus, manualCheckBlockers, nextActions |
개선 전후를 비교하면 다음과 같다.
| 항목 | 기존 | 개선 |
|---|---|---|
| 최종 점수 | 위험 + 불확실성 합산 | 실제 위험 점수와 근거 충족도 분리 |
| 보증금 비율 | 문장 설명 중심 | DEPOSIT_TO_VALUE_RISK에 직접 반영 |
| 자료 부족 | 점수 벌점으로 섞일 수 있음 | 판단 보류 또는 근거 부족으로 표현 |
| 등기부/보증보험 | 0점 가중치 직접 확인 항목 | 판정 gate로 강하게 노출 |
| 관심 리포트 | 주소 문자열로 진단 이력 연결 | 안정 식별자 기반 연결 우선 |
| UI 문구 | 위험도와 추가 확인이 섞임 | 위험도, 근거 충족도, 직접 확인 분리 |
화면 문구도 바뀌어야 한다
로직을 나누어도 화면 문구가 모호하면 사용자는 여전히 헷갈린다.
피해야 할 표현은 다음과 같다.
| 피해야 할 표현 | 이유 |
|---|---|
| D등급 | 사용자가 의미를 직관적으로 이해하기 어려움 |
| 위험 낮음 + 추가 확인 필요 | 서로 모순처럼 보임 |
| AI가 판단한 안전도 | AI가 최종 판단한 것처럼 보임 |
| 대시보드 점수 | 위험 점수인지 리포트 점수인지 불명확 |
| 자료 부족이지만 위험도 낮음 | 근거 부족과 안전 판단이 충돌 |
대신 조건을 분리해서 말해야 한다.
| 상황 | 개선 문구 |
|---|---|
| 위험 신호가 높음 | “보증금이 확인 가능한 가격 기준 대비 높은 편입니다.” |
| 근거가 부족함 | “현재 자료만으로는 위험도를 단정하기 어렵습니다.” |
| 직접 확인 필요 | “등기부 권리관계 확인 전까지 계약 안전 여부는 확정할 수 없습니다.” |
| 공공데이터상 위험 낮지만 확인 필요 | “현재 확인 가능한 공공데이터 기준 위험 신호는 낮습니다. 다만 등기부와 선순위 임차인 확인 전까지 안전 여부는 확정할 수 없습니다.” |
구현은 점진적으로 해야 한다
이 개선은 기존 구조를 한 번에 갈아엎자는 뜻이 아니다.
현재 프론트엔드가 riskAssessment.totalRiskScore를 사용하고 있을 수 있고, 저장된 진단 이력이나 관심 리포트도 기존 응답 구조에 의존할 수 있다.
따라서 안전한 적용 순서는 다음과 같다.
| 순서 | 작업 |
|---|---|
| 1 | 현재 DTO, enum, service, calculator, test 구조 확인 |
| 2 | 기존 totalRiskScore는 유지 |
| 3 | 새 필드로 riskScore, evidenceQualityScore, decisionStatus 추가 |
| 4 | 보증금 비율 계산 결과를 DEPOSIT_TO_VALUE_RISK에 연결 |
| 5 | 0점 가중치 항목을 manualCheckBlockers 또는 decision gate에 반영 |
| 6 | 프론트엔드는 새 필드가 있으면 우선 사용, 없으면 기존 필드 fallback |
| 7 | 관심 리포트는 문자열 주소 매칭 의존도를 낮춤 |
| 8 | 테스트로 기존 흐름이 깨지지 않았는지 확인 |
테스트로 고정해야 할 것
이번 개선은 느낌으로 끝나면 안 된다. 테스트가 있어야 한다.
| 테스트 영역 | 확인할 내용 |
|---|---|
| 보증금 비율 | 50%, 65%, 82%, 92% 구간별 위험도 차이 |
| 가격 근거 없음 | 위험 50점 고정이 아니라 근거 부족 상태로 처리 |
| 자료 부족 | riskScore가 아니라 evidenceQuality와 decisionStatus에 반영 |
| 등기부 미확인 | 점수 가산이 아니라 CHECK_REQUIRED 또는 blocker로 반영 |
| 보증보험 미확인 | next action 또는 직접 확인 항목으로 노출 |
| 관심 리포트 매칭 | 주소 표현 차이가 있어도 가능한 경우 진단 이력 연결 |
| 기존 호환성 | 홈 진단, 저장 진단, 관심 리포트 화면이 깨지지 않음 |
| OpenAI 비활성 | rule-based fallback만으로도 정상 동작 |
정리
기존 위험도 산정 로직은 틀린 출발이 아니었다.
오히려 초기에는 합리적인 선택이 많았다. AI가 최종 판단자가 되지 않게 했고, 백엔드가 점수와 등급을 통제했으며, 자동 확정이 어려운 항목은 직접 확인으로 분리했다.
문제는 그 의도가 사용자 화면에서 충분히 드러나지 않았다는 점이다.
| 구분해야 하는 것 | 왜 구분해야 하는가 |
|---|---|
| 실제 위험 | 계약 자체의 위험 신호 |
| 자료 부족 | 판단 신뢰도의 문제 |
| 직접 확인 필요 | 사용자가 계약 전 확인해야 하는 문제 |
| 리포트 준비도 | 분석 결과가 얼마나 잘 연결되었는지의 문제 |
그래서 개선의 핵심은 단일 점수를 더 정교하게 만드는 것이 아니다.
위험도, 근거 충족도, 직접 확인 gate를 분리하는 것이다.
이렇게 바꾸면 ZIP:ON은 사용자에게 더 정직하게 말할 수 있다.
이 매물은 위험 신호가 높습니다.
또는, 현재 자료만으로는 판단하기 어렵습니다.
또는, 공공데이터 기준 위험은 낮지만 등기부 확인 전까지 안전하다고 말할 수 없습니다.부동산 계약 전 진단 서비스에서 중요한 것은 그럴듯한 점수가 아니다.
사용자가 계약 전에 어떤 판단을 멈추고, 무엇을 더 확인해야 하는지 알게 만드는 것이다.