LexQLexQ
블로그로 돌아가기
decision-replaysimulationrule-enginebusiness-rules

의사결정 재실행과 변경 영향 시뮬레이션: 규칙 변경을 무엇과 비교할지 고르는 법

변경 영향 시뮬레이션은 대상 버전과 비교 기준 버전을 같은 입력에 다시 돌려 비교하고, 의사결정 재실행은 후보 버전 하나만 다시 돌려 호출 당시 실제로 나간 결정과 비교합니다. 하루치 카드 거래 2,000건에서 두 방법으로 센 수는 67건과 45건으로 달랐지만, 둘 다 맞았습니다.

Sanghyun Park·2026년 10월 23일11분 읽기

규칙 변경을 지난 트래픽으로 확인하는 방법은 LexQ에 둘 있습니다. 변경 영향 시뮬레이션(Impact Simulation)은 지난 트래픽에 바꾸려는 대상 버전과 비교 기준 버전을 모두 다시 돌려 비교하고, 의사결정 재실행(Decision Replay)은 바꾸려는 후보 버전 하나만 돌려 호출 당시 실제로 나간 결정(LexQ가 호출마다 저장해 둔 결정)과 비교합니다.

둘 중 어느 쪽을 쓰느냐에 따라 위험관리팀에 보고할 숫자가 달라집니다. 카드 거래 2,000건이 쌓인 하루치 트래픽에서 검토 기준 금액을 $500에서 $300까지 낮춘 버전을 두 방법으로 돌려 보면 답이 서로 다릅니다. 변경 영향 시뮬레이션의 규칙별 통계로 세면 검토가 67건 증가하고, 의사결정 재실행으로 세면 결정 45건이 변경됩니다. 67건도 45건도 맞는 결과입니다. 두 숫자가 다른 것은 기준 금액을 $200까지 낮춘 긴급 조치 버전이 그날 거래의 절반을 결정했기 때문입니다. 변경 영향 시뮬레이션에서 평소의 $500 버전을 비교 기준으로 고르면, 그날 거래를 모두 그 버전이 결정했다고 놓고 셉니다. 긴급 조치 버전이 낸 결정은 보지 않습니다.

하루치 카드 거래로 잰 두 숫자

카드사 위험관리팀이 신규 계정 거래의 검토 기준 금액을 $500에서 $300까지 낮추려 합니다. 지금 규칙은 가입한 지 30일이 안 된 신규 계정의 거래를 금액이 $500 초과일 때 수동 검토로 보내고, 나머지는 승인합니다. LexQ에서는 이것을 규칙 둘이 든 정책 그룹 하나로 나타내고, 카드 시스템은 거래마다 이 정책 그룹을 한 번 호출합니다. 수동 검토 규칙의 우선순위를 가장 높게 두고, 조건이 비어 있는 catch-all을 가장 낮게 둬서 남은 거래를 모두 승인하게 합니다. 두 규칙은 상호 배타 그룹(Mutex Group) review-route로 묶어, 거래 하나에 둘 중 하나만 적용되게 합니다.

기준 금액을 $300까지 낮춘 버전이 v3이고, 운영 중인 v1과는 검토 규칙 하나만 다릅니다. v2는 첫머리에 말한 긴급 조치 버전입니다.

{
  "name": "Manual review: account under 30 days, over $300",
  "condition": {
    "type": "GROUP",
    "operator": "AND",
    "children": [
      {
        "type": "SINGLE",
        "field": "accountAgeDays",
        "operator": "LESS_THAN",
        "value": 30,
        "valueType": "NUMBER"
      },
      {
        "type": "SINGLE",
        "field": "transactionAmountUsd",
        "operator": "GREATER_THAN",
        "value": 300,
        "valueType": "NUMBER"
      }
    ]
  },
  "actions": [
    {
      "type": "SET_FACT",
      "parameters": { "targetVar": "reviewRoute", "value": "MANUAL_REVIEW" }
    }
  ],
  "mutexGroup": "review-route",
  "mutexMode": "EXCLUSIVE",
  "mutexStrategy": "HIGHEST_PRIORITY",
  "mutexLimit": 1,
  "isEnabled": true
}

LexQ는 호출에 담겨 오는 값과 규칙이 담는 값을 모두 fact라고 부릅니다. 이 규칙은 카드 시스템이 호출마다 보내는 fact 둘(accountAgeDays, transactionAmountUsd)을 읽고, 결정은 다른 fact인 reviewRoute에 담습니다.

확인하려고 고른 날에는 하루가 시작될 때부터 v2가 운영 중이었습니다. 사기 거래가 늘어 기준 금액을 $200까지 낮춘 버전입니다. 팀은 같은 날 v1으로 되돌렸습니다. 그래서 그날 들어온 2,000건 가운데 앞의 1,000건은 v2가, 뒤의 1,000건은 v1이 결정했습니다.

버전신규 계정의 거래를 검토로 보내는 금액그날 결정한 거래
v1$500 초과 (평소 규칙)뒤의 1,000건
v2$200 초과 (긴급 조치)앞의 1,000건
v3$300 초과 (바꾸려는 초안)없음 (배포하지 않음)

세 버전은 같은 정책 그룹에 속하고, 이 금액 하나만 다릅니다. 아래 결과는 모두 이 2,000건에서 나왔습니다.

방법무엇과 비교하는지결과
변경 영향 시뮬레이션, 두 번같은 2,000건에 다시 돌린 v3와 v1검토 규칙에 걸린 거래가 v3는 204건, v1은 137건. 차이는 67건
의사결정 재실행 (후보 버전 v3)저장된 결정45건 변경: 승인에서 검토로 30건, 검토에서 승인으로 15건

변경 영향 시뮬레이션의 숫자는 「시뮬레이션 상세」 화면의 「규칙별 통계」에서 읽었습니다. 규칙별 통계에는 대상 버전 쪽 건수만 나옵니다. 그래서 v3를 대상 버전으로, v1을 비교 기준 버전으로 두고 한 번 돌려 204건을 얻었고, 둘을 맞바꿔 한 번 더 돌려 137건을 얻었습니다. API로 시작할 때는 includeRuleStats를 켜야 규칙별 통계가 나옵니다(콘솔의 「규칙별 통계 포함」은 기본으로 켜져 있습니다). 두 건수의 차이는 순증감입니다. 금액이 $500보다 큰 거래는 $300보다도 크므로, v1이 검토로 보낸 거래는 v3도 모두 검토로 보냅니다. 그래서 두 버전의 결정이 갈린 거래는 모두 v1이 승인하고 v3가 검토로 보낸 것이고, 차이 67건은 그 거래 수와 같습니다.

같은 화면의 「지표 요약」에 나오는 기본 지표는 규칙 하나에라도 걸린 입력 수입니다. 검토 규칙에 걸리지 않은 거래는 catch-all이 모두 승인하므로, 모든 거래가 규칙 하나에는 걸립니다. 그래서 기본 지표는 두 버전 모두 2,000건입니다. 두 버전이 67건을 다르게 결정하는데도, 이 지표로는 차이가 0건입니다. 시뮬레이션을 시작할 때 숫자 fact 하나를 「분석 대상 fact」로 골라 그 합계나 평균을 볼 수도 있지만, 여기서는 소용이 없습니다. 집계 방식인 SUM(합계)과 AVG(평균)는 숫자 값만 읽는데, reviewRoute에는 MANUAL_REVIEW나 APPROVE 같은 문자열 값이 들어 있습니다.

두 방법 모두 최대 레코드 수(다시 평가할 입력 수의 상한, maxRecords)를 따로 주지 않으면 저장된 호출을 최근 것부터 1,000건만 읽습니다. 그날의 최근 1,000건은 v1이 결정한 뒤의 1,000건이어서, 그대로 두면 긴급 조치 버전이 결정한 거래가 빠집니다. 그래서 모든 실행에 최대 레코드 수를 2,000 이상으로 줘서 2,000건을 모두 읽게 했습니다.

67건과 45건이 다른 까닭

세 버전의 결정이 다른 거래는 신규 계정의 $200 초과 $500 이하 거래뿐입니다. 신규 계정의 $200 이하 거래와 가입한 지 30일 이상 된 계정의 거래는 세 버전 모두 승인하고, 신규 계정의 $500 초과 거래는 모두 검토로 보냅니다. 신규 계정의 $200 초과 $500 이하 거래를 그날 결정한 버전과 금액으로 나누면 네 묶음입니다.

신규 계정의 거래건수저장된 결정v1이면v3이면
앞의 1,000건 중 $200 초과 $300 이하15건검토 (v2)승인승인
앞의 1,000건 중 $300 초과 $500 이하37건검토 (v2)승인검토
뒤의 1,000건 중 $200 초과 $300 이하29건승인 (v1)승인승인
뒤의 1,000건 중 $300 초과 $500 이하30건승인 (v1)승인검토

변경 영향 시뮬레이션은 「v1이면」과 「v3이면」 칸을 비교하고, 저장된 결정은 보지 않습니다. 두 칸이 다른 줄은 둘째와 넷째 줄이어서, 37건과 30건을 더한 67건이 승인에서 검토로 바뀝니다.

의사결정 재실행은 「저장된 결정」과 「v3이면」 칸을 비교합니다. 이번에는 첫째와 넷째 줄이 다릅니다. 넷째 줄 30건은 v1이 승인했지만 v3이면 검토로 갑니다. 첫째 줄 15건은 반대로, 긴급 조치 v2가 검토로 보냈지만 v3이면 승인됩니다. 둘째 줄 37건은 v2가 이미 검토로 보냈으니 v3에서도 검토 그대로입니다. 그래서 바뀌는 결정은 30건과 15건, 모두 45건입니다.

기준 금액을 $300까지 낮췄을 때 하루에 검토가 얼마나 느는지를 위험관리팀이 묻는다면, 67건을 보고합니다. v1과 v3만 비교해 얻은 수입니다. 그날 결제한 고객이 받은 결정 가운데 몇 건이 바뀌는지 묻는다면 45건을 보고합니다.

의사결정 재실행은 기간을 정해 돌릴 수도 있고(윈도우 재실행), 저장된 호출 하나를 traceId로 골라 돌릴 수도 있습니다(단건 재실행). traceId는 LexQ가 결정마다 응답에 실어 돌려주는 ID입니다. 첫째 줄 15건 가운데 가입한 지 14일 된 계정의 $290.09 거래를 단건 재실행하면, 저장된 결정을 어느 규칙이 냈는지까지 나옵니다.

{
  "traceId": "e16ac25f-d8fe-404e-8741-b69d729455c3",
  "decisionChanged": true,
  "baselineFired": [
    { "ruleName": "Manual review: account under 30 days, over $200", ... }
  ],
  "candidateFired": [
    { "ruleName": "Approve: everything else", ... }
  ],
  ...
}

콘솔에서 「기준 (저장된 결정)」으로 보이는 baselineFired는 그날 이 거래의 결정을 낸 규칙이고, v2에만 있는 $200 규칙입니다. 「후보 버전」으로 보이는 candidateFired는 v3에서 이 거래가 걸린 규칙이고, 남은 거래를 승인하는 catch-all입니다.

확인 방법 고르기

방법무엇과 비교하는지필요한 것얻는 것맞지 않는 경우
단위 테스트손으로 적은 기댓값직접 고른 입력경우마다 통과 또는 실패실제 호출 몇 건이 바뀌는지 알아야 할 때
드라이런(Dry Run)없음, 또는 같은 입력에 돌린 다른 버전 하나fact 한 묶음그 입력의 결정과, 그 결정을 낸 규칙건수가 필요할 때
변경 영향 시뮬레이션같은 입력에 다시 돌린, 직접 고른 비교 기준 버전저장된 호출의 날짜 범위, 파일, 요청 본문에 담은 입력 가운데 하나같은 입력에서 두 버전이 낸 결과의 비교바뀐 호출의 traceId가 필요하거나, 저장된 결정과 비교해야 할 때
의사결정 재실행저장된 결정플랜 보존 기간 안의 저장된 호출바뀐 호출 수, 액션마다 그 액션이 추가되거나 제거된 호출 수(콘솔의 「효과 영향 범위」. 이 글에서는 검토로 보내는 액션이 30건에 추가되고 15건에서 제거됨), 바뀐 호출의 traceId(100건까지)정책 그룹에 저장된 호출이 없거나, 후보 버전이 저장된 호출에 없는 fact를 읽을 때
A/B 테스트실제 트래픽에서 도는 다른 버전요청마다 버전을 가르는 값 context.trafficKey시간이 지나며 쌓이는 실제 결과아직 고객에게 테스트 버전을 보일 수 없을 때

변경 영향 시뮬레이션과 의사결정 재실행의 사용량은 레코드 수로 셉니다. 레코드 하나는 다시 평가한 입력 하나입니다. 월 한도는 기능마다 따로 있고 크기는 같아서, 둘 다 Free 100건, Growth 10,000건, Pro 100,000건입니다. 의사결정 재실행 가운데 사용량에 잡히는 것은 윈도우 재실행뿐이고, traceId 하나를 다시 돌리는 단건 재실행은 세지 않습니다. 이 글의 변경 영향 시뮬레이션과 윈도우 재실행은 한 번에 2,000건씩 사용량에 잡혔습니다.

의사결정 재실행이 맞지 않는 경우

  • 정책 그룹이 호출을 받은 적이 없을 때. 의사결정 재실행은 저장된 호출을 읽으므로 새 정책 그룹에는 돌릴 것이 없습니다. 이때는 파일로 변경 영향 시뮬레이션을 돌립니다. 배포 전에 규칙 변경을 시험하는 패턴이 그 방법을 다룹니다.
  • 후보 버전이 새 fact를 읽을 때. 저장된 호출 어디에도 그 값이 없습니다. 윈도우 재실행에서는 그 fact를 읽는 조건을 판정할 수 없어 규칙이 발동하지 않을 수 있고, 그렇게 생긴 차이도 바뀐 결정으로 셉니다. 단건 재실행은 같은 경우에 오류를 내고 멈춥니다. 그래서 윈도우 재실행 전에 한 건을 먼저 단건 재실행해 보면 이 문제를 미리 알 수 있습니다.
  • 고른 기간이 보존 기간을 벗어났을 때. 의사결정 재실행은 아직 조회할 수 있게 보관 중인 호출만 읽습니다. Free 30일, Growth 180일, Pro 365일입니다.
  • 작업이 기간 안의 호출을 다 읽지 못할 때. 최대 레코드 수(CLI의 --max-records, API의 maxRecords)를 주지 않으면 작업은 최근 호출 1,000건만 읽습니다. 최대 레코드 수는 50,000건까지 줄 수 있습니다. 기간 안의 호출이 50,000건을 넘으면 기간을 나눠 돌리고, 바뀐 결정 수를 더합니다. 이 글에서 고른 날을 기본값으로 돌리면 v1이 결정한 뒤의 1,000건만 읽으므로, v3 재실행은 그 가운데 신규 계정의 $300 초과 $500 이하 거래 30건만 바뀐 것으로 셉니다. 기간 안의 호출이 최대 레코드 수보다 많으면 결과의 capped가 true가 되고, 콘솔에는 「표본 상한에서 결과가 잘렸습니다」가 뜹니다.

윈도우 재실행은 여러 정책 그룹을 요청 하나로 돌리는 복합 실행(composite)도 읽지 않고, 몇 건을 건너뛰었는지는 결과에 나오지 않습니다. 이런 예외는 환불 한도를 예로 든 의사결정 재실행 패턴이 다룹니다.

변경 영향 시뮬레이션이 맞지 않는 경우

기준 금액을 $300까지 낮추면 검토가 몇 건 느는지 묻는 데는 변경 영향 시뮬레이션이 맞는 도구였습니다. 맞지 않는 경우는 둘입니다.

  • 저장된 결정과 비교해야 할 때. 변경 영향 시뮬레이션은 저장된 결정을 읽지 않습니다. 고른 비교 기준 버전을 같은 입력에 다시 돌릴 뿐입니다. 그래서 앞의 예에서는 그날 2,000건을 모두 v1이 결정했다고 놓고 셌고, 앞의 1,000건을 긴급 조치 버전이 결정했다는 것은 결과 어디에도 나오지 않습니다.
  • 바뀐 호출을 열어 봐야 할 때. 결과에 traceId가 없어서, 건수에 들어간 호출을 하나씩 열어 볼 수 없습니다. 의사결정 재실행은 바뀐 호출의 traceId를 100건까지 돌려주고, 단건 재실행은 저장된 결정 하나가 어느 규칙에서 나왔는지까지 보여 줍니다.

둘 다 필요 없는 경우

결제 기록에 거래마다 금액, 그때의 가입 일수, 그때 내린 결정이 이미 남아 있다고 해 봅시다. 규칙이 이 글처럼 금액과 가입 일수만 본다면, 두 기능 없이도 쿼리 하나로 두 질문에 답할 수 있습니다. 신규 계정의 $300 초과 $500 이하 거래를 세면 67건이 나오고, 기록된 결정이 v3가 냈을 결정과 다른 거래를 세면 45건이 나옵니다.

→ 규칙이 쿼리 하나로 다시 짜기 어려워지면, 다음 기준 금액 변경은 가지고 있는 거래 기록으로 배포 전에 시험해 보세요. lexq.io에서 무료로 시작하기

결정을 배포 파이프라인 밖으로 꺼낼 준비가 되셨나요?

신용카드 없이 무료로 시작하세요. fact를 보내면 결과와 근거를 돌려받습니다.

무료로 시작하기