행동적 무결성 마인드셋 — Distrust and Verify (Buisson Ch.2.3)

데이터를 의심하고 검증하는 실무 체크리스트와 변수 정제의 7가지 함정

Buisson (2021) Ch.2 의 마무리 절을 정리한다. 데이터 변수를 신뢰하기 전에 의심해야 할 7가지 패턴, 그 검증 절차, 분석 시작 전 체크리스트를 다룬다. AirCnC 의 CSAT·M6Spend 변수에 Distrust and Verify 를 적용하는 단계별 시연과 자동 변수 점검 코드를 포함한다.

Experimentation
Causal Inference
저자

Kwangmin Kim

공개

2026년 05월 08일

1 정의

정의: Behavioral Integrity Mindset (행동적 무결성 마인드셋)

데이터의 모든 변수에 대해 “이 변수가 측정한다고 주장하는 행동을 정말 측정하는가” 를 의심하고, 데이터·외부 소스·도메인 지식으로 검증하는 분석 자세 (Buisson, 2021, Ch.2).

핵심 명제는 단순하다: 유죄 증명 전까지 모든 변수를 의심한다.

직관 — 분석가의 두 자세

분석가는 두 자세 중 하나를 선택할 수 있다.

자세 A — 신뢰 우선 자세 B — 의심 우선
변수 이름·정의를 액면 그대로 받아들임 변수의 데이터 생성 과정·기록 시점 의심
빠른 분석 가능 분석 전 1~2주 변수 점검
결과 발표 후 “왜 그랬지” 발견 — burning down 결과 발표 시 confidence 높음
비즈니스 결정 후 부메랑 위험 비즈니스 결정의 ROI 안정

자세 A 는 단기 효율에서 이긴다. 자세 B 는 장기 신뢰에서 이긴다.

→ Behavioral Integrity Mindset = 자세 B 의 체화.

2 개념 및 원리 — 7 가지 변수 함정 패턴

Buisson 이 제시한 변수 함정을 7 패턴으로 일반화한다.

2.1 함정 1: Default Assignment (기본값 할당)

사례

“user 1234 의 marketing_consentTRUE 로 기록됨.”

함정 가능성: - 사용자가 아무 응답 안 했고 시스템이 default 로 TRUE 처리 - 사용자가 폼의 다른 필드만 채우고 동의 박스를 안 건드림 - “마케팅 수신 거부” 가 default 로 unchecked → 미응답 = 동의 처리

검증 방법:

  • 회원가입 폼의 default 상태 확인 — checked? unchecked?
  • 사용자 명시적 클릭 이벤트 로그 추적 — 진짜로 box 를 클릭했는가?
  • A/B 테스트로 default 변경 → 동의율 차이 측정 → default 효과 정량화
직관 — 장기 기증 사례 재방문

독일 (opt-in default) 과 오스트리아 (opt-out default) 의 87% 차이가 default 효과의 극단 사례. 비즈니스 데이터에서도 같은 메커니즘이 작동:

  • 쿠키 동의 default → “동의” 비율이 default 에 강하게 의존
  • 유료 멤버십 자동 갱신 default → 갱신율의 큰 부분이 default 효과

marketing_consent = TRUE 100% 가 default 효과면, “고객 관심” 으로 해석하면 잘못이다.

2.3 함정 3: Self-Reported (자기 보고)

사례

설문 “지난 주에 운동했나요?” → 60% “예”

함정:

  • Introspection illusion (자기 인식의 부정확)
  • Social desirability bias (사회적 바람직성 — 운동 안 한 게 부끄러워서 거짓 응답)
  • Recency bias (최근 1회만 기억하고 “한 주 내내 했다” 답변)

검증 방법:

  • 자기 보고 + 실제 행동 측정 (피트니스 트래커·gym 출입 데이터) 의 cross-check
  • 일관성 점검: 같은 질문 다른 표현으로 반복 → 응답 변동 측정
직관 — Nisbett & Wilson (1977) 의 introspection illusion

심리학 고전 연구. 참가자에게 동일한 양말 4개를 줄지어 놓고 “어느 것이 가장 좋은가” 묻는다. 73% 가 가장 오른쪽 양말 선택.

연구자: “왜 골랐어요?”

참가자들: “재질이 좋아서요”, “색이 좋아서요”, “두께가 좋아서요” — 다양한 합리화. 진짜 이유 (위치 효과) 를 답한 사람 0명.

→ 사람들은 자기 행동의 진짜 원인을 모른 채 자신감 있게 합리화한다. 자기 보고 데이터의 근본적 한계.

2.4 함정 4: External Purchased (외부 구매 데이터)

사례

벤더에서 데이터 구매. “이 사용자는 럭셔리 자동차에 관심” 같은 라벨.

함정:

  • 라벨의 정의 불명 (“관심” 의 측정 시점·방법 모름)
  • 데이터 신선도 — 1년 전 행동 기반?
  • 다른 ID 매칭 정확도 (벤더의 user 와 우리 user 가 같은가)
  • 벤더의 모형 가정·편향 미공개

검증 방법:

  • 벤더 라벨과 자체 행동 데이터의 일관성 점검
  • 작은 표본에서 라벨 검증 (manual review)
  • 라벨 사용 전 limitations 명시 (분석 보고서에)

2.5 함정 5: Intent vs Action (의도 vs 행동)

사례

변수 이름: purchased_subscription = TRUE

함정:

  • “buy now” 버튼 클릭 (Intent) 만 있고 결제 미완료
  • 결제 페이지에서 fail (카드 거부) 후 retry — 단일 trial 인지 multi 인지 모호
  • 환불 → 결국 매출 0 인데 purchased = TRUE 그대로

검증 방법:

  • “구매” 의 정의 정확히: 의도? 첫 결제? 환불 제외 최종? 1년 유지?
  • 의도와 실제 결제 사이 funnel 분석
  • 환불·취소 후 변수 업데이트 정책 점검

2.6 함정 6: Repeated Action (반복된 단일 행동)

사례

button_click_count = 4 — 사용자가 정말 4번 의도적 클릭?

함정:

  • 페이지가 새로고침 안 돼서 4번 연속 클릭 (단일 의도 → 4 행동)
  • 더블클릭 습관 (2 클릭이 의도 1)
  • 봇·매크로 (대량 자동 클릭)

검증 방법:

  • 클릭 간격 분석 — 0.5초 미만은 의도적 클릭 아님 가능
  • 클라이언트 idempotency 처리 — 짧은 시간 내 중복 무시
  • 사용자별 클릭 패턴 분포 검사 — outlier 식별

2.7 함정 7: Delayed Logging (지연 기록)

사례

event_timestamp = "2026-05-08 14:00" — 이 시점에 이벤트가 발생?

함정:

  • 사용자가 5월 1일에 행동 → 시스템이 5월 8일 batch processing → 5월 8일로 기록
  • 모바일 앱에서 오프라인 행동 → 온라인 복귀 후 동기화 시점 기록
  • 규제 (GDPR 등) 로 인해 일정 시점 이전 데이터는 “오늘” 로 일괄 기록

검증 방법:

  • 이벤트 발생 timestamp 와 기록 timestamp 분리 추적 (event_time vs ingest_time)
  • 시간대별 이벤트 분포 점검 — 비정상 spike 가 batch 의 흔적일 수 있음
  • 규제 정책으로 인한 변동 문서화
직관 — 시간 함정의 비즈니스 영향

다음 두 분석은 같은 데이터에서 정반대 결론을 낼 수 있다.

분석 A 분석 B
event_time 사용 ingest_time 사용
5월 1일에 행동 발생, 영향 전파 7일 5월 8일에 행동 발생, 영향 즉시
“캠페인이 즉시 효과” “캠페인이 7일 지연 효과”

같은 데이터, 다른 결론, 다른 비즈니스 의사결정. 시간 함정의 위험은 작지 않다.

3 Distrust and Verify 의 적용 — AirCnC 사례

AirCnC CSAT 와 M6Spend 변수 점검

비즈니스 문제 (반복):

“CSAT 가 향후 6개월 지출 (M6Spend) 에 미치는 효과는?”

3.1 CSAT 점검

함정 AirCnC 의 가능성 검증
Default 무응답 → 5점 default? 아닌 경우 점수 높게 가정? 응답률 + 무응답 처리 정책 확인
Fine print 평가 기준이 명확한가 (“전반적 만족도”의 정의) 설문 문구 점검
Self-reported 매우 강함 — CSAT 자체가 자기 보고 행동 (재예약·추천) 과 cross-check
External 외부 평가 사이트 (Trustpilot 등) 의 데이터 사용? 자체 vs 외부 일관성
Intent vs Action “만족했다고 답함” vs “정말 만족” 행동 일관성 (재예약) 으로 검증
Repeated 같은 사용자 여러 번 평가 시 평균? 최신? 처리 정책 명시
Delayed 평가 시점 (예약 직후 vs 일주일 후) 의 차이 timestamp 분리 추적

→ 가장 큰 위험: self-reported. CSAT 만으로 “만족” 결론 짓지 말고 행동 데이터와 통합한다.

3.2 M6Spend 점검

함정 가능성 검증
Default 미사용 사용자 → 0 처리? null? 처리 정책 확인
Intent vs Action “예약” vs “결제 완료” vs “취소 후 환불 제외” 정확한 정의
Repeated 한 예약을 여러 번 cancel/reorder dedup 로직
Delayed 환불은 30일 후 — M6 안에 환불된 건 포함? 시점별 정의

→ 두 변수의 정의를 명확히 하지 않으면 분석 결과의 의미가 흔들린다.

4 왜 필요한가 — 분석 신뢰의 비대칭성

분석 결과의 비대칭 위험
시나리오 결과
변수 점검 후 분석 → “효과 있음” 비즈니스 의사결정 + 결과 검증 → 신뢰 ↑
변수 점검 후 분석 → “효과 없음” 의사결정 회피 — 손실 없음
변수 점검 안 함 → “효과 있음” 의사결정 후 부메랑 — 큰 손실
변수 점검 안 함 → “효과 없음” 진짜 효과 놓침 — 기회 손실

→ 분석가의 “조심성” 은 점검 시간 대비 ROI 가 매우 높다. 점검 안 한 비용 이 분석 시간의 10~100배.

4.1 분석 시작 전 체크리스트

직관 — 7 함정 체크리스트

분석을 시작하기 전 변수마다 다음 7 질문을 묻는다.

[1] Default — 사용자 응답 없을 때 어떻게 처리?
[2] Fine print — 동의·기준이 명확한가?
[3] Self-reported — 자기 보고? 행동과 cross-check 됐나?
[4] External — 외부 데이터? 정의·신선도 검증?
[5] Intent vs Action — 정확히 어느 단계 측정?
[6] Repeated — 단일 의도의 반복인가, 다중 의도인가?
[7] Delayed — event_time vs ingest_time 차이?

각 질문에 명확한 답이 없으면 → 사용 전 도메인 전문가에게 확인.

5 응용 분야 — 도메인별 가장 자주 발생하는 함정

도메인 가장 흔한 함정 사례
이커머스 Intent vs Action 장바구니 추가 ≠ 구매
구독 Default + Delayed 자동 갱신, 환불 처리 시점
광고 External (벤더) “관심사” 라벨의 정의·신선도
HR Self-reported 평가 점수의 매니저 주관
헬스케어 Self-reported + Recency “지난주 운동” 응답
금융 Delayed 거래 처리 시점 vs 기록 시점
모바일 앱 Delayed (offline sync) 비행기 모드 후 동기화
직관 — 도메인별 first-line defense

각 도메인은 자기 함정에 익숙해져야 한다.

  • 이커머스 분석가: funnel 의 모든 단계를 분리 (impression / click / cart / checkout / pay / refund)
  • HR 분석가: 평가 점수와 객관 KPI (매출·코드 commits·고객 피드백) 의 매트릭스 점검
  • 모바일 앱 분석가: event_time + ingest_time + sync_time 3 timestamp 분리 기록

6 예시 — 변수 점검의 실무 다큐멘테이션

직관 — 점검 결과의 분석 보고서 통합

좋은 분석 보고서는 다음 섹션을 포함한다.

## 변수 정의 및 점검

### CSAT (Cognition·Emotion)
- 정의: 예약 후 발송된 설문에서 1~10 점 자기 보고
- 응답률: 22%
- 점검 결과:
  - [Self-reported] 행동 데이터 (재예약률) 와 상관 r=0.35 — 약한 일관성
  - [Delayed] 예약 후 7일 시점 평균 응답 — 즉시 응답 (1일 내) 대비 평균 0.4 점 낮음
- 한계 명시: 응답 편향 (만족한 사용자가 더 많이 응답) — 결과 해석 시 주의

이 섹션이 없으면 분석 결과의 신뢰도가 평가될 수 없다.

7 코드 예시 — Distrust and Verify 자동화 도구

import pandas as pd
import numpy as np
from typing import Dict, List

class VariableAuditor:
    """7 함정 자동 점검."""

    def __init__(self, df: pd.DataFrame):
        self.df = df
        self.audit_results: Dict[str, List[str]] = {}

    def audit(self, var: str) -> List[str]:
        flags = []

        # 1. Default 의심: 한 값이 90% 이상 차지
        value_counts = self.df[var].value_counts(normalize=True, dropna=False)
        if not value_counts.empty and value_counts.iloc[0] > 0.9:
            flags.append(
                f"[Default 의심] '{value_counts.index[0]}' 가 "
                f"{value_counts.iloc[0]:.0%} — default 처리 가능성"
            )

        # 2. NaN 의 처리 정책 점검
        nan_pct = self.df[var].isna().mean()
        if nan_pct > 0.05:
            flags.append(
                f"[Default 의심] NaN {nan_pct:.0%} — 결측 처리 정책 확인 필요"
            )

        # 3. Self-reported (수치형) — outlier 분포
        if pd.api.types.is_numeric_dtype(self.df[var]):
            std = self.df[var].std()
            mean = self.df[var].mean()
            if std > 0:
                outlier_pct = (
                    (self.df[var] - mean).abs() > 3 * std
                ).mean()
                if outlier_pct > 0.01:
                    flags.append(
                        f"[Outlier] {outlier_pct:.1%} 가 3σ 밖 — "
                        f"이상 응답 또는 측정 오류 의심"
                    )

        # 4. Repeated — 동일 user 의 다중 record
        if "user_id" in self.df.columns:
            dup_users = (
                self.df.groupby("user_id")[var].count() > 1
            ).sum()
            if dup_users > 0:
                flags.append(
                    f"[Repeated] {dup_users} user 가 다중 record — "
                    f"단일 의도의 반복인지 점검"
                )

        # 5. Delayed — timestamp 컬럼이 있는 경우
        ts_cols = [c for c in self.df.columns if "time" in c.lower() or "date" in c.lower()]
        if len(ts_cols) >= 2:
            flags.append(
                f"[Delayed 점검 필요] timestamp 컬럼 {len(ts_cols)} 개 — "
                f"event_time vs ingest_time 차이 점검"
            )

        self.audit_results[var] = flags
        return flags

    def report(self) -> pd.DataFrame:
        """점검 결과를 DataFrame 으로."""
        rows = []
        for var, flags in self.audit_results.items():
            for flag in flags:
                rows.append({"변수": var, "발견": flag})
        return pd.DataFrame(rows)


# 가상 AirCnC 데이터 점검
np.random.seed(42)
df = pd.DataFrame({
    "user_id": np.random.choice(range(100), 500),
    "csat_score": np.random.choice([5, 6, 7, 8, 9, 10], 500, p=[0.05, 0.05, 0.1, 0.2, 0.3, 0.3]),
    "marketing_consent": np.random.choice(
        [True, False], 500, p=[0.95, 0.05]  # 95% TRUE — default 의심
    ),
    "event_time": pd.to_datetime("2026-05-01") + pd.to_timedelta(np.random.randint(0, 7*24*60, 500), unit="m"),
    "ingest_time": pd.to_datetime("2026-05-08"),  # 모두 같은 시점 — delayed 의심
})

auditor = VariableAuditor(df)
for var in ["csat_score", "marketing_consent"]:
    print(f"\n=== {var} 점검 ===")
    flags = auditor.audit(var)
    for f in flags:
        print(f"  - {f}")

print("\n\n=== 요약 ===")
print(auditor.report())
직관 — 자동 점검의 한계

이 도구는 휴리스틱 1차 필터다. 실제 함정은 비즈니스 컨텍스트를 알아야 발견된다.

  • “marketing_consent 가 95% TRUE” 가 자동 발견됨 — 그런데 이게 진짜 default 효과인지, 아니면 진짜로 95% 가 동의했는지는 컨텍스트 (회원가입 폼 디자인) 점검 필요.
  • “event_time 과 ingest_time 분리 컬럼 존재” 자동 발견 — 두 시점 차이의 비즈니스 의미는 도메인 전문가에게.

→ 자동 점검 + 사람의 도메인 지식이 결합되어야 한다. 자동 도구만으로는 60% 정도, 사람만으로는 40% 정도 — 둘 결합 시 90% 이상 함정 발견 가능.

8 코드 예시 — AirCnC CSAT vs M6Spend 의 변수 일관성 점검

def consistency_check(df, csat_col, action_col):
    """CSAT (자기 보고) vs Action 의 일관성 점검."""
    # 분기별로 CSAT 점수와 행동 비율 비교
    csat_quartile = pd.qcut(df[csat_col], q=4, labels=["Q1", "Q2", "Q3", "Q4"])
    action_by_quartile = df.groupby(csat_quartile, observed=True)[action_col].mean()

    print(f"CSAT 분위별 {action_col} 비율:")
    print(action_by_quartile)

    # CSAT 가 높을수록 action 비율이 단조 증가해야 일관
    if action_by_quartile.is_monotonic_increasing:
        print("→ 단조 증가 — CSAT 와 행동 일관성 양호")
    else:
        print("→ 단조 증가 아님 — CSAT 자기 보고와 실제 행동 불일치 가능")
        print("  추가 점검 필요: introspection illusion? 응답 편향?")


# 가상 데이터로 점검
np.random.seed(42)
df_check = pd.DataFrame({
    "user_id": range(1000),
    "csat_score": np.random.choice([5, 6, 7, 8, 9, 10], 1000),
    "actual_rebooked": np.random.binomial(1, 0.4, 1000),
})
# CSAT 와 rebooked 약하게 상관 만들기
df_check["actual_rebooked"] = (
    np.random.binomial(1, 0.2 + 0.05 * (df_check["csat_score"] - 5), 1000)
)

consistency_check(df_check, "csat_score", "actual_rebooked")
직관 — 일관성 점검이 보여주는 것

만약 결과가:

  • 단조 증가 (CSAT Q1: 0.20 → Q4: 0.45) → 좋은 신호. CSAT 가 행동을 예측.
  • 비단조 (CSAT Q1: 0.30, Q2: 0.25, Q3: 0.40, Q4: 0.35) → CSAT 가 행동을 잘 예측 못함.

비단조라면 가능한 해석:

  • Self-reported 함정: 만족 점수가 진짜 만족과 안 맞음
  • 다른 confounder: CSAT 와 행동 사이 제3의 변수가 있음
  • 비선형 관계: 매우 만족한 사람이 오히려 다른 옵션 탐색 (충성도와 다양성 추구의 trade-off)

각 가설을 별도로 검증해야 한다.

9 관련 주제

9.1 Ch.2 의 형제 글

9.2 후속 챕터

9.4 카테고리 진입점

Subscribe

Enjoy this blog? Get notified of new posts by email: