> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lexq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 드라이런

> 단일 입력을 정책 버전에 대해 부작용 없이 테스트합니다. 발행 전에 규칙 동작을 검증하세요.

## 드라이런이란?

\*\*드라이런(Dry Run)\*\*은 단일 입력 팩트가 특정 정책 버전의 규칙에 대해 어떻게 평가되는지 테스트합니다 — 부작용 없이. 실행 로그가 기록되지 않고, 연동도 호출되지 않으며, 할당량도 차감되지 않습니다. 규칙에 대한 **단위 테스트**라고 보면 됩니다.

<Info>
  **드라이런 vs 변경 영향 시뮬레이션 vs 의사결정 재실행:** 드라이런은 빠른 검증을 위해 **직접 입력한 하나의 값**을 테스트합니다. [변경 영향 시뮬레이션](/ko/guides/simulation)은 **수백\~수천 개**의 과거 입력에 대해 버전을 테스트하고 베이스라인과 비교합니다. [의사결정 재실행(Decision Replay)](/ko/guides/decision-replay)은 **실제 과거 결정 하나**를 후보 버전으로 재평가해 결과를 diff합니다. 개발 중에는 드라이런을, 배포 전 회귀 검증에는 시뮬레이션을, "바로 이 프로덕션 결정은 어떻게 됐을까"라는 질문에는 재실행을 사용하세요.
</Info>

## 언제 사용하나요

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

## 드라이런 실행

### 콘솔

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

### CLI

```bash theme={null}
lexq analytics dry-run \
  --version-id <versionId> \
  --debug \
  --mock \
  --json '{"facts": {"payment_amount": 150000, "customer_tier": "VIP"}}'
```

| 플래그       | 설명                     |
| --------- | ---------------------- |
| `--debug` | 실행 트레이스와 의사결정 트레이스 포함  |
| `--mock`  | 외부 연동 호출 (웹훅, 알림 등) 모킹 |

### API

```bash theme={null}
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
  }'
```

## 응답

```json theme={null}
{
  "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)

엔진이 평가한 모든 규칙은 실행 트레이스 하나를 남깁니다. 트레이스는 어떤 매칭 표현식이 검증됐는지, 규칙이 본 입력 팩트가 무엇이었는지, 어떤 액션이 생성됐는지를 기록합니다:

```json theme={null}
{
  "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와 그 사유 코드와 함께 등장합니다:

```json theme={null}
{
  "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 — 향후 박을 영역)           |
| `BLOCKED`      | mutex 또는 activation 그룹 경쟁에서 탈락 (`reasonCode` 가 영역 명시) |
| `ERROR`        | 액션 실행 중 오류 발생                                         |

| `reasonCode`             | 등장 시점                                                        |
| ------------------------ | ------------------------------------------------------------ |
| `FINAL_WINNER`           | 최종 승자로 선택됨                                                   |
| `CONDITION_MISMATCH`     | 입력이 조건을 만족하지 않음                                              |
| `EFFECTIVE_DATE_INVALID` | 규칙의 유효 기간을 벗어남 (향후 박을 영역)                                    |
| `MUTEX_PRIORITY_LOST`    | EXCLUSIVE mutex (limit=1) — 다른 룰이 더 높은 우선순위 또는 점수            |
| `MUTEX_LIMIT_REACHED`    | MAX\_N mutex (limit>1) — `mutexLimit` 이 더 높은 우선순위 룰들로 이미 채워짐 |
| `GROUP_PRIORITY_LOST`    | EXCLUSIVE activation 그룹 (limit=1) — 다른 룰이 priority 로 승리      |
| `GROUP_LIMIT_REACHED`    | MAX\_N activation 그룹 (limit>1) — `executionLimit` 이 이미 채워짐   |
| `ACTION_ERROR`           | 액션 실행 중 오류                                                   |
| `ENGINE_ERROR`           | 평가 도중 엔진 내부 오류                                               |

`reasonDetail`은 nullable입니다 — 엔진이 추가로 노출할 컨텍스트가 있을 때 사람이 읽을 수 있는 메시지가 들어갑니다 (예: 도달한 구체적 한도 값).

<Info>
  status × reasonCode 전체 매핑은 [정책 룰의 Decision Trace](/ko/guides/policy-rules#decision-trace) 섹션을 참조하세요.
</Info>

## 요구사항 확인

드라이런 전에 버전이 기대하는 팩트를 확인하세요:

```bash theme={null}
lexq analytics requirements --group-id <gid> --version-id <vid>
```

## 드라이런 비교

**두 버전**이 동일한 입력에 대해 어떻게 평가하는지 나란히 비교:

```bash theme={null}
lexq analytics dry-run-compare --json '{
  "versionIdA": "<베이스라인-버전>",
  "versionIdB": "<후보-버전>",
  "facts": { "payment_amount": 200000, "customer_tier": "GOLD" }
}'
```

## 다음 단계

<CardGroup cols={2}>
  <Card title="변경 영향 시뮬레이션" icon="chart-bar" href="/ko/guides/simulation">
    수백 개의 과거 입력에 대해 한 번에 테스트합니다.
  </Card>

  <Card title="배포" icon="rocket" href="/ko/guides/deployments">
    검증된 버전을 프로덕션에 배포합니다.
  </Card>
</CardGroup>
