흐름은 이렇습니다

1. 기존 구조가 왜 나왔는지
2. 그 구조가 어떤 점에서는 합리적이었는지
3. 그런데 실제 사용자 화면에서 어떤 오해가 생겼는지
4. 그래서 무엇을 분리했는지
5. 개선 후 어떤 판단 흐름이 되었는지

위험한 매물과 판단이 부족한 매물을 분리하기

ZIP:ON은 사용자가 계약을 고민하는 특정 매물에 대해 “이 매물을 계약해도 되는지 판단하기 전에 무엇을 확인해야 하는가”를 알려주는 서비스다.

그래서 위험도 산정 로직을 만들 때 처음부터 가장 중요했던 것은 점수의 정교함보다 잘못 확신하지 않는 것이었다. 부동산 계약에는 보증금이라는 큰돈이 걸려 있고, 서비스가 “안전하다”고 말하는 순간 사용자는 그 말을 실제 판단 근거로 받아들일 수 있다.

이 때문에 기존 구조는 꽤 보수적으로 설계되었다.

설계 선택당시 의도
AI가 최종 점수를 결정하지 않음같은 입력에는 같은 결과가 나와야 하고, 판단 근거를 추적할 수 있어야 함
백엔드가 항목별 점수와 가중치로 최종 등급 계산AI 응답이 흔들려도 서비스의 최종 판단은 통제 가능해야 함
등기부, 선순위 임차인, 보증보험은 0점 가중치로 분리공공데이터만으로 확정하기 어려운 항목을 무리하게 점수화하지 않기 위함
데이터가 부족하면 보수적으로 처리근거가 부족한데 “안전”처럼 보이는 것을 막기 위함

이 구조는 멍청한 설계가 아니었다. MVP 단계에서는 오히려 안전한 선택이었다.

문제는 사용자가 보는 화면에서 이 의도가 충분히 드러나지 않았다는 점이다.


기존 구조의 핵심 문제

기존 위험도 산정에는 여러 층이 있었다.

구분역할
riskAssessment.totalRiskScore정확 주소 진단의 공식 위험 점수
rule-based fallbackAI 비활성 또는 실패 시 사용하는 기본 산정 로직
보증금/월세 비율 계산기보증금이 가격 대비 높은지 문장으로 설명
관심 부동산 리포트 점수저장된 진단 이력 또는 리포트 근거 상태를 화면에 표시

내부적으로는 역할이 나뉘어 있었지만, 사용자가 보는 결과에서는 다음 네 가지가 섞여 보일 수 있었다.

실제 의미사용자에게 보일 수 있는 표현문제
실제 위험 신호가 높음위험 등급 높음정상
자료가 부족함추가 확인 필요, 보류위험한 것인지 판단 불가인지 헷갈림
직접 확인이 필요함체크리스트, 확인 필요총점보다 약하게 보일 수 있음
리포트 근거가 부족함대시보드 점수 낮음위험 점수로 오해될 수 있음

사용자는 내부 구조를 모른다. 화면에 D등급, 추가 확인 필요, 건물 유형 확인 불가, 자료 부족이 함께 보이면 이렇게 받아들일 수 있다.

“이게 진짜 위험하다는 건가, 아니면 시스템이 판단을 못 했다는 건가?”

이 질문에 명확히 답하지 못하는 것이 기존 구조의 가장 큰 문제였다.


문제 1. 위험도와 자료 부족이 같은 점수에 섞였다

기존 계산은 대략 이런 형태였다.

totalRiskScore = 실제 위험 점수 + 불확실성 벌점

이 방식은 나름의 이유가 있다. 자료가 부족한데 위험도를 낮게 보여주면 사용자가 안전하다고 오해할 수 있기 때문이다.

하지만 실제 위험과 자료 부족은 다르다.

케이스의미사용자가 알아야 할 것
보증금이 매매가의 90% 이상실제 위험이 높음계약 자체가 위험할 수 있음
매매가·공시가격·실거래가 근거 없음판단 근거 부족위험하다고 단정할 수 없지만, 판단 보류가 필요함

둘 다 높은 점수로만 보이면 사용자는 차이를 알 수 없다.

그래서 개선 방향은 단순하다.

기존개선
totalRiskScore 하나에 위험과 불확실성을 합산위험도, 근거 충족도, 판정 상태를 분리
자료 부족을 점수 벌점으로 표현자료 부족은 evidenceQualitydecisionStatus로 표현
높은 점수의 이유가 불명확“위험해서 높은지”, “근거가 부족한지” 구분 가능

개선 후에는 결과가 이렇게 나뉘어야 한다.

필드의미
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로 쓰기에는 불안정하다.

같은 매물인데 달라질 수 있는 값
서울시 / 서울특별시
도로명주소 / 지번주소
건물명 포함 여부
동·호수 포함 여부
공백, 괄호, 특수문자
정규화 주소 포맷

그래서 관심 리포트와 진단 이력은 문자열 주소보다 안정적인 식별자로 연결해야 한다.

우선순위연결 기준
1diagnosis_history_id 직접 저장
2PNU + 동/호수
3도로명주소 관리번호 + 상세주소 hash
4normalized 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에 연결
50점 가중치 항목을 manualCheckBlockers 또는 decision gate에 반영
6프론트엔드는 새 필드가 있으면 우선 사용, 없으면 기존 필드 fallback
7관심 리포트는 문자열 주소 매칭 의존도를 낮춤
8테스트로 기존 흐름이 깨지지 않았는지 확인

테스트로 고정해야 할 것

이번 개선은 느낌으로 끝나면 안 된다. 테스트가 있어야 한다.

테스트 영역확인할 내용
보증금 비율50%, 65%, 82%, 92% 구간별 위험도 차이
가격 근거 없음위험 50점 고정이 아니라 근거 부족 상태로 처리
자료 부족riskScore가 아니라 evidenceQualitydecisionStatus에 반영
등기부 미확인점수 가산이 아니라 CHECK_REQUIRED 또는 blocker로 반영
보증보험 미확인next action 또는 직접 확인 항목으로 노출
관심 리포트 매칭주소 표현 차이가 있어도 가능한 경우 진단 이력 연결
기존 호환성홈 진단, 저장 진단, 관심 리포트 화면이 깨지지 않음
OpenAI 비활성rule-based fallback만으로도 정상 동작

정리

기존 위험도 산정 로직은 틀린 출발이 아니었다.

오히려 초기에는 합리적인 선택이 많았다. AI가 최종 판단자가 되지 않게 했고, 백엔드가 점수와 등급을 통제했으며, 자동 확정이 어려운 항목은 직접 확인으로 분리했다.

문제는 그 의도가 사용자 화면에서 충분히 드러나지 않았다는 점이다.

구분해야 하는 것왜 구분해야 하는가
실제 위험계약 자체의 위험 신호
자료 부족판단 신뢰도의 문제
직접 확인 필요사용자가 계약 전 확인해야 하는 문제
리포트 준비도분석 결과가 얼마나 잘 연결되었는지의 문제

그래서 개선의 핵심은 단일 점수를 더 정교하게 만드는 것이 아니다.

위험도, 근거 충족도, 직접 확인 gate를 분리하는 것이다.

이렇게 바꾸면 ZIP:ON은 사용자에게 더 정직하게 말할 수 있다.

이 매물은 위험 신호가 높습니다.
또는, 현재 자료만으로는 판단하기 어렵습니다.
또는, 공공데이터 기준 위험은 낮지만 등기부 확인 전까지 안전하다고 말할 수 없습니다.

부동산 계약 전 진단 서비스에서 중요한 것은 그럴듯한 점수가 아니다.

사용자가 계약 전에 어떤 판단을 멈추고, 무엇을 더 확인해야 하는지 알게 만드는 것이다.