LabHub
배우기 러닝패스 코스

보험 도메인 심화 · 보장 조건을 코드로 옮기기 · 이론

같은 청구인데 지급액이 담당자마다 달랐다

LabHub 에서 이어서 보기

한 줄 요약

보장 판정은 청구액에서 자기부담금·공동부담률·건당 한도·연간 한도를 정해진 순서대로 깎아 가는 계산이고, 그 순서와 반올림과 "무엇 단위로 세는가" 가 결과를 바꾼다.

왜 이게 필요했나

보험금 계산은 겉으로 보면 곱셈과 뺄셈 몇 번이다. 그런데 현장에 들어가면 같은 청구를 두 시스템이 다르게 계산하고 있는 장면을 거의 매번 본다. 청구 심사 시스템과 회계 시스템이 다르고, 지급 담당자의 스프레드시트가 또 다르다. 셋 다 약관을 읽고 만들었는데 결과가 다르다.

원인은 대개 약관이 말로 적혀 있고 순서를 명시하지 않기 때문이다. "자기부담금을 공제한 금액의 80%를 지급하되 1회 30만원, 연간 300만원을 한도로 한다" 는 문장에서, 자기부담금을 먼저 빼는지 80%를 먼저 곱하는지, 30만원 한도를 곱하기 전에 보는지 뒤에 보는지, 자기부담금이 청구 한 건마다인지 사고 한 건마다인지는 문장이 정하지 않는다. 각 팀은 자기 해석으로 만들고, 그 해석이 코드 안에 흩어져 숨는다.

이 어긋남은 오류로 드러나지 않는다. 두 시스템 다 정상 종료하고, 로그에는 아무것도 남지 않는다. 드러나는 것은 고객의 문의와 결산 차이다. 그래서 FDE 가 이 판정을 다시 만들 때 하는 일은 규칙을 데이터로 끌어내고, 어떤 규칙이 얼마를 깎았는지 청구마다 남기는 것이다. 판정이 맞느냐 이전에, 판정을 설명할 수 있어야 한다.

어떻게 동작하나

판정기는 청구 한 건을 받아 금액을 차례로 깎는다. 이 실습이 쓰는 순서는 다음과 같다 — 이것은 이 실습의 가정이고, 실제 상품의 약관은 상품마다 다르다. 보험계약의 기본 골격은 [상법 제4편(보험)](https://www.law.go.kr/%EB%B2%95%EB%A0%B9/%EC%83%81%EB%B2%95)에 있지만, 자기부담금이나 한도 같은 수치는 법이 아니라 약관이 정한다.

청구액 ├─ 1. 자기부담금(공제액)   사고 하나에 한 번   → 남은 금액 ├─ 2. 공동부담률           비율로 깎음        → 남은 금액 ├─ 3. 건당 한도            이 청구의 상한     → 남은 금액 └─ 4. 연간 한도            남은 연간 여유     → 지급액

순서가 왜 결과를 바꾸는가. 청구 100만원, 자기부담금 30만원, 공동부담 20%라고 하자. 공제를 먼저 하면 (100 - 30) × 0.8 = 56만원이다. 공동부담을 먼저 하면 100 × 0.8 - 30 = 50만원이다. 6만원 차이가 나고, 이 차이는 청구 수만큼 곱해진다. 두 결과 모두 "약관대로" 라고 주장할 수 있다는 것이 문제의 핵심이다.

무엇 단위로 세는가도 바꾼다. 자기부담금이 청구 한 건마다라면, 같은 사고로 외래·약제·검사를 따로 청구한 사람은 자기부담금을 세 번 낸다. 사고 한 건마다라면 한 번이다. 연간 한도도 마찬가지로 "사고일 기준 연도" 인지 "접수일 기준 연도" 인지에 따라 연말 청구의 판정이 갈린다.

면책기간(대기기간)은 계약일과 사고일 사이의 거리다. 계약 시작 뒤 일정 기간 안에 난 사고는 보장하지 않는다. 여기서 흔한 실수는 사고일이 아니라 접수일로 재는 것이다. 사고는 기간 뒤에 났는데 접수가 늦어 지급이 거절되거나, 반대가 된다. 날짜 계산은 문자열 비교로 시작해서 반드시 탈이 나므로, 파이썬이라면 [datetime](https://docs.python.org/3/library/datetime.html)의 date.fromisoformattimedelta 로 다루고, SQL 안에서 다뤄야 한다면 [SQLite 의 날짜 함수](https://www.sqlite.org/lang_datefunc.html)를 쓴다.

반올림은 정책이다. 공동부담률을 곱하면 원 단위 아래가 남는다. 파이썬의 내장 [round()](https://docs.python.org/3/library/functions.html)는 정확히 절반인 값을 짝수 쪽으로 붙인다(은행가 반올림). [decimal 모듈](https://docs.python.org/3/library/decimal.html)의 기본 반올림 방식도 ROUND_HALF_EVEN 이다. 약관이 사사오입을 말하고 있다면 ROUND_HALF_UP 을 명시해야 하고, 그 차이는 건당 1원이지만 수십만 건이면 결산에서 보인다. 더 중요한 것은 두 시스템이 서로 다른 모드를 쓰고 있을 때 차이가 무작위로 흩어져 원인을 찾기 어려워진다는 점이다.

현장에서 만나는 모습

첫째, 규칙 수치가 코드 안에 박혀 있다. if plan == "BASIC": deductible = 20000 같은 줄이 여러 파일에 흩어져 있고, 상품이 하나 늘 때마다 모든 파일을 고쳐야 한다. 규칙을 표로 빼면 상품 추가가 데이터 한 줄이 되고, 무엇보다 과거 판정을 그때의 표로 재현할 수 있다.

둘째, 판정 결과만 있고 근거가 없다. 지급액 12만원만 저장돼 있으면 고객이 "왜 12만원이냐" 고 물었을 때 아무도 답하지 못한다. 규칙마다 깎은 금액을 배열로 남기면, 그 자체가 고객 안내문이자 회귀 시험의 기대값이 된다. 실습의 판정기가 steps 를 내는 이유다.

셋째, 누적 한도를 청구 단위로만 본다. 연간 한도는 그 계약의 앞선 청구들이 얼마를 썼는지에 달려 있다. 청구 한 건만 보고 계산하면 한도를 넘겨 지급하거나, 순서가 바뀌면 다른 결과가 나온다. 판정은 정해진 순서(보통 접수일)로 처리해야 하고, 늦게 들어온 청구가 앞선 판정을 되돌리지 않는지도 정해 두어야 한다.

넷째, 거절 사유가 하나뿐이다. 면책기간 안에 난 사고이면서 보장 제외 항목이기도 한 청구가 있다. 사유를 하나만 남기면 고객이 한 가지를 해명한 뒤 다시 거절당한다. 사유는 목록으로 남긴다.

실무에서 진짜 중요한 것

다음 실습에서 할 것

합성 약관표와 청구 141건을 직접 만들고, 적용 순서 두 가지가 합계를 얼마나 벌리는지 숫자로 본다. 그다음 판정기를 만들어 공제·공동부담·건당 한도를 순서대로 적용하고, 면책기간과 보장 제외로 거절하고, 같은 사고의 복수 청구가 자기부담금을 나눠 쓰게 하고, 연간 한도를 소진시킨다. 채점기는 매번 다른 약관 수치로 여러분의 판정기를 실제로 실행해 지급액과 판정 근거를 대조하고, 절반인 자리의 반올림까지 본다.