거래 데이터로 연체 위험을 예측하기: 학습한 모델을 서비스에 연결하며 확인한 것들
들어가며
셀러의 사업은 매일 움직입니다.
주문이 들어오고, 취소와 환불이 발생하고, 판매 대금이 정산됩니다.
같은 매출 규모의 셀러라도 최근 거래가 늘고 있는지, 정산 흐름이 달라졌는지에 따라 사업의 상태는 다를 수 있습니다.
올라는 이 영업활동 데이터를 선정산의 위험 판단에 활용합니다.
이번 글에서 소개하는 모델은 외부 신용평가사의 CB 점수나 재무제표를 직접 입력받지 않습니다.
이커머스 거래 데이터에서 만든 37개 피처와 내부 이용 이력에서 만든 2개 피처, 총 39개 입력으로 LightGBM이 연체 위험을 추정합니다.
다만 이 글의 주제는 모델 자체가 아니라 그 모델을 서비스에 연결하는 과정입니다.
학습이 끝난 모델 파일이 있어도 평가를 시작할 시점, 데이터를 가져오는 방법, 학습과 서빙의 입력을 맞추는 규칙, 결과를 저장하고 실패를 추적하는 방식은 따로 정해야 합니다.
올라에서는 쇼핑몰 데이터 수집 서비스와 모델 평가 워커를 연결해 이 과정을 구현했고, 그 과정에서 확인한 것들을 정리했습니다.
조회 흐름에서 시작하는 비동기 평가
평가의 시작점은 쇼핑몰 판매자 계정의 데이터 조회입니다.
크롤러가 모델 평가 API로 요청을 보내면, 평가 API는 작업을 큐에 넣고 작업 식별자를 반환합니다.
이후 데이터 수집과 추론은 별도의 평가 워커가 담당합니다.

▲ 쇼핑몰 데이터 조회 → 평가 API 호출 → 평가 작업 큐 → 거래 데이터 수집 → 정제 및 피처 생성 → 추론 → 점수와 입력 스냅샷 저장
외부 플랫폼의 응답을 기다리는 수집 작업은 요청마다 시간이 달라질 수 있습니다.
API에서 작업을 접수하고 워커에서 실행하도록 나누면, 호출부가 전체 평가가 끝날 때까지 연결을 유지할 필요가 없습니다.
평가 요청을 전달하는 HTTP 호출에는 짧은 제한 시간을 두었습니다.
전송에 실패하면 오류를 기록하고 기존 크롤링 흐름을 이어갑니다.
새로 연결한 평가 기능의 장애가 기존 조회까지 중단시키지 않도록 한 것입니다.
학습과 서빙의 입력을 맞추는 일이 먼저다
모델 서빙에서 어려운 부분은 추론 자체가 아닙니다.
학습 때 정의한 피처를 요청 시점에도 같은 방식으로 계산하는 일입니다.
같은 이름의 피처가 학습에서와 조금 다른 값으로 계산되어도 추론은 정상적으로 끝나고, 점수도 그럴듯한 범위에서 나옵니다.
오류가 나지 않기 때문에 알아차리기 어렵습니다.
그래서 평가 워커는 추론 직전에 입력을 검사합니다.
모델이 요구하는 컬럼 순서에 맞춰 입력을 정렬하고, 필수 컬럼이 빠졌거나 무한대 값이 들어 있으면 추론을 중단합니다.
비율 피처에서 분모가 0이 되는 경우처럼, 계산은 성공했지만 의미가 없는 값을 걸러내기 위한 검사입니다.
반대로 결측값은 그대로 통과시킵니다.
LightGBM이 결측을 자체적으로 처리하기 때문에, 서빙 단계에서 임의로 채우면 오히려 학습 때와 달라집니다.
결측을 어떻게 볼지도 피처마다 다릅니다.
금액과 건수 피처에서 데이터가 없다는 것은 그 기간에 활동이 없었다는 뜻이므로 0으로 채웁니다.
반면 비율 피처에서는 계산할 근거가 없다는 뜻이므로 결측 그대로 둡니다.
같은 '값이 없음'이라도 0과 결측 중 무엇으로 표현할지는 피처의 의미가 정합니다.
집계 기간도 규칙으로 고정했습니다.
피처 생성 코드는 평가 기준일 이전 90일만 집계 대상으로 삼고, 기준일 이후의 거래는 제외합니다.
기준일 시점에 알 수 없었던 정보가 입력에 섞이면 그 점수는 실제 심사 상황을 재현하지 못합니다.
이런 규칙을 정해 두어도, 새 플랫폼을 지원 범위에 추가할 때는 매번 다시 확인해야 합니다.
아래 두 가지가 그런 경우였습니다.
같은 이름의 금액이 같은 의미는 아니다
플랫폼마다 데이터의 생김새가 다르다는 문제는 수집 단계에서도 같은 모양으로 나타납니다.
사이트별로 컬럼명과 날짜 포맷, 숫자 타입이 제각각인 정산 데이터를 하나의 기준으로 맞춰간 과정은 데이터 표준화 레이어를 나눈 이야기에 정리되어 있습니다.
여기서 다루는 것은 그 다음 단계, 형태가 아니라 의미가 어긋나는 경우입니다.
한 오픈마켓 플랫폼을 서빙에 연결할 때, 수집한 원본에는 매출을 나타내는 금액 컬럼이 둘 있었습니다.
하나는 주문 금액을 그대로 담은 값이고, 다른 하나는 플랫폼이 부담한 할인을 반영한 값입니다.
둘 다 정상적인 숫자이고, 어느 쪽을 쓰더라도 피처 생성과 추론은 문제없이 끝납니다.
차이는 플랫폼이 부담하는 할인의 처리에 있습니다.
앞의 값은 수량과 단가를 곱한 것이고, 뒤의 값은 여기서 그 할인을 뺀 것입니다.
학습 단계에서 매출 피처에 사용한 것은 후자였습니다.
서빙 코드는 전자를 참조하고 있었습니다.
실제 수집 데이터로 두 컬럼을 대조해 보니 차이 금액이 해당 기간 할인 총액과 정확히 일치했고, 그만큼 매출 피처가 학습 기준보다 크게 계산되고 있었습니다.
2026년 9월 변경에서 서빙이 참조하는 컬럼을 학습 기준에 맞췄습니다.
이 변경에서 더 눈에 띈 것은 기존 테스트였습니다.
그 자리에는 이미 회귀 테스트가 있었는데, 정반대 방향을 고정하고 있었습니다.
과거에 컬럼명이 어긋나 오류가 났던 시점에 작성된 테스트라, '그 컬럼을 참조하면 오류가 난다'는 당시의 증상을 그대로 검증하고 있었던 것입니다.
통과하는 테스트가 있다는 사실만으로는 학습 기준과 맞는다는 근거가 되지 않았습니다.
테스트는 두 컬럼의 값이 다를 때 서빙이 어느 쪽을 고르는지 확인하도록 고쳤습니다.
두 값이 같은 테스트 데이터에서는 무엇을 참조하든 결과가 같아서, 이런 종류의 어긋남을 잡아내지 못합니다.

▲ 두 컬럼 모두 정상적인 숫자라서 추론은 오류 없이 끝난다. 값이 같은 테스트 데이터로는 어느 쪽을 참조하는지가 드러나지 않는다
| 확인할 것 | 통과 조건 |
|---|---|
| 수집이 성공했는가 | 컬럼이 존재하고 값이 파싱된다 |
| 학습과 같은 값을 쓰는가 | 후보 컬럼의 값이 서로 다를 때 학습 기준 컬럼을 고른다 |
플랫폼을 추가할 때 확인해야 할 것은 수집 성공 여부만이 아닙니다.
그 플랫폼의 어떤 필드가 학습에서 쓴 개념에 해당하는지를 확인해야 합니다.
날짜의 경계는 시간대에서 갈린다
또 하나는 날짜였습니다.
플랫폼에 따라 수집한 날짜 필드에 타임존 오프셋이 포함되어 있습니다.
이 값을 그대로 두면 기준일이나 집계 시작일과 비교할 때 타입이 맞지 않습니다.
여기서 타임존을 떼어내는 방식이 결과를 바꿉니다.
흔한 방법은 UTC로 변환한 뒤 타임존 정보를 지우는 것인데, 이렇게 하면 한국 시간 기준의 날짜 경계가 아홉 시간 밀립니다.
자정 직후부터 아침 아홉 시 전까지 발생한 거래는 하루 전 날짜로 기록되고, 그 하루가 90일 구간의 시작과 끝에 걸치면 집계 대상에서 빠지거나 다른 주차로 옮겨 갑니다.
평가 워커의 기준일은 한국 시간 기준의 날짜 개념입니다.
그래서 타임존이 있는 날짜는 한국 시간으로 변환한 뒤 타임존 정보를 지우도록 맞췄습니다.
날짜를 비교 가능한 형태로 만드는 것과 원래 의미를 유지하는 것은 다른 문제였습니다.

▲ 같은 거래 시각이라도 UTC로 변환한 뒤 타임존을 지우면 날짜가 하루 앞으로 밀린다
이 두 가지 모두 실제 데이터로 수집을 돌려 보는 과정에서 드러났습니다.
단위 테스트에서 쓰던 데이터에는 타임존 오프셋이 없었고, 두 금액 컬럼도 같은 값이었기 때문입니다.
점수와 그 점수를 만든 입력을 함께 남긴다
추론이 끝나면 점수와 입력 스냅샷을 저장합니다.
점수에는 모델 버전과 평가 기준일 등 결과를 해석하는 데 필요한 정보를 연결하고, 스냅샷에는 실제 추론에 사용한 피처 값을 보존합니다.
이 두 기록이 있어야 나중에 특정 평가를 다시 살펴볼 수 있습니다.
어떤 버전의 모델이 어떤 정보를 보고 위험을 추정했는지 확인할 수 있기 때문입니다.
앞에서 본 두 사례처럼 입력 계산 규칙이 바뀌었을 때, 그 변경 전후로 점수가 어떻게 달라졌는지 확인하려면 당시의 입력값이 남아 있어야 합니다.
구현에서는 점수와 입력을 하나의 데이터베이스 트랜잭션으로 묶었습니다.
둘 중 하나라도 저장에 실패하면 함께 되돌립니다.
같은 작업 식별자로 두 기록을 연결해 점수에 대응하는 입력을 찾을 수 있도록 했습니다.
실행 이력은 별도로 관리합니다.
평가가 어느 조회에서 시작됐는지, 성공했는지, 어디서 실패했는지를 추적하기 위한 기록입니다.
이력 저장은 핵심 결과 저장과 분리해, 부가 기록의 오류가 완료된 점수·입력 저장을 무효로 만들지 않도록 했습니다.
실패한 단계와 그 원인을 구분해서 남긴다
평가 결과가 나오지 않았을 때 운영자가 알아야 할 것은 실패한 단계와 원인입니다.
인증 정보 문제, 외부 플랫폼 수집 실패, 잘못된 모델 입력, 제한 시간 초과는 각각 대응 방법이 다릅니다.
평가 워커는 이런 오류를 서로 다른 상태로 구분해 기록합니다.
구분이 필요한 이유는 같은 계정에 대한 중복 실행을 막는 잠금에서도 드러납니다.
잠금을 얻지 못했다는 결과와 잠금 서버에서 응답이 오지 않았다는 결과는 다릅니다.
앞은 이미 같은 평가가 진행 중이라는 뜻이고, 뒤는 중복 여부를 판단할 수 없는 상태라는 뜻입니다.
두 경우를 같은 실패로 처리하면 장애 상황이 정상적인 중복 요청으로 보입니다.
분류되지 않은 예외는 내부 오류로 남기되, 외부에서 조회하는 상태 메시지에는 그 내용을 넣지 않습니다.
데이터베이스 오류 문구처럼 내부 구조가 드러나는 정보가 그대로 나갈 수 있기 때문입니다.
그리고 어떤 실패도 셀러의 위험 신호로 해석하지 않습니다.
수집에 실패했다는 것은 우리 쪽에서 판단할 근거를 얻지 못했다는 뜻이지, 그 셀러가 위험하다는 뜻이 아닙니다.
운영 상태와 예측 결과를 섞지 않는 것이 이 구분의 목적입니다.
소요 시간도 수집, 클렌징, 피처 생성, 추론, 저장으로 나누어 기록합니다.
처리가 느려졌을 때 어느 단계가 늘어났는지 확인하고 개선할 수 있습니다.
모델은 워커 프로세스에서 재사용하고 최초 사용 시점에 한 번만 불러와, 요청마다 파일을 다시 읽는 비용을 줄였습니다.
이 단계별 기록은 성능 측정의 출발점입니다.
사용자 관점의 처리 시간을 평가하려면 여기에 큐 대기 시간과 외부 수집 조건까지 포함해 살펴봐야 합니다.
기록이 다음 모델의 출발점이 된다
저장한 입력 스냅샷은 이후 실제 상환 결과와 연결할 수 있는 기반입니다.
평가 당시 이용할 수 있었던 정보로 만든 예측을 실제 결과와 비교하고, 다음 모델의 학습과 검증에 활용할 수 있습니다.
다만 쌓인 기록을 그대로 학습 표본으로 쓸 수는 없습니다.
실행마다 한 건씩 표본을 만들면 자주 이용하는 셀러의 특성이 모델에 더 많이 반영됩니다.
후속 모델링의 모집단 분석에서는 약 2년간 쌓인 선정산 실행 이력 485,471건을 대상으로 삼고, 월별로 동일인의 중복 실행을 제거해 88,055건의 표본을 만들었습니다.
자주 이용하는 집단은 한 달에 열 번 이상 실행하기도 합니다.
동일인이 여러 달에 등장할 수 있으므로 이 표본 수는 고유 셀러 수와 다르고, 이 분석의 모집단은 현재 서빙 모델의 학습 표본과도 별개입니다.
재학습에서는 이런 이용 빈도 차이를 반영해 표본 구성과 평가 방법을 정해야 합니다.
성능 기준도 다시 세워야 합니다.
모델 메타데이터에는 기존 홀드아웃 실험의 ROC-AUC가 0.7대로 기록되어 있습니다.
이는 당시 실험의 구분 성능을 나타내는 기록입니다.
현재 운영 성능을 판단하려면 모델 버전과 평가 기간, 대상 플랫폼을 정하고 다시 측정해야 합니다.
데이터가 쌓인 뒤에는 결과 관측이 끝난 표본을 선별하고, 상환 결과를 라벨로 연결하고, 새 모델을 학습·검증해 배포하는 과정이 이어져야 합니다.
현재 서빙 파이프라인은 그 출발점인 예측 결과와 입력의 보존을 담당합니다.
외부 신용정보를 결합하는 다음 단계에서도, 거래 데이터만 사용하는 기준 모델과 비교해야 추가 정보가 실제로 무엇을 더 설명하는지 알 수 있습니다.
마치며 — 예측값보다 예측 과정을 먼저 남긴다
이번 구현에서 반복해서 확인한 것은 모델 서빙의 어려움이 추론이 아닌 곳에 있다는 점이었습니다.
금액 컬럼을 잘못 골라도, 날짜 경계가 밀려도, 추론은 오류 없이 끝나고 점수도 나옵니다.
틀린 입력이 틀린 티를 내지 않기 때문에, 입력이 학습과 같은 의미인지 확인하는 일은 추론 코드 바깥에서 별도로 해야 합니다.
그래서 올라의 모델 서빙은 수집부터 피처 생성, 추론, 저장까지 이어지는 경로를 구현하는 것과 함께, 그 경로의 각 단계에서 무엇이 어떻게 계산됐는지 남기는 데 초점을 맞췄습니다.
거래 데이터를 금융 판단에 활용하려면 예측값을 계산하는 능력만으로는 부족합니다.
그 값이 만들어진 과정을 나중에 확인하고 고칠 수 있어야 합니다.