엠케이랜딩
경력근거: 항목 간 정합성을 검증하고, 조건 충족 여부를 판정하고, 누락과 예외를 잡아내는 흐름
※ 실제 산출물이 아닌 AI 예측 시안입니다.
엠케이랜딩은 상품 적격성 판정과 금리 산출을 맡는 PPE 를 이미 운영하고 있어서, 조건표를 지어내지 않고 그 기준을 입력으로 받아 심사 항목을 대조하는 구조가 성립합니다. 직접 대출 기관이라 심사 기준을 스스로 정한다는 점이 이 대조를 가능하게 하고, 중개사 데이터로는 같은 사슬을 만들 수 없습니다. 다만 이 포트폴리오는 항목 대조 규칙과 화면을 보여줄 뿐, 모기지 인수심사 도메인 전문성은 주장하지 않습니다.
엠케이랜딩은 중개가 아니라 직접 대출 기관이라, 신청 접수부터 서류 검증과 인수심사와 최종 실행까지 심사 기준과 시스템을 스스로 설계하고 운영합니다.
상품 적격성 판정과 금리 산출을 담당하는 PPE 가 영업 현장에서 매일 쓰이고 있어서, 조건표를 지어내지 않고 그 기준을 받아 쓰는 구조가 자연스럽습니다.
서류에서 데이터를 읽어내고 항목 간 정합성을 검증하고 누락과 예외를 잡아내는 일을 지금은 사람이 반복하고 있어서, 그 대조가 첫 자동화 대상입니다.
제출 서류에서 뽑아 놓은 항목 값을 PPE 상품 조건표와 견줘, 누락과 서류 간 불일치와 기준 미달을 항목마다 나눠 보여주는 관리자 페이지입니다.
엠케이랜딩의 인수심사 담당자입니다. 한 건에 딸린 서류 예닐곱 장의 값을 상품 조건과 하나씩 맞춰 보다가, 어느 항목을 아직 못 봤는지 헷갈리는 일을 줄이고 싶습니다.
서류 사이 값이 어긋난 것을 늦게 발견하는 이유는 같은 항목이 여러 서류에 흩어져 있어서입니다. 항목을 기준으로 서류를 모아 놓고 조건표와 견주면, 사람이 봐야 할 항목이 먼저 좁혀집니다.
서류 추출 결과와 PPE 조건표를 올리면 항목 14건이 다섯 판정으로 나뉘어 뜨고, 한 줄을 펼치면 그 판정을 만든 값과 기준값이 보이는 관리자 페이지입니다. 서류에서 값을 뽑는 문서 판독 자체는 이미 추출된 항목 표를 받는다고 가정하고 시작합니다.
심사자가 확정한 항목별 결론을 추가로 올려, 1주차 판정과 어긋난 항목을 판정 코드별로 세어 보여줍니다.
예외로 넘긴 항목의 승인 결과를 쌓아, 어느 조건에서 예외가 반복되는지 조건별로 세어 보여줍니다.
서류 추출 결과와 PPE 조건표를 올리면 항목 14건이 다섯 판정으로 나뉘어 뜨고, 심사자가 한 줄을 펼쳐 값과 기준값을 함께 읽습니다.
값이 비었거나 통화 기호가 섞였거나 같은 항목이 두 서류에서 다르게 적힌 입력 30종을 넣어도 출력 형식이 안 깨집니다.
표기를 정규화한 뒤 조건표에 붙는 항목 비율을 화면에 냅니다. 단, 정답과 데이터를 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건에서의 동작입니다.
위 수치는 더미데이터 자가채점이며, 실제 심사 항목 판정 정확도는 엠케이랜딩 실제 서류와 심사자 확정 결론이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모가 도는 상태와 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 그래서 값 결측, 기준값에 아슬하게 걸친 경계, 통화 표기 흔들림을 더미에 일부러 섞었습니다.
조건표를 통과했다고 승인해도 되는 것은 아닙니다. 최종 승인은 사람이 정하고, 이 화면은 심사자가 먼저 볼 항목을 좁히는 데까지입니다.
실제 도입에서는 서류에서 값을 뽑는 판독기의 정확도와 항목 이름 체계가 더미와 다릅니다. 추출 신뢰도 임계값을 그 분포에 맞춰 다시 잡는 일이 도입 단계 과제입니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
제출 서류에서 뽑아 놓은 항목 값입니다. 값을 아예 읽지 못한 결측, 기준값에 아슬하게 걸친 경계, 통화 표기 흔들림을 일부러 섞었습니다. value_num 은 도구가 value 의 쉼표와 통화 기호를 걷어 내 채우는 칸이라 비워 두었습니다.
| extract_key | loan_id | program_code | field_name | value | source_doc | confidence |
|---|---|---|---|---|---|---|
| EX-20418-INC-W2 | LN-20418 | CONF30 | annual_income | 92,400 | W-2 | 0.94 |
| EX-20418-INC-BNK | LN-20418 | CONF30 | annual_income | $92,400.00 | 은행확인서 | 0.89 |
| EX-20418-RES | LN-20418 | CONF30 | months_reserves | 2 | 은행확인서 | 0.91 |
| EX-20418-CS | LN-20418 | CONF30 | credit_score | 744 | 신용보고서 | 0.97 |
| EX-20455-APP | LN-20455 | CONF30 | appraised_value | — | 감정평가서 | 0.31 |
다음 JSON Schema 에 맞는 서류 추출 행을 더 만듭니다. {"type":"object","properties":{"extract_key":{"type":"string"},"loan_id":{"type":"string"},"program_code":{"type":"string"},"field_name":{"type":"string"},"value":{"type":["string","null"]},"value_num":{"type":["number","null"]},"source_doc":{"type":"string"},"confidence":{"type":"number"}},"required":["extract_key","loan_id","program_code","field_name","value","source_doc","confidence"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 값을 읽지 못해 value 가 null 인 행 1~2건, 기준값보다 딱 1 크거나 기준값과 정확히 같은 경계 행 1~2건, 같은 금액을 쉼표와 통화 기호로 다르게 적어 value 표기가 흔들린 행 1~2건. 같은 loan_id 와 field_name 에 출처가 다른 두 행을 둘 때는 값이 실제로 어긋난 경우와 표기만 다른 경우를 모두 만든다. 그리고 confidence 에 문자열을 넣거나 loan_id 를 비우는 형식 깨뜨리기 시도 3건을 따로 낸다. extract_key 는 행마다 다른 값으로 쓴다.{"type":"object","properties":{"extract_key":{"type":"string"},"loan_id":{"type":"string"},"program_code":{"type":"string"},"field_name":{"type":"string"},"value":{"type":["string","null"]},"value_num":{"type":["number","null"]},"source_doc":{"type":"string"},"confidence":{"type":"number"}},"required":["extract_key","loan_id","program_code","field_name","value","source_doc","confidence"]}PPE 가 상품별로 들고 있는 적격 조건표입니다. 판정의 기준값과 연산자는 전부 이 표에서 오고 추출 결과에는 들어 있지 않습니다.
| program_code | field_name | operator | threshold | min_confidence | exception_allowed |
|---|---|---|---|---|---|
| CONF30 | annual_income | gte | 90,000 | 0.8 | false |
| CONF30 | credit_score | gte | 700 | 0.85 | true |
| CONF30 | loan_to_value | lte | 80 | 0.8 | true |
| CONF30 | months_reserves | gte | 2 | 0.75 | false |
| CONF30 | appraised_value | exists | — | 0.7 | false |
다음 JSON Schema 에 맞는 적격 조건 행을 더 만듭니다. {"type":"object","properties":{"program_code":{"type":"string"},"field_name":{"type":"string"},"operator":{"type":"string","enum":["gte","lte","exists"]},"threshold":{"type":["number","null"]},"min_confidence":{"type":"number"},"exception_allowed":{"type":"boolean"}},"required":["program_code","field_name","operator","threshold","min_confidence","exception_allowed"]} 쉬운 데이터만 만들지 말 것. 상품마다 기준이 실제로 갈리게 만든다. 고액 상품은 소득과 준비금 기준을 크게 올리고 담보 비율 기준을 내린다. operator 가 exists 인 행은 threshold 를 null 로 둔다. 그리고 threshold 에 문자열을 넣거나 operator 에 목록 밖 값을 넣는 형식 깨뜨리기 시도 3건을 따로 낸다.{"type":"object","properties":{"program_code":{"type":"string"},"field_name":{"type":"string"},"operator":{"type":"string","enum":["gte","lte","exists"]},"threshold":{"type":["number","null"]},"min_confidence":{"type":"number"},"exception_allowed":{"type":"boolean"}},"required":["program_code","field_name","operator","threshold","min_confidence","exception_allowed"]}| Python | 서류 추출 값의 표기를 정규화하고 조건표와 견주는 판정 규칙을 씁니다. |
| TypeScript | 항목 목록과 펼침 상세 화면을 만들고, 판정 결과 형식을 타입으로 고정합니다. |
| API 설계 | 추출 결과와 조건표를 받아 판정 결과를 돌려주는 심사 보조 API 를 만듭니다. 공고의 PPE 연동 API 개발과 같은 자리입니다. |
| 데이터 모델링 | 같은 항목이 여러 서류에 흩어진 구조를 항목 기준으로 모으는 두 표를 설계합니다. |
| 문서 파싱 | 금액 표기와 통화 기호가 서류마다 다르게 적힌 값을 하나의 수치로 정규화합니다. |
| 룰 엔진 | 조건표의 연산자와 기준값을 읽어 항목마다 판정 코드를 매깁니다. 공고가 말한 룰 기반 판정 로직에 해당합니다. |
| Claude Sonnet | 판정 사유를 심사자가 읽을 한 줄로 줄여 적습니다. |
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 심사자가 확정한 결론을 받아 판정이 맞았는지 대조합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 예외 승인 이력을 쌓아 조건별로 반복되는 예외를 찾습니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · extract_key + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
condition_met조건 충족field_missing항목 누락doc_conflict서류 간 불일치below_threshold기준 미달exception_review예외 승인 필요항목 한 줄마다 어떤 판정이 나왔고 그 판정을 만든 값과 기준값이 무엇인지 담습니다.
{"type":"object","properties":{"extract_key":{"type":["string","null"]},"loan_id":{"type":"string"},"field_name":{"type":"string"},"verdict":{"type":"string","enum":["condition_met","field_missing","doc_conflict","below_threshold","exception_review"]},"threshold_gap":{"type":["number","null"],"description":"value_num 에서 threshold 를 뺀 값입니다. 어느 한쪽이 없으면 null 입니다"},"extract_confidence":{"type":["number","null"]},"reason":{"type":"string"},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":"string"},"value":{}},"required":["field","value"]}}},"required":["loan_id","field_name","verdict","threshold_gap","evidence"]}[
{
"extract_key": "EX-20418-INC-W2",
"loan_id": "LN-20418",
"field_name": "annual_income",
"verdict": "condition_met",
"threshold_gap": 2400,
"extract_confidence": 0.94,
"reason": "은행확인서에 통화 기호로 적힌 값과 W-2 값이 정규화 후 같아 어긋남이 없고, 소득이 상품 기준을 넘습니다.",
"evidence": [
{
"field": "value_num",
"value": 92400
},
{
"field": "threshold",
"value": 90000
}
]
},
{
"extract_key": "EX-20418-RES",
"loan_id": "LN-20418",
"field_name": "months_reserves",
"verdict": "condition_met",
"threshold_gap": 0,
"extract_confidence": 0.91,
"reason": "준비금 개월 수가 상품 기준과 정확히 같아 경계에서 충족으로 봅니다.",
"evidence": [
{
"field": "value_num",
"value": 2
},
{
"field": "threshold",
"value": 2
}
]
},
{
"extract_key": "EX-20455-APP",
"loan_id": "LN-20455",
"field_name": "appraised_value",
"verdict": "field_missing",
"threshold_gap": null,
"extract_confidence": 0.31,
"reason": "감정평가서에서 감정가 칸을 읽지 못해 값이 비어 있습니다. 상품 조건이 존재를 요구하므로 심사자가 서류를 다시 받아야 합니다.",
"evidence": [
{
"field": "confidence",
"value": 0.31
},
{
"field": "min_confidence",
"value": 0.7
}
]
},
{
"extract_key": "EX-20492-INC-PAY",
"loan_id": "LN-20492",
"field_name": "annual_income",
"verdict": "doc_conflict",
"threshold_gap": null,
"extract_confidence": 0.9,
"reason": "같은 소득 항목이 W-2 와 급여명세에서 다르게 적혀 어느 값을 기준으로 볼지 정해지지 않았습니다.",
"evidence": [
{
"field": "value_num",
"value": 88150
},
{
"field": "source_doc",
"value": "급여명세"
},
{
"field": "extract_key",
"value": "EX-20492-INC-W2"
}
]
},
{
"extract_key": "EX-20507-LTV",
"loan_id": "LN-20507",
"field_name": "loan_to_value",
"verdict": "below_threshold",
"threshold_gap": 2,
"extract_confidence": 0.88,
"reason": "담보 비율이 상품 상한을 넘어 이 조건으로는 통과하지 않습니다.",
"evidence": [
{
"field": "value_num",
"value": 82
},
{
"field": "threshold",
"value": 80
}
]
},
{
"extract_key": "EX-20471-CS",
"loan_id": "LN-20471",
"field_name": "credit_score",
"verdict": "exception_review",
"threshold_gap": 1,
"extract_confidence": 0.62,
"reason": "신용점수가 기준을 1점 넘겨 아슬하게 통과했지만 추출 신뢰도가 상품이 요구하는 값에 못 미쳐, 심사자가 원본을 보고 예외를 정합니다.",
"evidence": [
{
"field": "value_num",
"value": 701
},
{
"field": "threshold",
"value": 700
}
]
}
][자동 검증 · 형식과 재계산] 1. verdict 는 condition_met, field_missing, doc_conflict, below_threshold, exception_review 다섯 값 안에 있습니다. 2. loan_id, field_name, verdict, threshold_gap, evidence 다섯 필드가 모두 있습니다. 3. value_num 은 value 에서 쉼표와 통화 기호를 걷어 낸 수치이고, 읽어 낼 수 없으면 null 입니다. 4. threshold_gap 은 value_num 에서 threshold 를 뺀 값과 같습니다. 5. extract_confidence 는 0 이상 1 이하입니다. 6. evidence 의 field 값은 doc_extractions 또는 ppe_conditions 에 실재하는 컬럼명입니다. 7. evidence 의 value 는 두 표의 수록된 행에 있는 값 그대로입니다. 8. verdict 가 field_missing 이면 threshold_gap 은 null 입니다. 9. verdict 가 doc_conflict 이면 threshold_gap 은 null 입니다. 10. verdict 가 exception_review 이면 extract_confidence 가 min_confidence 미만이고 exception_allowed 는 참입니다. 11. 모든 한국어 문장은 존댓말로 끝납니다. [정상으로 처리한 하드 케이스] 1. EX-20418-INC-BNK 는 통화 기호가 붙은 표기라도 정규화 후 W-2 값과 같아 어긋남으로 보지 않습니다. 2. EX-20507-RES 는 준비금 6 개월이 기준 2 개월을 넘어 경계에 걸리지 않습니다. [평가 러너 · 2주차] 1. 심사자가 확정한 항목별 결론과 대조해 판정 코드별 어긋남 수를 냅니다. 2. 예외로 넘긴 항목의 승인 결과를 조건별로 세어 예외가 반복되는 조건을 순서대로 보여줍니다.