Playball Logo

Command Palette

Search for a command to run...

목차 열기

03. Runtime 판단 파이프라인

Runtime은 요청 하나가 들어올 때마다 4단 파이프라인을 순서대로 거칩니다. 각 단계는 명확히 분리된 책임을 가집니다.

4단 구조

1. 점수 계산 (Guard)
    ↓  위험 점수·등급 갱신
2. 증거 축적 (Analyzer)
    ↓  규칙 기반 파생 신호·세션 카운터 갱신
3. 정책 적용 (Planner)
    ↓  우선순위 규칙에 따라 Action 결정
4. 실행·상태 (Orchestrator)
    ↓  HTTP 응답 구성 + 상태 전이 확정

각 단계의 역할

단계입력출력책임
1. Guard행동 지표 + 외부 인간 검증 점수위험 점수, Tier숫자 계산만
2. Analyzer이벤트 스트림파생 신호, 세션 카운터규칙 기반 패턴 감지
3. Planner점수 + 증거Action 결정우선순위 규칙 적용
4. OrchestratorAction 결정HTTP 응답 + 상태 저장흐름 검증, 챌린지 강제, 차단 기록

왜 네 단으로 나누었나

이유설명
책임 분리한 단으로 합치면 버그 원인 추적 어렵고 단위 테스트 난해
단계별 감사로그가 단계별로 찍혀 "어느 단계가 잘못 판단했나" 즉시 확인
상태 쓰기 분리각 단계가 쓸 수 있는 상태 필드가 엄격히 분리 → 오염 위험 최소화

상태 쓰기 권한 분리

각 단계가 접근 가능한 세션 상태 필드가 엄격히 분리됩니다.

단계쓸 수 있는 필드
GuardriskScore, defenseTier, lastStepRisk, lastGuardTsMs
AnalyzerchallengeFailCount, seatTakenStreak, holdFailStreak, probationUntilMs, challengeHaltUntilMs
OrchestratorflowState, s3Passed, s3PassedAtMs, lastDecisionAction
Planner(없음 — 읽기만)

효과 — 한 컴포넌트의 버그가 다른 컴포넌트의 상태를 오염시킬 위험 최소화.


1. Guard — 점수 계산

자세한 내용은 04-risk-scoring 참조.

주요 입력

입력출처
마우스 행동 feature 5가지클라이언트 telemetry
외부 인간 검증 점수Turnstile 등 제3자 서비스
이전 점수세션 상태 (Redis)

주요 처리

  1. 5 feature를 0~1 범위로 정규화
  2. 가중 합으로 내부 점수 계산
  3. 외부 점수와 결합
  4. EWMA로 누적
  5. 비활성 감쇠·챌린지 통과 감쇠 적용
  6. Tier 결정

중복 제거 (Dedup)

같은 traceId·eventType·ts bucket의 중복 처리로 인한 점수 중복 상승을 방지합니다.

항목기본값
중복 제거 TTL600초
traceId + eventType + ts_bucket

2. Analyzer — 증거 축적

자세한 내용은 06-analyzer-signals 참조.

주요 입력

입력출처
최신 위험 점수·TierGuard 결과
이벤트 타입현재 요청 정보
세션 카운터Redis 버킷 카운터

주요 처리

이벤트 타입처리
고가치 클릭rapid_high_value_click 카운터 증가
조회성 APIexcessive_read_scanning 카운터 증가
챌린지 실패s3_fail_burst 카운터 증가 + challengeFailCount 증가
챌린지 통과challengeFailCount 0 리셋 + probationUntilMs 설정
좌석 hold 성공seatTakenStreak 증가, holdFailStreak 리셋
좌석 hold 실패holdFailStreak 증가, seatTakenStreak 리셋

파생 신호 3종

신호조건기본 임계
rapid_high_value_click1.5초 윈도우 안에 3회 이상 고가치 클릭3회
excessive_read_scanning2초 윈도우 안에 조회 API 8회 이상8회
s3_fail_burst60초 안에 챌린지 2회 이상 실패2회

3. Planner — 정책 적용

우선순위 규칙

① 이미 차단 중 (Redis block key 존재) → BLOCK
② 종료 상태 (flowState == SX) → NONE (무개입)
③ 구역/좌석 단계(S4/S5)에서 s3_passed == false → REQUIRE_S3 + HTTP 428
④ 결제 단계 (S6) → NONE (새 마찰 없음)
⑤ 그 외 → Tier-Action matrix 적용

Tier → Action 기본 매트릭스

TierAction
T0NONE
T1THROTTLE
T2THROTTLE
T3BLOCK

보조 신호 — Analyzer의 scan_pressure·hv_click_pressure·rule_hits는 Action을 직접 변경하지 않고 reason suffix(이유 코드)에 맥락 추가.

왜 이 순서인가

순위이유
block 우선terminal 상태이므로 한 번 차단되면 state 유지
terminal 우선이미 끝난 세션은 판단 불필요
S3 gate 강제S4/S5에서 챌린지 미통과는 tier와 무관하게 게이트 적용
S6 완화결제 단계에서 새 friction은 구매 전환 방해

4. Orchestrator — 실행·상태

주요 책임

책임내용
Terminal-first blockblock 상태이거나 plan이 BLOCK이면 HTTP 403 즉시 반환
전이 검증허용된 FlowState 전이인지 확인 (아니면 HTTP 409)
S3 fixed gatePlanner와 별개로 한 번 더 강제 (defense-in-depth)
상태 커밋세션 상태를 Redis에 저장
응답 구성HTTP 응답 + x-defense-* 헤더

FlowState 허용 전이

canonical D0 FlowState: S0 → S1 → S2 → S3 → S4 → S5 → S6 → SX

전이허용 여부
순차 전이
S6 → S5✅ (결제 중도 취소)
S0~S5 → SX✅ (abort/timeout)
그 외❌ HTTP 409 INVALID_TRANSITION

SX 도달 후 — 새 replan 적용되지 않음. terminal no-replan.

S3 fixed gate 강제

Planner가 REQUIRE_S3를 판단 안 했더라도, Orchestrator가 S4/S5 요청에 대해 s3_passed를 직접 확인합니다.

요청이 S4 또는 S5를 요구?
    ├─ YES + s3_passed = true → 통과
    ├─ YES + s3_passed = false → HTTP 428 CHALLENGE_REQUIRED (강제)
    └─ NO → Planner 결정대로

defense-in-depth — 두 계층이 독립적으로 gate를 강제하므로 한쪽 버그도 방어력 유지.

응답 종류

ActionHTTP 응답헤더
NONE / THROTTLE200 allowx-defense-throttle-ms (THROTTLE만)
REQUIRE_S3428 CHALLENGE_REQUIREDx-defense-reason=CHALLENGE_REQUIRED
BLOCK403 BLOCKEDx-defense-reason=BLOCKED

동시성 제어

범위역할
프로세스 로컬 RLock같은 세션한 프로세스 내 동시 평가 직렬화
Redis 분산 락같은 세션여러 프로세스 간 직렬화 (best-effort)

Redis 락 timeout 시 로컬 락만으로 진행하지만 경고 로그.

감사 이벤트

단계이벤트
GuardDEF_GUARD_SCORED
AnalyzerDEF_ANALYZER_EVIDENCE_UPDATED
PlannerDEF_PLAN_COMPUTED
OrchestratorDEF_ORCH_EXECUTED, DEF_BLOCK_DECIDED, DEF_BLOCK_ENFORCED, DEF_THROTTLE_APPLIED

자세한 내용은 11-observability 참조.

참조