메인 콘텐츠로 건너뛰기

드라이런이란?

**드라이런(Dry Run)**은 단일 입력 팩트가 특정 정책 버전의 규칙에 대해 어떻게 평가되는지 테스트합니다 — 부작용 없이. 실행 로그가 기록되지 않고, 연동도 호출되지 않으며, 할당량도 차감되지 않습니다. 규칙에 대한 단위 테스트라고 보면 됩니다.
드라이런 vs 변경 영향 시뮬레이션: 드라이런은 빠른 검증을 위해 하나의 입력을 테스트합니다. 변경 영향 시뮬레이션수백~수천 개의 과거 입력에 대해 버전을 테스트하고 베이스라인과 비교합니다. 개발 중에는 드라이런을, 배포 전에는 변경 영향 시뮬레이션을 사용하세요.

언제 사용하나요

  • 규칙을 만들거나 수정한 직후 — 의도한 입력에 매칭되는지 확인
  • DRAFT 버전을 발행하기 전 — 안전망
  • 예상과 다른 실행 결과를 디버깅할 때 — 시나리오 재현
  • 두 버전을 동일 입력으로 비교하고 싶을 때 (Dry Run Compare)

드라이런 실행

콘솔

정책 버전 페이지 → Dry Run 탭 → 입력 팩트 입력 → Run 클릭.

CLI

lexq analytics dry-run \
  --version-id <versionId> \
  --debug \
  --mock \
  --json '{"facts": {"payment_amount": 150000, "customer_tier": "VIP"}}'
플래그설명
--debug실행 트레이스와 의사결정 트레이스 포함
--mock외부 연동 호출 (웹훅, 알림 등) 모킹

API

curl -X POST https://api.lexq.io/api/v1/partners/analytics/dry-run/versions/{versionId} \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "facts": { "payment_amount": 150000, "customer_tier": "VIP" },
    "includeDebugInfo": true,
    "mockExternalCalls": true
  }'

응답

{
  "result": "SUCCESS",
  "data": {
    "inputFacts": {
      "payment_amount": 150000,
      "customer_tier": "VIP"
    },
    "mutatedFacts": {
      "payment_amount": 135000
    },
    "generatedVariables": {
      "payment_amount__delta": -15000,
      "discount_applied": true
    },
    "executionTraces": [ ... ],
    "decisionTraces": [ ... ],
    "latencyMs": 12,
    "versionNo": 3
  }
}

상태 3 층위 (State Layers)

data 객체는 상태를 세 층위로 명시적으로 분리해 노출합니다 — 한 덩어리로 합쳐서 돌려주지 않습니다.
  • inputFacts — 요청에서 받은 입력 팩트 (정규화 후).
  • mutatedFacts — 규칙 액션에 의해 값이 변경된 팩트만.
  • generatedVariables — 규칙 액션이 새로 생성한 변수 (입력에 없었던 것). 액션별 변화 키도 포함됩니다 — {refVar}__delta(MUTATE_FACT의 부호 포함 변화량)와 {targetVar}__delta(INCREMENT_FACT의 0 이상 증분량).
합본이 필요하면 언제든 { ...inputFacts, ...mutatedFacts, ...generatedVariables }로 재구성할 수 있습니다. 분리해서 돌려주는 이유는 감사·재현·디버그가 결정적이어야 하기 때문입니다 — 어떤 값이 어디서 왔는지가 항상 명확합니다.

실행 트레이스 (Execution Traces)

엔진이 평가한 모든 규칙은 실행 트레이스 하나를 남깁니다. 트레이스는 어떤 매칭 표현식이 검증됐는지, 규칙이 본 입력 팩트가 무엇이었는지, 어떤 액션이 생성됐는지를 기록합니다:
{
  "tenantId": "acme-corp",
  "policyGroupId": "01f2b274-...",
  "policyVersionId": "a6062090-...",
  "ruleId": "3b16ced1-...",
  "ruleName": "VIP 10% 할인",
  "executedAt": "2026-04-27T09:44:10Z",
  "matched": true,
  "matchExpression": "(customer_tier == 'VIP') && (payment_amount >= 100000)",
  "inputFacts": {
    "customer_tier": "VIP",
    "payment_amount": 150000
  },
  "generatedActions": [
    {
      "type": "MUTATE_FACT",
      "parameters": { "refVar": "payment_amount", "operator": "SUB", "method": "PERCENTAGE", "rate": 10 }
    }
  ]
}
4개의 식별자(tenantId, policyGroupId, policyVersionId, ruleId)가 모든 트레이스를 자급 식별 가능하게 만듭니다 — 외부 조회 없이도 어느 tenant·그룹·버전·규칙에서 나온 트레이스인지 알 수 있습니다.

의사결정 트레이스 (Decision Traces)

실행 트레이스가 무엇을 평가했는가를 기록한다면, 의사결정 트레이스는 무엇이 선택됐는가를 기록합니다. 모든 규칙은 decisionTraces에 최종 status와 그 사유 코드와 함께 등장합니다:
{
  "ruleId": "3b16ced1-...",
  "ruleName": "VIP 10% 할인",
  "policyGroupId": "01f2b274-...",
  "policyVersionId": "a6062090-...",
  "status": "SELECTED",
  "reasonCode": "FINAL_WINNER",
  "reasonDetail": null
}
status의미
SELECTED규칙이 선택되어 액션이 적용됨
NO_MATCH룰의 condition 이 false 로 평가됨
NOT_SELECTED경쟁 이전 사전 필터링 (예: effective date — 향후 박을 영역)
BLOCKEDmutex 또는 activation 그룹 경쟁에서 탈락 (reasonCode 가 영역 명시)
ERROR액션 실행 중 오류 발생
reasonCode등장 시점
FINAL_WINNER최종 승자로 선택됨
CONDITION_MISMATCH입력이 조건을 만족하지 않음
EFFECTIVE_DATE_INVALID규칙의 유효 기간을 벗어남 (향후 박을 영역)
MUTEX_PRIORITY_LOSTEXCLUSIVE mutex (limit=1) — 다른 룰이 더 높은 우선순위 또는 점수
MUTEX_LIMIT_REACHEDMAX_N mutex (limit>1) — mutexLimit 이 더 높은 우선순위 룰들로 이미 채워짐
GROUP_PRIORITY_LOSTEXCLUSIVE activation 그룹 (limit=1) — 다른 룰이 priority 로 승리
GROUP_LIMIT_REACHEDMAX_N activation 그룹 (limit>1) — executionLimit 이 이미 채워짐
ACTION_ERROR액션 실행 중 오류
ENGINE_ERROR평가 도중 엔진 내부 오류
reasonDetail은 nullable입니다 — 엔진이 추가로 노출할 컨텍스트가 있을 때 사람이 읽을 수 있는 메시지가 들어갑니다 (예: 도달한 구체적 한도 값).
status × reasonCode 전체 매핑은 정책 룰의 Decision Trace 섹션을 참조하세요.

요구사항 확인

드라이런 전에 버전이 기대하는 팩트를 확인하세요:
lexq analytics requirements --group-id <gid> --version-id <vid>

드라이런 비교

두 버전이 동일한 입력에 대해 어떻게 평가하는지 나란히 비교:
lexq analytics dry-run-compare --json '{
  "versionIdA": "<베이스라인-버전>",
  "versionIdB": "<후보-버전>",
  "facts": { "payment_amount": 200000, "customer_tier": "GOLD" }
}'

다음 단계

변경 영향 시뮬레이션

수백 개의 과거 입력에 대해 한 번에 테스트합니다.

배포

검증된 버전을 프로덕션에 배포합니다.